113 lines
3.7 KiB
JavaScript
113 lines
3.7 KiB
JavaScript
const fs = require("node:fs");
|
||
const path = require("node:path");
|
||
const readline = require("node:readline/promises");
|
||
const { callServer, downloadFromUrl } = require("./server-client");
|
||
const { publishConfig } = require("./b-admin");
|
||
const { plotCsv } = require("./plot-csv");
|
||
const { plotJson } = require("./plot-json");
|
||
|
||
const API_URL = process.env.REINLOOP_API_URL;
|
||
const B_ADMIN_TOKEN = process.env.B_ADMIN_TOKEN;
|
||
const DEVICE_ID = process.env.REINLOOP_DEVICE_ID;
|
||
const REVIEW_MODE = process.env.REVIEW_MODE || "manual";
|
||
const POLL_INTERVAL_MS = Number(process.env.POLL_INTERVAL_MS || 3000);
|
||
|
||
let polling = false;
|
||
|
||
async function reviewIdentification(message, imagePath) {
|
||
if (REVIEW_MODE !== "manual") {
|
||
console.log(`已生成 ${imagePath};REVIEW_MODE=${REVIEW_MODE},跳过人工评审`);
|
||
return;
|
||
}
|
||
|
||
const prompt = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||
try {
|
||
console.log(`请查看辨识图像: ${imagePath}`);
|
||
let decision;
|
||
while (decision !== 0 && decision !== 1) {
|
||
const answer = (await prompt.question("参数是否通过?输入 1=通过,0=不通过: ")).trim();
|
||
if (answer === "0" || answer === "1") decision = Number(answer);
|
||
}
|
||
|
||
if (decision === 0) {
|
||
const configPath = (await prompt.question("请输入新的函数2参数 JSON 路径: ")).trim();
|
||
const config = JSON.parse(await fs.promises.readFile(path.resolve(configPath), "utf8"));
|
||
await publishConfig("identification", config.parameters || config, {
|
||
apiUrl: API_URL,
|
||
adminToken: B_ADMIN_TOKEN,
|
||
deviceId: DEVICE_ID
|
||
});
|
||
}
|
||
|
||
const result = await callServer({
|
||
type: "setIdentificationFeedback",
|
||
deviceId: DEVICE_ID,
|
||
runId: message.fileName,
|
||
result: decision
|
||
});
|
||
console.log(`辨识结果已提交: ${result.result === 1 ? "通过" : "不通过"}`);
|
||
} finally {
|
||
prompt.close();
|
||
}
|
||
}
|
||
|
||
async function processPendingMessage() {
|
||
const message = await callServer({ type: "getPendingPanelFile", deviceId: DEVICE_ID });
|
||
if (!message.pending) return false;
|
||
|
||
const sourcePath = await downloadFromUrl(message.url, message.fileName);
|
||
if (message.mediaType === "json") {
|
||
await plotJson(sourcePath);
|
||
} else {
|
||
const imagePath = await plotCsv(sourcePath);
|
||
await reviewIdentification(message, imagePath);
|
||
}
|
||
|
||
await callServer({
|
||
type: "ackPanelFile",
|
||
deviceId: DEVICE_ID,
|
||
fileID: message.fileID
|
||
});
|
||
return true;
|
||
}
|
||
|
||
async function poll() {
|
||
if (polling) return;
|
||
polling = true;
|
||
try {
|
||
while (await processPendingMessage()) {
|
||
// Drain messages already queued on the server before waiting again.
|
||
}
|
||
} catch (error) {
|
||
console.error(`[${new Date().toISOString()}] 消息处理失败: ${error.message}`);
|
||
} finally {
|
||
polling = false;
|
||
}
|
||
}
|
||
|
||
function validateConfig() {
|
||
if (!API_URL) throw new Error("缺少环境变量 REINLOOP_API_URL");
|
||
if (!DEVICE_ID) throw new Error("缺少环境变量 REINLOOP_DEVICE_ID");
|
||
if (REVIEW_MODE === "manual" && !B_ADMIN_TOKEN) {
|
||
throw new Error("人工评审模式缺少环境变量 B_ADMIN_TOKEN");
|
||
}
|
||
if (!Number.isFinite(POLL_INTERVAL_MS) || POLL_INTERVAL_MS < 1000) {
|
||
throw new Error("POLL_INTERVAL_MS 必须是大于或等于 1000 的数字");
|
||
}
|
||
}
|
||
|
||
function main() {
|
||
try {
|
||
validateConfig();
|
||
console.log(`开始获取设备 ${DEVICE_ID} 的待处理消息,轮询间隔 ${POLL_INTERVAL_MS}ms`);
|
||
void poll();
|
||
setInterval(poll, POLL_INTERVAL_MS);
|
||
} catch (error) {
|
||
console.error(error.message);
|
||
process.exitCode = 1;
|
||
}
|
||
}
|
||
|
||
if (require.main === module) main();
|
||
|
||
module.exports = { processPendingMessage }; |