Skip to content

Commit b7ba4f6

Browse files
committed
Split JS
1 parent 8471115 commit b7ba4f6

2 files changed

Lines changed: 181 additions & 180 deletions

File tree

assets/js/dp100.js

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
const vendorId = 11836, productId = 44801 // DP100's HID IDs
2+
const deviceAddr = 251 // DP100's device address
3+
4+
/**
5+
* Calculate the buffers CRC-16/MODBUS checksum.
6+
*
7+
* @param {ArrayBuffer} buffer - The buffer to calculate the CRC16 for.
8+
* @return {number} - The CRC16 checksum.
9+
*/
10+
export function crc16 (buffer) {
11+
let crc = 0xFFFF
12+
13+
for (const byte of new Uint8Array(buffer)) {
14+
crc = crc ^ byte
15+
16+
for (let j = 0; j < 8; j++) {
17+
const odd = crc & 0x0001
18+
crc = crc >> 1
19+
if (odd) {
20+
crc = crc ^ 0xA001
21+
}
22+
}
23+
}
24+
25+
return crc
26+
}
27+
28+
/** DP100 Modbus Function IDs */
29+
const FUNCTIONS = Object.freeze({
30+
DEVICE_INFO: 0x10,
31+
FIRM_INFO: 17,
32+
START_TRANS: 18,
33+
DATA_TRANS: 19,
34+
END_TRANS: 20,
35+
DEV_UPGRADE: 21,
36+
BASIC_INFO: 48,
37+
BASIC_SET: 53,
38+
SYSTEM_INFO: 0x40,
39+
SYSTEM_SET: 69,
40+
SCAN_OUT: 80,
41+
SERIAL_OUT: 85,
42+
DISCONNECT: 0x80,
43+
NONE: 0xFF,
44+
})
45+
46+
const WORK_MODES = Object.freeze({
47+
CC: 0,
48+
CV: 1,
49+
OFF: 2,
50+
})
51+
52+
const WORK_MODE_MAP = {
53+
[WORK_MODES.CV]: 'Constant Voltage',
54+
[WORK_MODES.CC]: 'Constant Current',
55+
[WORK_MODES.OFF]: 'Off',
56+
}
57+
58+
/** DP100 device class.
59+
*
60+
* This class is used to interact with the DP100 power supply.
61+
* @example
62+
*
63+
* class MyPSU extends DP100 {
64+
* receiveBasicInfo ({vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt}) {
65+
* console.info('Input Voltage:', vIn, 'V')
66+
* console.info('Output Voltage:', vOut, 'V')
67+
* console.info('Output Current:', iOut, 'A')
68+
* console.info('Max Output Voltage:', voMax, 'V')
69+
* console.info('Temperature 1:', temp1, '°C')
70+
* console.info('Temperature 2:', temp2, '°C')
71+
* console.info('DC 5V:', dc5V, 'V')
72+
* console.info('Output Mode:', WORK_MODE_MAP[outMode])
73+
* console.info('Work State:', workSt)
74+
* }
75+
* }
76+
*
77+
* const psu = new MyPSU()
78+
* await psu.connect()
79+
*/
80+
export class DP100 {
81+
82+
device = null
83+
84+
/** Connect to the DP100 device. */
85+
async connect () {
86+
[this.device] = await navigator.hid.requestDevice({
87+
filters: [{ vendorId, productId }]
88+
})
89+
await this.device.open()
90+
this.device.addEventListener('inputreport', this.inputReportHandler.bind(this))
91+
setInterval(
92+
() => this.sendReport(FUNCTIONS.BASIC_INFO), 10
93+
)
94+
}
95+
96+
/** Send a report to the DP100
97+
* @param {Number} functionId -- The function to call on the DP100.
98+
* @param {Uint8Array} content -- The data to send to the DP100.
99+
*/
100+
async sendReport (functionId, content = null) {
101+
content = content || new Uint8Array(0)
102+
const report = new Uint8Array([
103+
deviceAddr,
104+
functionId,
105+
content.length,
106+
content,
107+
0, // checksum
108+
0 // checksum
109+
])
110+
const reportView = new DataView(report.buffer, report.byteOffset, report.byteLength)
111+
const checksum = crc16(report.buffer.slice(0, report.length - 2))
112+
reportView.setUint16(report.length - 2, checksum, true)
113+
console.debug('device.sendReport', reportView)
114+
return this.device.sendReport(0, report)
115+
}
116+
117+
/** Handle input reports from the DP100
118+
* @param {HIDInputReportEvent} event
119+
*/
120+
inputReportHandler (event) {
121+
console.debug('device.inputreport', event)
122+
const data = event.data
123+
const headerLength = 4
124+
const header = {
125+
deviceAddr: data.getUint8(0),
126+
functionType: event.data.getUint8(1),
127+
sequence: event.data.getUint8(2),
128+
contentLength: event.data.getUint8(3),
129+
}
130+
const contentView = new DataView(data.buffer.slice(headerLength, headerLength + header.contentLength))
131+
const checksum = data.getUint16(headerLength + header.contentLength, true)
132+
const computedChecksum = crc16(data.buffer.slice(0, headerLength + header.contentLength))
133+
if (computedChecksum !== checksum) {
134+
console.error('Checksum Failed', {
135+
expected: computedChecksum.toString(16),
136+
received: checksum.toString(16)
137+
})
138+
return
139+
}
140+
console.debug('content', contentView)
141+
142+
switch (header.functionType) {
143+
case FUNCTIONS.BASIC_INFO:
144+
const basicInfo = {
145+
vIn: contentView.getUint16(0, true) / 1000,
146+
vOut: contentView.getUint16(2, true) / 1000,
147+
iOut: contentView.getUint16(4, true) / 1000,
148+
voMax: contentView.getUint16(6, true) / 1000,
149+
temp1: contentView.getUint16(8, true) / 10,
150+
temp2: contentView.getUint16(10, true) / 10,
151+
dc5V: contentView.getUint16(12, true) / 1000,
152+
outMode: contentView.getUint8(14),
153+
workSt: contentView.getUint8(15)
154+
}
155+
this.receiveBasicInfo(basicInfo)
156+
break
157+
default:
158+
console.warn('Unhandled function', header.functionType)
159+
}
160+
}
161+
162+
/** Handle basic info from the DP100
163+
* @param {Object} basicInfo
164+
* @param {Number} basicInfo.vIn - Input voltage in mV.
165+
* @param {Number} basicInfo.vOut - Output voltage in mV.
166+
* @param {Number} basicInfo.iOut - Output current in mA.
167+
* @param {Number} basicInfo.voMax - Max output voltage in mV.
168+
* @param {Number} basicInfo.temp1 - Temperature 1 in 0.1°C.
169+
* @param {Number} basicInfo.temp2 - Temperature 2 in 0.1°C.
170+
* @param {Number} basicInfo.dc5V - 5V rail in mV.
171+
* @param {Number} basicInfo.outMode - Output mode.
172+
* @param {Number} basicInfo.workSt - Work state.
173+
*/
174+
receiveBasicInfo ({ vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt }) {
175+
const BasicInfoEvent = new CustomEvent('basicInfo', {
176+
detail: { vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt }
177+
})
178+
document.dispatchEvent(BasicInfoEvent)
179+
}
180+
}

