|
|
@ -1,34 +1,94 @@ |
|
|
|
<template> |
|
|
|
<div class="graphite_home component-page overflow-auto" id="heatArea"> |
|
|
|
<div class="heat_area"> |
|
|
|
<div v-for="(item, index) in heatList" :key="item"> |
|
|
|
<HeatPosition :heatInfo="{...item, index}" :tubeIndex="index + 1" @onSelectedTray="onSelectedTray"></HeatPosition> |
|
|
|
<div v-for="(item, index) in heatList" :key="item.id"> |
|
|
|
<HeatPosition |
|
|
|
:heatInfo="{ ...item, index }" |
|
|
|
:tubeIndex="index + 1" |
|
|
|
@onSelectedTray="onSelectedTray" |
|
|
|
@onSetHeatAreaTemp = "onSetHeatAreaTemp" |
|
|
|
></HeatPosition> |
|
|
|
</div> |
|
|
|
<!--执行中状态的遮罩层--> |
|
|
|
</div> |
|
|
|
<!--拍照区--> |
|
|
|
<div class="picture_area"> |
|
|
|
<TakePickture></TakePickture> |
|
|
|
<!--加液区和拍照区可切换--> |
|
|
|
<div style="display: flex; justify-content: center; align-items: center"> |
|
|
|
<div style="font-size: 1rem; margin-left: 0.3rem">加液区</div> |
|
|
|
<van-switch |
|
|
|
v-model="switchModule" |
|
|
|
size="1rem" |
|
|
|
active-color="#ee0a24" |
|
|
|
inactive-color="green" |
|
|
|
/> |
|
|
|
<div style="font-size: 1rem; margin-left: 0.3rem">拍照区</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<TakePickture v-if="switchModule"></TakePickture> |
|
|
|
<div v-else class="home_tube"> |
|
|
|
<div |
|
|
|
class="inner-circle" |
|
|
|
v-for="(tubeItem, index) in tubeList" |
|
|
|
:key="index" |
|
|
|
:style="{ background: tubeItem.color }" |
|
|
|
@click="onChooseTube(tubeItem, index)" |
|
|
|
></div> |
|
|
|
</div> |
|
|
|
<!--操作区--> |
|
|
|
<div class="graphite_btn_container"> |
|
|
|
<van-button size="large" class="btn_size op_open_door">开门</van-button> |
|
|
|
<van-button size="large" class="btn_size op_start_task">开始实验</van-button> |
|
|
|
<van-button size="large" class="btn_size op_stop_task">结束实验</van-button> |
|
|
|
<van-button size="large" class="btn_size op_select_craft" @click="onChooseCaft">选择工艺</van-button> |
|
|
|
<van-button size="large" class="btn_size op_exec_craft">执行工艺</van-button> |
|
|
|
<van-button size="large" class="btn_size op_add_liquid" @click="onAddLiquid">添加溶液</van-button> |
|
|
|
<van-button size="large" class="btn_size op_open_door" @click="onOPen" |
|
|
|
>开门</van-button |
|
|
|
> |
|
|
|
<van-button size="large" class="btn_size op_start_task" |
|
|
|
>开始实验</van-button |
|
|
|
> |
|
|
|
<van-button size="large" class="btn_size op_stop_task" |
|
|
|
>结束实验</van-button |
|
|
|
> |
|
|
|
<van-button |
|
|
|
size="large" |
|
|
|
class="btn_size op_select_craft" |
|
|
|
@click="onChooseCaft" |
|
|
|
>选择工艺</van-button |
|
|
|
> |
|
|
|
<van-button size="large" class="btn_size op_exec_craft" |
|
|
|
>执行工艺</van-button |
|
|
|
> |
|
|
|
<van-button |
|
|
|
size="large" |
|
|
|
class="btn_size op_add_liquid" |
|
|
|
@click="onAddLiquid" |
|
|
|
>添加溶液</van-button |
|
|
|
> |
|
|
|
<van-button size="large" class="btn_size op_shake_up">摇匀</van-button> |
|
|
|
<van-button size="large" class="btn_size op_move_heat" @click="onMoveToHeat">移至加热</van-button> |
|
|
|
<van-button size="large" class="btn_size op_move_act">移至加液</van-button> |
|
|
|
<van-button size="large" class="btn_size op_move_exception">移至特殊</van-button> |
|
|
|
<van-button size="large" class="btn_size op_start_heat">开始加热</van-button> |
|
|
|
<van-button size="large" class="btn_size op_up_tray">抬起托盘</van-button> |
|
|
|
<van-button |
|
|
|
size="large" |
|
|
|
class="btn_size op_move_heat" |
|
|
|
@click="onMoveToHeat" |
|
|
|
>移至加热</van-button |
|
|
|
> |
|
|
|
<van-button size="large" class="btn_size op_move_act" @click="onMoveToOperationArea" |
|
|
|
>移至加液</van-button |
|
|
|
> |
|
|
|
<van-button size="large" class="btn_size op_move_exception" |
|
|
|
>移至特殊</van-button |
|
|
|
> |
|
|
|
<van-button size="large" class="btn_size op_start_heat" @click="onStartHeat" |
|
|
|
>开始加热</van-button |
|
|
|
> |
|
|
|
<van-button size="large" class="btn_size op_up_tray" |
|
|
|
>抬起托盘</van-button |
|
|
|
> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<van-overlay :show="liquidVisible" style="z-index: 9999"> |
|
|
|
<div class="liquid"> |
|
|
|
<div class="addLiquid"> |
|
|
|
<AddLiquid @cancel="liquidVisible = false"></AddLiquid> |
|
|
|
<AddLiquid |
|
|
|
@cancel="liquidVisible = false" |
|
|
|
@onAddSolution="onAddSolution" |
|
|
|
></AddLiquid> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</van-overlay> |
|
|
@ -39,74 +99,301 @@ |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
<script lang="ts" setup> |
|
|
|
import { ref, onMounted } from "vue"; |
|
|
|
import { ref, reactive, onMounted, onUnmounted, watch } from "vue"; |
|
|
|
//@ts-ignore |
|
|
|
import { ElMessage } from "element-plus"; |
|
|
|
import { createWebSocket, sharedWsUrl } from "@/services/socket"; |
|
|
|
import { HeatPosition, TakePickture, AddLiquid } from "./components"; |
|
|
|
import OverlayModal from "@/components/OverlayModal.vue"; |
|
|
|
import CraftList from "@/views/graphite/components/CraftList.vue"; |
|
|
|
import { graphiteMock } from './components/mock' |
|
|
|
import { graphiteMock } from "./components/mock"; |
|
|
|
import { useStatusStore } from "@/stores/status"; |
|
|
|
import { getConfig } from "@/services/sysConfig/sysConfig"; |
|
|
|
import { |
|
|
|
CmdDescMap, |
|
|
|
taskCmd, |
|
|
|
type OperationCmd, |
|
|
|
} from "@/services/globalCmd/globalCmd"; |
|
|
|
import { getTxnRecord } from "@/services/txn"; |
|
|
|
import { useSettingStore } from "@/stores/setting"; |
|
|
|
|
|
|
|
const settingStore = useSettingStore(); |
|
|
|
console.log('settingStore---', settingStore.heatAreaConfig) |
|
|
|
|
|
|
|
//设备的全局状态 |
|
|
|
const useStatus = useStatusStore() |
|
|
|
const statusStore = useStatusStore(); |
|
|
|
const heatList: any = ref([]); |
|
|
|
const craftVisible = ref(false); |
|
|
|
const tubeRackList = ['A-1','A-2', 'A-3','A-4','A-5','A-5'] |
|
|
|
const switchModule = ref(false); |
|
|
|
let tubeList = reactive<any>([]); |
|
|
|
const selectedColor = "#4F85FB"; |
|
|
|
const emptyColor = "#FFFFFF"; |
|
|
|
const defaultColor = "#189952"; |
|
|
|
onMounted(() => { |
|
|
|
//6个加热区数据 |
|
|
|
heatList.value = settingStore.heatAreaConfig |
|
|
|
heatList.value = settingStore.heatAreaConfig; |
|
|
|
//设备16个试管的基础数据 |
|
|
|
tubeBaseConfig(); |
|
|
|
//连接socket |
|
|
|
const wsClient = createWebSocket(sharedWsUrl); |
|
|
|
const subscription = wsClient.dataOb.subscribe((data) => { |
|
|
|
if (data.type === "cmd") { |
|
|
|
const cmdInfo = getTxnRecord(data.data.commandId, "task"); |
|
|
|
if (cmdInfo) { |
|
|
|
const cmdName = CmdDescMap[cmdInfo.command]; |
|
|
|
const result = data.data.success |
|
|
|
? "执行完毕" |
|
|
|
: `执行失败 ${data.data.message}`; |
|
|
|
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(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
//选择的托盘 |
|
|
|
const selectedTrayList = ref<any>([]); |
|
|
|
const currentIndex = ref(); |
|
|
|
const selectedTrayObj:any = {} |
|
|
|
const selectedTrayObj: any = {}; |
|
|
|
const onSelectedTray = (data: any) => { |
|
|
|
if(selectedTrayObj[data.id]){ |
|
|
|
delete selectedTrayObj[data.id] |
|
|
|
console.log("data==操作加热区:=", data); |
|
|
|
if (!data.isSelect) { |
|
|
|
delete selectedTrayObj[data.id]; |
|
|
|
} else { |
|
|
|
selectedTrayObj[data.id] = data |
|
|
|
selectedTrayObj[data.id] = data; |
|
|
|
} |
|
|
|
selectedTrayList.value = Object.values(selectedTrayObj); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
//设置加热区温度 |
|
|
|
const onSetHeatAreaTemp = (dataInfo:any) => { |
|
|
|
selectedTrayObj[dataInfo.id] = dataInfo; |
|
|
|
selectedTrayList.value = Object.values(selectedTrayObj); |
|
|
|
} |
|
|
|
|
|
|
|
const tubeBaseConfig = () => { |
|
|
|
//默认为16个 |
|
|
|
for (let i = 0; i < 16; i++) { |
|
|
|
tubeList.push({ |
|
|
|
id: i + 1, |
|
|
|
color: "rgb(212, 212, 212)", |
|
|
|
}); |
|
|
|
} |
|
|
|
selectedTrayList.value = Object.values(selectedTrayObj) |
|
|
|
console.log('data==操作加热区:=', data) |
|
|
|
}; |
|
|
|
|
|
|
|
//添加溶液 |
|
|
|
const liquidVisible = ref(false); |
|
|
|
const onAddLiquid = () => { |
|
|
|
//检查加液区是否有试管。但是现在没有这个状态,暂时不考虑。 |
|
|
|
liquidVisible.value = true; |
|
|
|
}; |
|
|
|
|
|
|
|
const onAddSolution = (data: any) => { |
|
|
|
let ids = data.map((item: any) => item.id); |
|
|
|
tubeList.forEach((item: any) => { |
|
|
|
if (ids.includes(item.id)) { |
|
|
|
item.default = defaultColor; |
|
|
|
item.color = defaultColor; |
|
|
|
item.isSelected = true; |
|
|
|
} |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
const changeVisible = () => { |
|
|
|
craftVisible.value = false; |
|
|
|
}; |
|
|
|
|
|
|
|
//选择工艺 |
|
|
|
const onChooseCaft = () => { |
|
|
|
craftVisible.value = true; |
|
|
|
}; |
|
|
|
|
|
|
|
const onChooseTube = (tubeItem: any, index: any) => { |
|
|
|
if (!tubeItem.id) return; |
|
|
|
//@ts-ignore |
|
|
|
let list = [...tubeList]; |
|
|
|
for (let i = 0; i < list.length; i++) { |
|
|
|
let item = list[i]; |
|
|
|
if (index == i) { |
|
|
|
item.color = selectedColor; |
|
|
|
item.isSelected = true; |
|
|
|
} else { |
|
|
|
item.color = item.default ? item.default : emptyColor; |
|
|
|
item.isSelected = false; |
|
|
|
} |
|
|
|
} |
|
|
|
tubeList = [...list]; |
|
|
|
}; |
|
|
|
|
|
|
|
//移至加热 |
|
|
|
const onMoveToHeat = () => { |
|
|
|
//1、是否选择了加热区,且只能选择一个加热区 |
|
|
|
//1、是否选择了加热区 |
|
|
|
if (!selectedTrayList.value.length) { |
|
|
|
ElMessage.error("请选择目标加热区"); |
|
|
|
return; |
|
|
|
} |
|
|
|
//2、只能选择一个加热区 |
|
|
|
if (selectedTrayList.value.length != 1) { |
|
|
|
ElMessage.error('只能选择一个加热区') |
|
|
|
ElMessage.error("只能选择一个加热区"); |
|
|
|
return; |
|
|
|
} |
|
|
|
let selectedData = selectedTrayList.value[0] |
|
|
|
|
|
|
|
//2、判断加液区是否有试管 |
|
|
|
let heatAearStatus:any = useStatus.status || graphiteMock.heatAreaStatus |
|
|
|
if(!heatAearStatus[selectedData.index]){ |
|
|
|
ElMessage.error('选择的加热区已有试管架,重新选择加热区') |
|
|
|
let selectedDataItem = selectedTrayList.value[0]; |
|
|
|
//2、判断选择的加热区是否已经有了试管架 |
|
|
|
let heatAearStatus: any = statusStore.status?.trayStatus || graphiteMock.heatAreaStatus; |
|
|
|
if (!heatAearStatus[selectedDataItem.index]) { |
|
|
|
ElMessage.error("选择的加热区已有试管架,重新选择加热区"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
//调用移至加热接口 |
|
|
|
const params = { |
|
|
|
areaId: selectedDataItem.id, |
|
|
|
}; |
|
|
|
const command: OperationCmd = "moveToHeatArea"; |
|
|
|
taskCmd({ command, params }).then((res) => { |
|
|
|
if (res.success) { |
|
|
|
ElMessage.success("指令已发送,请稍等"); |
|
|
|
} else { |
|
|
|
ElMessage.error(res.msg); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
//指令完成成更新UI |
|
|
|
let list = [...heatList.value]; |
|
|
|
list.forEach((item: any) => { |
|
|
|
if (item.id == selectedDataItem.id) { |
|
|
|
item.tubeList = JSON.parse(JSON.stringify(tubeList)); |
|
|
|
|
|
|
|
selectedTrayObj[item.id] = item; |
|
|
|
selectedTrayList.value = Object.values(selectedTrayObj); |
|
|
|
} |
|
|
|
}); |
|
|
|
tubeList.forEach((item:any) => { |
|
|
|
item.color = ''; |
|
|
|
item.default = '' |
|
|
|
}); |
|
|
|
heatList.value = [...list]; |
|
|
|
}; |
|
|
|
|
|
|
|
//移至加液区(操作区) |
|
|
|
const onMoveToOperationArea = () => { |
|
|
|
//1、判断加液区是否有试管架(暂时获取不到这个状态) |
|
|
|
//1、是否选择了试管架/加热区 |
|
|
|
if (!selectedTrayList.value.length) { |
|
|
|
ElMessage.error("请选择试管架"); |
|
|
|
return; |
|
|
|
} |
|
|
|
//2、只能选择一个试管架/加热区 |
|
|
|
if (selectedTrayList.value.length != 1) { |
|
|
|
ElMessage.error("只能选择一个试管架"); |
|
|
|
return; |
|
|
|
} |
|
|
|
let selectedDataItem = selectedTrayList.value[0]; |
|
|
|
selectedDataItem.isSelect = false |
|
|
|
//3、选择的加热区有没有试管架 |
|
|
|
if(!selectedDataItem.tubeList || !selectedDataItem.tubeList.length){ |
|
|
|
ElMessage.error("选择的加热区没有试管架"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
//4、发送移至加液区指令 |
|
|
|
const params = { |
|
|
|
areaId: selectedDataItem.id |
|
|
|
} |
|
|
|
onSendCmd('moveToActionArea', params) |
|
|
|
//更新UI |
|
|
|
heatList.value.forEach((item:any) => { |
|
|
|
if(item.id == selectedDataItem.id){ |
|
|
|
tubeList = [...item.tubeList] |
|
|
|
item.tubeList = null; |
|
|
|
|
|
|
|
} |
|
|
|
}) |
|
|
|
onSelectedTray(selectedDataItem) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//开门 |
|
|
|
const onOPen = () => { |
|
|
|
const params = {}; |
|
|
|
const command: OperationCmd = "openDoor"; |
|
|
|
taskCmd({ command, params }).then((res) => { |
|
|
|
if (res.success) { |
|
|
|
ElMessage.success("指令已发送,请稍等"); |
|
|
|
} else { |
|
|
|
ElMessage.error(res.msg); |
|
|
|
} |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
//开始加热 |
|
|
|
const onStartHeat = () => { |
|
|
|
//选择的加热区 |
|
|
|
if (!selectedTrayList.value.length) { |
|
|
|
ElMessage.error("请选择目标加热区"); |
|
|
|
return; |
|
|
|
} |
|
|
|
//判断选中的加热区是否有试管架 |
|
|
|
let existTubeRack = true; |
|
|
|
//加热区是否设置了加热温度 |
|
|
|
let hasSetTemp = true; |
|
|
|
selectedTrayList.value.forEach((item:any) => { |
|
|
|
let tubeList = item.tubeList; |
|
|
|
if(!tubeList || !tubeList.length){ |
|
|
|
|
|
|
|
existTubeRack = false; |
|
|
|
} |
|
|
|
if(!item.temperature){ |
|
|
|
hasSetTemp = false; |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
if(!existTubeRack){ |
|
|
|
ElMessage.error("选择的加热区未放置试管架,请重新选择") |
|
|
|
return; |
|
|
|
} |
|
|
|
if(!hasSetTemp){ |
|
|
|
ElMessage.error("选择的加热区未设置温度,请设置温度") |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
//后台批量发送指令 |
|
|
|
|
|
|
|
const cmdList:any = [] |
|
|
|
selectedTrayList.value.forEach((heatArea:any) => { |
|
|
|
cmdList.push({ |
|
|
|
command:'startHeat', |
|
|
|
params:{ |
|
|
|
areaId : heatArea.id |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
console.log('cmdList---', cmdList) |
|
|
|
onSendCmd("startHeat", cmdList) |
|
|
|
} |
|
|
|
|
|
|
|
const onSendCmd = (command:OperationCmd,params:any)=> { |
|
|
|
//发送加热指令 |
|
|
|
taskCmd({ command, params }).then((res) => { |
|
|
|
if (res.success) { |
|
|
|
ElMessage.success("指令已发送,请稍等"); |
|
|
|
} else { |
|
|
|
ElMessage.error(res.msg); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
</script> |
|
|
|
<style lang="scss" scoped> |
|
|
@ -268,4 +555,23 @@ const onMoveToHeat = () => { |
|
|
|
background: #ffffff; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.home_tube { |
|
|
|
height: 12rem; |
|
|
|
background: #384d5d; |
|
|
|
opacity: 1; |
|
|
|
margin-top: 0.5rem; |
|
|
|
display: flex; |
|
|
|
flex-wrap: wrap; |
|
|
|
gap: 0.5rem; |
|
|
|
padding-left: 0.4rem; |
|
|
|
padding-top: 0.2rem; |
|
|
|
border-radius: 1.5rem; |
|
|
|
.inner-circle { |
|
|
|
border-radius: 50%; |
|
|
|
width: 2.5rem; |
|
|
|
height: 2.5rem; |
|
|
|
background-color: rgb(212, 212, 212); |
|
|
|
} |
|
|
|
} |
|
|
|
</style> |