Browse Source

添加banner,仿真添加手动控制

一只小菜杨 1 month ago
parent
commit
a7139cf3e0

+ 1 - 1
package.json

@@ -18,7 +18,7 @@
     "pinia-plugin-persistedstate": "^4.2.0",
     "qrcode": "^1.5.4",
     "uuid": "^11.0.5",
-    "vant": "^4.9.15",
+    "vant": "^4.9.19",
     "vue": "^3.5.13",
     "vue-draggable-next": "^2.2.1",
     "vue-router": "^4.5.0"

+ 4 - 4
pnpm-lock.yaml

@@ -21,8 +21,8 @@ dependencies:
     specifier: ^11.0.5
     version: 11.1.0
   vant:
-    specifier: ^4.9.15
-    version: 4.9.17(vue@3.5.13)
+    specifier: ^4.9.19
+    version: 4.9.19(vue@3.5.13)
   vue:
     specifier: ^3.5.13
     version: 3.5.13(typescript@5.6.3)
@@ -4241,8 +4241,8 @@ packages:
     hasBin: true
     dev: false
 
-  /vant@4.9.17(vue@3.5.13):
-    resolution: {integrity: sha512-so/plH9G4au9rUrqy7yLB1slmZqY2Ub3Gq8ckMSQXbUEwTzmViVjEDqh/hNck/XELxwL+gIy2hLXiSgVcf2EBQ==}
+  /vant@4.9.19(vue@3.5.13):
+    resolution: {integrity: sha512-fRt32XI0fO0vB3/YGhZOpTnHKjplUiNuA05yZy8rPZntmbQE5GA57Y7iC7jmMDxSOaLebovynhgCvWnyk9zmDw==}
     peerDependencies:
       vue: ^3.0.0
     dependencies:

BIN
src/assets/images/banner1.png


BIN
src/assets/images/banner2.png


BIN
src/assets/images/banner3.png


BIN
src/assets/images/banner4.png


BIN
src/assets/images/banner5.png


BIN
src/assets/images/banner6.png


BIN
src/assets/images/banner7.png


+ 3 - 0
src/assets/main.css

