feat: 串口连接参数存储
This commit is contained in:
29
src/lib/stores/serial.options.ts
Normal file
29
src/lib/stores/serial.options.ts
Normal 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
47
src/lib/stores/serial.ts
Normal 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]);
|
||||||
|
}
|
||||||
60
src/lib/stores/serial.ui.ts
Normal file
60
src/lib/stores/serial.ui.ts
Normal 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;
|
||||||
6
src/lib/stores/utils/typeGuard.ts
Normal file
6
src/lib/stores/utils/typeGuard.ts
Normal 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]);
|
||||||
|
}
|
||||||
36
src/lib/stores/utils/uiBridge.ts
Normal file
36
src/lib/stores/utils/uiBridge.ts
Normal 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();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
22
src/lib/stores/utils/useLocalStorage.ts
Normal file
22
src/lib/stores/utils/useLocalStorage.ts
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user