@@ -48,7 +48,7 @@ const FUNCTIONS = Object.freeze({
4848 * This class is used to interact with the DP100 power supply.
4949 * @example
5050 *
51- * class MyPSU extends DP100 {
51+ * class MyPSU extends DP100() {
5252 * receiveBasicInfo ({vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt}) {
5353 * console.info('Input Voltage:', vIn, 'V')
5454 * console.info('Output Voltage:', vOut, 'V')
@@ -64,108 +64,162 @@ const FUNCTIONS = Object.freeze({
6464 *
6565 * const psu = new MyPSU()
6666 * await psu.connect()
67+ *
68+ * @param {* } Base - The base class to extend.
69+ * @mixin
70+ * @returns {Base } The new class.
6771 */
68- export class DP100 {
72+ export function DP100 ( Base ) {
73+ return class extends Base {
6974
70- /** The connected DP100 device. */
71- device = null
75+ /** The connected DP100 device. */
76+ settingsQueue = [ ]
7277
73- /** Connect to the DP100 device. */
74- async connect ( ) {
75- [ this . device ] = await navigator . hid . requestDevice ( {
76- filters : [ { vendorId, productId } ]
77- } )
78- await this . device . open ( )
79- this . device . addEventListener ( 'inputreport' , this . inputReportHandler . bind ( this ) )
80- setInterval (
81- ( ) => this . sendReport ( FUNCTIONS . BASIC_INFO ) , 10
82- )
83- }
78+ /** Connect to the DP100 device. */
79+ async connect ( ) {
80+ [ this . device ] = await navigator . hid . requestDevice ( {
81+ filters : [ { vendorId, productId } ]
82+ } )
83+ await this . device . open ( )
84+ this . device . addEventListener ( 'inputreport' , this . inputReportHandler . bind ( this ) )
85+ this . getBasicSettings ( ) . then ( ( ) => setInterval ( ( ) => this . sendReport ( FUNCTIONS . BASIC_INFO ) , 100 ) )
86+ }
8487
85- /**
86- * Send a report to the DP100.
87- *
88- * @param {Number } functionId -- The function to call on the DP100.
89- * @param {Uint8Array } content -- The data to send to the DP100.
90- * @returns {Promise<void> } -- A promise that resolves when the report is sent.
91- */
92- async sendReport ( functionId , content = null ) {
93- content = content || new Uint8Array ( 0 )
94- const report = new Uint8Array ( [
95- deviceAddr ,
96- functionId ,
97- content . length ,
98- content ,
99- 0 , // checksum
100- 0 // checksum
101- ] )
102- const reportView = new DataView ( report . buffer , report . byteOffset , report . byteLength )
103- const checksum = crc16 ( report . buffer . slice ( 0 , report . length - 2 ) )
104- reportView . setUint16 ( report . length - 2 , checksum , true )
105- console . debug ( 'device.sendReport' , reportView )
106- return await this . device . sendReport ( 0 , report )
107- }
88+ /**
89+ * Send a report to the DP100.
90+ *
91+ * @param {Number } functionId -- The function to call on the DP100.
92+ * @param {Uint8Array } content -- The data to send to the DP100.
93+ * @param {Number } sequence -- The sequence number for the report.
94+ * @returns {Promise<void> } -- A promise that resolves when the report is sent.
95+ */
96+ async sendReport ( functionId , content = null , sequence = null ) {
97+ content = content || new Uint8Array ( [ 0 ] )
98+ const header = [ deviceAddr , functionId , sequence , // sequence, unused if there is no content
99+ content . length , ...content , 0 , // checksum
100+ 0 // checksum
101+ ]
102+ if ( sequence === null ) {
103+ header . splice ( 2 , 1 )
104+ }
105+ const report = new Uint8Array ( header )
106+ const reportView = new DataView ( report . buffer , report . byteOffset , report . byteLength )
107+ const checksum = crc16 ( report . buffer . slice ( 0 , report . length - 2 ) )
108+ reportView . setUint16 ( report . length - 2 , checksum , true )
109+ console . debug ( 'device.sendReport' , reportView )
110+ return await this . device . sendReport ( 0 , report )
111+ }
108112
109- /** Handle input reports from the DP100
110- * @param {HIDInputReportEvent } event
111- */
112- inputReportHandler ( event ) {
113- console . debug ( 'device.inputreport' , event )
114- const data = event . data
115- const headerLength = 4
116- const header = {
117- deviceAddr : data . getUint8 ( 0 ) ,
118- functionType : event . data . getUint8 ( 1 ) ,
119- sequence : event . data . getUint8 ( 2 ) ,
120- contentLength : event . data . getUint8 ( 3 ) ,
113+ async getBasicSettings ( ) {
114+ console . info ( 'getBasicSettings' )
115+ await this . sendReport ( FUNCTIONS . BASIC_SET , new Uint8Array ( [ 0 | 0x80 ] ) , 0 )
121116 }
122- const contentView = new DataView ( data . buffer . slice ( headerLength , headerLength + header . contentLength ) )
123- const checksum = data . getUint16 ( headerLength + header . contentLength , true )
124- const computedChecksum = crc16 ( data . buffer . slice ( 0 , headerLength + header . contentLength ) )
125- if ( computedChecksum !== checksum ) {
126- console . error ( 'Checksum Failed' , {
127- expected : computedChecksum . toString ( 16 ) ,
128- received : checksum . toString ( 16 )
129- } )
130- return
117+
118+ async setBasicSettings ( { state, vo_set, io_set, ovp_set, ocp_set } ) {
119+ if ( this . settings === undefined ) {
120+ throw new Error ( 'Settings not loaded' )
121+ }
122+ console . info ( 'setBasicSettings' , { state, vo_set, io_set, ovp_set, ocp_set } )
123+ const basicSet = Object . assign ( { } , this . settings , Object . fromEntries ( Object . entries ( {
124+ state, vo_set, io_set, ovp_set, ocp_set
125+ } ) . filter ( ( [ k , v ] ) => v !== undefined ) ) )
126+ const index = this . settingsQueue . length
127+ this . settingsQueue [ index ] = basicSet
128+ const out = new Uint8Array ( 10 )
129+ const outDv = new DataView ( out . buffer , out . byteOffset , out . length )
130+ outDv . setUint8 ( 0 , index | 0x20 )
131+ outDv . setUint8 ( 1 , basicSet . state )
132+ outDv . setUint16 ( 2 , basicSet . vo_set * 1000 , true )
133+ outDv . setUint16 ( 4 , basicSet . io_set * 1000 , true )
134+ outDv . setUint16 ( 6 , basicSet . ovp_set * 1000 , true )
135+ outDv . setUint16 ( 8 , basicSet . ocp_set * 1000 , true )
136+ await this . sendReport ( FUNCTIONS . BASIC_SET , out , 0 )
131137 }
132- console . debug ( 'content' , contentView )
133138
134- switch ( header . functionType ) {
135- case FUNCTIONS . BASIC_INFO :
136- this . receiveBasicInfo ( {
137- vIn : contentView . getUint16 ( 0 , true ) / 1000 ,
138- vOut : contentView . getUint16 ( 2 , true ) / 1000 ,
139- iOut : contentView . getUint16 ( 4 , true ) / 1000 ,
140- voMax : contentView . getUint16 ( 6 , true ) / 1000 ,
141- temp1 : contentView . getUint16 ( 8 , true ) / 10 ,
142- temp2 : contentView . getUint16 ( 10 , true ) / 10 ,
143- dc5V : contentView . getUint16 ( 12 , true ) / 1000 ,
144- outMode : contentView . getUint8 ( 14 ) ,
145- workSt : contentView . getUint8 ( 15 )
139+ /** Handle input reports from the DP100
140+ * @param {HIDInputReportEvent } event
141+ */
142+ inputReportHandler ( event ) {
143+ console . debug ( 'device.inputreport' , event )
144+ const data = event . data
145+ const headerLength = 4
146+ const header = {
147+ deviceAddr : data . getUint8 ( 0 ) ,
148+ functionType : event . data . getUint8 ( 1 ) ,
149+ sequence : event . data . getUint8 ( 2 ) ,
150+ contentLength : event . data . getUint8 ( 3 ) ,
151+ }
152+ const contentView = new DataView ( data . buffer . slice ( headerLength , headerLength + header . contentLength ) )
153+ const checksum = data . getUint16 ( headerLength + header . contentLength , true )
154+ const computedChecksum = crc16 ( data . buffer . slice ( 0 , headerLength + header . contentLength ) )
155+ if ( computedChecksum !== checksum ) {
156+ console . error ( 'Checksum Failed' , {
157+ expected : computedChecksum . toString ( 16 ) , received : checksum . toString ( 16 )
146158 } )
147- break
148- default :
149- console . warn ( 'Unhandled function' , header . functionType )
159+ return
160+ }
161+ switch ( header . functionType ) {
162+ case FUNCTIONS . BASIC_INFO :
163+ this . receiveBasicInfo ( {
164+ vIn : contentView . getUint16 ( 0 , true ) / 1000 ,
165+ vOut : contentView . getUint16 ( 2 , true ) / 1000 ,
166+ iOut : contentView . getUint16 ( 4 , true ) / 1000 ,
167+ voMax : contentView . getUint16 ( 6 , true ) / 1000 ,
168+ temp1 : contentView . getUint16 ( 8 , true ) / 10 ,
169+ temp2 : contentView . getUint16 ( 10 , true ) / 10 ,
170+ dc5V : contentView . getUint16 ( 12 , true ) / 1000 ,
171+ outMode : contentView . getUint8 ( 14 ) ,
172+ workSt : contentView . getUint8 ( 15 )
173+ } )
174+ break
175+ case FUNCTIONS . BASIC_SET :
176+ if ( contentView . byteLength === 1 ) {
177+ this . settings = this . settingsQueue . pop ( contentView . getUint8 ( 0 ) )
178+ break
179+ }
180+ this . receiveBasicSettings ( {
181+ index : contentView . getUint8 ( 0 ) ,
182+ state : contentView . getUint8 ( 1 ) ,
183+ vo_set : contentView . getUint16 ( 2 , true ) / 1000 ,
184+ io_set : contentView . getUint16 ( 4 , true ) / 1000 ,
185+ ovp_set : contentView . getUint16 ( 6 , true ) / 1000 ,
186+ ocp_set : contentView . getUint16 ( 8 , true ) / 1000 ,
187+ } )
188+ break
189+ default :
190+ console . warn ( 'Unhandled function' , header . functionType )
191+ }
192+ }
193+
194+ /** Handle basic info from the DP100
195+ * @param {Object } basicInfo
196+ * @param {Number } basicInfo.vIn - Input voltage in V.
197+ * @param {Number } basicInfo.vOut - Output voltage in V.
198+ * @param {Number } basicInfo.iOut - Output current in A.
199+ * @param {Number } basicInfo.voMax - Max output voltage in V.
200+ * @param {Number } basicInfo.temp1 - Temperature 1 in °C.
201+ * @param {Number } basicInfo.temp2 - Temperature 2 in °C.
202+ * @param {Number } basicInfo.dc5V - 5V rail in V.
203+ * @param {Number } basicInfo.outMode - Output mode.
204+ * @param {Number } basicInfo.workSt - Work state.
205+ */
206+ receiveBasicInfo ( { vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt } ) {
207+ console . debug ( 'receiveBasicInfo' , { vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt } )
208+ this . info = { vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt }
150209 }
151- }
152210
153- /** Handle basic info from the DP100
154- * @param {Object } basicInfo
155- * @param {Number } basicInfo.vIn - Input voltage in mV.
156- * @param {Number } basicInfo.vOut - Output voltage in mV.
157- * @param {Number } basicInfo.iOut - Output current in mA.
158- * @param {Number } basicInfo.voMax - Max output voltage in mV.
159- * @param {Number } basicInfo.temp1 - Temperature 1 in 0.1°C.
160- * @param {Number } basicInfo.temp2 - Temperature 2 in 0.1°C.
161- * @param {Number } basicInfo.dc5V - 5V rail in mV.
162- * @param {Number } basicInfo.outMode - Output mode.
163- * @param {Number } basicInfo.workSt - Work state.
164- */
165- receiveBasicInfo ( { vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt } ) {
166- const BasicInfoEvent = new CustomEvent ( 'basicInfo' , {
167- detail : { vIn, vOut, iOut, voMax, temp1, temp2, dc5V, outMode, workSt }
168- } )
169- document . dispatchEvent ( BasicInfoEvent )
211+ /** Handle basic settings from the DP100
212+ * @param {Object } basicSettings
213+ * @param {Number } basicSettings.index - Setting index.
214+ * @param {Number } basicSettings.state - Setting state.
215+ * @param {Number } basicSettings.vo_set - Output voltage setting in V.
216+ * @param {Number } basicSettings.io_set - Output current setting in A.
217+ * @param {Number } basicSettings.ovp_set - Over-voltage protection setting in V.
218+ * @param {Number } basicSettings.ocp_set - Over-current protection setting in A.
219+ */
220+ receiveBasicSettings ( { index, state, vo_set, io_set, ovp_set, ocp_set } ) {
221+ console . debug ( 'receiveBasicSettings' , { index, state, vo_set, io_set, ovp_set, ocp_set } )
222+ this . settings = { state, vo_set, io_set, ovp_set, ocp_set }
223+ }
170224 }
171225}
0 commit comments