update server

This commit is contained in:
2026-07-30 11:12:31 +08:00
commit 4312cb878c
99 changed files with 24034 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
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 };