From 4c10fe96e1ba8e4f7132f58b59e6a8f67391b291 Mon Sep 17 00:00:00 2001 From: MyHomeMyData Date: Sat, 30 Dec 2023 11:11:13 +0100 Subject: [PATCH 1/7] Hide collect config table when no collect devices are available. --- admin/jsonConfig.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/admin/jsonConfig.json b/admin/jsonConfig.json index 07d0f06..1006bd4 100644 --- a/admin/jsonConfig.json +++ b/admin/jsonConfig.json @@ -205,7 +205,7 @@ "confirm": { "condition": "true", "title": "Confirm scan for datapoints", - "text": "Scan will take up to 5 minutes. Please do not close the tab during scan. You may follow up the progress by watching the Log in an additional tab.", + "text": "Scan will take up to 5 minutes. Plaese make sure, no other UDSonCAN client (e.g. Open3Eclient.py) is running. Please do not close the tab during scan. You may follow up the progress by watching the Log in an additional tab.", "ok": "OK", "cancel": "Cancel" }, @@ -241,7 +241,7 @@ "onChange": {"alsoDependsOn": ["tableUdsDidsSelDev"], "calculateFunc": "", "ignoreOwnChanges": false}, "hidden": "if ( (_alive) && (data.tableUdsDidsSelDev) && (data.tableUdsDidsSelDev != 'Select device') ) { return false; }; return true;", "noDelete": true, - "import": false, + "import": true, "export": true, "items": [ { @@ -350,6 +350,14 @@ "introTableColExt": { "type": "staticText", "text": "Listen for data on addresses given in this table. No writing to CAN bus. Press '+' to begin.", + "hidden": "if (data.tableUdsDevices) {for(let i=0; i Date: Sat, 30 Dec 2023 16:31:25 +0100 Subject: [PATCH 2/7] Updatetime for cmndLoop changed from 10 to 40 ms --- lib/canUds.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/canUds.js b/lib/canUds.js index c2f6dc7..5a8fa19 100644 --- a/lib/canUds.js +++ b/lib/canUds.js @@ -66,7 +66,7 @@ class uds { this.canIDhex = '0x'+Number(this.config.canID).toString(16); this.cmndsQueue = []; this.cmndsHandle = null; - this.cmndsUpdateTime = 10; // Check for new commands (ms) + this.cmndsUpdateTime = 40; // Check for new commands (ms) this.busy = false; // Worker is busy this.commBusy = false; // Communication routine running this.schedules = {}; From 06ef152663cf6892a947b8f90000971103687f3e Mon Sep 17 00:00:00 2001 From: MyHomeMyData Date: Sun, 31 Dec 2023 20:52:54 +0100 Subject: [PATCH 3/7] First version of WriteByDid. --- lib/canUds.js | 195 +++++++++++++++++++++++++++++++++++-------------- lib/storage.js | 19 +++++ main.js | 34 +++------ 3 files changed, 169 insertions(+), 79 deletions(-) diff --git a/lib/canUds.js b/lib/canUds.js index 5a8fa19..2889d12 100644 --- a/lib/canUds.js +++ b/lib/canUds.js @@ -42,7 +42,7 @@ class uds { this.config.statId = 'statUDS'; this.config.worker = 'uds'; this.storage = new storage.storage(this.config); - this.states = ['standby','waitForFF','waitForCF']; + this.states = ['standby','waitForFFrbd','waitForCFrbd','waitForFFwbd']; this.frameFC = [0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00]; this.readByDidProt = { 'idTx' : this.config.canID, @@ -53,6 +53,15 @@ class uds { 'SIDnr' : 0x7F, // SID negative response 'FC' : [0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // Flow Control frame }; + this.writeByDidProt = { + 'idTx' : this.config.canID, + 'idRx' : Number(this.config.canID) + 0x10, + 'PCI' : 0x07, // Protocol Control Information + 'SIDtx' : 0x2E, // Service ID transmit + 'SIDrx' : 0x6E, // Service ID receive + 'SIDnr' : 0x7F, // SID negative response + 'FCrx' : 0x30, // Flow Control ID for MF transfer + }; this.data = { 'len' : 0, 'tsRequest' : 0, @@ -115,41 +124,6 @@ class uds { this.stat.state = 'standby'; } - async setWorkerOpMode(opMode) { - await this.storage.setOpMode(opMode); - } - - async getWorkerOpMode() { - return this.storage.getOpMode(); - } - - async getComState() { - return this.data.state; - } - - async setComState(comState) { - this.data.state = comState; - } - - async setDidDone(coolDownTime) { - // Finalize communication for recent did - this.coolDownTs = new Date().getTime()+coolDownTime; - if (this.cmndsQueue.length == 0) this.busy = false; - await this.setComState(0); - if (this.timeoutHandle) await clearTimeout(this.timeoutHandle); - } - - async setDidStart(ctx, did) { - await this.setComState(1); // 'waitForFF' - const tsNow = new Date().getTime(); - const minWaiting = this.coolDownTs - tsNow; - if (minWaiting > 0) await this.sleep(minWaiting); - this.busy = true; - this.timeoutHandle = await setTimeout(this.onTimeout, this.config.timeout, ctx, this); - this.data.did = did; - this.data.tsRequest = tsNow; - } - async startup(ctx, opMode) { await this.setComState(0); await this.setWorkerOpMode(opMode); @@ -166,7 +140,6 @@ class uds { } else { await ctx.log.silly('UDS worker started in mode '+opMode+' on '+this.config.stateBase); } - if (['standby','normal'].includes(opMode)) await ctx.registerUdsOnStateChange(ctx, this, this.userReadByDidId, this.onUserReadDidsChange); this.cmndsHandle = setInterval(async () => { await this.cmndsLoop(ctx); }, this.cmndsUpdateTime); @@ -179,10 +152,6 @@ class uds { this.stat.state = 'stopped'; const opMode = await this.storage.getOpMode(); - if (['standby','normal'].includes(opMode)) { - await ctx.unRegisterUdsOnStateChange(this.userReadByDidId); - } - await this.storage.storeStatistics(ctx, this); await this.storage.setOpMode('standby'); @@ -208,6 +177,50 @@ class uds { } } + async setWorkerOpMode(opMode) { + await this.storage.setOpMode(opMode); + } + + async getWorkerOpMode() { + return this.storage.getOpMode(); + } + + async getComState() { + return this.data.state; + } + + async setComState(comState) { + this.data.state = comState; + } + + async setDidDone(coolDownTime) { + // Finalize communication for recent did + this.coolDownTs = new Date().getTime()+coolDownTime; + if (this.cmndsQueue.length == 0) this.busy = false; + await this.setComState(0); + if (this.timeoutHandle) await clearTimeout(this.timeoutHandle); + } + + async setDidStart(ctx, did, mode) { + switch (mode) { + case 'read': + await this.setComState(1); // 'waitForFFrbd' + break; + case 'write': + await this.setComState(3); // 'waitForFFwbd' + break; + default: + ctx.log.warn('UDS worker started on '+this.config.stateBase+': mode '+mode+' not implemented.'); + } + const tsNow = new Date().getTime(); + const minWaiting = this.coolDownTs - tsNow; + if (minWaiting > 0) await this.sleep(minWaiting); + this.busy = true; + this.timeoutHandle = await setTimeout(this.onTimeout, this.config.timeout, ctx, this); + this.data.did = did; + this.data.tsRequest = tsNow; + } + async calcStat() { this.data.tsReply = new Date().getTime(); const rt = this.data.tsReply - this.data.tsRequest; @@ -285,20 +298,44 @@ class uds { await ctxLocal.setDidDone(0); } - async onUserReadDidsChange(ctxGlobal, ctxLocal, state) { - const dids = JSON.parse(state.val); - if (!state.ack) { - // Execute user command - await ctxGlobal.log.debug('UDS user command on device '+ctxLocal.config.stateBase+'. Dids='+JSON.stringify(dids)); - await ctxLocal.pushCmnd(ctxGlobal, 'read', dids); - await ctxGlobal.setStateAsync(ctxLocal.userReadByDidId, { val: JSON.stringify(dids), ack: true }); // Acknowlegde user command + async onUdsStateChange(ctx, id, state) { + //ctx.log.debug(`onUdsStateChange(${this.config.stateBase}): state ${id} changed: ${state.val} (ack = ${state.ack})`); + if (id.includes(this.userReadByDidId)) { + // User requests ReadByDid + const dids = JSON.parse(state.val); + await ctx.log.debug('UDS user command ReadByDid on device '+this.config.stateBase+'. Dids='+JSON.stringify(dids)); + await this.pushCmnd(ctx, 'read', dids); + await ctx.setStateAsync(id, { val: JSON.stringify(dids), ack: true }); // Acknowlegde user command + } + if (id.includes('.tree.')) { + // User requests WriteByDid for specific did + const i = id.indexOf('.tree.')+6; // Index of start of did + const did = Number(id.slice(i,i+4)); + if (id.slice(i).includes('.')) { + // Datapoint has sub structure. Not supported yet + ctx.log.error('UDS user command WriteByDid on device '+this.config.stateBase+'.'+String(did)+': Did has sub structure. Not supported yet.'); + return; + } + const val = await this.storage.encodeDataCAN(ctx, this, did, JSON.parse(state.val)); + await ctx.log.debug('UDS user command WriteByDid on device '+this.config.stateBase+'.'+String(did)+'='+this.storage.arr2Hex(val)); + await this.pushCmnd(ctx, 'write', [[did,val]]); + //await ctx.setStateAsync(id, { val: state.val, ack: true }); // Acknowlegde user command + setTimeout(function(ctx,did){ctx.cmndsQueue.push({'mode':'read', 'did': did});},1000,this,did); // Read value after 1000 ms } } - initialRequestSF(did) { + initialRequestReadSF(did) { return [this.readByDidProt.PCI, this.readByDidProt.SIDtx,((did >> 8) & 0xFF),(did & 0xFF),0x00,0x00,0x00,0x00]; } + initialRequestWriteSF(did, valRaw) { + const frame = [this.writeByDidProt.PCI, this.writeByDidProt.SIDtx,((did >> 8) & 0xFF),(did & 0xFF),0x00,0x00,0x00,0x00]; + for (let i=0; i Date: Mon, 1 Jan 2024 19:59:00 +0100 Subject: [PATCH 4/7] WriteByDid operational. Raw data fully supported, other data partly supported, --- lib/canUds.js | 124 ++++++++++++++++++++++++++++++++++++------------- lib/storage.js | 9 ++++ main.js | 2 +- 3 files changed, 103 insertions(+), 32 deletions(-) diff --git a/lib/canUds.js b/lib/canUds.js index 2889d12..ae80316 100644 --- a/lib/canUds.js +++ b/lib/canUds.js @@ -42,7 +42,7 @@ class uds { this.config.statId = 'statUDS'; this.config.worker = 'uds'; this.storage = new storage.storage(this.config); - this.states = ['standby','waitForFFrbd','waitForCFrbd','waitForFFwbd']; + this.states = ['standby','waitForFFrbd','waitForCFrbd','waitForFFSFwbd','waitForFFMFwbd']; this.frameFC = [0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00]; this.readByDidProt = { 'idTx' : this.config.canID, @@ -56,7 +56,7 @@ class uds { this.writeByDidProt = { 'idTx' : this.config.canID, 'idRx' : Number(this.config.canID) + 0x10, - 'PCI' : 0x07, // Protocol Control Information + 'PCI' : 0x00, // Protocol Control Information = length of data +3 'SIDtx' : 0x2E, // Service ID transmit 'SIDrx' : 0x6E, // Service ID receive 'SIDnr' : 0x7F, // SID negative response @@ -70,7 +70,8 @@ class uds { 'databytes' : [], 'did' : 0, 'state' : 0, - 'D0' : 0x21 + 'D0' : 0x21, + 'txPos' : 0 }; this.canIDhex = '0x'+Number(this.config.canID).toString(16); this.cmndsQueue = []; @@ -201,13 +202,19 @@ class uds { if (this.timeoutHandle) await clearTimeout(this.timeoutHandle); } - async setDidStart(ctx, did, mode) { + async setDidStart(ctx, did, mode, len) { switch (mode) { case 'read': await this.setComState(1); // 'waitForFFrbd' break; case 'write': - await this.setComState(3); // 'waitForFFwbd' + if (len<=4) { + // Single frame communication + await this.setComState(3); // 'waitForFFSFwbd' + } else { + // Multi frame communication + await this.setComState(4); // 'waitForFFMFwbd' + } break; default: ctx.log.warn('UDS worker started on '+this.config.stateBase+': mode '+mode+' not implemented.'); @@ -299,28 +306,42 @@ class uds { } async onUdsStateChange(ctx, id, state) { - //ctx.log.debug(`onUdsStateChange(${this.config.stateBase}): state ${id} changed: ${state.val} (ack = ${state.ack})`); if (id.includes(this.userReadByDidId)) { // User requests ReadByDid const dids = JSON.parse(state.val); - await ctx.log.debug('UDS user command ReadByDid on device '+this.config.stateBase+'. Dids='+JSON.stringify(dids)); + await ctx.log.debug('UDS user command ReadByDid on '+this.config.stateBase+'. Dids='+JSON.stringify(dids)); await this.pushCmnd(ctx, 'read', dids); await ctx.setStateAsync(id, { val: JSON.stringify(dids), ack: true }); // Acknowlegde user command } + if (id.includes('.json.')) { + // User requests WriteByDid for specific did + const i = id.indexOf('.json.')+6; // Index of start of did + const did = Number(id.slice(i,i+4)); + //const val = await this.storage.toByteArray(JSON.parse(state.val)); + await ctx.log.error('UDS user command WriteByDid on '+this.config.stateBase+'.'+String(did)+' - JSON format not supported yet.'); + } + if (id.includes('.raw.')) { + // User requests WriteByDid for specific did + const i = id.indexOf('.raw.')+5; // Index of start of did + const did = Number(id.slice(i,i+4)); + const val = await this.storage.toByteArray(JSON.parse(state.val)); + await ctx.log.debug('UDS user command WriteByDid on '+this.config.stateBase+'.'+String(did)+'='+this.storage.arr2Hex(val)); + await this.pushCmnd(ctx, 'write', [[did,val]]); + setTimeout(function(ctx,did){ctx.cmndsQueue.push({'mode':'read', 'did': did});},2500,this,did); // Read value after 2500 ms + } if (id.includes('.tree.')) { // User requests WriteByDid for specific did const i = id.indexOf('.tree.')+6; // Index of start of did const did = Number(id.slice(i,i+4)); if (id.slice(i).includes('.')) { // Datapoint has sub structure. Not supported yet - ctx.log.error('UDS user command WriteByDid on device '+this.config.stateBase+'.'+String(did)+': Did has sub structure. Not supported yet.'); + ctx.log.error('UDS user command WriteByDid on '+this.config.stateBase+'.'+String(did)+': Did has sub structure. Not supported yet.'); return; } const val = await this.storage.encodeDataCAN(ctx, this, did, JSON.parse(state.val)); - await ctx.log.debug('UDS user command WriteByDid on device '+this.config.stateBase+'.'+String(did)+'='+this.storage.arr2Hex(val)); + await ctx.log.debug('UDS user command WriteByDid on '+this.config.stateBase+'.'+String(did)+'='+this.storage.arr2Hex(val)); await this.pushCmnd(ctx, 'write', [[did,val]]); - //await ctx.setStateAsync(id, { val: state.val, ack: true }); // Acknowlegde user command - setTimeout(function(ctx,did){ctx.cmndsQueue.push({'mode':'read', 'did': did});},1000,this,did); // Read value after 1000 ms + setTimeout(function(ctx,did){ctx.cmndsQueue.push({'mode':'read', 'did': did});},2500,this,did); // Read value after 2500 ms } } @@ -328,10 +349,20 @@ class uds { return [this.readByDidProt.PCI, this.readByDidProt.SIDtx,((did >> 8) & 0xFF),(did & 0xFF),0x00,0x00,0x00,0x00]; } - initialRequestWriteSF(did, valRaw) { - const frame = [this.writeByDidProt.PCI, this.writeByDidProt.SIDtx,((did >> 8) & 0xFF),(did & 0xFF),0x00,0x00,0x00,0x00]; - for (let i=0; i> 8) & 0xFF),(did & 0xFF),0x00,0x00,0x00,0x00]; + for (let i=0; i> 8) & 0xFF),(did & 0xFF),0x00,0x00,0x00]; + for (let i=0; i<3; i++) { + frame[i+5] = valRaw[i]; + } } return(frame); } @@ -356,7 +387,7 @@ class uds { return; } this.stat.cntCommTotal += 1; - await this.setDidStart(ctx, did, 'read'); + await this.setDidStart(ctx, did, 'read', 0); await this.sendFrame(ctx, await this.initialRequestReadSF(did)); await ctx.log.silly('UDS worker on '+this.config.stateBase+': ReadByDid(): '+String(this.canIDhex)+'.'+String(did)); } @@ -364,15 +395,16 @@ class uds { async writeByDid(ctx, didArr) { const did=didArr[0]; const valRaw=didArr[1]; - if (valRaw.length <= 4) { - // only SF communication for writeByDid implememnted yet - this.stat.cntCommTotal += 1; - await this.setDidStart(ctx, did, 'write'); - await this.sendFrame(ctx, await this.initialRequestWriteSF(did, valRaw)); - await ctx.log.silly('UDS worker on '+this.config.stateBase+': WriteByDid(): '+String(this.canIDhex)+'.'+String(did)+'='+this.storage.arr2Hex(valRaw)); - } else { - await ctx.log.error('UDS worker error on '+this.config.stateBase+': writeByDid() is not implemented yet for MF communication. Did '+JSON.stringify(did)+' ignored.'); - } + const len=valRaw.length; + this.stat.cntCommTotal += 1; + this.data.len = len; + this.data.databytes = valRaw.concat(0x00,0x00,0x00,0x00,0x00,0x00,0x00); // Add padding + this.data.did = did; + this.data.txPos = 3; + this.data.D0 = 0x21; + await this.setDidStart(ctx, did, 'write', len); + await this.sendFrame(ctx, await this.initialRequestWrite(did, valRaw, len)); + await ctx.log.silly('UDS worker on '+this.config.stateBase+': WriteByDid(): '+String(this.canIDhex)+'.'+String(did)+'='+this.storage.arr2Hex(valRaw)); } async msgUds(ctx, msg) { @@ -479,9 +511,7 @@ class uds { } else { // More data to come this.data.D0 += 1; - if (this.data.D0 > 0x2F) { - this.data.D0 = 0x20; - } + if (this.data.D0 > 0x2F) this.data.D0 = 0x20; } } else { // Bad CF @@ -495,7 +525,7 @@ class uds { } break; - case 3: // waitForFFwbd + case 3: // waitForFFSFwbd if ( (candata[0] == 0x03) && (candata[1] == 0x7F) && (candata[2] == this.writeByDidProt.SIDtx) ) { // Negative response this.stat.cntCommNR += 1; @@ -509,7 +539,7 @@ class uds { if (didRx == this.data.did) { // Did does match this.stat.cntCommOk += 1; - ctx.log.debug('UDS worker on '+this.config.stateBase+': writeByDid SF confirmation received.'); + ctx.log.silly('UDS worker on '+this.config.stateBase+': writeByDid SF confirmation received.'); await this.calcStat(); this.storage.storeStatistics(ctx, this); await this.setDidDone(0); @@ -524,11 +554,43 @@ class uds { break; } } - ctx.log.error('UDS worker on '+this.config.stateBase+': Bad frame for writeByDid. candata: '+this.storage.arr2Hex(candata)); + ctx.log.error('UDS worker on '+this.config.stateBase+': Bad frame for writeByDid SF. candata: '+this.storage.arr2Hex(candata)); this.stat.cntCommBadProtocol += 1; await this.calcStat(); await this.setDidDone(2500); break; + + case 4: // waitForFFFwbd + if ( (candata[0] == 0x03) && (candata[1] == 0x7F) && (candata[2] == this.writeByDidProt.SIDtx) ) { + // Negative response + this.stat.cntCommNR += 1; + ctx.log.error('UDS worker error on '+this.config.stateBase+': Negative response on device '+this.canIDhex+'. Code=0x'+Number(candata[3]).toString(16)); + await this.setDidDone(0); + break; + } + if ( (candata.length == 8) && (candata[0] == 0x30) && (candata[1] == 0x00) ) { + // Multi-frame communication confirmed + // Send data in slices of 7 bytes + let ST = candata[2]; // Separation Time (ms) + if ((ST<20) || (ST>127)) ST=50; // Accept ST 20 .. 127 ms. Default to 50 ms. + while (this.data.txPos < this.data.len) { + // More data to send + await this.sleep(ST); + const frame = [this.data.D0].concat(this.data.databytes.slice(this.data.txPos,this.data.txPos+7)); + await this.sendFrame(ctx, frame); + this.data.txPos += 7; + this.data.D0 += 1; + if (this.data.D0 > 0x2f) this.data.D0 = 0x20; + } + await this.setComState(3); // waitForFFSFwbd (wait for confirmation) + break; + } + ctx.log.error('UDS worker on '+this.config.stateBase+': Bad frame for writeByDid MF. candata: '+this.storage.arr2Hex(candata)); + this.stat.cntCommBadProtocol += 1; + await this.calcStat(); + await this.setDidDone(2500); + break; + default: this.stat.cntCommBadProtocol += 1; if (this.callback) { diff --git a/lib/storage.js b/lib/storage.js index 3780ef7..b29c097 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -184,6 +184,15 @@ class storage { return hs; } + toByteArray(hs) { + // Convert hex string, e.g. '21A8' to byte array: [33,168] + const ba = []; + for (let i=0; i Date: Mon, 1 Jan 2024 20:32:55 +0100 Subject: [PATCH 5/7] Min. time step for storing statistical data. --- lib/canCollect.js | 6 +++--- lib/canUds.js | 14 ++++++++------ lib/storage.js | 11 +++++++---- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/canCollect.js b/lib/canCollect.js index 4d3c68a..e258eab 100644 --- a/lib/canCollect.js +++ b/lib/canCollect.js @@ -33,7 +33,7 @@ class collect { async initStates(ctx, opMode) { await this.storage.initStates(ctx, opMode); this.stat.state = 'standby'; - await this.storage.storeStatistics(ctx, this); + await this.storage.storeStatistics(ctx, this, true); } async onTimeout(ctxGlobal, ctxLocal) { @@ -44,7 +44,7 @@ class collect { async startup(ctx) { this.stat.state = 'active'; - await this.storage.storeStatistics(ctx, this); + await this.storage.storeStatistics(ctx, this, true); this.data.collecting = false; await this.storage.setOpMode('normal'); await ctx.log.info('Collect worker started on '+this.config.stateBase); @@ -61,7 +61,7 @@ class collect { this.timeoutHandle = null; this.stat.state = 'stopped'; - await this.storage.storeStatistics(ctx, this); + await this.storage.storeStatistics(ctx, this, true); // Stop worker: this.data.collecting = false; diff --git a/lib/canUds.js b/lib/canUds.js index ae80316..6ac5406 100644 --- a/lib/canUds.js +++ b/lib/canUds.js @@ -93,7 +93,9 @@ class uds { cntCommTimeoutPerDid: {}, // Number of communications ending in timeout for specific did cntCommBadProtocol : 0, // Number of bad communications, e.g. bad frame cntTooBusy : 0, // Number of conflicting calls of msgUds() - replyTime : {min:this.config.timeout,max:0,mean:0} + replyTime : {min:this.config.timeout,max:0,mean:0}, + nextTs : 0, // Timestamp for next storage (earliest) + tsMinStep : 5000 // Minimum time step between storages }; } @@ -120,7 +122,7 @@ class uds { native: {}, }); await ctx.setStateAsync(this.userReadByDidId, { val: JSON.stringify([]), ack: true }); - await this.storage.storeStatistics(ctx, this); + await this.storage.storeStatistics(ctx, this, true); } this.stat.state = 'standby'; } @@ -129,7 +131,7 @@ class uds { await this.setComState(0); await this.setWorkerOpMode(opMode); this.stat.state = 'active'; - await this.storage.storeStatistics(ctx, this); + await this.storage.storeStatistics(ctx, this, true); if (opMode == 'normal') { for (const sched of Object.values(this.schedules)) { // Start schedules on startup and do one-time schedules @@ -153,7 +155,7 @@ class uds { this.stat.state = 'stopped'; const opMode = await this.storage.getOpMode(); - await this.storage.storeStatistics(ctx, this); + await this.storage.storeStatistics(ctx, this, true); await this.storage.setOpMode('standby'); // Stop loops: @@ -541,7 +543,7 @@ class uds { this.stat.cntCommOk += 1; ctx.log.silly('UDS worker on '+this.config.stateBase+': writeByDid SF confirmation received.'); await this.calcStat(); - this.storage.storeStatistics(ctx, this); + this.storage.storeStatistics(ctx, this, false); await this.setDidDone(0); break; } else { @@ -549,7 +551,7 @@ class uds { this.stat.cntCommBadProtocol += 1; ctx.log.error('UDS worker on '+this.config.stateBase+': Did mismatch writeByDid SF. Expected='+String(this.data.did)+'; Received='+String(didRx)); await this.calcStat(); - this.storage.storeStatistics(ctx, this); + this.storage.storeStatistics(ctx, this, true); await this.setDidDone(1000); break; } diff --git a/lib/storage.js b/lib/storage.js index b29c097..38f0c56 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -192,10 +192,13 @@ class storage { } return ba; } - - async storeStatistics(ctx, ctxWorker) { + + async storeStatistics(ctx, ctxWorker, forceStore) { if (['standby','normal','udsDidScan'].includes(await this.getOpMode())) { + const ts = new Date().getTime(); + if ( (!forceStore) && (ts < ctxWorker.stat.nextTs) ) return; // Min. time step not reached. Do not store. if (ctxWorker.stat) await ctx.setStateAsync(ctxWorker.config.stateBase+'.info.'+this.config.statId, JSON.stringify(ctxWorker.stat), true); + ctxWorker.stat.nextTs = ts+ctxWorker.stat.tsMinStep; } } @@ -336,13 +339,13 @@ class storage { await storeObjectTree(ctx, stateIdTree, val); await storeObjectJson(ctx, stateIdJson, val); await storeObjectJson(ctx, stateIdRaw, raw); - await this.storeStatistics(ctx, ctxWorker); + await this.storeStatistics(ctx, ctxWorker, false); break; case this.opModes[3]: // 'normal' await storeObjectTree(ctx, stateIdTree, val); await storeObjectJson(ctx, stateIdJson, val); await storeObjectJson(ctx, stateIdRaw, raw); - await this.storeStatistics(ctx, ctxWorker); + await this.storeStatistics(ctx, ctxWorker, false); break; default: ctx.log.error('Invalid opMode at class storage. Change to "standby"'); From ef39bc96f5d14d4ff97ee77bc92fa11cce20961e Mon Sep 17 00:00:00 2001 From: MyHomeMyData Date: Tue, 2 Jan 2024 11:25:10 +0100 Subject: [PATCH 6/7] Small improvement on writeByDid. Update of README => V0.5.0 --- README.md | 8 ++++---- io-package.json | 2 +- lib/canUds.js | 18 +++++++++++++++++- package.json | 2 +- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3651e7c..5558eb6 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Important parts are based on the project [open3e](https://github.com/open3e). A python based implementation of a pure listening approach using MQTT messaging is also availabe, see [E3onCAN](https://github.com/MyHomeMyData/E3onCAN). -**Present implementation is restricted on listening and reading via UDSonCAN (ReadByDid).** One of next steps will be implemention of UDSonCAN service WriteByDid. +**Present implementation supports reading and writing of datapoints via UDSonCAN (ReadByDid and WriteByDid).** Writing is restricted to raw data and numeric datapoints w/o sub structure. During first start of adapter instance a device scan will be done providing a list of all available devices for configuration dialog. A scan for datapoints of each device is also available. @@ -84,8 +84,8 @@ You may use datapoints informations on tab "LIST OF DATAPOINTS" for reference (o ## What is different to open3e project? * Obviously, the main differece is the direct integration to ioBroker. Configuration can be done via dialogs, data get's directly listed in object tree. -* WriteByDid is not supported yet. To come soon. -* Devices specific datapoints are not supported yet. A scan for datapoints per device (as depict tool of open3e is doing) is under development and hopefully comming soon. +* WriteByDid is supported for raw data and for numeric datapoints without sub structure under tree-view of objects. Writing of data is triggered by storing the datapoint with ack=false. The datapoint will be read again two seconds after writing. +* A scan for datapoints per device (as depict tool of open3e is doing) is available now. After a successful scan, device specific datapoints are listed in object tree. * In addation to open3e real time collecting of data via listening is supported. ## May open3 be used in parallel? @@ -101,7 +101,7 @@ Yes, that is possible under certain conditions: ### **WORK IN PROGRESS** * This is beta stage! -* Implement WriteByDid +* Add writing for datapoints with complex structure. * Improve usability for tab "LIST OF DATAPOINTS" ## License diff --git a/io-package.json b/io-package.json index 2e89416..c105b3b 100644 --- a/io-package.json +++ b/io-package.json @@ -1,7 +1,7 @@ { "common": { "name": "e3oncan", - "version": "0.4.2", + "version": "0.5.0", "news": { "0.0.1": { "en": "initial release", diff --git a/lib/canUds.js b/lib/canUds.js index 6ac5406..cd06f11 100644 --- a/lib/canUds.js +++ b/lib/canUds.js @@ -337,7 +337,19 @@ class uds { const did = Number(id.slice(i,i+4)); if (id.slice(i).includes('.')) { // Datapoint has sub structure. Not supported yet - ctx.log.error('UDS user command WriteByDid on '+this.config.stateBase+'.'+String(did)+': Did has sub structure. Not supported yet.'); + await ctx.log.error('UDS user command WriteByDid on '+this.config.stateBase+'.'+String(did)+': Did has sub structure. Not supported yet.'); + /* + //const idBase = id.slice(ctx.namespace.length+1,i+id.slice(i).indexOf('.')); + const idBase = id.slice(0,i+id.slice(i).indexOf('.')); + await ctx.log.debug(idBase); + await ctx.getStatesOf(function(err,obj) { + for (const state of Object.values(obj)) { + if (state._id.includes(idBase)) ctx.log.debug(state._id); + } + }); + //await ctx.getObject(idBase, function(err,obj) {ctx.log.debug(JSON.stringify(obj));}); + //await ctx.log.debug(await JSON.stringify((await ctx.getObject((idBase)).val)); + */ return; } const val = await this.storage.encodeDataCAN(ctx, this, did, JSON.parse(state.val)); @@ -395,6 +407,10 @@ class uds { } async writeByDid(ctx, didArr) { + if (await this.storage.getOpMode() == 'standby') { + ctx.log.warn('UDS worker warning on '+this.config.stateBase+': Could not execute WriteByDid() for '+String(this.canIDhex)+'.'+JSON.stringify(didArr)+' due to opMode == standby.'); + return; + } const did=didArr[0]; const valRaw=didArr[1]; const len=valRaw.length; diff --git a/package.json b/package.json index 23e00f9..8b99e18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iobroker.e3oncan", - "version": "0.4.2", + "version": "0.5.0", "description": "Collect data on CAN bus for Viessmann E3 devices, e.g. Vitocal, Vitocharge, Energy Meter E380", "author": { "name": "Juergen", From 448b997955a7909e37be347676c5ccba4a0c76ea Mon Sep 17 00:00:00 2001 From: MyHomeMyData Date: Tue, 2 Jan 2024 12:01:15 +0100 Subject: [PATCH 7/7] Bugfix canCollect statistics --- lib/canCollect.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/canCollect.js b/lib/canCollect.js index e258eab..9e0476e 100644 --- a/lib/canCollect.js +++ b/lib/canCollect.js @@ -27,6 +27,8 @@ class collect { cntCommTimeout : 0, // Number of timeouts cntCommBadProt : 0, // Number of bad communications cntTooBusy : 0, // Number of conflicting calls of msgCollect() + nextTs : 0, // Timestamp for next storage (earliest) + tsMinStep : 5000 // Minimum time step between storages }; }