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.

664 lines
20 KiB

5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
  1. <script setup lang="ts">
  2. import { list as listMatrix } from 'apis/matrix'
  3. import { getListByMatrixId, update } from 'apis/matrixCraft'
  4. import { getDeviceStatus, getSprayStatus } from 'apis/system'
  5. import route from 'assets/images/route.png'
  6. import route_active from 'assets/images/route_active.png'
  7. import route_horizontal_active from 'assets/images/route_horizontal2.png'
  8. import route_horizontal from 'assets/images/route_horizontal.png'
  9. import route_vertical_active from 'assets/images/route_vertical2.png'
  10. import route_vertical from 'assets/images/route_vertical.png'
  11. import Edit from 'components/martixCraft/Edit/index.vue'
  12. import TrayGraph from 'components/spray/trayGraph/index.vue'
  13. import { FtMessage } from 'libs/message'
  14. import { socket } from 'libs/socket'
  15. import { colors, sendControl } from 'libs/utils'
  16. import { useSystemStore } from 'stores/useSystemStore'
  17. import { nextTick, onMounted, onUnmounted, ref } from 'vue'
  18. const systemStore = useSystemStore()
  19. onMounted(async () => {
  20. socket.init(sprayPointReceiveMessage, 'spray_point')
  21. socket.init(finishMessage, 'cmd_response')
  22. await getMatrixList()
  23. await getDeviceStatus().then((res: any) => {
  24. systemStore.updateSystemStatus(res)
  25. if (systemStore.systemStatus.spraying) {
  26. FtMessage.info('正在喷涂中...')
  27. maskVisible.value = true
  28. getSpraying()
  29. }
  30. })
  31. })
  32. onUnmounted(() => {
  33. socket.unregisterCallback(sprayPointReceiveMessage, 'spray_point')
  34. socket.unregisterCallback(finishMessage, 'cmd_response')
  35. })
  36. const matrixList = ref<{ name: string, id: number }[]>([])
  37. const getMatrixList = async () => {
  38. const res = await listMatrix({ pageNum: 1, pageSize: 100, matrixName: '' })
  39. matrixList.value = res.list
  40. }
  41. const matrixCraftList = ref<any>([])
  42. const matrixChange = async (value: number) => {
  43. form.value.matrixCraftId = undefined
  44. matrixCraftList.value = await getListByMatrixId(value)
  45. }
  46. const matrixCraftChange = (value: number) => {
  47. form.value = {
  48. ...form.value,
  49. ...matrixCraftList.value.find((item: { id: number }) => item.id === value),
  50. }
  51. // formRef.value.validate()
  52. }
  53. const getSpraying = async () => {
  54. const res: any = await getSprayStatus()
  55. const sprayTaskSprayedList = res.sprayTaskSprayedList
  56. cmdId = res.cmdId
  57. form.value = {
  58. ...res.sprayParams,
  59. position: [{ select: false }, { select: false }, { select: false }, { select: false }],
  60. }
  61. res.sprayParams.position.forEach((p: any) => {
  62. form.value.position[p.index] = p
  63. nextTick(() => {
  64. console.log('x', p.x1 * 5)
  65. console.log('y', p.y1 * 5)
  66. console.log('width', (p.x1 + p.x2) * 5)
  67. console.log('height', (p.y1 + p.y2) * 5)
  68. sprayRefs.value[p.index].updateSelection(p.x1 * 5, p.y1 * 5, (p.x2 - p.x1) * 5, (p.y2 - p.y1) * 5)
  69. })
  70. })
  71. sprayTaskSprayedList.forEach((task: any) => {
  72. nextTick(() => {
  73. drawLine(task.index, { x: task.sprayedPoints.x * 5, y: task.sprayedPoints.y * 5 }, task.number)
  74. })
  75. })
  76. }
  77. const sprayRefs = ref<any>([])
  78. const infoVisible = ref(false)
  79. const updateParam = () => {
  80. updateForm.value = {
  81. motorZHeight: form.value.motorZHeight, // 高度
  82. gasPressure: form.value.gasPressure, // Mpa兆帕
  83. volume: form.value.volume, // 单位uL微升
  84. highVoltage: form.value.highVoltage, // 是否打开高压
  85. highVoltageValue: form.value.highVoltageValue, // 高压值
  86. movingSpeed: form.value.movingSpeed, // 毫米
  87. }
  88. infoVisible.value = true
  89. }
  90. const updateFormRef = ref()
  91. const submitParam = async () => {
  92. updateFormRef.value.validate(async (valid: boolean) => {
  93. if (!valid) {
  94. return
  95. }
  96. const params = {
  97. cmdCode: 'matrix_spray_change_param',
  98. cmdId: '',
  99. params: updateForm.value,
  100. }
  101. form.value = {
  102. ...form.value,
  103. ...updateForm.value,
  104. }
  105. await sendControl(params)
  106. infoVisible.value = false
  107. })
  108. }
  109. const form = ref<SprayForm>({
  110. matrixId: undefined,
  111. matrixCraftId: undefined,
  112. matrixPathType: 'horizontal', // 路径类型
  113. motorZHeight: undefined, // 高度
  114. gasPressure: undefined, // Mpa兆帕
  115. volume: undefined, // 单位uL微升
  116. highVoltage: true, // 是否打开高压
  117. highVoltageValue: undefined, // 高压值
  118. spacing: undefined, // 毫米
  119. movingSpeed: undefined, // 移动速度
  120. times: undefined, // 喷涂遍数
  121. position: [
  122. { select: true, x1: 0, y1: 0, x2: 25, y2: 75, index: 0 },
  123. { select: false, x1: 0, y1: 0, x2: 25, y2: 75, index: 1 },
  124. { select: false, x1: 0, y1: 0, x2: 25, y2: 75, index: 2 },
  125. { select: false, x1: 0, y1: 0, x2: 25, y2: 75, index: 3 },
  126. ],
  127. })
  128. const updateForm = ref<SprayUpdateForm>({
  129. motorZHeight: undefined, // 高度
  130. gasPressure: undefined, // Mpa兆帕
  131. volume: undefined, // 单位uL微升
  132. highVoltage: true, // 是否打开高压
  133. highVoltageValue: undefined, // 高压值
  134. movingSpeed: undefined, // 移动速度
  135. })
  136. const formRef = ref()
  137. const checkPosition = () => {
  138. let position = form.value.position.filter((item: { select: boolean }) => item.select)
  139. if (!position.length) {
  140. FtMessage.error('至少选择一个玻片')
  141. return false
  142. }
  143. position = form.value.position
  144. .map((item: any, index: number) => {
  145. return {
  146. ...item,
  147. ...sprayRefs.value[index].getSelection(),
  148. index,
  149. }
  150. })
  151. .filter((item: { select: boolean }) => item.select)
  152. for (let i = 0; i < position.length; i++) {
  153. const p = position[i]
  154. if (p.x1 < 0 || p.y1 < 0 || p.x2 < 0 || p.y2 < 0 || p.x1 > 25 || p.y1 > 75 || p.x2 > 25 || p.y2 > 75) {
  155. FtMessage.error(`玻片${p.index + 1}喷涂区域超出玻片范围`)
  156. return false
  157. }
  158. }
  159. return position
  160. }
  161. const maskVisible = ref(false)
  162. const startWork = async () => {
  163. formRef.value.validate(async (valid: boolean) => {
  164. console.log('valid', valid)
  165. if (!valid) {
  166. FtMessage.error('请检查参数配置')
  167. }
  168. const position = checkPosition()
  169. if (!position) {
  170. return
  171. }
  172. cmdId = Date.now().toString()
  173. const params = {
  174. cmdCode: 'matrix_spray_start',
  175. cmdId,
  176. params: {
  177. ...form.value,
  178. position,
  179. },
  180. }
  181. maskVisible.value = true
  182. await sendControl(params)
  183. currentSpeed = Number(form.value.movingSpeed)
  184. })
  185. }
  186. const pauseWork = async () => {
  187. const params = {
  188. cmdCode: 'matrix_spray_pause',
  189. cmdId: '',
  190. }
  191. await sendControl(params)
  192. }
  193. const continueWork = async () => {
  194. const params = {
  195. cmdCode: 'matrix_spray_continue',
  196. cmdId: '',
  197. params: {
  198. motorZHeight: form.value.motorZHeight, // 高度
  199. gasPressure: form.value.gasPressure, // Mpa兆帕
  200. volume: form.value.volume, // 单位uL微升
  201. highVoltage: form.value.highVoltage, // 是否打开高压
  202. highVoltageValue: form.value.highVoltageValue, // 高压值
  203. movingSpeed: form.value.movingSpeed, // 毫米
  204. },
  205. }
  206. await sendControl(params)
  207. currentSpeed = Number(form.value.movingSpeed)
  208. }
  209. const stopWork = async () => {
  210. const params = {
  211. cmdCode: 'matrix_spray_stop',
  212. cmdId: '',
  213. }
  214. await sendControl(params)
  215. form.value.position.forEach((item, index) => {
  216. if (item.select) {
  217. sprayRefs.value[index].clearLines()
  218. }
  219. })
  220. }
  221. let currentSpeed = 0
  222. console.log(currentSpeed)
  223. // let poll: ReturnType<typeof setInterval>
  224. let cmdId = ''
  225. const sprayPointReceiveMessage = (data: any) => {
  226. if (data.cmdId === cmdId) {
  227. const { currentPoint, index, number } = data
  228. drawLine(index, { x: currentPoint.x * 5, y: currentPoint.y * 5 }, number)
  229. // drawLine(index, { x: nextPoint.x * 5, y: nextPoint.y * 5 }, number)
  230. }
  231. // if (poll) {
  232. // clearInterval(poll)
  233. // }
  234. // const { currentPoint, nextPoint, index } = data
  235. // // 计算累加的mm
  236. // const moveDistance = currentSpeed / 2
  237. // // y轴移动
  238. // if (currentPoint.x === nextPoint.x) {
  239. // let currentY = currentPoint.y
  240. // poll = setInterval(() => {
  241. // currentY += moveDistance
  242. // if (currentY >= nextPoint.y) {
  243. // clearInterval(poll)
  244. // }
  245. // drawLine(index, { x: currentPoint.x * 5, y: currentY * 5 })
  246. // }, 500)
  247. // }
  248. // else if (currentPoint.y === nextPoint.y) {
  249. // // x轴移动
  250. // let currentX = currentPoint.x
  251. // poll = setInterval(() => {
  252. // currentX += moveDistance
  253. // if (currentX >= nextPoint.x) {
  254. // clearInterval(poll)
  255. // }
  256. // drawLine(index, { x: currentX * 5, y: currentPoint.y * 5 })
  257. // }, 500)
  258. // }
  259. }
  260. const finishMessage = (data: any) => {
  261. if (data.cmdId === cmdId) {
  262. if (data.status === 'fail') {
  263. FtMessage.error(data.title)
  264. }
  265. if (data.status === 'success') {
  266. FtMessage.success('喷涂执行成功')
  267. }
  268. if (data.status === 'spray_task_finish') {
  269. form.value.position.forEach((item, index) => {
  270. if (item.select) {
  271. sprayRefs.value[index].clearLines()
  272. }
  273. })
  274. maskVisible.value = false
  275. }
  276. }
  277. }
  278. const drawLine = async (index: number, point: { x: number, y: number }, number: number) => {
  279. console.log('drawLine', sprayRefs.value[index], index, point, number)
  280. await nextTick(() => {
  281. if (!sprayRefs.value[index].hasLine(number)) {
  282. sprayRefs.value[index].addLine(colors[number])
  283. }
  284. sprayRefs.value[index].updateLine(point, number, colors[number])
  285. })
  286. }
  287. const updateFormRules = {
  288. motorZHeight: [
  289. {
  290. required: true,
  291. trigger: 'blur',
  292. validator: (rule: any, value: any, callback: any) => {
  293. setTimeout(() => {
  294. if (!value) {
  295. callback(new Error('请输入高度'))
  296. }
  297. else
  298. if (value < 15) {
  299. callback(new Error('最小安全高度为15mm'))
  300. }
  301. else {
  302. callback()
  303. }
  304. }, 500)
  305. },
  306. },
  307. ],
  308. gasPressure: [{ required: true, message: '请输入氮气气压', trigger: 'blur' }],
  309. volume: [{ required: true, trigger: 'blur', validator: (rule: any, value: any, callback: any) => {
  310. if (!value) {
  311. callback(new Error('请输入基质流速'))
  312. }
  313. else if (value > 100) {
  314. callback(new Error('基质流速最大为100 uL/min'))
  315. }
  316. else {
  317. callback()
  318. }
  319. } }],
  320. highVoltageValue: [
  321. {
  322. required: true,
  323. trigger: 'blur',
  324. validator: (rule: any, value: any, callback: any) => {
  325. if (form.value.highVoltage && !value) {
  326. callback(new Error('请输入电压'))
  327. }
  328. else if (value > 6000) {
  329. callback(new Error('最大电压为6000V'))
  330. }
  331. else {
  332. callback()
  333. }
  334. },
  335. },
  336. ],
  337. movingSpeed: [{ required: true, trigger: 'blur', validator: (rule: any, value: any, callback: any) => {
  338. if (!value) {
  339. callback(new Error('请输入移动速度'))
  340. }
  341. else if (value > 30) {
  342. callback(new Error('移动速度最大为30 mm/s'))
  343. }
  344. else {
  345. callback()
  346. }
  347. } }],
  348. }
  349. const rules = {
  350. matrixId: [{ required: true, message: '请选择基质', trigger: 'change' }],
  351. spacing: [{ required: true, message: '请输入间距', trigger: 'blur' }],
  352. times: [{ required: true, message: '请输入喷涂次数', trigger: 'blur' }],
  353. ...updateFormRules,
  354. }
  355. const addVisible = ref(false)
  356. const ok = async () => {
  357. addVisible.value = false
  358. matrixCraftList.value = await getListByMatrixId(form.value.matrixId as number)
  359. }
  360. const upDateLoading = ref(false)
  361. const updateCraft = async () => {
  362. if (!form.value.matrixCraftId) {
  363. FtMessage.error('请选择工艺')
  364. return
  365. }
  366. upDateLoading.value = true
  367. const params = {
  368. ...form.value,
  369. id: form.value.matrixCraftId,
  370. }
  371. await update(params)
  372. FtMessage.success('保存成功')
  373. upDateLoading.value = false
  374. }
  375. const formData = ref({})
  376. const addCraft = () => {
  377. console.log(form.value)
  378. formData.value = {
  379. ...form.value,
  380. id: undefined,
  381. name: undefined,
  382. }
  383. console.log(formData.value)
  384. addVisible.value = true
  385. }
  386. </script>
  387. <template>
  388. <div class="spray-container">
  389. <el-form ref="formRef" label-width="120" :model="form" :rules="rules">
  390. <div class="button-box">
  391. <ft-button type="primary" :disabled="systemStore.systemStatus.spraying" @click="startWork">
  392. 开始喷涂
  393. </ft-button>
  394. <ft-button type="primary" :disabled="!systemStore.systemStatus.spraying" @click="updateParam">
  395. 调整参数
  396. </ft-button>
  397. <ft-button type="primary" :disabled="!systemStore.systemStatus.suspendable" @click="pauseWork">
  398. 暂停喷涂
  399. </ft-button>
  400. <ft-button type="primary" :disabled="!systemStore.systemStatus.paused" @click="continueWork">
  401. 继续喷涂
  402. </ft-button>
  403. <ft-button type="primary" :disabled="!systemStore.systemStatus.spraying" @click="stopWork">
  404. 结束喷涂
  405. </ft-button>
  406. </div>
  407. <div class="spray-box">
  408. <div class="spray-left">
  409. <div style="display: flex; position: relative;height: fit-content">
  410. <div v-show="maskVisible" class="mask-box" />
  411. <div
  412. v-for="(p, index) in form.position"
  413. :key="index"
  414. style="display: flex; flex-direction: column; align-items: center"
  415. >
  416. <p class="tray-name">
  417. 玻片{{ index + 1 }}
  418. </p>
  419. <TrayGraph ref="sprayRefs" :key="index" :container="`spray-${index + 1}`" :select="p.select" />
  420. <el-checkbox v-model="p.select" />
  421. </div>
  422. </div>
  423. </div>
  424. <div class="spray-form">
  425. <el-form-item label="基质" prop="matrixId">
  426. <el-select v-model="form.matrixId" placeholder="" @change="matrixChange">
  427. <el-option v-for="item in matrixList" :key="item.id" :label="item.name" :value="item.id" />
  428. </el-select>
  429. </el-form-item>
  430. <el-form-item label="工艺">
  431. <el-select v-model="form.matrixCraftId" placeholder="" @change="matrixCraftChange">
  432. <el-option v-for="item in matrixCraftList" :key="item.id" :label="item.name" :value="item.id" />
  433. </el-select>
  434. </el-form-item>
  435. <el-form-item label="喷涂路线">
  436. <div class="route-img">
  437. <img
  438. style="margin-left: 20px"
  439. :src="form.matrixPathType === 'horizontal' ? route_horizontal_active : route_horizontal"
  440. alt=""
  441. @click="form.matrixPathType = 'horizontal'"
  442. >
  443. <img
  444. style="margin: 0 20px"
  445. :src="form.matrixPathType === 'vertical' ? route_vertical_active : route_vertical"
  446. alt=""
  447. @click="form.matrixPathType = 'vertical'"
  448. >
  449. <img
  450. :src="form.matrixPathType === 'grid' ? route_active : route"
  451. alt=""
  452. @click="form.matrixPathType = 'grid'"
  453. >
  454. </div>
  455. </el-form-item>
  456. <el-form-item label="Z轴高度" prop="motorZHeight">
  457. <el-input v-model="form.motorZHeight" type="number" />
  458. <span class="unit-text">mm</span>
  459. </el-form-item>
  460. <el-form-item label="氮气气压" prop="gasPressure">
  461. <el-input v-model="form.gasPressure" type="number" />
  462. <span class="unit-text">Mpa</span>
  463. </el-form-item>
  464. <el-form-item label="基质流速" prop="volume">
  465. <el-input v-model="form.volume" type="number" />
  466. <span class="unit-text">uL/min</span>
  467. </el-form-item>
  468. <el-form-item label="是否加电" prop="highVoltageValue">
  469. <div class="voltage-box">
  470. <el-switch v-model="form.highVoltage" />
  471. <el-input
  472. v-show="form.highVoltage"
  473. v-model="form.highVoltageValue"
  474. type="number"
  475. class="voltage-input"
  476. />
  477. <span v-show="form.highVoltage" class="unit-text"> V</span>
  478. </div>
  479. </el-form-item>
  480. <el-form-item label="移动速度" prop="movingSpeed">
  481. <el-input v-model="form.movingSpeed" type="number" />
  482. <span class="unit-text">mm/s</span>
  483. </el-form-item>
  484. <el-form-item label="间距" prop="spacing">
  485. <el-input v-model="form.spacing" type="number" />
  486. <span class="unit-text">mm</span>
  487. </el-form-item>
  488. <el-form-item label="喷涂次数" prop="times">
  489. <el-input v-model="form.times" type="number" />
  490. <span class="unit-text"></span>
  491. </el-form-item>
  492. <el-form-item>
  493. <div style="display: flex; justify-content: center;width: 100%">
  494. <ft-button type="primary" :loading="upDateLoading" @click="updateCraft">
  495. 更新工艺
  496. </ft-button>
  497. <ft-button type="primary" @click="addCraft">
  498. 保存到新工艺
  499. </ft-button>
  500. </div>
  501. </el-form-item>
  502. </div>
  503. </div>
  504. </el-form>
  505. <el-drawer v-model="infoVisible" title="调整参数" direction="rtl" :close-on-click-modal="false">
  506. <el-form ref="updateFormRef" :model="updateForm" label-width="auto" style="display: flex; flex-direction: column; justify-content: center" :rules="updateFormRules">
  507. <div>
  508. <el-form-item label="Z轴高度" prop="motorZHeight">
  509. <el-input v-model="updateForm.motorZHeight" type="number" />
  510. <span class="unit-text">mm</span>
  511. </el-form-item>
  512. <el-form-item label="氮气气压" prop="gasPressure">
  513. <el-input v-model="updateForm.gasPressure" type="number" />
  514. <span class="unit-text">Mpa</span>
  515. </el-form-item>
  516. <el-form-item label="基质流速" prop="volume">
  517. <el-input v-model="updateForm.volume" type="number" />
  518. <span class="unit-text">uL/min</span>
  519. </el-form-item>
  520. <el-form-item label="是否加电" prop="highVoltageValue">
  521. <div class="voltage-box">
  522. <el-switch v-model="updateForm.highVoltage" />
  523. <el-input
  524. v-show="updateForm.highVoltage"
  525. v-model="updateForm.highVoltageValue"
  526. type="number"
  527. class="voltage-input"
  528. />
  529. <span v-show="updateForm.highVoltage" class="unit-text"> V</span>
  530. </div>
  531. </el-form-item>
  532. <el-form-item label="移动速度" prop="movingSpeed">
  533. <el-input v-model="updateForm.movingSpeed" type="number" />
  534. <span class="unit-text">mm/s</span>
  535. </el-form-item>
  536. <el-form-item>
  537. <div style="display: flex; justify-content: center; width: 100%">
  538. <ft-button type="primary" @click="submitParam">
  539. 确定
  540. </ft-button>
  541. </div>
  542. </el-form-item>
  543. </div>
  544. </el-form>
  545. </el-drawer>
  546. <Edit v-if="addVisible" :matrix-list :form-data other-page @ok="ok" @cancel="addVisible = false" />
  547. </div>
  548. </template>
  549. <style scoped lang="scss">
  550. .spray-container {
  551. width: 100%;
  552. height: 100%;
  553. .el-form {
  554. width: 100%;
  555. height: 100%;
  556. display: flex;
  557. flex-direction: column;
  558. justify-content: space-between;
  559. overflow: auto;
  560. .button-box {
  561. display: flex;
  562. justify-content: center;
  563. }
  564. .spray-box {
  565. display: flex;
  566. justify-content: space-between;
  567. .spray-left {
  568. display: flex;
  569. flex-direction: column;
  570. justify-content: center
  571. }
  572. .spray-form {
  573. display: flex;
  574. flex-direction: column;
  575. justify-content: center;
  576. }
  577. }
  578. }
  579. }
  580. .el-input,
  581. .el-select {
  582. width: 400px;
  583. margin: 0 40px;
  584. }
  585. .unit-text {
  586. font-size: 40px;
  587. color: #0349a8;
  588. font-weight: 500;
  589. }
  590. .select-box {
  591. display: flex;
  592. margin: 40px;
  593. }
  594. .route-img {
  595. display: flex;
  596. img {
  597. width: 70px;
  598. }
  599. }
  600. .voltage-box {
  601. display: flex;
  602. align-items: center;
  603. margin-left: 40px;
  604. height: 100px;
  605. .voltage-input {
  606. width: 280px;
  607. }
  608. }
  609. :deep(.el-form-item__error) {
  610. font-size: 22px;
  611. margin-left: 50px;
  612. }
  613. .mask-box {
  614. position: absolute;
  615. width: 100%;
  616. height: 100%;
  617. background: rgba(255, 255, 255, 0.1);
  618. z-index: 2000;
  619. }
  620. .tray-name {
  621. color: var(--el-color-primary);
  622. }
  623. :deep(.el-drawer) {
  624. width: 33% !important;
  625. }
  626. </style>