6868<script lang="ts" setup>
6969import { ref , reactive , nextTick , onMounted , onUnmounted , computed } from ' vue' ;
7070import { useAppStore } from ' @/store' ;
71- import { eeprom_write , eeprom_reboot , eeprom_init , hexReverseStringToUint8Array , stringToUint8Array } from ' @/utils/serial.js' ;
71+ import { eeprom_write , eeprom_reboot , eeprom_init , hexReverseStringToUint8Array , stringToUint8Array , shared_read , shared_write } from ' @/utils/serial.js' ;
7272import useLoading from ' @/hooks/loading' ;
7373import QRCode from ' qrcode' ;
7474import { Input , Select } from ' tdesign-vue-next' ;
7575import { Message } from ' @arco-design/web-vue' ;
7676
77+ // Must match ESP32 mapping in src/app/driver/eeprom.cpp
78+ // EEPROM 0x1E200..0x20000 -> shared offset 0x10000..
79+ const UVE5_SHARED_TLE_BASE = 0x10000 ;
80+
7781const { loading, setLoading } = useLoading (true );
7882
7983const appStore = useAppStore ();
@@ -142,6 +146,48 @@ const state: {
142146 satsData: []
143147})
144148
149+ // Auto-detect UVE5 shared-partition mode.
150+ // For UVE5 we always store TLE in the ESP32 shared partition (mapped from logical EEPROM 0x1E200..0x20000).
151+ // This is derived from the handshake firmware version at connect time.
152+ const isUveDevice = computed (() => appStore .isUve5 );
153+
154+ const uint8ToAscii = (bytes : Uint8Array ) => {
155+ // Keep it simple: the payload is ASCII. Replace NUL with spaces for readability.
156+ const text = new TextDecoder (' utf-8' , { fatal: false }).decode (bytes );
157+ return text .replace (/ \u0000 / g , ' ' );
158+ }
159+
160+ const readSharedBytes = async (start : number , length : number ) => {
161+ const out = new Uint8Array (length );
162+ for (let i = 0 ; i < length ; i += 0x80 ) {
163+ const chunkLen = Math .min (0x80 , length - i );
164+ const chunk = await shared_read (appStore .connectPort , start + i , chunkLen );
165+ out .set (chunk .subarray (0 , chunkLen ), i );
166+ }
167+ return out ;
168+ }
169+
170+ const validateTleRecordBytes = (record : Uint8Array ) => {
171+ if (! record || record .length < 160 ) return { ok: false , reason: ' readback too short' };
172+ const nameBytes = record .subarray (0 , 9 );
173+ const line1Bytes = record .subarray (9 , 9 + 69 );
174+ const line2Bytes = record .subarray (9 + 69 , 9 + 69 + 69 );
175+
176+ const allSame = (bytes : Uint8Array , value : number ) => bytes .every ((b ) => b === value );
177+ if (allSame (record , 0x00 ) || allSame (record , 0xff )) return { ok: false , reason: ' all 0x00/0xFF' };
178+
179+ const name = uint8ToAscii (nameBytes ).trim ();
180+ const line1 = uint8ToAscii (line1Bytes );
181+ const line2 = uint8ToAscii (line2Bytes );
182+
183+ // Minimal structure check; firmware parser is stricter.
184+ const looksLikeTle = line1 .startsWith (' 1 ' ) && line2 .startsWith (' 2 ' );
185+ if (! looksLikeTle ) {
186+ return { ok: false , reason: ` unexpected TLE header (name='${name }') ` };
187+ }
188+ return { ok: true , reason: ' ' , name , line1 , line2 };
189+ }
190+
145191const editableCellState = (cellParams ) => {
146192 // 第一行不允许编辑
147193 const { row } = cellParams ;
@@ -432,6 +478,20 @@ const restoreRange = async (start: any = 0, uint8Array: any) => {
432478 state .status = state .status + " 写入进度:100.0%<br/>" ;
433479}
434480
481+ const restoreRangeShared = async (start : number , uint8Array : Uint8Array ) => {
482+ await eeprom_init (appStore .connectPort );
483+ for (let i = 0 ; i < uint8Array .length ; i += 0x40 ) {
484+ const chunk = uint8Array .slice (i , i + 0x40 );
485+ await shared_write (appStore .connectPort , start + i , chunk , chunk .length );
486+ state .status = state .status + " 写入进度:" + ((i / uint8Array .length ) * 100 ).toFixed (1 ) + " %<br/>" ;
487+ nextTick (() => {
488+ const textarea = document ?.getElementById (' statusArea' );
489+ if (textarea ) textarea .scrollTop = textarea ?.scrollHeight ;
490+ })
491+ }
492+ state .status = state .status + " 写入进度:100.0%<br/>" ;
493+ }
494+
435495const calculateChecksum = (line : string ) => {
436496 const chars = line .replace (/ \d $ / , ' ' ); // 移除末位校验和
437497 let sum = 0 ;
@@ -450,7 +510,7 @@ const validateChecksum = (line: string) => {
450510
451511const writeIt = async () => {
452512 if (appStore .connectState != true ) { alert (sessionStorage .getItem (' noticeConnectK5' )); return ; };
453- if ( appStore .configuration ?.sat2 != true ){
513+ if ( ! isUveDevice . value && appStore .configuration ?.sat2 != true ) {
454514 alert (sessionStorage .getItem (' noticeVersionNoSupport' ));
455515 return ;
456516 }
@@ -501,8 +561,33 @@ const writeIt = async () => {
501561 duration: 10 * 1000 ,
502562 });
503563 }
504- await restoreRange (0x1E200 , payload )
505- await syncTime ()
564+ if (isUveDevice .value ) {
565+ state .status += ` 检测到 UVE 设备:将写入 shared@0x${UVE5_SHARED_TLE_BASE .toString (16 )}<br/> ` ;
566+ await restoreRangeShared (UVE5_SHARED_TLE_BASE , payload )
567+
568+ // Read-back verify first record so users immediately know whether the shared write actually landed.
569+ try {
570+ const first = await readSharedBytes (UVE5_SHARED_TLE_BASE , 160 );
571+ const chk = validateTleRecordBytes (first );
572+ if (! chk .ok ) {
573+ setLoading (false )
574+ return Message .error ({
575+ content: ` 写入完成但读回校验失败:${chk .reason }。这通常表示 shared 分区不可用(分区表没刷/label 不匹配)或固件未支持 shared 读写命令。\n\n 如果你是用 app0_main 的快速上传刷机:请确保 partitions.bin 也被写入(本仓库 scripts/upload_app0.py 已改为同时刷 partitions.bin@0x8000)。或者完整刷一次 factory/分区表后再重试。 ` ,
576+ duration: 12 * 1000 ,
577+ });
578+ }
579+ state .status += ` 读回校验通过:${chk .name }<br/> ` ;
580+ } catch (e : any ) {
581+ setLoading (false )
582+ return Message .error ({
583+ content: ` 写入完成但读回校验异常:${e ?.message ?? e }。可能固件不支持 shared_read/shared_write。 ` ,
584+ duration: 12 * 1000 ,
585+ });
586+ }
587+ } else {
588+ state .status += ` 非 UVE 设备:将写入 EEPROM@0x1E200<br/> ` ;
589+ await restoreRange (0x1E200 , payload )
590+ }
506591 await eeprom_reboot (appStore .connectPort );
507592 setLoading (false )
508593}
0 commit comments