Browse Source

添加状态类型,请求添加命令ID

feature/layout_0214
zhangjiming 6 months ago
parent
commit
83022a41ae
  1. 14
      src/services/debug/debugApi.ts
  2. 53
      src/services/socket.ts
  3. 42
      src/services/txn.ts
  4. 12
      src/stores/status.ts
  5. 154
      src/views/debug/debug.vue

14
src/services/debug/debugApi.ts

@ -1,4 +1,5 @@
import httpRequest, { type BaseResponse } from "../httpRequest";
import { addTxnRecord } from "../txn";
export const CmdDescMap: { [k in DebugCmd]: string } = {
upTray: "抬起托盘",
@ -11,7 +12,7 @@ export const CmdDescMap: { [k in DebugCmd]: string } = {
stopHeat: "停止加热",
keepHeat: "恒温",
takePhoto: "拍照",
moveToUnusual: "移至异常区",
// moveToUnusual: "移至异常区",
moveToHeatArea: "移至加热区",
takeOffCap: "取下拍子",
putBackCap: "装回拍子",
@ -27,20 +28,21 @@ export type DebugCmd =
| "injectFluid" // 注入溶液
| "moveToActionArea" // 移至操作区
| "startShakeUp" // 开始摇匀
| "stopShakeUp" // 结束摇匀
| "stopShakeUp" // 结束摇匀
| "startHeat" // 开始加热
| "stopHeat" // 停止加热
| "keepHeat" // 恒温
| "takePhoto" // 拍照
| "moveToUnusual" // 移至异常区
// | "moveToUnusual" // 移至异常区
| "moveToHeatArea" // 移至加热区
| "takeOffCap" // 取下拍子
| "putBackCap" // 装回拍子
| "openClaw" // 张开夹爪
| "closeClaw" // 收合夹爪
| "openClaw" // 张开夹爪
| "closeClaw" // 收合夹爪
| "moveMachineArm" // 移动机械臂
| "moveTube"; // 移动试管
export function debugCmd(params: { command: DebugCmd; params: Record<string, any> }) {
return httpRequest<BaseResponse<string>>({ url: "/api/cmd/", params, method: "POST" });
const commandId = addTxnRecord({ ...params, category: "debug" });
return httpRequest<BaseResponse<string>>({ url: "/api/cmd/", params: { ...params, commandId }, method: "POST" });
}

53
src/services/socket.ts

@ -7,7 +7,7 @@ export type CmdDatagram = {
type: "cmd"; // 指令
data: {
commandId: string;
commandName: DebugCmd;
// commandName: DebugCmd;
status: CmdSuccess | CmdFailure;
message: string;
};
@ -19,10 +19,59 @@ export type WarnDatagram = {
message: string;
};
};
export type StatusDatagram = {
type: "status"; // 状态
data: {
message: string;
emergencyStop: boolean; // 硬件急停信号,true 为急停触发,false 为正常运行
railArm: {
x: number;
y: number;
z: number;
joint1: number;
joint2: number;
joint3: number;
railDistance: number;
clawDistance: number;
speed: number;
isZeroPos: 0 | 1; // 导轨是否在原点,0 表示不在原点,非 0 表示在原点
isLimitPos: 0 | 1; // 导轨是否在限位点,0 表示不在限位点,非 0 表示在限位点
};
liquidArm: {
x: number;
y: number;
z: number;
joint1: number;
joint2: number;
speed: number;
};
// 加液泵状态列表
motors: Array<{
pumpId: string;
speed: number;
position: number; // 电机实时位置
isZeroPos: 0 | 1; // 电机是否在原点,0 表示不在原点,非 0 表示在原点
isLimitPos: 0 | 1; // 电机是否在限位点,0 表示不在限位点,非 0 表示在限位点
}>;
heatingStatus: boolean;
// 加热器状态列表
heater: Array<{
heaterId: string;
temperature: number;
current: number;
}>;
trayStatus: boolean[]; // 加热位托盘状态列表,true 为存在托盘,false 为无托盘
capStatus: boolean[]; // 拍子状态列表,true 为存在拍子,false 为无拍子
// 酸液桶状态
lyeTank: {
isEmpty: boolean;
isFull: boolean;
};
// 废液桶状态
wasteTank: {
isEmpty: boolean;
isFull: boolean;
};
};
};

42
src/services/txn.ts

@ -0,0 +1,42 @@
import type { DebugCmd } from "./debug/debugApi";
let _lastTimestamp = 0;
export function generateTxnNo() {
const txnNo = Date.now();
// 确保前后两条指定的txn 不一样
if (txnNo !== _lastTimestamp) {
if (txnNo < _lastTimestamp) {
_lastTimestamp++;
} else {
_lastTimestamp = txnNo;
}
} else {
_lastTimestamp = txnNo + 1;
}
return _lastTimestamp;
}
type DebugCmdRecord = {
category: 'debug';
command: DebugCmd;
params: Record<string, any>
}
type TxnRecord = DebugCmdRecord // | xxxRecord
const txnCmdMap: Record<string, TxnRecord> = {};
export function addTxnRecord(val: TxnRecord) {
const txn = generateTxnNo().toString();
txnCmdMap[txn] = val;
return txn
}
export function getTxnRecord(txn: string, del: boolean = true) {
const record = txnCmdMap[txn];
console.log(txn,':', record)
if (del) {
delete txnCmdMap[txn];
}
return record;
}

12
src/stores/status.ts

@ -0,0 +1,12 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import type { StatusDatagram } from '@/services/socket'
export const useStatusStore = defineStore('status', () => {
const status = ref<StatusDatagram['data']|undefined>()
const setStatus = (data: StatusDatagram['data']) => {
status.value = data
}
return { status, setStatus }
})

154
src/views/debug/debug.vue

@ -1,52 +1,16 @@
<template>
<div class="component-page p-4 text-title">
<el-tabs type="border-card">
<el-tab-pane label="复合动作">
<div class="tabFrame">
<div class="frame">
<div class="flex items-center gap-4 flex-wrap">
<label>加热区编号:</label>
<el-select v-model="selectedArea" placeholder="Select" style="width: 200px">
<el-option
v-for="item in areaOptions"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
<!-- <label>托盘高度:</label>
<input type="number" class="rounded-sm px-2" />
<span>mm</span> -->
</div>
<div class="flex gap-4 flex-wrap">
<button class="btn-light px-2 py-1" @click="onCmdClick('takeOffCap')">取下拍子</button>
<button class="btn-light px-2 py-1" @click="onCmdClick('putBackCap')">装回拍子</button>
<button class="btn-light px-2 py-1" @click="onCmdClick('moveToActionArea')">
移至操作区(加液摇匀拍照)
</button>
<button class="btn-light px-2 py-1" @click="onCmdClick('moveToHeatArea')">
(从操作区)移至加热区
</button>
</div>
</div>
<div class="frame">
<div class="flex items-center gap-4">
<label>源位置:</label>
<input type="number" class="rounded-sm px-2" />
<label>目标位置:</label>
<input type="number" class="rounded-sm px-2" />
</div>
<button class="btn-light px-2 py-1 min-w-20" @click="onCmdClick('moveTube')">移动试管</button>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="单一动作">
<div class="frame">
<div class="flex items-center gap-4 flex-wrap">
<label>加热区编号:</label>
<el-select v-model="selectedAreaForHeat" placeholder="Select" style="width: 200px">
<el-select v-model="selectedAreaForTray" placeholder="Select" style="width: 200px">
<el-option v-for="item in areaOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<label>升降高度:</label>
<input type="number" v-model="trayHeight" class="rounded-sm px-2" />
<span>mm</span>
</div>
<div class="flex gap-4 flex-wrap">
<button class="btn-light px-2 py-1" @click="onCmdClick('upTray')">抬起托盘</button>
@ -61,7 +25,11 @@
class="rounded-sm px-1 w-16" />
Z:<input type="number" v-model="z" class="rounded-sm px-1 w-16" />
<label for="">当前位置</label>
<span class="text-warn">505050</span>
<span class="text-warn">{{
`${statusStore.status?.railArm.x || 0},${statusStore.status?.railArm.y || 0},${
statusStore.status?.railArm.z || 0
}`
}}</span>
</div>
<button class="btn-light px-2 py-1 min-w-20" @click="onCmdClick('moveMachineArm')">移动机械臂</button>
</div>
@ -78,8 +46,8 @@
<label for="">注入量:</label>
<input type="number" v-model="pumpAmount" class="rounded-sm px-2" placeholder="输入注入量" />
<span>ml</span>
<label for="">当前容量</label>
<span class="text-warn">50</span>
<!-- <label for="">当前容量</label>
<span class="text-warn">50</span> -->
</div>
<div class="flex gap-4 flex-wrap">
<button class="btn-light px-2 py-1" @click="onCmdClick('injectFluid')">注入溶液</button>
@ -94,14 +62,16 @@
<label>温度:</label>
<input type="number" v-model="heatTemperature" class="rounded-sm px-2" placeholder="输入温度" />
<span></span>
<label for="">当前温度</label>
<span class="text-warn">50</span>
</div>
<div class="flex gap-4 flex-wrap">
<button class="btn-light px-2 py-1" @click="onCmdClick('startHeat')">开始加热</button>
<button class="btn-light px-2 py-1" @click="onCmdClick('stopHeat')">停止加热</button>
<!-- <button class="btn-light px-2 py-1 min-w-20" @click="onCmdClick('keepHeat')">恒温</button> -->
</div>
<div>
<label for="">当前温度</label>
<span class="text-warn">{{ (statusStore.status?.heater || []).map(h => h.current).join(",") }}</span>
</div>
</div>
<div class="frame">
@ -116,6 +86,44 @@
</div>
</div>
</el-tab-pane>
<el-tab-pane label="复合动作">
<div class="tabFrame">
<div class="frame">
<div class="flex items-center gap-4 flex-wrap">
<label>加热区编号:</label>
<el-select v-model="selectedArea" placeholder="Select" style="width: 200px">
<el-option
v-for="item in areaOptions"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</div>
<div class="flex gap-4 flex-wrap">
<button class="btn-light px-2 py-1" @click="onCmdClick('takeOffCap')">取下拍子</button>
<button class="btn-light px-2 py-1" @click="onCmdClick('putBackCap')">装回拍子</button>
<button class="btn-light px-2 py-1" @click="onCmdClick('moveToActionArea')">
移至操作区(加液摇匀拍照)
</button>
<button class="btn-light px-2 py-1" @click="onCmdClick('moveToHeatArea')">
(从操作区)移至加热区
</button>
</div>
</div>
<div class="frame">
<div class="flex items-center gap-4">
<label>源位置:</label>
<input type="number" class="rounded-sm px-2" />
<label>目标位置:</label>
<input type="number" class="rounded-sm px-2" />
</div>
<button class="btn-light px-2 py-1 min-w-20" @click="onCmdClick('moveTube')">移动试管</button>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="校准">
<h1 class="text-lg font-medium py-2">加热区坐标</h1>
<div
@ -167,12 +175,18 @@ import { updateConfig } from "@/services/sysConfig/sysConfig";
import { useSettingStore } from "@/stores/setting";
import { showToast } from "vant";
import { computed, onMounted, onUnmounted, ref } from "vue";
import { ElMessage } from "element-plus";
import { useStatusStore } from "@/stores/status";
import { getTxnRecord } from "@/services/txn";
const settingStore = useSettingStore();
const statusStore = useStatusStore();
const selectedArea = ref("");
const selectedAreaForHeat = ref("");
const selectedArea = ref(2);
const selectedAreaForTray = ref(2);
const selectedAreaForHeat = ref(2);
const heatTemperature = ref(0);
const trayHeight = ref(0);
const x = ref(0);
const y = ref(0);
@ -192,9 +206,22 @@ onMounted(() => {
const subscription = wsClient.dataOb.subscribe(data => {
console.log(data);
if (data.type === "cmd") {
const cmdName = CmdDescMap[data.data.commandName];
const result = data.data.status === "D0000" ? "执行完毕" : "执行失败";
showToast(`${cmdName} ${result}`);
const cmdInfo = getTxnRecord(data.data.commandId);
if (cmdInfo.category === "debug") {
const cmdName = CmdDescMap[cmdInfo.command];
const result = data.data.status === "D0000" ? "执行完毕" : "执行失败";
ElMessage({
message: `${cmdName} ${result}`,
type: data.data.status === "D0000" ? "success" : "error",
});
}
} else if (data.type === "status") {
statusStore.setStatus(data.data);
} else if (data.type === "warn") {
ElMessage({
message: data.data.message,
type: "warning",
});
}
});
wsClient.connect();
@ -205,7 +232,30 @@ onMounted(() => {
});
function onCmdClick(command: DebugCmd) {
debugCmd({ command, params: {} }).then(res => {
let params: Record<string, any> = {};
if (command === "upTray") {
params = { areaId: selectedAreaForTray.value, height: trayHeight.value };
} else if (command === "downTray") {
params = { areaId: selectedAreaForTray.value, height: trayHeight.value };
} else if (command === "moveMachineArm") {
params = { x: x.value, y: y.value, z: z.value };
} else if (command === "injectFluid") {
params = { areaId: pumpId.value, volume: pumpAmount.value };
} else if (command === "startHeat") {
params = { areaId: selectedAreaForHeat.value, temperature: heatTemperature.value };
} else if (command === "stopHeat") {
params = { areaId: selectedAreaForHeat.value };
} else if (command === "startShakeUp") {
params = { speed: shakeUpSpeed.value };
} else if (
command === "takeOffCap" ||
command === "putBackCap" ||
command === "moveToActionArea" ||
command === "moveToHeatArea"
) {
params = { areaId: selectedArea.value };
}
debugCmd({ command, params }).then(res => {
if (res.success) {
// showToast("");
} else {
@ -213,6 +263,7 @@ function onCmdClick(command: DebugCmd) {
}
});
}
function saveConfig() {
const heatCfg = settingStore.heatAreaConfig.map(c => ({
id: c.id,
@ -233,6 +284,7 @@ function saveConfig() {
});
}
</script>
<style lang="scss" scoped>
.tabFrame {
overflow: auto;

Loading…
Cancel
Save