Files
SmartScale_MODBUS_Woer/app/scale-utils.ts
T

98 lines
2.8 KiB
TypeScript

export type WeightUnit = "kg" | "g" | "lb";
const UNIT_FACTORS: Record<WeightUnit, number> = {
kg: 1,
g: 1000,
lb: 2.20462262,
};
/**
* 将内部统一保存的千克值转换为界面单位,并固定显示两位小数。
*/
export function formatWeight(valueKg: number, unit: WeightUnit) {
const safeValue = Number.isFinite(valueKg) ? Math.max(0, valueKg) : 0;
return (safeValue * UNIT_FACTORS[unit]).toFixed(2);
}
/**
* 把 ISO 时间转换为终端文件名使用的本地时间格式:YYYYMMDDHHmmss。
*/
export function createFileTimestamp(iso: string) {
const date = new Date(iso);
const pad = (value: number) => value < 10 ? `0${value}` : String(value);
return [
date.getFullYear(),
pad(date.getMonth() + 1),
pad(date.getDate()),
pad(date.getHours()),
pad(date.getMinutes()),
pad(date.getSeconds()),
].join("");
}
/**
* 兼容电子秤服务常见的消息格式:
* - 纯数字,例如 `2.49`
* - JSON,例如 `{"weight": 2490, "unit": "g"}`
* - JSON 千克字段,例如 `{"weightKg": 2.49}`
*
* 返回值统一为千克;无效值、负数或不支持的单位返回 null。
*/
export function parseScaleMessage(message: string): number | null {
const trimmed = message.trim();
if (!trimmed) return null;
let payload: unknown = trimmed;
try {
payload = JSON.parse(trimmed);
} catch {
// 非 JSON 消息继续按纯数字处理。
}
if (typeof payload === "number" || typeof payload === "string") {
const value = Number(payload);
return Number.isFinite(value) && value >= 0 ? value : null;
}
if (!payload || typeof payload !== "object") return null;
const record = payload as Record<string, unknown>;
const rawValue = record.weightKg ?? record.weight ?? record.value;
const value = Number(rawValue);
if (!Number.isFinite(value) || value < 0) return null;
const unit = String(record.unit ?? (record.weightKg != null ? "kg" : "kg")).toLowerCase();
if (unit === "kg") return value;
if (unit === "g") return value / 1000;
if (unit === "lb" || unit === "lbs") return value / UNIT_FACTORS.lb;
return null;
}
export type StabilityResult = {
stable: boolean;
progress: number;
};
/**
* 根据连续采样窗口判断重量是否稳定。
* 至少需要 6 个有效样本,样本极差小于 8 克时判定稳定。
*/
export function evaluateStability(samples: number[]): StabilityResult {
// 不使用 Array.prototype.at,确保兼容终端内置的 Chrome 52 WebView。
const latest = samples.length ? samples[samples.length - 1] : 0;
if (samples.length < 6 || latest < 0.01) {
return {
stable: false,
progress: Math.min(85, samples.length * 14),
};
}
const spread = Math.max(...samples) - Math.min(...samples);
const stable = spread < 0.008;
return {
stable,
progress: stable ? 100 : Math.max(25, 90 - spread * 500),
};
}