mirror of
https://github.com/silenty4ng/k5web
synced 2025-04-19 08:39:56 +00:00
update
This commit is contained in:
parent
46e7bff599
commit
9a805143c6
5 changed files with 184 additions and 53 deletions
src
components/navbar
router/routes/modules
utils
views
|
@ -81,7 +81,7 @@
|
|||
import useLocale from '@/hooks/locale';
|
||||
import useUser from '@/hooks/user';
|
||||
import Menu from '@/components/menu/index.vue';
|
||||
import { connect, disconnect, sendPacket, readPacket } from '@/utils/serial.js';
|
||||
import { connect, disconnect, eeprom_init } from '@/utils/serial.js';
|
||||
const drivers = import.meta.glob('@/drivers/*.json', { eager: true });
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
@ -168,15 +168,6 @@
|
|||
appStore.updateSettings({ connectState: false, connectPort: null, firmwareVersion: "" });
|
||||
}
|
||||
}
|
||||
|
||||
const eeprom_init = async (port: any) => {
|
||||
const packet = new Uint8Array([0x14, 0x05, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff]);
|
||||
await sendPacket(port, packet);
|
||||
const response = await readPacket(port, 0x15);
|
||||
const decoder = new TextDecoder();
|
||||
const version = new Uint8Array(response.slice(4, 4+16));
|
||||
return decoder.decode(version.slice(0, version.indexOf(0)));
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
@ -12,22 +12,22 @@ const LIST: AppRouteRecordRaw = {
|
|||
order: 2,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'search-table', // The midline path complies with SEO specifications
|
||||
name: 'SearchTable',
|
||||
component: () => import('@/views/list/search-table/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.list.searchTable',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
// {
|
||||
// path: 'search-table', // The midline path complies with SEO specifications
|
||||
// name: 'SearchTable',
|
||||
// component: () => import('@/views/list/search-table/index.vue'),
|
||||
// meta: {
|
||||
// locale: 'menu.list.searchTable',
|
||||
// requiresAuth: true,
|
||||
// roles: ['*'],
|
||||
// },
|
||||
// },
|
||||
{
|
||||
path: 'card',
|
||||
name: 'Card',
|
||||
component: () => import('@/views/list/card/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.list.cardList',
|
||||
locale: '备份/还原',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
|
@ -35,4 +35,4 @@ const LIST: AppRouteRecordRaw = {
|
|||
],
|
||||
};
|
||||
|
||||
// export default LIST;
|
||||
export default LIST;
|
||||
|
|
|
@ -240,4 +240,69 @@ function uint8ArrayToHexString(uint8Array) {
|
|||
.join('');
|
||||
}
|
||||
|
||||
export { connect, disconnect, sendPacket, readPacket, uint8ArrayToHexString }
|
||||
|
||||
async function eeprom_init(port) {
|
||||
const packet = new Uint8Array([0x14, 0x05, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff]);
|
||||
await sendPacket(port, packet);
|
||||
const response = await readPacket(port, 0x15);
|
||||
const decoder = new TextDecoder();
|
||||
const version = new Uint8Array(response.slice(4, 4+16));
|
||||
return decoder.decode(version.slice(0, version.indexOf(0)));
|
||||
}
|
||||
|
||||
async function eeprom_read(port, address, size = 0x80) {
|
||||
// packet format: uint16 ID, uint16 length, uint16 address, uint8 size, uint8 padding, uint32 timestamp
|
||||
// size can be up to 0x80 bytes
|
||||
const address_msb = (address & 0xff00) >> 8;
|
||||
const address_lsb = address & 0xff;
|
||||
|
||||
const address_msb_h = (address & 0xff000000) >> 24;
|
||||
const address_lsb_h = (address & 0xff0000) >> 16;
|
||||
|
||||
const packet = new Uint8Array([0x2b, 0x05, 0x08, 0x00, address_lsb_h, address_msb_h, 0x80, 0x00, 0xff, 0xff, 0xff, 0xff, address_lsb, address_msb]);
|
||||
|
||||
await sendPacket(port, packet);
|
||||
const response = await readPacket(port, 0x1c);
|
||||
|
||||
// reply format: uint16 ID, uint16 length, uint16 offset, uint8 size, uint8 padding, uint8[128] data
|
||||
// extract data from response using size
|
||||
if (response[6] !== size) {
|
||||
throw ('eeprom read reply has wrong size.');
|
||||
}
|
||||
const data = new Uint8Array(response.slice(8));
|
||||
return data;
|
||||
}
|
||||
|
||||
async function eeprom_write(port, address, input, size = 0x80){
|
||||
// packet format: uint16 ID, uint16 length, uint16 address, uint8 size, uint8 padding, uint32 timestamp
|
||||
// size can be up to 0x80 bytes
|
||||
const address_msb = (address & 0xff00) >> 8;
|
||||
const address_lsb = address & 0xff;
|
||||
|
||||
const address_msb_h = (address & 0xff000000) >> 24;
|
||||
const address_lsb_h = (address & 0xff0000) >> 16;
|
||||
|
||||
const packet = new Uint8Array([0x38, 0x05, 0x1c, 0x00, address_lsb_h, address_msb_h, 0x82, 0x00, 0xff, 0xff, 0xff, 0xff, address_lsb, address_msb]);
|
||||
const mergedArray = new Uint8Array(packet.length + input.length);
|
||||
mergedArray.set(packet);
|
||||
mergedArray.set(input, packet.length);
|
||||
|
||||
await sendPacket(port, mergedArray);
|
||||
const response = await readPacket(port, 0x1e);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function eeprom_reboot(port) {
|
||||
// packet format: uint16 ID
|
||||
const packet = new Uint8Array([0xdd, 0x05]);
|
||||
await sendPacket(port, packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function check_eeprom(port){
|
||||
alert('TODO')
|
||||
return null;
|
||||
}
|
||||
|
||||
export { connect, disconnect, sendPacket, readPacket, uint8ArrayToHexString, eeprom_init, eeprom_read, eeprom_reboot, check_eeprom, eeprom_write }
|
|
@ -8,15 +8,25 @@
|
|||
<a-divider class="panel-border" />
|
||||
<a-card v-show="appStore.connectState" :style="{ width: '360px', marginTop: '2em', marginBottom: '2em' }" title="手台信息">
|
||||
当前固件版本:{{ appStore.firmwareVersion }} <br />
|
||||
匹配写频配置:{{ appStore.configuration?.name }}
|
||||
匹配写频配置:{{ appStore.configuration?.name }} <br />
|
||||
存储大小:{{ state.eepromSize }} <a-button size="mini" type="primary" @click="checkEeprom">检测</a-button>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue';
|
||||
import { useAppStore } from '@/store';
|
||||
import { eeprom_read } from '@/utils/serial.js';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const state = reactive({
|
||||
eepromSize: "点击检测按钮检测"
|
||||
})
|
||||
|
||||
const checkEeprom = async () => {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
@ -1,33 +1,22 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<Breadcrumb :items="['menu.list', 'menu.list.cardList']" />
|
||||
<Breadcrumb :items="['小工具', '备份/还原']" />
|
||||
<a-row :gutter="20" align="stretch">
|
||||
<a-col :span="24">
|
||||
<a-card class="general-card" :title="$t('menu.list.cardList')">
|
||||
<a-row justify="space-between">
|
||||
<a-col :span="24">
|
||||
<a-tabs :default-active-tab="1" type="rounded">
|
||||
<a-tab-pane key="1" :title="$t('cardList.tab.title.all')">
|
||||
<QualityInspection />
|
||||
<TheService />
|
||||
<RulesPreset />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :title="$t('cardList.tab.title.content')">
|
||||
<QualityInspection />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" :title="$t('cardList.tab.title.service')">
|
||||
<TheService />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="4" :title="$t('cardList.tab.title.preset')">
|
||||
<RulesPreset />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-col>
|
||||
<a-input-search
|
||||
:placeholder="$t('cardList.searchInput.placeholder')"
|
||||
style="width: 240px; position: absolute; top: 60px; right: 20px"
|
||||
/>
|
||||
</a-row>
|
||||
<a-card class="general-card" title="备份/还原">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="backup">备份</a-button>
|
||||
<a-button @click="restore">恢复</a-button>
|
||||
<a-select v-model="state.eepromType" :style="{width:'320px'}" placeholder="选择EEPROM大小">
|
||||
<a-option value="1">8KB(64Kbit)</a-option>
|
||||
<a-option value="2">128KB(1Mbit)</a-option>
|
||||
<a-option value="3">256KB(2Mbit)</a-option>
|
||||
<a-option value="4">512KB(4Mbit)</a-option>
|
||||
</a-select>
|
||||
<a-button type="text" @click="checkEeprom">自动检测</a-button>
|
||||
</a-space>
|
||||
<a-divider />
|
||||
<div id="statusArea" style="height: 20em; background-color: azure; color: silver; overflow: auto; padding: 20px" v-html="state.status"></div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
@ -35,9 +24,85 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import QualityInspection from './components/quality-inspection.vue';
|
||||
import TheService from './components/the-service.vue';
|
||||
import RulesPreset from './components/rules-preset.vue';
|
||||
import { reactive, nextTick } from 'vue';
|
||||
import { useAppStore } from '@/store';
|
||||
import { check_eeprom, eeprom_read, eeprom_write, eeprom_reboot } from '@/utils/serial.js';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const state = reactive({
|
||||
status: "点击备份按钮将生成EEPROM备份文件<br/><br/>",
|
||||
eepromType: ""
|
||||
})
|
||||
|
||||
const checkEeprom = async () => {
|
||||
await check_eeprom();
|
||||
}
|
||||
|
||||
const backup = async() => {
|
||||
let _max = 0x2000;
|
||||
switch(state.eepromType){
|
||||
case "1":
|
||||
_max = 0x2000;
|
||||
break;
|
||||
case "2":
|
||||
_max = 0x20000;
|
||||
break;
|
||||
case "3":
|
||||
_max = 0x40000;
|
||||
break;
|
||||
case "4":
|
||||
_max = 0x80000;
|
||||
break;
|
||||
default:
|
||||
_max = 0x2000;
|
||||
}
|
||||
let rawEEPROM = new Uint8Array(_max);
|
||||
for (let i = 0; i < _max; i += 0x80) {
|
||||
const data = await eeprom_read(appStore.connectPort, i);
|
||||
rawEEPROM.set(data, i);
|
||||
state.status = state.status + "备份进度:" + ((i / _max) * 100).toFixed(1) + "%<br/>";
|
||||
nextTick(()=>{
|
||||
const textarea = document?.getElementById('statusArea');
|
||||
if(textarea)textarea.scrollTop = textarea?.scrollHeight;
|
||||
})
|
||||
}
|
||||
state.status = state.status + "备份进度:100%<br/>";
|
||||
nextTick(()=>{
|
||||
const textarea = document?.getElementById('statusArea');
|
||||
if(textarea)textarea.scrollTop = textarea?.scrollHeight;
|
||||
})
|
||||
console.log(rawEEPROM);
|
||||
const blob = new Blob([rawEEPROM], { type: 'application/octet-stream' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = new Date() + '_backup.bin';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
const restore = async() => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.onchange = async() => {
|
||||
const blob = new Blob([input.files[0]], {type: 'application/octet-stream' });
|
||||
const rawEEPROM = new Uint8Array(await blob.arrayBuffer());
|
||||
for (let i = 0; i < input.files[0].size; i += 0x80) {
|
||||
await eeprom_write(appStore.connectPort, i, rawEEPROM.slice(i, i + 0x80));
|
||||
state.status = state.status + "恢复进度:" + ((i / input.files[0].size) * 100).toFixed(1) + "%<br/>";
|
||||
nextTick(()=>{
|
||||
const textarea = document?.getElementById('statusArea');
|
||||
if(textarea)textarea.scrollTop = textarea?.scrollHeight;
|
||||
})
|
||||
}
|
||||
state.status = state.status + "恢复进度:100%<br/>";
|
||||
eeprom_reboot(appStore.connectPort);
|
||||
};
|
||||
input.click();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
|
Loading…
Add table
Reference in a new issue