Files
pikasTech-HWLAB/internal/device-pod/fake-data.mjs
T
2026-05-28 01:39:03 +08:00

314 lines
9.9 KiB
JavaScript

export const DEVICE_POD_SERVICE_ID = "hwlab-device-pod";
export const DEVICE_POD_CONTRACT_VERSION = "device-pod-fake-v1";
export const DEFAULT_DEVICE_POD_ID = "device-pod-71-freq";
const DEFAULT_OBSERVED_AT = "2026-05-27T08:00:00.000Z";
export function buildDevicePodList(options = {}) {
const pod = buildFakeDevicePod(options);
return {
serviceId: DEVICE_POD_SERVICE_ID,
contractVersion: DEVICE_POD_CONTRACT_VERSION,
status: "ok",
observedAt: observedAt(options),
devicePods: [devicePodListItem(pod)],
selectedDevicePodId: pod.devicePodId,
source: sourceSummary(options)
};
}
export function buildDevicePodStatus(devicePodId = DEFAULT_DEVICE_POD_ID, options = {}) {
const pod = buildFakeDevicePod({ ...options, devicePodId });
return {
serviceId: DEVICE_POD_SERVICE_ID,
contractVersion: DEVICE_POD_CONTRACT_VERSION,
status: pod.status,
observedAt: observedAt(options),
source: sourceSummary(options),
devicePod: pod,
summary: {
devicePodId: pod.devicePodId,
targetId: pod.target.targetId,
status: pod.status,
statusLabel: "Fake 数据在线",
freshness: pod.freshness,
profileHash: pod.profile.profileHash,
blocker: pod.blocker,
latestEvent: buildDevicePodEvents(pod.devicePodId, { ...options, limit: 1 }).events[0] ?? null
}
};
}
export function buildDevicePodEvents(devicePodId = DEFAULT_DEVICE_POD_ID, options = {}) {
const pod = buildFakeDevicePod({ ...options, devicePodId });
const limit = normalizeLimit(options.limit, 80);
const events = fakeEvents(pod).slice(0, limit);
return {
serviceId: DEVICE_POD_SERVICE_ID,
contractVersion: DEVICE_POD_CONTRACT_VERSION,
status: "ok",
observedAt: observedAt(options),
devicePodId: pod.devicePodId,
targetId: pod.target.targetId,
source: sourceSummary(options),
events,
lines: events.map(formatDevicePodEventLine),
truncation: {
limit,
returned: events.length,
truncated: fakeEvents(pod).length > limit
}
};
}
export function buildDevicePodChipId(devicePodId = DEFAULT_DEVICE_POD_ID, options = {}) {
const pod = buildFakeDevicePod({ ...options, devicePodId });
return {
serviceId: DEVICE_POD_SERVICE_ID,
contractVersion: DEVICE_POD_CONTRACT_VERSION,
status: "ok",
observedAt: observedAt(options),
devicePodId: pod.devicePodId,
targetId: pod.target.targetId,
source: sourceSummary(options),
profileHash: pod.profile.profileHash,
interface: "debug-probe",
intent: "debug.probe.chip-id",
chipId: pod.debugInterface.chipId,
probe: pod.debugInterface.probe,
freshness: pod.debugInterface.freshness,
blocker: null,
refs: fakeRefs("debug-chip-id")
};
}
export function buildDevicePodUartStatus(devicePodId = DEFAULT_DEVICE_POD_ID, options = {}) {
const pod = buildFakeDevicePod({ ...options, devicePodId });
return {
serviceId: DEVICE_POD_SERVICE_ID,
contractVersion: DEVICE_POD_CONTRACT_VERSION,
status: "ok",
observedAt: observedAt(options),
devicePodId: pod.devicePodId,
targetId: pod.target.targetId,
source: sourceSummary(options),
profileHash: pod.profile.profileHash,
interface: "io-probe",
intent: "io.uart.status",
resourcePath: "/uart/1",
uart: pod.ioInterface.uart1,
freshness: pod.ioInterface.uart1.freshness,
blocker: null,
refs: fakeRefs("io-uart-status")
};
}
export function buildDevicePodUartTail(devicePodId = DEFAULT_DEVICE_POD_ID, options = {}) {
const pod = buildFakeDevicePod({ ...options, devicePodId });
const maxBytes = normalizeLimit(options.maxBytes, 12000);
const raw = pod.ioInterface.uart1.tail;
const truncated = Buffer.byteLength(raw, "utf8") > maxBytes;
const text = truncated ? raw.slice(0, maxBytes) : raw;
return {
serviceId: DEVICE_POD_SERVICE_ID,
contractVersion: DEVICE_POD_CONTRACT_VERSION,
status: "ok",
observedAt: observedAt(options),
devicePodId: pod.devicePodId,
targetId: pod.target.targetId,
source: sourceSummary(options),
profileHash: pod.profile.profileHash,
interface: "io-probe",
intent: "io.uart.fetch",
resourcePath: "/uart/1",
text,
bytes: Buffer.byteLength(text, "utf8"),
truncation: {
maxBytes,
truncated
},
freshness: pod.ioInterface.uart1.freshness,
blocker: null,
refs: fakeRefs("io-uart-tail")
};
}
export function buildDevicePodRestPayload(pathname, searchParams = new URLSearchParams(), options = {}) {
const cleanPath = String(pathname ?? "/").replace(/\/+$/u, "") || "/";
if (cleanPath === "/v1/device-pods") return buildDevicePodList(options);
const prefix = "/v1/device-pods/";
if (!cleanPath.startsWith(prefix)) return null;
const suffix = cleanPath.slice(prefix.length);
const [devicePodId, ...segments] = suffix.split("/").map((item) => decodeURIComponent(item)).filter(Boolean);
const route = segments.join("/");
if (!devicePodId) return null;
if (route === "status") return buildDevicePodStatus(devicePodId, options);
if (route === "events") return buildDevicePodEvents(devicePodId, { ...options, limit: searchParams.get("limit") });
if (route === "debug-probe/chip-id") return buildDevicePodChipId(devicePodId, options);
if (route === "io-probe/uart/1") return buildDevicePodUartStatus(devicePodId, options);
if (route === "io-probe/uart/1/tail") return buildDevicePodUartTail(devicePodId, { ...options, maxBytes: searchParams.get("maxBytes") });
return null;
}
export function formatDevicePodEventLine(event) {
const clock = event.ts ? event.ts.slice(11, 19) : "00:00:00";
const refs = event.refs ?? {};
return [
clock,
event.scope?.toUpperCase() ?? "SYSTEM",
event.status ?? "ok",
event.intent ?? "event",
event.summary ?? "",
refs.traceId ? `trace=${refs.traceId}` : null,
refs.operationId ? `op=${refs.operationId}` : null,
refs.evidenceId ? `evidence=${refs.evidenceId}` : null,
event.blocker?.code ? `blocker=${event.blocker.code}` : null
].filter(Boolean).join(" ");
}
function buildFakeDevicePod(options = {}) {
const now = observedAt(options);
const devicePodId = stringOr(options.devicePodId, DEFAULT_DEVICE_POD_ID);
return {
devicePodId,
target: {
targetId: "stm32f103-minsys-01",
name: "STM32F103 最小系统",
lock: {
status: "free",
owner: null
}
},
status: "ok",
freshness: {
observedAt: now,
ageMs: 8000,
stale: false
},
profile: {
profilePath: `.device-pod/${devicePodId}.json`,
profileHash: "sha256:8ad3f0b75a6f1f2b.fake-device-pod-profile",
loadedAt: now,
validation: "ok"
},
projectWorkspace: {
status: "ok",
workspaceRoot: "f:/work/hwlab-device/71-freq",
projectFile: "firmware/71-freq.uvprojx",
buildProfile: "debug",
latestBuild: {
status: "ok",
artifact: "build/71-freq.hex",
completedAt: "2026-05-27T07:59:42.000Z",
refs: fakeRefs("workspace-build")
}
},
debugInterface: {
status: "ok",
probe: "DAPLink CMSIS-DAP",
chipId: "STM32F103C8T6",
latestJob: {
status: "ok",
intent: "debug.probe.chip-id",
completedAt: now,
refs: fakeRefs("debug-chip-id")
},
freshness: {
observedAt: now,
ageMs: 8000,
stale: false
}
},
ioInterface: {
status: "ok",
uart1: {
status: "ok",
baudRate: 115200,
freshness: {
observedAt: now,
ageMs: 6000,
stale: false
},
tail: [
"[boot] device-pod fake UART ready",
"[app] freq=1000Hz duty=50% mode=debug",
"[io] uart/1 sample window ok"
].join("\n")
}
},
blocker: null,
refs: fakeRefs("device-pod-status")
};
}
function devicePodListItem(pod) {
return {
devicePodId: pod.devicePodId,
targetId: pod.target.targetId,
status: pod.status,
freshness: pod.freshness,
profileHash: pod.profile.profileHash,
summary: "Fake Device Pod 数据源,仅用于前端右侧看板迁移。",
blocker: pod.blocker
};
}
function fakeEvents(pod) {
return [
event(pod, "debug", "debug.probe.chip-id", "ok", "chip-id STM32F103C8T6", "debug-chip-id"),
event(pod, "io", "io.uart.fetch", "ok", "uart/1 tail bytes=86 truncated=false freshness=6s", "io-uart-tail"),
event(pod, "workspace", "workspace.build", "ok", "artifact build/71-freq.hex", "workspace-build"),
event(pod, "profile", "profile.sync", "ok", `profile=${pod.profile.profileHash}`, "profile-sync"),
event(pod, "system", "device-pod.fake-data", "ok", "fake device-pod service ready", "device-pod-status")
];
}
function event(pod, scope, intent, status, summary, refKey) {
return {
eventId: `evt_${refKey}`,
devicePodId: pod.devicePodId,
targetId: pod.target.targetId,
ts: observedAt(),
level: status === "ok" ? "info" : "warn",
scope,
intent,
status,
summary,
freshness: pod.freshness,
blocker: null,
refs: fakeRefs(refKey)
};
}
function fakeRefs(key) {
return {
operationId: `op_devicepod_${key}`,
traceId: `trc_devicepod_${key}`,
evidenceId: `evd_devicepod_${key}`,
jobId: `job_devicepod_${key}`
};
}
function sourceSummary(options = {}) {
return {
kind: options.sourceKind ?? "FAKE",
serviceId: DEVICE_POD_SERVICE_ID,
note: "当前 device-pod 服务只提供前端迁移用 fake 数据,不连接真实硬件。"
};
}
function observedAt(options = {}) {
return stringOr(options.observedAt, new Date().toISOString() || DEFAULT_OBSERVED_AT);
}
function normalizeLimit(value, fallback) {
const parsed = Number.parseInt(value ?? "", 10);
if (!Number.isInteger(parsed) || parsed <= 0) return fallback;
return Math.min(parsed, 1000);
}
function stringOr(value, fallback) {
const text = value === undefined || value === null ? "" : String(value).trim();
return text || fallback;
}