石墨仪设备 前端仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

305 lines
11 KiB

<template>
<div class="component-page p-4 text-title">
<el-tabs type="border-card">
<el-tab-pane label="单一动作">
<div class="frame">
<div class="flex items-center gap-4 flex-wrap">
<label>加热区编号:</label>
<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>
<button class="btn-light px-2 py-1" @click="onCmdClick('downTray')">降下托盘</button>
</div>
</div>
<div class="frame">
<div class="flex items-center gap-4 flex-wrap">
X:<input type="number" v-model="x" class="rounded-sm px-1 w-16" /> Y:<input
type="number"
v-model="y"
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">{{
`${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>
<div class="frame">
<div class="flex gap-4 flex-wrap">
<button class="btn-light px-2 py-1 min-w-20" @click="onCmdClick('openClaw')">张开夹爪</button>
<button class="btn-light px-2 py-1 min-w-20" @click="onCmdClick('closeClaw')">收合夹爪</button>
</div>
</div>
<div class="frame">
<div class="flex items-center gap-4 flex-wrap">
<label for="">加液泵编号:</label>
<input type="number" v-model="pumpId" class="rounded-sm px-2" placeholder="输入加液泵编号" />
<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> -->
</div>
<div class="flex gap-4 flex-wrap">
<button class="btn-light px-2 py-1" @click="onCmdClick('injectFluid')">注入溶液</button>
</div>
</div>
<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-option v-for="item in areaOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<label>温度:</label>
<input type="number" v-model="heatTemperature" class="rounded-sm px-2" placeholder="输入温度" />
<span>℃</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">
<div class="flex items-center gap-4 flex-wrap">
<label>摇匀速度:</label>
<input type="number" v-model="shakeUpSpeed" class="rounded-sm px-2" />
</div>
<div class="flex gap-4 flex-wrap">
<button class="btn-light px-2 py-1 min-w-20" @click="onCmdClick('startShakeUp')">开始摇匀</button>
<button class="btn-light px-2 py-1 min-w-20" @click="onCmdClick('stopShakeUp')">结束摇匀</button>
<button class="btn-light px-2 py-1 min-w-20" @click="onCmdClick('takePhoto')">拍照</button>
</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
v-for="config in settingStore.heatAreaConfig"
:key="config.code"
class="flex items-center gap-4 flex-wrap mb-2">
<p class="min-w-6">{{ config.name }}</p>
<span>X:</span>
<input type="number" class="rounded-sm px-1 w-16" v-model="config.valueObj.x" />
<span>Y:</span>
<input type="number" class="rounded-sm px-1 w-16" v-model="config.valueObj.y" />
<span>Z:</span>
<input type="number" class="rounded-sm px-1 w-16" v-model="config.valueObj.z" />
</div>
<h1 class="text-lg font-medium py-2">操作区(加液、摇匀、拍照)坐标:</h1>
<div class="flex items-center gap-4 flex-wrap mb-2">
<span>X:</span>
<input type="number" class="rounded-sm px-1 w-16" v-model="settingStore.actionAreaConfig.valueObj.x" />
<span>Y:</span>
<input type="number" class="rounded-sm px-1 w-16" v-model="settingStore.actionAreaConfig.valueObj.y" />
<span>Z:</span>
<input type="number" class="rounded-sm px-1 w-16" v-model="settingStore.actionAreaConfig.valueObj.z" />
</div>
<h1 class="text-lg font-medium py-2">拍子区坐标:</h1>
<div class="flex items-center gap-4 flex-wrap mb-2">
<span>X:</span>
<input type="number" class="rounded-sm px-1 w-16" v-model="settingStore.capAreaConfig.valueObj.x" />
<span>Y:</span>
<input type="number" class="rounded-sm px-1 w-16" v-model="settingStore.capAreaConfig.valueObj.y" />
<span>Z:</span>
<input type="number" class="rounded-sm px-1 w-16" v-model="settingStore.capAreaConfig.valueObj.z" />
</div>
<h1 class="text-lg font-medium py-1">加液:</h1>
<div class="flex items-center gap-4 flex-wrap">
<p>1毫升对应</p>
<input type="number" class="rounded-sm px-1 w-16" /> 步
</div>
<footer class="flex justify-end mt-4">
<button class="btn-dark px-2 py-1 min-w-20" @click="saveConfig">保存</button>
</footer>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts">
import { CmdDescMap, debugCmd, type DebugCmd } from "@/services/debug/debugApi";
import { createWebSocket, sharedWsUrl } from "@/services/socket";
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(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);
const z = ref(0);
const pumpId = ref(1);
const pumpAmount = ref(10);
const shakeUpSpeed = ref(5);
const areaOptions = computed(() => {
return settingStore.heatAreaConfig.map(c => ({ label: c.name, value: c.id }));
});
onMounted(() => {
const wsClient = createWebSocket(sharedWsUrl);
const subscription = wsClient.dataOb.subscribe(data => {
console.log(data);
if (data.type === "cmd") {
const cmdInfo = getTxnRecord(data.data.commandId, "debug");
if (cmdInfo) {
const cmdName = CmdDescMap[cmdInfo.command];
const result = data.data.success ? "执行完毕" : "执行失败";
ElMessage({
message: `${cmdName} ${result}`,
type: data.data.success ? "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();
onUnmounted(() => {
subscription.unsubscribe();
});
});
function onCmdClick(command: DebugCmd) {
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 {
showToast(res.msg);
}
});
}
function saveConfig() {
const heatCfg = settingStore.heatAreaConfig.map(c => ({
id: c.id,
value: `${c.valueObj.x},${c.valueObj.y},${c.valueObj.z}`,
}));
const actionCfg = {
id: settingStore.actionAreaConfig.id || 0,
value: `${settingStore.actionAreaConfig.valueObj.x},${settingStore.actionAreaConfig.valueObj.y},${settingStore.actionAreaConfig.valueObj.z}`,
};
const capCfg = {
id: settingStore.capAreaConfig.id || 0,
value: `${settingStore.capAreaConfig.valueObj.x},${settingStore.capAreaConfig.valueObj.y},${settingStore.capAreaConfig.valueObj.z}`,
};
updateConfig([...heatCfg, actionCfg, capCfg]).then(res => {
if (res.success) {
showToast("保存成功");
}
});
}
</script>
<style lang="scss" scoped>
.tabFrame {
overflow: auto;
height: calc(100vh - var(--headerHeight) - var(--footerHeight) - 90px);
}
.frame {
border-bottom: solid 1px #ddd;
padding: 1rem 0.5rem;
display: flex;
flex-direction: column;
align-items: start;
gap: 1rem;
}
input {
background-color: #f5f5f5;
height: 1.75rem;
}
</style>