• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taichunmin / chameleon-ultra.js / 15847482271

24 Jun 2025 10:04AM UTC coverage: 69.194% (+5.8%) from 63.351%
15847482271

push

github

web-flow
v0.3.30: change to vitest and lodash-es (#196)

502 of 657 branches covered (76.41%)

Branch coverage included in aggregate %.

12 of 18 new or added lines in 14 files covered. (66.67%)

218 existing lines in 5 files now uncovered.

2683 of 3946 relevant lines covered (67.99%)

3159783.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/plugin/SerialPortAdapter.ts
NEW
1
import * as _ from 'lodash-es'
×
2
import { SerialPort } from 'serialport'
×
3
import { Duplex } from 'stream'
×
4
import { type ChameleonPlugin, type ChameleonUltra, type PluginInstallContext } from '../ChameleonUltra'
5
import { setObject } from '../iifeExportHelper'
×
6

7
async function findDevicePath (): Promise<string> {
×
8
  const device = _.find(await SerialPort.list(), { vendorId: '6868', productId: '8686' }) // ChameleonUltra
×
9
  if (_.isNil(device)) throw new Error('device not found')
×
10
  return device?.path
×
11
}
×
12

13
export default class SerialPortAdapter implements ChameleonPlugin {
×
14
  duplex: SerialPort | null = null
×
15
  name = 'adapter'
×
16
  readonly #emitErr: (err: Error) => void
×
17
  ultra?: ChameleonUltra
×
18

19
  constructor () {
×
20
    this.#emitErr = (err: Error): void => { this.ultra?.emitter.emit('error', _.set(new Error(err.message), 'originalError', err)) }
×
21
  }
×
22

23
  #debug (formatter: any, ...args: [] | any[]): void {
×
24
    this.ultra?.emitter.emit('debug', 'serial', formatter, ...args)
×
25
  }
×
26

27
  async install (context: AdapterInstallContext, pluginOption: SerialPortOption = {}): Promise<AdapterInstallResp> {
×
28
    const ultra = this.ultra = context.ultra
×
29

30
    if (!_.isNil(ultra.$adapter)) {
×
31
      await ultra.disconnect(new Error('adapter replaced'))
×
32
    }
×
33

34
    const adapter: AdapterInstallResp = {
×
35
      isSupported: () => !_.isNil(SerialPort),
×
36
    }
×
37

38
    ultra.addHook('connect', async (ctx: any, next: () => Promise<unknown>) => {
×
39
      if (ultra.$adapter !== adapter) return await next() // 代表已經被其他 adapter 接管
×
40

41
      try {
×
42
        if (!adapter.isSupported()) throw new Error('SerialPort not supported')
×
43

44
        const baudRate = pluginOption?.baudRate ?? 115200
×
45
        const path = pluginOption?.path ?? await findDevicePath()
×
46

47
        this.duplex = await new Promise<SerialPort>((resolve, reject) => {
×
48
          const tmp = new SerialPort({ baudRate, path }, err => { _.isNil(err) ? resolve(tmp) : reject(err) })
×
49
        })
×
50
        this.duplex?.once('close', () => { void ultra.disconnect(new Error('SerialPort closed')) })
×
51
        this.#debug(`port connected, path = ${path}, baudRate = ${baudRate}`)
×
52
        ultra.port = _.merge(Duplex.toWeb(this.duplex), {
×
53
          isOpen: () => { return this.duplex?.isOpen ?? false },
×
54
          isDfu: () => false, // TODO: dfu
×
55
        })
×
56
        return await next()
×
57
      } catch (err) {
×
58
        this.#debug(err)
×
59
        throw err
×
60
      }
×
61
    })
×
62

63
    ultra.addHook('disconnect', async (ctx: any, next: () => Promise<unknown>) => {
×
64
      if (ultra.$adapter !== adapter || _.isNil(this.duplex)) return await next() // 代表已經被其他 adapter 接管
×
65

66
      await next().catch(this.#emitErr)
×
67
      await new Promise<void>((resolve, reject) => { this.duplex?.close(err => { _.isNil(err) ? resolve() : reject(err) }) })
×
68
      this.duplex = null
×
69
    })
×
70

71
    return adapter
×
72
  }
×
73
}
×
74

75
setObject(globalThis, ['ChameleonUltraJS', 'SerialPortAdapter'], SerialPortAdapter)
×
76

77
/** @inline */
78
type AdapterInstallContext = PluginInstallContext & {
79
  ultra: PluginInstallContext['ultra'] & { $adapter?: any }
80
}
81

82
/** @inline */
83
interface SerialPortOption {
84
  path?: string
85
  baudRate?: number
86
}
87

88
/** @inline */
89
interface AdapterInstallResp {
90
  isSupported: () => boolean
91
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc