Browse Source

初步mock命令

master
zhangjiming 5 months ago
parent
commit
f95a3fa473
  1. 14
      public/index.html
  2. 11
      public/main.js
  3. 0
      src/config/db.ts
  4. 0
      src/controllers/cmd.ts
  5. 56
      src/index.ts
  6. 0
      src/middlewares/auth.ts
  7. 0
      src/middlewares/logger.ts
  8. 0
      src/models/User.ts
  9. 20
      src/routes/cmd.ts
  10. 23
      src/routes/debug.ts
  11. 142
      src/types/cmdTypes.ts
  12. 266
      src/types/wsTypes.ts
  13. 7
      src/utils/helper.ts
  14. 11
      src/utils/wss.ts

14
public/index.html

@ -8,12 +8,14 @@
</head> </head>
<body> <body>
<div class="main"> <div class="main">
<div class="form">
<label for="username">用户名</label>
<input type="text" id="username" />
<label for="password">密码</label>
<input type="password" id="password" />
<button type="submit" id="submit_user_pwd">确定</button>
<div>
<span>x</span>
<input type="number" id="armX" />
<span>y</span>
<input type="number" id="armY" />
<span>z</span>
<input type="number" id="armZ" />
<button id="moveArm">移动机械臂</button>
</div> </div>
</div> </div>
<script src="lib/zepto.min.js"></script> <script src="lib/zepto.min.js"></script>

11
public/main.js

@ -13,13 +13,14 @@ ws.onclose = () => {
console.log("Disconnected from server"); 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({ $.ajax({
type: "POST", 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", contentType: "application/json",
success: res => { success: res => {
console.log("Success", res); console.log("Success", res);

0
src/config/db.ts

0
src/controllers/cmd.ts

56
src/index.ts

@ -3,46 +3,56 @@ import { Server } from "ws";
import http from "http"; import http from "http";
import bodyParser from "body-parser"; 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(); const app = express();
app.use(express.static("public")); app.use(express.static("public"));
app.use(bodyParser.urlencoded()); app.use(bodyParser.urlencoded());
app.use(bodyParser.json()); 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); const server = http.createServer(app);
// 在HTTP服务器上初始化WebSocket服务器 // 在HTTP服务器上初始化WebSocket服务器
const wss = new Server({ server }); const wss = new Server({ server });
wss.on("connection", ws => { wss.on("connection", ws => {
console.log("Client connected"); 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", () => { ws.on("close", () => {
console.log("Client disconnected"); console.log("Client disconnected");
}); });
}); });
function getCurrStatus() {
return app.locals["status"] as StatusDatagram["data"];
}
app.locals["wss"] = wss; 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; const PORT = process.env.PORT || 3003;

0
src/middlewares/auth.ts

0
src/middlewares/logger.ts

0
src/models/User.ts

20
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;

23
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;

142
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"; //关门

266
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, // 容器是否已满
},
};

7
src/utils/helper.ts

@ -0,0 +1,7 @@
export async function delay(mill: number) {
return new Promise(resolve => {
setTimeout(() => {
resolve({});
}, mill);
});
}

11
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));
}
});
}
Loading…
Cancel
Save