Blame view

泰额版/Food Labeling Management App UniApp/src/utils/smartScaleService.ts 3.91 KB
5a192b24   杨鑫   最新同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  export type SmartScaleReadKind = 'tared' | 'gross'
  
  const STORAGE_IP = 'smart_scale_ip'
  const STORAGE_PORT = 'smart_scale_port'
  const DEFAULT_IP = '127.0.0.1'
  const DEFAULT_PORT = 6900
  const READ_TIMEOUT_MS = 4000
  
  type MoeTcpPlugin = {
    connect: (opts: { ip: string; port: number }, cb: (res: { code?: number; msg?: string }) => void) => void
    disconnect: () => void
    sendStr: (opts: { message: string }) => void
    onReceive: (cb: (res: { code?: number; data?: string; msg?: string }) => void) => void
    onDisconnect: (cb: (res: unknown) => void) => void
  }
  
  function getTcpPlugin(): MoeTcpPlugin | null {
    // #ifdef APP-PLUS
    try {
      const u = uni as any
      return u?.requireNativePlugin ? (u.requireNativePlugin('moe-tcp-client') as MoeTcpPlugin) : null
    } catch {
      return null
    }
    // #endif
    // #ifndef APP-PLUS
    return null
    // #endif
  }
  
  function readScaleSettings(): { ip: string; port: number } {
    let ip = DEFAULT_IP
    let port = DEFAULT_PORT
    try {
      const storedIp = uni.getStorageSync(STORAGE_IP)
      const storedPort = uni.getStorageSync(STORAGE_PORT)
      if (storedIp) ip = String(storedIp).trim() || ip
      const p = Number(storedPort)
      if (Number.isFinite(p) && p > 0) port = p
    } catch {
      /* ignore */
    }
    return { ip, port }
  }
  
  function parseWeightFromMessage(msg: string): string | null {
    const text = String(msg ?? '').replace(/,/g, '.')
    const matches = text.match(/-?\d+(?:\.\d+)?/g)
    if (!matches?.length) return null
    const last = matches[matches.length - 1]
    const n = Number(last)
    if (!Number.isFinite(n)) return null
    return String(n)
  }
  
  function connectTcp(plugin: MoeTcpPlugin, ip: string, port: number): Promise<void> {
    return new Promise((resolve, reject) => {
      plugin.connect({ ip, port }, (res) => {
        if (res?.code === 1) resolve()
        else reject(new Error(res?.msg || 'Could not connect to smart scale.'))
      })
    })
  }
  
  /** Read weight from smart scale (TCP). `tared` sends tare then reads net; `gross` reads gross. */
  export async function readWeightFromSmartScale(kind: SmartScaleReadKind): Promise<string> {
    const plugin = getTcpPlugin()
    if (!plugin) {
      throw new Error('Smart scale is only available in the mobile app.')
    }
  
    const { ip, port } = readScaleSettings()
    let latest: string | null = null
    let settled = false
  
    return new Promise<string>((resolve, reject) => {
      const finish = (err?: Error) => {
        if (settled) return
        settled = true
        try {
          plugin.onReceive(() => {})
          plugin.onDisconnect(() => {})
          plugin.disconnect()
        } catch {
          /* ignore */
        }
        if (err) reject(err)
        else if (latest) resolve(latest)
        else reject(new Error('No weight reading received from scale.'))
      }
  
      const timer = setTimeout(() => finish(new Error('Smart scale read timed out.')), READ_TIMEOUT_MS)
  
      plugin.onReceive((res) => {
        if (res?.code !== 1) return
        const parsed = parseWeightFromMessage(String(res.data ?? ''))
        if (parsed) latest = parsed
      })
  
      plugin.onDisconnect(() => {
        if (!settled && latest) {
          clearTimeout(timer)
          finish()
        }
      })
  
      connectTcp(plugin, ip, port)
        .then(() => {
          if (kind === 'tared') {
            try {
              plugin.sendStr({ message: 'T\r\n' })
            } catch {
              /* ignore */
            }
            setTimeout(() => {
              try {
                plugin.sendStr({ message: 'W\r\n' })
              } catch {
                /* ignore */
              }
            }, 600)
          } else {
            try {
              plugin.sendStr({ message: 'W\r\n' })
            } catch {
              /* ignore */
            }
          }
        })
        .catch((e) => {
          clearTimeout(timer)
          finish(e instanceof Error ? e : new Error(String(e)))
        })
  
      const poll = setInterval(() => {
        if (latest && !settled) {
          clearInterval(poll)
          clearTimeout(timer)
          finish()
        }
      }, 200)
    })
  }