index.html

Lines changed: 1 addition & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -35,188 +35,9 @@
3535
<button id="connect">connect</button>
3636
<script type="module">
3737
import uplot from 'https://cdn.jsdelivr.net/npm/uplot@1.6.31/+esm'
38+
import { DP100 } from './assets/js/dp100.js'
3839

3940
const dark = window.matchMedia('(prefers-color-scheme: dark)').matches
40-
const vendorId = 11836, productId = 44801 // DP100's HID IDs
41-
const deviceAddr = 251 // DP100's device address
42-
43-
/**
44-
* Calculate the buffers CRC-16/MODBUS checksum.
45-
*
46-
* @param {ArrayBuffer} buffer - The buffer to calculate the CRC16 for.
47-
* @return {number} - The CRC16 checksum.
48-
*/
49-
function crc16 (buffer) {
50-
let crc = 0xFFFF
51-
52-
for (const byte of new Uint8Array(buffer)) {
53-
crc = crc ^ byte
54-
55-
for (let j = 0; j < 8; j++) {
56-
const odd = crc & 0x0001
57-
crc = crc >> 1
58-
if (odd) {
59-
crc = crc ^ 0xA001
60-
}
61-
}
62-
}
63-
64-
return crc
65-
}
66-
67-
/** DP100 Modbus Function IDs */
68-
const FUNCTIONS = Object.freeze({
69-
DEVICE_INFO: 0x10,
70-
FIRM_INFO: 17,
71-
START_TRANS: 18,
72-
DATA_TRANS: 19,
73-
END_TRANS: 20,
74-
DEV_UPGRADE: 21,
75-
BASIC_INFO: 48,
76-
BASIC_SET: 53,
77-
SYSTEM_INFO: 0x40,
78-
SYSTEM_SET: 69,
79-
SCAN_OUT: 80,
80-
SERIAL_OUT: 85,
81-
DISCONNECT: 0x80,
82-
NONE: 0xFF,
83-
})
84-
85-
const WORK_MODES = Object.freeze({
86-
CC: 0,
87-
CV: 1,
88-
OFF: 2,
89-
})
90-
91-
const WORK_MODE_MAP = {
92-
[WORK_MODES.CV]: 'Constant Voltage',
93-
[WORK_MODES.CC]: 'Constant Current',
94-
[WORK_MODES.OFF]: 'Off',
95-
}
96-
97-
/** DP100 device class.
98-
*
99-
* This class is used to interact with the DP100 power supply.
100-
* @example
101-
*
102-
* class MyPSU extends DP100 {
103-
* receiveBasicInfo ({vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt}) {
104-
* console.info('Input Voltage:', vIn, 'V')
105-
* console.info('Output Voltage:', vOut, 'V')
106-
* console.info('Output Current:', iOut, 'A')
107-
* console.info('Max Output Voltage:', voMax, 'V')
108-
* console.info('Temperature 1:', temp1, '°C')
109-
* console.info('Temperature 2:', temp2, '°C')
110-
* console.info('DC 5V:', dc5V, 'V')
111-
* console.info('Output Mode:', WORK_MODE_MAP[outMode])
112-
* console.info('Work State:', workSt)
113-
* }
114-
* }
115-
*
116-
* const psu = new MyPSU()
117-
* await psu.connect()
118-
*/
119-
class DP100 {
120-
121-
device = null
122-
123-
/** Connect to the DP100 device. */
124-
async connect () {
125-
[this.device] = await navigator.hid.requestDevice({
126-
filters: [{ vendorId, productId }]
127-
})
128-
await this.device.open()
129-
this.device.addEventListener('inputreport', this.inputReportHandler.bind(this))
130-
setInterval(
131-
() => this.sendReport(FUNCTIONS.BASIC_INFO), 10
132-
)
133-
}
134-
135-
/** Send a report to the DP100
136-
* @param {Number} functionId -- The function to call on the DP100.
137-
* @param {Uint8Array} content -- The data to send to the DP100.
138-
*/
139-
async sendReport (functionId, content = null) {
140-
content = content || new Uint8Array(0)
141-
const report = new Uint8Array([
142-
deviceAddr,
143-
functionId,
144-
content.length,
145-
content,
146-
0, // checksum
147-
0 // checksum
148-
])
149-
const reportView = new DataView(report.buffer, report.byteOffset, report.byteLength)
150-
const checksum = crc16(report.buffer.slice(0, report.length - 2))
151-
reportView.setUint16(report.length - 2, checksum, true)
152-
console.debug('device.sendReport', reportView)
153-
return this.device.sendReport(0, report)
154-
}
155-
156-
/** Handle input reports from the DP100
157-
* @param {HIDInputReportEvent} event
158-
*/
159-
inputReportHandler (event) {
160-
console.debug('device.inputreport', event)
161-
const data = event.data
162-
const headerLength = 4
163-
const header = {
164-
deviceAddr: data.getUint8(0),
165-
functionType: event.data.getUint8(1),
166-
sequence: event.data.getUint8(2),
167-
contentLength: event.data.getUint8(3),
168-
}
169-
const contentView = new DataView(data.buffer.slice(headerLength, headerLength + header.contentLength))
170-
const checksum = data.getUint16(headerLength + header.contentLength, true)
171-
const computedChecksum = crc16(data.buffer.slice(0, headerLength + header.contentLength))
172-
if (computedChecksum !== checksum) {
173-
console.error('Checksum Failed', {
174-
expected: computedChecksum.toString(16),
175-
received: checksum.toString(16)
176-
})
177-
return
178-
}
179-
console.debug('content', contentView)
180-
181-
switch (header.functionType) {
182-
case FUNCTIONS.BASIC_INFO:
183-
const basicInfo = {
184-
vIn: contentView.getUint16(0, true) / 1000,
185-
vOut: contentView.getUint16(2, true) / 1000,
186-
iOut: contentView.getUint16(4, true) / 1000,
187-
voMax: contentView.getUint16(6, true) / 1000,
188-
temp1: contentView.getUint16(8, true) / 10,
189-
temp2: contentView.getUint16(10, true) / 10,
190-
dc5V: contentView.getUint16(12, true) / 1000,
191-
outMode: contentView.getUint8(14),
192-
workSt: contentView.getUint8(15)
193-
}
194-
this.receiveBasicInfo(basicInfo)
195-
break
196-
default:
197-
console.warn('Unhandled function', header.functionType)
198-
}
199-
}
200-
201-
/** Handle basic info from the DP100
202-
* @param {Object} basicInfo
203-
* @param {Number} basicInfo.vIn - Input voltage in mV.
204-
* @param {Number} basicInfo.vOut - Output voltage in mV.
205-
* @param {Number} basicInfo.iOut - Output current in mA.
206-
* @param {Number} basicInfo.voMax - Max output voltage in mV.
207-
* @param {Number} basicInfo.temp1 - Temperature 1 in 0.1°C.
208-
* @param {Number} basicInfo.temp2 - Temperature 2 in 0.1°C.
209-
* @param {Number} basicInfo.dc5V - 5V rail in mV.
210-
* @param {Number} basicInfo.outMode - Output mode.
211-
* @param {Number} basicInfo.workSt - Work state.
212-
*/
213-
receiveBasicInfo ({ vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt }) {
214-
const BasicInfoEvent = new CustomEvent('basicInfo', {
215-
detail: { vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt }
216-
})
217-
document.dispatchEvent(BasicInfoEvent)
218-
}
219-
}
22041

22142
let opts = {
22243
id: 'uv-graph',

0 commit comments

Comments
 (0)