@@ -27,3 +27,6 @@ body {
 .van-button {
   white-space: nowrap;
 }
+.van-cell:after  {
+  border-color: #ccc !important;
+}

+ 1 - 1
src/stores/global.ts

@@ -54,7 +54,7 @@ export const useGlobalStore = defineStore(
                     id: '131',
                     type: 'output',
                     label: 'DO1',
-                    value: '开'
+                    value: '开'
                   },
                   {
                     id: '132',

+ 193 - 68
src/utils/worker.js

@@ -1,3 +1,16 @@
+const formatDateTime = (date) => {
+  if (!date) return
+  const dateObj = new Date(date)
+  const year = dateObj.getFullYear()
+  const month = String(dateObj.getMonth() + 1).padStart(2, '0')
+  const day = String(dateObj.getDate()).padStart(2, '0')
+  const hours = String(dateObj.getHours()).padStart(2, '0')
+  const minutes = String(dateObj.getMinutes()).padStart(2, '0')
+  const seconds = String(dateObj.getSeconds()).padStart(2, '0')
+
+  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+}
+
 const yieldCPU = () => {
   return new Promise((resolve) => {
     setTimeout(resolve, 0)
@@ -14,7 +27,7 @@ const initWasm = (url) => {
         drawPlc('DO', i, _GetTrustDO(i))
       }
       for (let i = 0; i < 2; i++) {
-        drawPlc('AO', i, _GetTrustAO(i))
+        drawPlc('AO', i, Math.floor(_GetTrustAO(i) / 100))
       }
       drawPlc('time', 0, _GetTimeStamp())
       await yieldCPU() // 让出cpu,保证有间隔进入消息接收回调
@@ -30,26 +43,58 @@ const canvasMsg = {
   height: 0
 }
 
+let distance = 48 // 每个模块间隔
+let textDis = 60
+let r = 18
+let l_r_dis = 25
 const drawPlc = async (key, index, val, type) => {
   if (!ctx) return
-  const distance = 48 // 每个模块间隔
-  const textDis = 95
-  const r = 18
-  const l_r_dis = 50
-  ctx.font = '24px 黑体'
+  ctx.font = `${16 * ratioPixel}px 黑体`
   ctx.textAlign = 'start'
   ctx.globalAlpha = 1
   if (type === 'init') {
     ctx.beginPath()
-    ctx.rect(l_r_dis - r - 10, 50 - r - 10, (r + 10) * 2, distance * 11 + r / 4)
-    ctx.rect(l_r_dis - r - 10, 50 - r - 10 + distance * 11 + r / 4, (r + 10) * 2, distance * 2)
-    ctx.rect(l_r_dis - r - 10, 50 - r - 10 + distance * 13 + r / 4, (r + 10) * 2, distance * 2)
+    const modBetweenDis = (distance - 2 * r) / 2
+    const safeDis = 10
+    ctx.rect(
+      l_r_dis - r - safeDis,
+      l_r_dis - r - safeDis,
+      (r + safeDis) * 2,
+      distance * 10 + r * 2 + safeDis + modBetweenDis
+    )
+    ctx.rect(l_r_dis - r - safeDis, l_r_dis + distance * 10 + r + modBetweenDis, (r + safeDis) * 2, distance * 2)
+    ctx.rect(l_r_dis - r - safeDis, l_r_dis + distance * 12 + r + modBetweenDis, (r + safeDis) * 2, distance * 2)
 
-    ctx.rect(canvasMsg.width - l_r_dis - r - 10, 50 - r - 10, (r + 10) * 2, distance * 11 + r / 4)
-    ctx.rect(canvasMsg.width - l_r_dis - r - 10, 50 + distance * 11 - r - 10 + r / 4, (r + 10) * 2, distance * 5)
-    ctx.rect(canvasMsg.width - l_r_dis - r - 10, 50 + distance * 16 - r - 10 + r / 4, (r + 10) * 2, distance * 2)
-    ctx.rect(canvasMsg.width - l_r_dis - r - 10, 50 + distance * 18 - r - 10 + r / 4, (r + 10) * 2, distance * 2)
-    ctx.rect(canvasMsg.width - l_r_dis - r - 10, 50 + distance * 20 - r - 10 + r / 4, (r + 10) * 2, distance * 2)
+    ctx.rect(
+      canvasMsg.width - l_r_dis - r - safeDis,
+      l_r_dis - r - safeDis,
+      (r + safeDis) * 2,
+      distance * 10 + r * 2 + safeDis + modBetweenDis
+    )
+    ctx.rect(
+      canvasMsg.width - l_r_dis - r - safeDis,
+      l_r_dis + distance * 10 + r + modBetweenDis,
+      (r + safeDis) * 2,
+      distance * 5
+    )
+    ctx.rect(
+      canvasMsg.width - l_r_dis - r - safeDis,
+      l_r_dis + distance * 15 + r + modBetweenDis,
+      (r + safeDis) * 2,
+      distance * 2
+    )
+    ctx.rect(
+      canvasMsg.width - l_r_dis - r - safeDis,
+      l_r_dis + distance * 17 + r + modBetweenDis,
+      (r + safeDis) * 2,
+      distance * 2
+    )
+    ctx.rect(
+      canvasMsg.width - l_r_dis - r - safeDis,
+      l_r_dis + distance * 19 + r + modBetweenDis,
+      (r + safeDis) * 2,
+      distance * 2
+    )
     ctx.stroke()
     ctx.closePath()
 
@@ -57,152 +102,180 @@ const drawPlc = async (key, index, val, type) => {
     ctx.beginPath()
     // ctx.fillStyle = val !== 0 ? 'green' : 'red'
     ctx.fillStyle = 'red'
-    ctx.arc(canvasMsg.width - l_r_dis, 50 + distance * 11, r, 0, 2 * Math.PI)
+    ctx.arc(canvasMsg.width - l_r_dis, l_r_dis + distance * 11, r, 0, 2 * Math.PI)
     ctx.fill()
     const GND_textInfo = ctx.measureText(`GND`)
     ctx.fillStyle = 'black'
     ctx.fillText(
       `GND`,
       canvasMsg.width - textDis - GND_textInfo.width,
-      50 + distance * 11 + GND_textInfo.actualBoundingBoxAscent / 2
+      l_r_dis + distance * 11 + GND_textInfo.actualBoundingBoxAscent / 2
     )
 
     // left com
     ctx.beginPath()
     ctx.fillStyle = 'red'
-    ctx.arc(l_r_dis, 50, r, 0, 2 * Math.PI)
+    ctx.arc(l_r_dis, l_r_dis, r, 0, 2 * Math.PI)
     ctx.fill()
     const textInfo = ctx.measureText('COM')
     ctx.fillStyle = 'black'
-    ctx.fillText('COM', textDis, 50 + textInfo.actualBoundingBoxAscent / 2)
+    ctx.fillText('COM', textDis, l_r_dis + textInfo.actualBoundingBoxAscent / 2)
 
     // right com
     ctx.beginPath()
     ctx.fillStyle = 'red'
-    ctx.arc(canvasMsg.width - l_r_dis, 50, r, 0, 2 * Math.PI)
+    ctx.arc(canvasMsg.width - l_r_dis, l_r_dis, r, 0, 2 * Math.PI)
     ctx.fill()
     ctx.fillStyle = 'black'
-    ctx.fillText('COM', canvasMsg.width - textDis - textInfo.width, 50 + textInfo.actualBoundingBoxAscent / 2)
+    ctx.fillText('COM', canvasMsg.width - textDis - textInfo.width, l_r_dis + textInfo.actualBoundingBoxAscent / 2)
 
     // RS485
     ctx.beginPath()
     ctx.fillStyle = '#a3a3a3'
-    ctx.arc(l_r_dis, 50 + 11 * distance, r, 0, 2 * Math.PI)
+    ctx.arc(l_r_dis, l_r_dis + 11 * distance, r, 0, 2 * Math.PI)
     ctx.fill()
     ctx.beginPath()
     ctx.fillStyle = '#a3a3a3'
-    ctx.arc(l_r_dis, 50 + 12 * distance, r, 0, 2 * Math.PI)
+    ctx.arc(l_r_dis, l_r_dis + 12 * distance, r, 0, 2 * Math.PI)
     ctx.fill()
     const rs485_textInfo = ctx.measureText('RS485_0')
     ctx.fillStyle = 'black'
-    ctx.fillText('RS485_0', textDis, 50 + 11 * distance + r + rs485_textInfo.actualBoundingBoxAscent / 2)
+    ctx.fillText('RS485_0', textDis, l_r_dis + 11 * distance + r + rs485_textInfo.actualBoundingBoxAscent / 2)
 
     let a_textInfo = ctx.measureText('A')
-    ctx.fillText('A', l_r_dis - a_textInfo.width / 2, 50 + 11 * distance + a_textInfo.actualBoundingBoxAscent / 2)
+    ctx.fillText('A', l_r_dis - a_textInfo.width / 2, l_r_dis + 11 * distance + a_textInfo.actualBoundingBoxAscent / 2)
     let b_textInfo = ctx.measureText('B')
-    ctx.fillText('B', l_r_dis - a_textInfo.width / 2, 50 + 12 * distance + b_textInfo.actualBoundingBoxAscent / 2)
+    ctx.fillText('B', l_r_dis - a_textInfo.width / 2, l_r_dis + 12 * distance + b_textInfo.actualBoundingBoxAscent / 2)
 
     ctx.beginPath()
     ctx.fillStyle = '#a3a3a3'
-    ctx.arc(l_r_dis, 50 + 13 * distance, r, 0, 2 * Math.PI)
+    ctx.arc(l_r_dis, l_r_dis + 13 * distance, r, 0, 2 * Math.PI)
     ctx.fill()
     ctx.beginPath()
     ctx.fillStyle = '#a3a3a3'
-    ctx.arc(l_r_dis, 50 + 14 * distance, r, 0, 2 * Math.PI)
+    ctx.arc(l_r_dis, l_r_dis + 14 * distance, r, 0, 2 * Math.PI)
     ctx.fill()
     ctx.fillStyle = 'black'
-    ctx.fillText('RS485_1', textDis, 50 + 13 * distance + r + rs485_textInfo.actualBoundingBoxAscent / 2)
+    ctx.fillText('RS485_1', textDis, l_r_dis + 13 * distance + r + rs485_textInfo.actualBoundingBoxAscent / 2)
 
-    ctx.fillText('A', l_r_dis - a_textInfo.width / 2, 50 + 13 * distance + a_textInfo.actualBoundingBoxAscent / 2)
-    ctx.fillText('B', l_r_dis - a_textInfo.width / 2, 50 + 14 * distance + b_textInfo.actualBoundingBoxAscent / 2)
+    ctx.fillText('A', l_r_dis - a_textInfo.width / 2, l_r_dis + 13 * distance + a_textInfo.actualBoundingBoxAscent / 2)
+    ctx.fillText('B', l_r_dis - a_textInfo.width / 2, l_r_dis + 14 * distance + b_textInfo.actualBoundingBoxAscent / 2)
 
     // 配置按钮
     ctx.beginPath()
     const response1 = await fetch(new URL('/wxapp/canvas-icon/cfg-icon.png', self.location.href).href)
     const blob1 = await response1.blob()
     const imgBitmap1 = await createImageBitmap(blob1)
-    ctx.drawImage(imgBitmap1, l_r_dis - r - 10, 50 + distance * 16 - r, (r + 10) * 2, (r + 10) * 2)
+    ctx.drawImage(imgBitmap1, l_r_dis - r - 10, l_r_dis + distance * 16 - r, (r + 10) * 2, (r + 10) * 2)
     const cfg_textInfo = ctx.measureText('配置按钮')
-    ctx.fillText('配置按钮', textDis, 50 + 16 * distance + cfg_textInfo.actualBoundingBoxAscent / 2 + r - 10)
+    ctx.fillText(
+      '配置按钮',
+      textDis,
+      l_r_dis + 16 * distance + cfg_textInfo.actualBoundingBoxAscent / 2 + r - 10 * ratioPixel
+    )
     // 屏接口
     ctx.beginPath()
     const response2 = await fetch(new URL('/wxapp/canvas-icon/screen-icon.png', self.location.href).href)
     const blob2 = await response2.blob()
     const imgBitmap2 = await createImageBitmap(blob2)
-    ctx.drawImage(imgBitmap2, l_r_dis - r + 5, 50 + distance * 18 - 20, 25, 80)
+    ctx.drawImage(imgBitmap2, l_r_dis - r + 5, l_r_dis + distance * 18 - 20, 25 * ratioPixel, 80 * ratioPixel)
     const screen_textInfo = ctx.measureText('屏接口')
-    ctx.fillText('屏接口', textDis, 50 + 18 * distance + screen_textInfo.actualBoundingBoxAscent / 2 + 40 - 20)
+    ctx.fillText(
+      '屏接口',
+      textDis,
+      l_r_dis + 18 * distance + screen_textInfo.actualBoundingBoxAscent / 2 + 40 * ratioPixel
+    )
     // 指示灯
     ctx.beginPath()
     const response3 = await fetch(new URL('/wxapp/canvas-icon/light-icon.png', self.location.href).href)
     const blob3 = await response3.blob()
     const imgBitmap3 = await createImageBitmap(blob3)
-    ctx.drawImage(imgBitmap3, l_r_dis - r, 50 + distance * 19 + 40, 40, 40)
+    ctx.drawImage(imgBitmap3, l_r_dis - r, l_r_dis + distance * 19 + 40 * ratioPixel, 35 * ratioPixel, 35 * ratioPixel)
     const light1_textInfo = ctx.measureText('指示灯')
-    ctx.fillText('指示灯', textDis, 50 + 19 * distance + light1_textInfo.actualBoundingBoxAscent / 2 + 60)
+    ctx.fillText(
+      '指示灯',
+      textDis,
+      l_r_dis + 19 * distance + light1_textInfo.actualBoundingBoxAscent / 2 + 60 * ratioPixel
+    )
     // 网口
     ctx.beginPath()
     const response6 = await fetch(new URL('/wxapp/canvas-icon/rg45-icon.png', self.location.href).href)
     const blob6 = await response6.blob()
     const imgBitmap6 = await createImageBitmap(blob6)
-    ctx.drawImage(imgBitmap6, l_r_dis - r, 50 + distance * 21 + 20, 40, 40)
+    ctx.drawImage(imgBitmap6, l_r_dis - r, l_r_dis + distance * 21 + 20, 40 * ratioPixel, 40 * ratioPixel)
     const rg45_textInfo = ctx.measureText('网口')
-    ctx.fillText('网口', textDis, 50 + 21 * distance + rg45_textInfo.actualBoundingBoxAscent / 2 + 40)
+    ctx.fillText(
+      '网口',
+      textDis,
+      l_r_dis + 21 * distance + rg45_textInfo.actualBoundingBoxAscent / 2 + (40 * ratioPixel) / 2 + 20
+    )
     // logo
     ctx.beginPath()
     const response4 = await fetch(new URL('/wxapp/canvas-icon/logo-icon.png', self.location.href).href)
     const blob4 = await response4.blob()
     const imgBitmap4 = await createImageBitmap(blob4)
-    ctx.drawImage(imgBitmap4, canvasMsg.width / 2 - imgBitmap4.width / 2, 100)
+    ctx.drawImage(
+      imgBitmap4,
+      canvasMsg.width / 2 - (imgBitmap4.width * ratioPixel) / 4,
+      100 * ratioPixel,
+      (imgBitmap4.width / 2) * ratioPixel,
+      (imgBitmap4.height / 2) * ratioPixel
+    )
     // brand
     ctx.beginPath()
     const response5 = await fetch(new URL('/wxapp/canvas-icon/brand.png', self.location.href).href)
     const blob5 = await response5.blob()
     const imgBitmap5 = await createImageBitmap(blob5)
-    ctx.drawImage(imgBitmap5, canvasMsg.width / 2 - 140 / 2, 300, 140, (140 * imgBitmap5.height) / imgBitmap5.width)
+    ctx.drawImage(
+      imgBitmap5,
+      canvasMsg.width / 2 - (140 * ratioPixel) / 4,
+      200 * ratioPixel,
+      (140 * ratioPixel) / 2,
+      (((140 * imgBitmap5.height) / imgBitmap5.width) * ratioPixel) / 2
+    )
 
     // GND VCC
     ctx.beginPath()
     ctx.fillStyle = '#a3a3a3'
-    ctx.arc(canvasMsg.width - l_r_dis, 50 + distance * 20, r, 0, 2 * Math.PI)
+    ctx.arc(canvasMsg.width - l_r_dis, l_r_dis + distance * 20, r, 0, 2 * Math.PI)
     ctx.fill()
     ctx.fillStyle = 'black'
     ctx.fillText(
       `GND`,
       canvasMsg.width - textDis - GND_textInfo.width,
-      50 + distance * 20 + GND_textInfo.actualBoundingBoxAscent / 2
+      l_r_dis + distance * 20 + GND_textInfo.actualBoundingBoxAscent / 2
     )
     ctx.beginPath()
     ctx.fillStyle = '#a3a3a3'
-    ctx.arc(canvasMsg.width - l_r_dis, 50 + distance * 21, r, 0, 2 * Math.PI)
+    ctx.arc(canvasMsg.width - l_r_dis, l_r_dis + distance * 21, r, 0, 2 * Math.PI)
     ctx.fill()
     ctx.fillStyle = 'black'
     const VCC_textInfo = ctx.measureText(`VCC`)
     ctx.fillText(
       `VCC`,
       canvasMsg.width - textDis - VCC_textInfo.width,
-      50 + distance * 21 + VCC_textInfo.actualBoundingBoxAscent / 2
+      l_r_dis + distance * 21 + VCC_textInfo.actualBoundingBoxAscent / 2
     )
     ctx.fillText(
       '+',
       canvasMsg.width - l_r_dis - a_textInfo.width / 2,
-      50 + 21 * distance + a_textInfo.actualBoundingBoxAscent / 2
+      l_r_dis + 21 * distance + a_textInfo.actualBoundingBoxAscent / 2
     )
     ctx.fillText(
       '-',
       canvasMsg.width - l_r_dis - a_textInfo.width / 2,
-      50 + 20 * distance + b_textInfo.actualBoundingBoxAscent / 2
+      l_r_dis + 20 * distance + b_textInfo.actualBoundingBoxAscent / 2
     )
 
     const tip_textInfo = ctx.measureText('当前plc系统时间')
     ctx.fillText(
       `当前plc系统时间`,
       canvasMsg.width / 2 - tip_textInfo.width / 2,
-      canvasMsg.height - 100 + tip_textInfo.actualBoundingBoxAscent
+      20 * ratioPixel + tip_textInfo.actualBoundingBoxAscent
     )
   }
 
-  const y = 50 + distance * (index + 1)
+  const y = l_r_dis + distance * (index + 1)
   switch (key) {
     case 'DI':
     case 'DO':
@@ -236,9 +309,9 @@ const drawPlc = async (key, index, val, type) => {
           y + distance * 11 + AI_textInfo.actualBoundingBoxAscent / 2
         )
       }
-      ctx.clearRect(canvasMsg.width - textDis - AI_textInfo.width - 120, y + distance * 11 - r, 100, r * 2)
+      ctx.clearRect(canvasMsg.width - textDis - (60 + 100) * ratioPixel, y + distance * 11 - r, 100 * ratioPixel, r * 2)
       ctx.beginPath()
-      ctx.rect(canvasMsg.width - textDis - AI_textInfo.width - 120, y + distance * 11 - r, 100, r * 2)
+      ctx.rect(canvasMsg.width - textDis - (60 + 100) * ratioPixel, y + distance * 11 - r, 100 * ratioPixel, r * 2)
       ctx.lineCap = 'round'
       ctx.stroke()
       ctx.closePath()
@@ -247,9 +320,9 @@ const drawPlc = async (key, index, val, type) => {
 
       ctx.fillText(
         `${val}`,
-        canvasMsg.width - textDis - AI_textInfo.width - 60 - AI_val_textInfo.width / 2,
+        canvasMsg.width - textDis - (60 + 50) * ratioPixel,
         y + distance * 11 + AI_textInfo.actualBoundingBoxAscent / 2,
-        100
+        100 * ratioPixel
       )
       break
     case 'AO':
@@ -269,9 +342,9 @@ const drawPlc = async (key, index, val, type) => {
         )
       }
 
-      ctx.clearRect(canvasMsg.width - textDis - AO_textInfo.width - 120, y1, 100, r * 2)
+      ctx.clearRect(canvasMsg.width - textDis - (60 + 100) * ratioPixel, y1, 100 * ratioPixel, r * 2)
       ctx.beginPath()
-      ctx.rect(canvasMsg.width - textDis - AO_textInfo.width - 120, y1, 100, r * 2)
+      ctx.rect(canvasMsg.width - textDis - (60 + 100) * ratioPixel, y1, 100 * ratioPixel, r * 2)
       ctx.lineCap = 'round'
       ctx.stroke()
       ctx.closePath()
@@ -279,9 +352,9 @@ const drawPlc = async (key, index, val, type) => {
       const AO_val_textInfo = ctx.measureText(`${val}`)
       ctx.fillText(
         `${val}`,
-        canvasMsg.width - textDis - AO_textInfo.width - 60 - AO_val_textInfo.width / 2.5,
+        canvasMsg.width - textDis - (60 + 50) * ratioPixel,
         y1 + r + AO_textInfo.actualBoundingBoxAscent / 2,
-        100
+        100 * ratioPixel
       )
       if (type === 'draw') {
         ctx.textAlign = 'start'
@@ -306,48 +379,100 @@ const drawPlc = async (key, index, val, type) => {
       }
       break
     case 'time':
-      const timeVal = new Date(val * 1000).toLocaleString()
+      const timeVal = formatDateTime(val * 1000)
       const valTextInfo = ctx.measureText(timeVal)
       ctx.clearRect(
         canvasMsg.width / 2 - valTextInfo.width / 2,
-        canvasMsg.height - 50 - valTextInfo.actualBoundingBoxAscent,
+        25 * ratioPixel + l_r_dis - valTextInfo.actualBoundingBoxAscent,
         valTextInfo.width,
-        valTextInfo.actualBoundingBoxAscent * 2
+        valTextInfo.actualBoundingBoxAscent * 2 + 4
       )
       ctx.fillText(
         timeVal,
         canvasMsg.width / 2 - valTextInfo.width / 2,
-        canvasMsg.height - 50 + valTextInfo.actualBoundingBoxAscent
+        25 * ratioPixel + l_r_dis + valTextInfo.actualBoundingBoxAscent
       )
   }
 }
 
+const DIValMap = {}
+
 onmessage = (e) => {
-  const { type, url, ratio, canvas } = e.data
+  const { type, url, ratio, canvas, position, val, i } = e.data
   if (!canvasEl && type === 'init') {
     canvasEl = canvas
     ctx = canvas.getContext('2d')
     initWasm(url)
     canvasMsg.width = canvas.width
     canvasMsg.height = canvas.height
+    // ratioPixel = Math.ceil(canvas.width / ratio / 375)
     ratioPixel = ratio
+    distance *= ratioPixel
+    textDis *= ratioPixel
+    r *= ratioPixel
+    console.log(r)
+
+    l_r_dis *= ratioPixel
+    console.log(ratioPixel)
     drawPlc(null, null, null, 'init')
     for (let i = 0; i < 10; i++) {
       drawPlc('DI', i, 0, 'draw')
       drawPlc('DO', i, 0, 'draw')
+      DIValMap[i] = 0
     }
     for (let i = 0; i < 4; i++) {
-      drawPlc('AI', i, i, 'draw')
+      drawPlc('AI', i, 0, 'draw')
     }
     for (let i = 0; i < 2; i++) {
-      drawPlc('AO', i, i, 'draw')
+      drawPlc('AO', i, 0, 'draw')
     }
   }
-  if (type === 1) {
-    if (sendMsg.type === 'AI') {
-      _SetTrustAI(sendMsg.index, +sendMsg.valuel)
-    } else {
-      _SetTrustDI(sendMsg.index, +sendMsg.value)
+  if (type === 'set') {
+    console.log(+i, +val);
+    
+    _SetTrustAI(+i, +val * 100)
+    drawPlc('AI', +i, +val)
+  }
+  if (type === 'time') {
+    _SetDiffTimeStamp(+val)
+  }
+  if (type === 'click') {
+    if (!position) return
+    const { x, y } = position
+    console.log(x, y)
+
+    // 判断是否点击到DI
+    for (let i = 0; i < 10; i++) {
+      const p_to_p_dis = Math.sqrt((l_r_dis - x * ratio) ** 2 + (l_r_dis + distance * (i + 1) - y * ratio) ** 2)
+      if (p_to_p_dis <= r) {
+        if (DIValMap[i] === 0) {
+          DIValMap[i] = 1
+        } else {
+          DIValMap[i] = 0
+        }
+        _SetTrustDI(i, DIValMap[i])
+        drawPlc('DI', i, DIValMap[i])
+        return
+      }
+    }
+    // 判断是否点击到AI
+    for (let i = 0; i < 4; i++) {
+      const rectX = canvasMsg.width - textDis - (60 + 100) * ratioPixel
+      const rectY = l_r_dis + distance * (i + 1) + distance * 11 - r
+      if (x * ratio >= rectX && x <= rectX + 100 * ratioPixel && y * ratio >= rectY && y * ratio <= rectY + r * 2) {
+        postMessage({ type: 'AI', i })
+        return
+      }
+    }
+    // 判断是否点击时间
+    if (
+      x * ratio >= canvasMsg.width / 2 - 80 * ratio &&
+      x * ratio <= canvasMsg.width / 2 + 80 * ratio &&
+      y * ratio >= 20 * ratioPixel + l_r_dis &&
+      y * ratio <= 40 * ratio + l_r_dis
+    ) {
+      postMessage({ type: 'time' })
+      return
     }
   }
 }

+ 10 - 5
src/views/CommandInfo.vue

@@ -75,7 +75,7 @@
                         <van-field
                           v-model="cItem.value"
                           placeholder="请输入"
-                          :number="2.2"
+                          number="2.2"
                           type="number"
                           :style="{ width: item.type === 'exec' ? '60%' : '35%' }"
                         />
@@ -141,7 +141,9 @@
       </div>
       <div class="empty" v-else>当前没有可执行任务</div>
       <div class="footer" v-if="crtCommand!.stepList.length || isExistCnt">
-        <div class="tip">如果没有执行命令,则本条任务不会被编译;如果条件不满足,将会等待这一步执行完毕才会继续往下执行。</div>
+        <div class="tip">
+          如果没有执行命令,则本条任务不会被编译;如果条件不满足,将会等待这一步执行完毕才会继续往下执行。
+        </div>
       </div>
     </div>
 
@@ -305,7 +307,7 @@ const columnsMap: Record<string, any> = {
   ],
   commandValue: [
     { text: '闭合', value: '闭合' },
-    { text: '打开', value: '打开' }
+    { text: '断开', value: '断开' }
   ],
   operation: [
     { text: '≥', value: '≥' },
@@ -437,7 +439,7 @@ const handleEditCommand = (type: string, index: number, cIndex?: number) => {
         id: '' + Date.now(),
         type: 'output',
         label: 'DO0',
-        value: '开'
+        value: '开'
         // x,
         // y
       })
@@ -536,7 +538,7 @@ const handleAddCommand = async (type: string) => {
           {
             id,
             label: 'DO0',
-            value: '开',
+            value: '开',
             type: 'output'
             // x,
             // y: y + 100
@@ -754,6 +756,9 @@ onUnmounted(() => {
   margin: 0 0 8px 8px;
   width: 100%;
   color: #999;
+  word-break: break-all;
+  overflow-wrap: break-word;
+  white-space: normal;
 }
 .card .item .content {
   flex: 1;

+ 8 - 7
src/views/Home.vue

@@ -2,7 +2,7 @@
   <div class="home-container">
     <div class="banner">
       <van-swipe :autoplay="3000" indicator-color="white" lazy-render>
-        <van-swipe-item v-for="item in 6">
+        <van-swipe-item v-for="item in 7">
           <a :href="linkMap[item]">
             <van-image :src="requireImgUrl(`banner${item}.png`)" />
           </a>
@@ -35,12 +35,13 @@ const { projectList } = storeToRefs(useGlobalStore())
 const router = useRouter()
 
 const linkMap: { [key: number]: string } = {
-  1: 'https://item.taobao.com/item.htm?id=846463487388',
-  2: 'https://item.taobao.com/item.htm?id=816872336305',
-  3: 'https://item.taobao.com/item.htm?id=874093615168',
-  4: 'https://item.taobao.com/item.htm?id=893370676910',
-  5: 'https://item.taobao.com/item.htm?id=898547563519',
-  6: 'https://item.taobao.com/item.htm?id=834170367404'
+  1: 'https://item.taobao.com/item.htm?id=914847234768',
+  2: 'https://item.taobao.com/item.htm?id=846463487388',
+  3: 'https://item.taobao.com/item.htm?id=816872336305',
+  4: 'https://item.taobao.com/item.htm?id=874093615168',
+  5: 'https://item.taobao.com/item.htm?id=893370676910',
+  6: 'https://item.taobao.com/item.htm?id=898547563519',
+  7: 'https://item.taobao.com/item.htm?id=834170367404'
 }
 </script>
 

+ 4 - 3
src/views/ProjectInfo.vue

@@ -41,7 +41,7 @@
         <van-cell-group :style="{ marginTop: '16px' }">
           <van-cell title="编辑工程名称" clickable @click="handleEditName" :title-style="{ color: '#07c160' }" />
           <van-cell title="编译文件" @click="get_c_code('wx')" clickable :title-style="{ color: '#1989fa' }" />
-          <van-cell title="虚拟运行" @click="get_c_code('wxsimu')" clickable :title-style="{ color: '#1989fa' }" />
+          <van-cell title="仿真模拟" @click="get_c_code('wxsimu')" clickable :title-style="{ color: '#1989fa' }" />
           <van-cell clickable v-show="crtProject?.isCompiled">
             <template #title>
               <div v-html="wxtag" class="wx-tag"></div>
@@ -1544,7 +1544,8 @@ const get_c_code = (codetype: string) => {
   })
   const valMap: Record<string, number> = {
     闭合: 1,
-    打开: 0
+    断开: 0,
+    打开: 0,
   }
   const weekMap: Record<string, number> = {
     每天: 0,
@@ -1647,7 +1648,7 @@ static PT_THREAD(PROGRAM${index}_body(struct pt *pt, TIME *pt_delay))
           })
           if (condition_code !== '') {
             body_code += `
-        PT_WAIT_UNTIL(pt, ${condition_code});`
+    PT_WAIT_UNTIL(pt, ${condition_code});`
           }
         }
       })

+ 80 - 4
src/views/runtime.vue

@@ -7,8 +7,30 @@
       <div class="title">{{ crtProject?.name }}</div>
     </div>
     <div class="canvas-main" ref="canvasMainRef">
-      <canvas ref="canvasRef"></canvas>
+      <canvas ref="canvasRef" @click="handleCanvasClick"></canvas>
     </div>
+
+    <van-popup v-model:show="showPopup" round :style="{ width: '80%' }" :close-on-click-overlay="false">
+      <van-form @submit="onSubmit" label-width="48">
+        <van-cell-group inset>
+          <van-field border v-model="port" label="端口" readonly name="port" />
+          <van-field
+            border
+            v-model="newVal"
+            label="值"
+            name="newVal"
+            type="number"
+            number="2.2"
+            placeholder="请输入数值"
+            :rules="[{ required: true, message: '请填写要设置的值' }]"
+          />
+        </van-cell-group>
+        <div class="van-button-group">
+          <van-button @click="handleCancel" size="small">取消</van-button>
+          <van-button type="primary" native-type="submit" size="small">确定</van-button>
+        </div>
+      </van-form>
+    </van-popup>
   </div>
 </template>
 
@@ -39,10 +61,55 @@ const ratio = window.devicePixelRatio
 
 const runtimeWorker = new Worker(new URL('@/utils/worker.js', import.meta.url))
 
+const popupType = ref('')
+runtimeWorker.onmessage = (e) => {
+  console.log(e.data)
+  const { type, i } = e.data
+  if (type === 'AI') {
+    port.value = `AI${i}`
+  } else {
+    port.value = `设备时间与系统时间差值`
+  }
+  popupType.value = type
+  showPopup.value = true
+}
+const port = ref('AI0')
+const showPopup = ref(false)
+const newVal = ref(0)
+const onSubmit = () => {
+  runtimeWorker.postMessage({
+    type: popupType.value === 'AI' ? 'set' : 'time',
+    val: newVal.value,
+    i: port.value.slice(-1)
+  })
+  handleCancel()
+}
+const handleCancel = () => {
+  showPopup.value = false
+  newVal.value = 0
+}
+
+let timer: any
+
+const handleCanvasClick = (e: MouseEvent) => {
+  if (timer) clearTimeout(timer)
+  timer = setTimeout(() => {
+    runtimeWorker.postMessage({
+      type: 'click',
+      ratio,
+      position: {
+        x: e.offsetX,
+        y: e.offsetY
+      }
+    })
+  }, 50)
+}
+
 onMounted(() => {
   if (!canvasRef.value || !canvasMainRef.value) return
   const w = Math.floor(canvasMainRef.value.clientWidth)
-  const h = Math.floor(canvasMainRef.value.clientHeight)
+  // const h = Math.floor(canvasMainRef.value.clientHeight)
+  const h = 1150
   canvasRef.value.width = w * ratio
   canvasRef.value.height = h * ratio
   canvasRef.value.style.width = `${w}px`
@@ -66,8 +133,8 @@ onBeforeUnmount(() => {
 
 <style scoped>
 .runtime-container {
-  padding: 16px 0 0;
-  height: 100%;
+  padding: 16px 0;
+  /* height: 100%; */
   /* overflow: hidden; */
   background: #fff;
 }
@@ -91,4 +158,13 @@ onBeforeUnmount(() => {
   border: 1px solid #666;
   /* overflow: hidden; */
 }
+.van-button-group {
+  margin: 16px;
+  display: flex;
+  justify-content: center;
+  gap: 32px;
+}
+.van-form {
+  margin-top: 16px;
+}
 </style>