|
@@ -0,0 +1,353 @@
|
|
|
+const yieldCPU = () => {
|
|
|
+ return new Promise((resolve) => {
|
|
|
+ setTimeout(resolve, 0)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const initWasm = (url) => {
|
|
|
+ importScripts(url)
|
|
|
+ Module.onRuntimeInitialized = async function () {
|
|
|
+ _Init()
|
|
|
+ while (true) {
|
|
|
+ _Loop()
|
|
|
+ for (let i = 0; i < 10; i++) {
|
|
|
+ drawPlc('DO', i, _GetTrustDO(i))
|
|
|
+ }
|
|
|
+ for (let i = 0; i < 2; i++) {
|
|
|
+ drawPlc('AO', i, _GetTrustAO(i))
|
|
|
+ }
|
|
|
+ drawPlc('time', 0, _GetTimeStamp())
|
|
|
+ await yieldCPU() // 让出cpu,保证有间隔进入消息接收回调
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+let canvasEl
|
|
|
+let ctx
|
|
|
+let ratioPixel
|
|
|
+const canvasMsg = {
|
|
|
+ width: 0,
|
|
|
+ height: 0
|
|
|
+}
|
|
|
+
|
|
|
+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.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)
|
|
|
+
|
|
|
+ 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.stroke()
|
|
|
+ ctx.closePath()
|
|
|
+
|
|
|
+ // GND
|
|
|
+ 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.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
|
|
|
+ )
|
|
|
+
|
|
|
+ // left com
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.fillStyle = 'red'
|
|
|
+ ctx.arc(l_r_dis, 50, r, 0, 2 * Math.PI)
|
|
|
+ ctx.fill()
|
|
|
+ const textInfo = ctx.measureText('COM')
|
|
|
+ ctx.fillStyle = 'black'
|
|
|
+ ctx.fillText('COM', textDis, 50 + textInfo.actualBoundingBoxAscent / 2)
|
|
|
+
|
|
|
+ // right com
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.fillStyle = 'red'
|
|
|
+ ctx.arc(canvasMsg.width - l_r_dis, 50, r, 0, 2 * Math.PI)
|
|
|
+ ctx.fill()
|
|
|
+ ctx.fillStyle = 'black'
|
|
|
+ ctx.fillText('COM', canvasMsg.width - textDis - textInfo.width, 50 + textInfo.actualBoundingBoxAscent / 2)
|
|
|
+
|
|
|
+ // RS485
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.fillStyle = '#a3a3a3'
|
|
|
+ ctx.arc(l_r_dis, 50 + 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.fill()
|
|
|
+ const rs485_textInfo = ctx.measureText('RS485_0')
|
|
|
+ ctx.fillStyle = 'black'
|
|
|
+ ctx.fillText('RS485_0', textDis, 50 + 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)
|
|
|
+ let b_textInfo = ctx.measureText('B')
|
|
|
+ ctx.fillText('B', l_r_dis - a_textInfo.width / 2, 50 + 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.fill()
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.fillStyle = '#a3a3a3'
|
|
|
+ ctx.arc(l_r_dis, 50 + 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('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.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)
|
|
|
+ const cfg_textInfo = ctx.measureText('配置按钮')
|
|
|
+ ctx.fillText('配置按钮', textDis, 50 + 16 * distance + cfg_textInfo.actualBoundingBoxAscent / 2 + r - 10)
|
|
|
+ // 屏接口
|
|
|
+ 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)
|
|
|
+ const screen_textInfo = ctx.measureText('屏接口')
|
|
|
+ ctx.fillText('屏接口', textDis, 50 + 18 * distance + screen_textInfo.actualBoundingBoxAscent / 2 + 40 - 20)
|
|
|
+ // 指示灯
|
|
|
+ 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)
|
|
|
+ const light1_textInfo = ctx.measureText('指示灯')
|
|
|
+ ctx.fillText('指示灯', textDis, 50 + 19 * distance + light1_textInfo.actualBoundingBoxAscent / 2 + 60)
|
|
|
+ // 网口
|
|
|
+ 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)
|
|
|
+ const rg45_textInfo = ctx.measureText('网口')
|
|
|
+ ctx.fillText('网口', textDis, 50 + 21 * distance + rg45_textInfo.actualBoundingBoxAscent / 2 + 40)
|
|
|
+ // 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)
|
|
|
+ // 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)
|
|
|
+
|
|
|
+ // GND VCC
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.fillStyle = '#a3a3a3'
|
|
|
+ ctx.arc(canvasMsg.width - l_r_dis, 50 + 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
|
|
|
+ )
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.fillStyle = '#a3a3a3'
|
|
|
+ ctx.arc(canvasMsg.width - l_r_dis, 50 + 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
|
|
|
+ )
|
|
|
+ ctx.fillText(
|
|
|
+ '+',
|
|
|
+ canvasMsg.width - l_r_dis - a_textInfo.width / 2,
|
|
|
+ 50 + 21 * distance + a_textInfo.actualBoundingBoxAscent / 2
|
|
|
+ )
|
|
|
+ ctx.fillText(
|
|
|
+ '-',
|
|
|
+ canvasMsg.width - l_r_dis - a_textInfo.width / 2,
|
|
|
+ 50 + 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
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ const y = 50 + distance * (index + 1)
|
|
|
+ switch (key) {
|
|
|
+ case 'DI':
|
|
|
+ case 'DO':
|
|
|
+ ctx.clearRect(key === 'DI' ? l_r_dis - r : canvasMsg.width - l_r_dis - r, y - r, r * 2, r * 2)
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.fillStyle = val !== 0 ? 'green' : 'red'
|
|
|
+ ctx.arc(key === 'DI' ? l_r_dis : canvasMsg.width - l_r_dis, y, r, 0, 2 * Math.PI)
|
|
|
+ ctx.fill()
|
|
|
+ const DI_DO_textInfo = ctx.measureText(`${key}_${index}`)
|
|
|
+ ctx.fillStyle = 'black'
|
|
|
+ if (type === 'draw') {
|
|
|
+ ctx.fillText(
|
|
|
+ `${key}_${index}`,
|
|
|
+ key === 'DI' ? textDis : canvasMsg.width - textDis - DI_DO_textInfo.width,
|
|
|
+ y + DI_DO_textInfo.actualBoundingBoxAscent / 2
|
|
|
+ )
|
|
|
+ }
|
|
|
+ break
|
|
|
+ case 'AI':
|
|
|
+ ctx.beginPath()
|
|
|
+ // ctx.fillStyle = val !== 0 ? 'green' : 'red'
|
|
|
+ const AI_textInfo = ctx.measureText(`${key}_${index}`)
|
|
|
+ if (type === 'draw') {
|
|
|
+ ctx.fillStyle = 'red'
|
|
|
+ ctx.arc(canvasMsg.width - l_r_dis, y + distance * 11, r, 0, 2 * Math.PI)
|
|
|
+ ctx.fill()
|
|
|
+ ctx.fillStyle = 'black'
|
|
|
+ ctx.fillText(
|
|
|
+ `${key}_${index}`,
|
|
|
+ canvasMsg.width - textDis - AI_textInfo.width,
|
|
|
+ y + distance * 11 + AI_textInfo.actualBoundingBoxAscent / 2
|
|
|
+ )
|
|
|
+ }
|
|
|
+ ctx.clearRect(canvasMsg.width - textDis - AI_textInfo.width - 120, y + distance * 11 - r, 100, r * 2)
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.rect(canvasMsg.width - textDis - AI_textInfo.width - 120, y + distance * 11 - r, 100, r * 2)
|
|
|
+ ctx.lineCap = 'round'
|
|
|
+ ctx.stroke()
|
|
|
+ ctx.closePath()
|
|
|
+ ctx.textAlign = 'center'
|
|
|
+ const AI_val_textInfo = ctx.measureText(`${val}`)
|
|
|
+
|
|
|
+ ctx.fillText(
|
|
|
+ `${val}`,
|
|
|
+ canvasMsg.width - textDis - AI_textInfo.width - 60 - AI_val_textInfo.width / 2,
|
|
|
+ y + distance * 11 + AI_textInfo.actualBoundingBoxAscent / 2,
|
|
|
+ 100
|
|
|
+ )
|
|
|
+ break
|
|
|
+ case 'AO':
|
|
|
+ const y1 = y + distance * (15 + index * 1)
|
|
|
+ // ctx.fillStyle = val !== 0 ? 'green' : 'red'
|
|
|
+ const AO_textInfo = ctx.measureText(`${key}_${index}`)
|
|
|
+ if (type === 'draw') {
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.fillStyle = 'red'
|
|
|
+ ctx.arc(canvasMsg.width - l_r_dis, y1, r, 0, 2 * Math.PI)
|
|
|
+ ctx.fill()
|
|
|
+ ctx.fillStyle = 'black'
|
|
|
+ ctx.fillText(
|
|
|
+ `${key}_${index}`,
|
|
|
+ canvasMsg.width - textDis - AO_textInfo.width,
|
|
|
+ y1 + r + AO_textInfo.actualBoundingBoxAscent / 2
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ ctx.clearRect(canvasMsg.width - textDis - AO_textInfo.width - 120, y1, 100, r * 2)
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.rect(canvasMsg.width - textDis - AO_textInfo.width - 120, y1, 100, r * 2)
|
|
|
+ ctx.lineCap = 'round'
|
|
|
+ ctx.stroke()
|
|
|
+ ctx.closePath()
|
|
|
+ ctx.textAlign = 'center'
|
|
|
+ const AO_val_textInfo = ctx.measureText(`${val}`)
|
|
|
+ ctx.fillText(
|
|
|
+ `${val}`,
|
|
|
+ canvasMsg.width - textDis - AO_textInfo.width - 60 - AO_val_textInfo.width / 2.5,
|
|
|
+ y1 + r + AO_textInfo.actualBoundingBoxAscent / 2,
|
|
|
+ 100
|
|
|
+ )
|
|
|
+ if (type === 'draw') {
|
|
|
+ ctx.textAlign = 'start'
|
|
|
+ const add_textInfo = ctx.measureText('+')
|
|
|
+ ctx.fillText(
|
|
|
+ `+`,
|
|
|
+ canvasMsg.width - l_r_dis - add_textInfo.width / 2,
|
|
|
+ y1 + add_textInfo.actualBoundingBoxAscent / 2
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ const y2 = y + distance * (15 + index * 1 + 1)
|
|
|
+ if (type === 'draw') {
|
|
|
+ ctx.beginPath()
|
|
|
+ // ctx.fillStyle = val !== 0 ? 'green' : 'red'
|
|
|
+ ctx.fillStyle = 'red'
|
|
|
+ ctx.arc(canvasMsg.width - l_r_dis, y2, r, 0, 2 * Math.PI)
|
|
|
+ ctx.fill()
|
|
|
+ ctx.fillStyle = 'black'
|
|
|
+ const sub_textInfo = ctx.measureText('-')
|
|
|
+ ctx.fillText(`-`, canvasMsg.width - l_r_dis - sub_textInfo.width / 2, y2 + sub_textInfo.actualBoundingBoxAscent)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ case 'time':
|
|
|
+ const timeVal = new Date(val * 1000).toLocaleString()
|
|
|
+ const valTextInfo = ctx.measureText(timeVal)
|
|
|
+ ctx.clearRect(
|
|
|
+ canvasMsg.width / 2 - valTextInfo.width / 2,
|
|
|
+ canvasMsg.height - 50 - valTextInfo.actualBoundingBoxAscent,
|
|
|
+ valTextInfo.width,
|
|
|
+ valTextInfo.actualBoundingBoxAscent * 2
|
|
|
+ )
|
|
|
+ ctx.fillText(
|
|
|
+ timeVal,
|
|
|
+ canvasMsg.width / 2 - valTextInfo.width / 2,
|
|
|
+ canvasMsg.height - 50 + valTextInfo.actualBoundingBoxAscent
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+onmessage = (e) => {
|
|
|
+ const { type, url, ratio, canvas } = e.data
|
|
|
+ if (!canvasEl && type === 'init') {
|
|
|
+ canvasEl = canvas
|
|
|
+ ctx = canvas.getContext('2d')
|
|
|
+ initWasm(url)
|
|
|
+ canvasMsg.width = canvas.width
|
|
|
+ canvasMsg.height = canvas.height
|
|
|
+ ratioPixel = ratio
|
|
|
+ drawPlc(null, null, null, 'init')
|
|
|
+ for (let i = 0; i < 10; i++) {
|
|
|
+ drawPlc('DI', i, 0, 'draw')
|
|
|
+ drawPlc('DO', i, 0, 'draw')
|
|
|
+ }
|
|
|
+ for (let i = 0; i < 4; i++) {
|
|
|
+ drawPlc('AI', i, i, 'draw')
|
|
|
+ }
|
|
|
+ for (let i = 0; i < 2; i++) {
|
|
|
+ drawPlc('AO', i, i, 'draw')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (type === 1) {
|
|
|
+ if (sendMsg.type === 'AI') {
|
|
|
+ _SetTrustAI(sendMsg.index, +sendMsg.valuel)
|
|
|
+ } else {
|
|
|
+ _SetTrustDI(sendMsg.index, +sendMsg.value)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|