feat: 串口连接参数存储

This commit is contained in:
2025-12-24 15:02:00 +08:00
parent b841b2641c
commit 03a56ec480
6 changed files with 200 additions and 0 deletions

View File

@ -0,0 +1,29 @@
import { derived, get } from 'svelte/store';
import { baudRate, dataBits, stopBits, parity, flowControl } from './serial';
export function getSerialOptions(): SerialOptions {
return {
baudRate: get(baudRate),
dataBits: get(dataBits) as 7 | 8,
stopBits: get(stopBits) as 1 | 2,
parity: get(parity),
flowControl: get(flowControl),
};
}
export const serialOptions = derived(
[baudRate, dataBits, stopBits, parity, flowControl],
([
$baudRate,
$dataBits,
$stopBits,
$parity,
$flowControl,
]): SerialOptions => ({
baudRate: $baudRate,
dataBits: $dataBits as 7 | 8,
stopBits: $stopBits as 1 | 2,
parity: $parity,
flowControl: $flowControl,
})
);

47
src/lib/stores/serial.ts Normal file
View File

@ -0,0 +1,47 @@
import { derived, writable, get } from 'svelte/store';
import { useLocalStorage } from './utils/useLocalStorage';
export type ReadType = 'hex' | 'ascii' | 'dec';
const defaultBaudRatesList = [9600, 19200, 38400, 57600, 115200];
/* ---------------- 派生状态 ---------------- */
export const baudRate = useLocalStorage<number>('baudRate', 9600);
export const baudRateList = useLocalStorage<number[]>(
'baudRateList',
defaultBaudRatesList
);
export const dataBits = writable<number>(8);
export const stopBits = writable<number>(1);
export const parity = writable<ParityType>('none');
export const flowControl = writable<FlowControlType>('none');
export const readType = useLocalStorage<ReadType>('readType', 'hex');
export const sendType = useLocalStorage<ReadType>('sendType', 'hex');
export const hasDecTypes = useLocalStorage<boolean>('hasDecTypes', false);
/* ---------------- 派生状态 ---------------- */
export const recordTypes = derived(hasDecTypes, ($hasDecTypes) =>
$hasDecTypes
? (['hex', 'ascii', 'dec'] satisfies ReadType[])
: (['hex', 'ascii'] satisfies ReadType[])
);
/* ---------------- Actions ---------------- */
export function nextReadType() {
const types = get(recordTypes);
const current = get(readType);
const index = types.indexOf(current);
readType.set(types[(index + 1) % types.length]);
}
export function nextSendType() {
const types = get(recordTypes);
const current = get(sendType);
const index = types.indexOf(current);
sendType.set(types[(index + 1) % types.length]);
}

View File

@ -0,0 +1,60 @@
import { derived } from 'svelte/store';
import { baudRate, baudRateList, dataBits, parity, stopBits } from './serial';
import { createUIBridge } from './utils/uiBridge';
import { isOneOf } from './utils/typeGuard';
export const baudRateItems = derived(baudRateList, ($list) =>
$list.map((rate) => ({
value: String(rate),
label: `${rate}`,
}))
);
const baudRateBridge = createUIBridge(
baudRate,
(v) => String(v),
(v) => {
const n = Number(v);
return Number.isFinite(n) ? n : undefined;
}
);
export const baudRateValue = baudRateBridge.store;
const dataBitsBridge = createUIBridge(
dataBits,
(v) => String(v),
(v) => {
const n = Number(v);
return n;
}
);
export const dataBitsValue = dataBitsBridge.store;
const parityBridge = createUIBridge(
parity,
(v) => String(v),
(v) => {
const PARITY_VALUES = ['none', 'odd', 'even'] as const;
if (!isOneOf(v, PARITY_VALUES)) {
console.warn(`Invalid parity value: ${v}`);
return undefined;
}
const n = v;
return n;
}
);
export const parityValue = parityBridge.store;
const stopBitsBridge = createUIBridge(
stopBits,
(v) => String(v),
(v) => {
const n = Number(v);
return n;
}
);
export const stopBitsValue = stopBitsBridge.store;

View File

@ -0,0 +1,6 @@
export function isOneOf<T extends readonly unknown[]>(
value: unknown,
allowed: T
): value is T[number] {
return allowed.includes(value as T[number]);
}

View File

@ -0,0 +1,36 @@
import { get, writable, type Writable } from 'svelte/store';
export function createUIBridge<T, U>(
source: Writable<T>,
toUI: (value: T) => U,
fromUI: (value: U) => T | undefined
) {
const ui = writable<U>(toUI(get(source)));
let syncing = false;
const unsubSource = source.subscribe((v) => {
if (syncing) return;
syncing = true;
ui.set(toUI(v));
syncing = false;
});
const unsubUI = ui.subscribe((v) => {
if (syncing) return;
const next = fromUI(v);
if (next === undefined) return;
syncing = true;
source.set(next);
syncing = false;
});
return {
store: ui,
destroy() {
unsubSource();
unsubUI();
},
};
}

View File

@ -0,0 +1,22 @@
import { writable, type Writable } from 'svelte/store';
export function useLocalStorage<T>(key: string, initialValue: T): Writable<T> {
let startValue = initialValue;
if (typeof localStorage !== 'undefined') {
const stored = localStorage.getItem(key);
if (stored !== null) {
startValue = JSON.parse(stored);
}
}
const store = writable<T>(startValue);
store.subscribe((value) => {
if (typeof localStorage !== 'undefined') {
localStorage.setItem(key, JSON.stringify(value));
}
});
return store;
}