diff --git a/public/index.html b/public/index.html index 79a7943..24578de 100644 --- a/public/index.html +++ b/public/index.html @@ -8,12 +8,14 @@
-
- - - - - +
+ x + + y + + z + +
diff --git a/public/main.js b/public/main.js index 2142440..47758b2 100644 --- a/public/main.js +++ b/public/main.js @@ -13,13 +13,14 @@ ws.onclose = () => { console.log("Disconnected from server"); }; -$("#submit_user_pwd").on("click", () => { - const name = $("#username").val(); - const pwd = $("#password").val(); +$("#moveArm").on("click", () => { + const x = $("#armX").val(); + const y = $("#armY").val(); + const z = $("#armZ").val(); $.ajax({ type: "POST", - url: "/test", - data: JSON.stringify({ name, pwd }), + url: "/api/debug/railArm", + data: JSON.stringify({ x: +x, y: +y, z: +z }), contentType: "application/json", success: res => { console.log("Success", res); diff --git a/src/config/db.ts b/src/config/db.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/controllers/cmd.ts b/src/controllers/cmd.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/index.ts b/src/index.ts index f9a794e..17faed5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,46 +3,56 @@ import { Server } from "ws"; import http from "http"; import bodyParser from "body-parser"; +import cmdRouter from "./routes/cmd"; +import debugRouter from "./routes/debug"; + +import { defaultStatus, StatusDatagram } from "./types/wsTypes"; +import { wsSend } from "./utils/wss"; + const app = express(); app.use(express.static("public")); app.use(bodyParser.urlencoded()); app.use(bodyParser.json()); -app.get("/", (req, res) => { - res.send("Hello World!"); -}); -app.post("/test", (req, res) => { - (req.app.locals["wss"] as Server).clients.forEach(client => { - if (client.readyState === 1) { - client.send(JSON.stringify(req.body)); - } - }); - res.json({ success: true, data: req.body }); -}); - const server = http.createServer(app); // 在HTTP服务器上初始化WebSocket服务器 const wss = new Server({ server }); wss.on("connection", ws => { console.log("Client connected"); - ws.send("Welcome to the WebSocket server!"); - ws.on("message", message => { - console.log(`Received message: ${message}`); - // 广播收到的消息给所有连接的客户端 - wss.clients.forEach(client => { - if (client.readyState === ws.OPEN) { - client.send(message.toString()); - } - }); - }); + // ws.send("Welcome to the WebSocket server!"); + // ws.on("message", message => { + // console.log(`Received message: ${message}`); + // // 广播收到的消息给所有连接的客户端 + // wss.clients.forEach(client => { + // if (client.readyState === ws.OPEN) { + // client.send(message.toString()); + // } + // }); + // }); // 当连接关闭时触发 + ws.send(JSON.stringify(getCurrStatus())); ws.on("close", () => { console.log("Client disconnected"); }); }); - +function getCurrStatus() { + return app.locals["status"] as StatusDatagram["data"]; +} app.locals["wss"] = wss; +app.locals["status"] = defaultStatus; + +// app.get("/", (req, res) => { +// res.send("Hello World!"); +// }); +app.use("/api/debug", debugRouter); +app.use("/api/cmd", cmdRouter); + +//@ts-ignore +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).send("Something broke!"); +}); // 监听端口 const PORT = process.env.PORT || 3003; diff --git a/src/middlewares/auth.ts b/src/middlewares/auth.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/middlewares/logger.ts b/src/middlewares/logger.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/models/User.ts b/src/models/User.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/routes/cmd.ts b/src/routes/cmd.ts new file mode 100644 index 0000000..6f6288f --- /dev/null +++ b/src/routes/cmd.ts @@ -0,0 +1,20 @@ +import express from "express"; +import { delay } from "../utils/helper"; +import { wsSend } from "../utils/wss"; +const router = express.Router(); + +router.post("/", async (req, res) => { + await delay(200); + setTimeout(() => { + wsSend(req.app.locals["wss"], { + type: "cmd", + data: { + commandId: req.body.commandId, + status: "D0000", + }, + }); + }, 2000); + res.json({ code: "00000", msg: "执行成功" }); +}); + +export default router; diff --git a/src/routes/debug.ts b/src/routes/debug.ts new file mode 100644 index 0000000..9f6e4a1 --- /dev/null +++ b/src/routes/debug.ts @@ -0,0 +1,23 @@ +import express from "express"; +import { delay } from "../utils/helper"; +import { wsSend } from "../utils/wss"; +import { StatusDatagram } from "../types/wsTypes"; +const router = express.Router(); + +router.post("/railArm", async (req, res) => { + // await delay(200); + const curr: StatusDatagram["data"] = req.app.locals["status"]; + setTimeout(() => { + curr.railArm.x = req.body.x; + curr.railArm.y = req.body.y; + curr.railArm.z = req.body.z; + wsSend(req.app.locals["wss"], { + type: "status", + data: curr, + }); + }, 1000); + + res.json({ code: "00000", msg: "执行成功" }); +}); + +export default router; diff --git a/src/types/cmdTypes.ts b/src/types/cmdTypes.ts new file mode 100644 index 0000000..a307a09 --- /dev/null +++ b/src/types/cmdTypes.ts @@ -0,0 +1,142 @@ + +type UpTrayStepStruct = { + method: "upTray"; + // params: { + // heaterId: number; + // }; +}; +type DownTrayStepStruct = { + method: "downTray"; + // params: { + // heaterId: number; + // }; +}; +export type TubeSolStruct = { + tubeNum: number; + addLiquidList: Array<{ + solId: number; + volume: number; + }>; +}; +type AddLiquidStepStruct = { + method: "addLiquid"; + params: { + solId: number; + volume: number; + tubeSolList?: TubeSolStruct[]; + }; +}; +type MoveToSolStepStruct = { + method: "moveToSol"; + // params: { + // heaterId: number; + // }; +}; +type MoveToHeaterStepStruct = { + method: "moveToHeat"; + // params: { + // heaterId: number; + // }; +}; +type ShakingStepStruct = { + method: "shaking"; + params: { + second: number; + }; +}; + +type StartHeatingStepStruct = { + method: "startHeating"; + params: { + // heaterId: number; + temperature: number; + }; +}; +type StopHeatingStepStruct = { + method: "stopHeating"; + // params: { + // heaterId: number; + // }; +}; +type TakePhotoStepStruct = { + method: "takePhoto"; +}; +type DelayStepStruct = { + method: "delay"; + params: { + second: number; + }; +}; + +export type StepStruct = + | UpTrayStepStruct + | DownTrayStepStruct + | AddLiquidStepStruct + | MoveToSolStepStruct + | MoveToHeaterStepStruct + | ShakingStepStruct + | StartHeatingStepStruct + | StopHeatingStepStruct + | TakePhotoStepStruct + | DelayStepStruct; + +export type StepCmd = StepStruct["method"]; + +export const StepCmdDescMap: { [k in StepCmd]: string } = { + upTray: "抬起托盘", + downTray: "降下托盘", + addLiquid: "添加溶液", + moveToSol: "移至加液", + moveToHeat: "移至加热", + shaking: "摇匀", + startHeating: "开始加热", + stopHeating: "停止加热", + takePhoto: "拍照", + delay: "等待", +}; + +export const CmdDescMap: { [k in OperationCmd]: string } = { + upTray: "抬起托盘", + downTray: "降下托盘", + injectFluid: "注入溶液", + moveToActionArea: "移至操作区", + startShakeUp: "开始摇匀", + stopShakeUp: "结束摇匀", + startHeat: "开始加热", + stopHeat: "停止加热", + // keepHeat: "恒温", + takePhoto: "拍照", + // moveToUnusual: "移至异常区", + moveToHeatArea: "移至加热区", + takeOffCap: "取下拍子", + putBackCap: "装回拍子", + openClaw: "张开夹爪", + closeClaw: "收合夹爪", + moveMachineArm: "移动机械臂", + moveTube: "移动试管", + openDoor: "开门", + closeDoor: "关门", + +}; + +export type OperationCmd = + | "upTray" // 抬起托盘 + | "downTray" // 降下托盘 + | "injectFluid" // 注入溶液 + | "moveToActionArea" // 移至操作区 + | "startShakeUp" // 开始摇匀 + | "stopShakeUp" // 结束摇匀 + | "startHeat" // 开始加热 + | "stopHeat" // 停止加热 + // | "keepHeat" // 恒温 + | "takePhoto" // 拍照 + // | "moveToUnusual" // 移至异常区 + | "moveToHeatArea" // 移至加热区 + | "takeOffCap" // 取下拍子 + | "putBackCap" // 装回拍子 + | "openClaw" // 张开夹爪 + | "closeClaw" // 收合夹爪 + | "moveMachineArm" // 移动机械臂 + | "moveTube" // 移动试管 + | "openDoor" //开门 + | "closeDoor"; //关门 diff --git a/src/types/wsTypes.ts b/src/types/wsTypes.ts new file mode 100644 index 0000000..cfef079 --- /dev/null +++ b/src/types/wsTypes.ts @@ -0,0 +1,266 @@ +import type { StepCmd } from "./cmdTypes"; + +export type CmdDatagram = { + type: "cmd"; // 指令 + data: { + commandId: string; + // commandName: DebugCmd; + status: "D0000" | "D1111"; + message?: string; + // success: boolean; + }; +}; + +export type CraftDatagram = { + type: "crafts"; + data: { + // 当前工艺执行状态,0 表示未执行,1 表示正在执行,2 表示暂停执行,3 表示停止执行 4:Error, 6: 完成。 + status: 0 | 1 | 2 | 3 | 4 | 6; + // 当前正在执行的具体工艺方法 + method: StepCmd; + // 当前正在执行的工艺步骤索引 + methodIndex: number; + // 加热区 ID + heatId: number; + }; +}; + +export type WarnDatagram = { + type: "warn"; // 报警 + data: { + code: string; + msg: string; + module: string; + }; +}; + +export type StatusDatagram = { + type: "status"; // 状态 + data: { + emergencyStop: boolean; // 硬件急停信号,true 为急停触发,false 为正常运行 + doorStatus: boolean; // 门的状态,false 表示关闭,true 表示开启 + railArm: { + x: number; + y: number; + z: number; + joint1: number; + joint2: number; + distance: number; // 当前机械臂(轴 3)上下移动的距离 + railDistance: number; + clawDistance: number; + clawStatus: boolean; // 夹爪状态,true 为张开,false 为闭合 + isZeroPos: boolean; // 导轨是否在原点 + isLimitPos: boolean; // 导轨是否在限位点 + }; + // 操作区(加液、摇匀、拍照)状态 + liquidArea: { + liquidArm: { + x: number; + y: number; + z: number; + joint1: number; + joint2: number; + pump: Array<{ + pumpId: number; + isPumping: boolean; // 是否正在加液,true正在加液 + }>; + }; + isShaking: boolean; // 是否正在摇匀 + liquidTray: boolean; // 是否存在托盘 + // 溶液容器状态 + solutionBucket: Array<{ + isEmpty: boolean; // 容器是否为空 + isFull: boolean; // 容器是否已满 + }>; + }; + + // 加热区列表 + heatArea: Array<{ + // heaterId: string; + hardwareId: string; + trayStatus: 0 | 1 | 2; // 0为无托盘,1为有托盘,2为托盘抬起 + isHeating: boolean; // 是否正在加热 + capStatus: boolean; // 是否存在拍子 + isSealed: boolean; // 拍子密封状态,true为已密封,false为未密封 + temperature: number; // 当前温度 + }>; + // 碱容器状态(废液桶) + alkaliBucket: { + isEmpty: boolean; // 容器是否为空 + isFull: boolean; // 容器是否已满 + }; + }; +}; + +export type CraftState = { + type: "crafts"; + data: { + heatId: string | number; + methodIndex: string | number; + status: string | number; + }; +}; + +export type ContainerDatagram = { + type: "container"; + data: { + containerList: Array<{ + id: number; + type: 0 | 1; // 0:酸液 1:废液 + solutionId: number; + pumpId: number; + capacityTotal: number; + capacityUsed: number; + }>; + }; +}; + +export type Datagram = CmdDatagram | WarnDatagram | StatusDatagram | CraftDatagram | ContainerDatagram; + +export const defaultStatus: StatusDatagram["data"] = { + emergencyStop: false, // 硬件急停信号,true 为急停触发,false 为正常运行 + doorStatus: false, // 门的状态,false 表示关闭,true 表示开启 + railArm: { + x: 0, + y: 0, + z: 0, + joint1: 0, + joint2: 0, + distance: 0, // 当前机械臂(轴 3)上下移动的距离 + railDistance: 0, + clawDistance: 0, + clawStatus: false, // 夹爪状态,true 为张开,false 为闭合 + isZeroPos: true, // 导轨是否在原点 + isLimitPos: false, // 导轨是否在限位点 + }, + // 操作区(加液、摇匀、拍照)状态 + liquidArea: { + liquidArm: { + x: 0, + y: 0, + z: 0, + joint1: 0, + joint2: 0, + pump: [ + { + pumpId: 1, + isPumping: false, // 是否正在加液,true正在加液 + }, + { + pumpId: 2, + isPumping: false, // 是否正在加液,true正在加液 + }, + { + pumpId: 3, + isPumping: false, // 是否正在加液,true正在加液 + }, + { + pumpId: 4, + isPumping: false, // 是否正在加液,true正在加液 + }, + { + pumpId: 5, + isPumping: false, // 是否正在加液,true正在加液 + }, + { + pumpId: 6, + isPumping: false, // 是否正在加液,true正在加液 + }, + ], + }, + isShaking: false, // 是否正在摇匀 + liquidTray: false, // 是否存在托盘 + // 溶液容器状态 + solutionBucket: [ + { + isEmpty: false, // 容器是否为空 + isFull: false, // 容器是否已满 + }, + { + isEmpty: false, // 容器是否为空 + isFull: false, // 容器是否已满 + }, + { + isEmpty: false, // 容器是否为空 + isFull: false, // 容器是否已满 + }, + { + isEmpty: false, // 容器是否为空 + isFull: false, // 容器是否已满 + }, + { + isEmpty: false, // 容器是否为空 + isFull: false, // 容器是否已满 + }, + { + isEmpty: false, // 容器是否为空 + isFull: false, // 容器是否已满 + }, + { + isEmpty: false, // 容器是否为空 + isFull: false, // 容器是否已满 + }, + { + isEmpty: false, // 容器是否为空 + isFull: false, // 容器是否已满 + }, + ], + }, + + // 加热区列表 + heatArea: [ + { + hardwareId: "hardware_1", + trayStatus: 0, // 0为无托盘,1为有托盘,2为托盘抬起 + isHeating: false, // 是否正在加热 + capStatus: false, // 是否存在拍子 + isSealed: false, // 拍子密封状态,true为已密封,false为未密封 + temperature: 0, // 当前温度 + }, + { + hardwareId: "hardware_2", + trayStatus: 0, // 0为无托盘,1为有托盘,2为托盘抬起 + isHeating: false, // 是否正在加热 + capStatus: false, // 是否存在拍子 + isSealed: false, // 拍子密封状态,true为已密封,false为未密封 + temperature: 0, // 当前温度 + }, + { + hardwareId: "hardware_3", + trayStatus: 0, // 0为无托盘,1为有托盘,2为托盘抬起 + isHeating: false, // 是否正在加热 + capStatus: false, // 是否存在拍子 + isSealed: false, // 拍子密封状态,true为已密封,false为未密封 + temperature: 0, // 当前温度 + }, + { + hardwareId: "hardware_4", + trayStatus: 0, // 0为无托盘,1为有托盘,2为托盘抬起 + isHeating: false, // 是否正在加热 + capStatus: false, // 是否存在拍子 + isSealed: false, // 拍子密封状态,true为已密封,false为未密封 + temperature: 0, // 当前温度 + }, + { + hardwareId: "hardware_5", + trayStatus: 0, // 0为无托盘,1为有托盘,2为托盘抬起 + isHeating: false, // 是否正在加热 + capStatus: false, // 是否存在拍子 + isSealed: false, // 拍子密封状态,true为已密封,false为未密封 + temperature: 0, // 当前温度 + }, + { + hardwareId: "hardware_6", + trayStatus: 0, // 0为无托盘,1为有托盘,2为托盘抬起 + isHeating: false, // 是否正在加热 + capStatus: false, // 是否存在拍子 + isSealed: false, // 拍子密封状态,true为已密封,false为未密封 + temperature: 0, // 当前温度 + }, + ], + // 碱容器状态(废液桶) + alkaliBucket: { + isEmpty: false, // 容器是否为空 + isFull: false, // 容器是否已满 + }, +}; \ No newline at end of file diff --git a/src/utils/helper.ts b/src/utils/helper.ts new file mode 100644 index 0000000..a00a99c --- /dev/null +++ b/src/utils/helper.ts @@ -0,0 +1,7 @@ +export async function delay(mill: number) { + return new Promise(resolve => { + setTimeout(() => { + resolve({}); + }, mill); + }); +} diff --git a/src/utils/wss.ts b/src/utils/wss.ts new file mode 100644 index 0000000..8181483 --- /dev/null +++ b/src/utils/wss.ts @@ -0,0 +1,11 @@ +import { Server } from "ws"; +import { Datagram } from "../types/wsTypes"; + +export function wsSend(wss: Server, data: Datagram) { + // 广播消息给所有连接的客户端 + wss.clients.forEach(client => { + if (client.readyState === client.OPEN) { + client.send(JSON.stringify(data)); + } + }); +}