iobroker.e3oncan 0.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/udsScan.js ADDED
@@ -0,0 +1,353 @@
1
+ const uds = require('./canUds');
2
+ const E3DidsWritable = require('./didsE3Writables.json');
3
+
4
+ class udsScan {
5
+
6
+ constructor() {
7
+
8
+ this.callbackBusy = false;
9
+ this.udsMaxTrialsDevScan = 2; // Number of trials during UDS device scan
10
+ this.udsMaxTrialsDidScan = 4; // Number of trials during UDS dids scan
11
+ this.udsTimeoutDevScan = 1500; // Timeout (ms) for UDS devive scan
12
+ this.udsTimeoutDidScan = 2500; // Timeout (ms) for UDS dids scan
13
+
14
+ this.udsDevName2CanId = {
15
+ 'HPMUMASTER': '0x693', // available only on internal bus (?)
16
+ 'EMCUMASTER': '0x451'
17
+ };
18
+ this.workers = {};
19
+ this.udsCntNewDevs = 0; // New devices found during scan
20
+ this.cntUdsScansActive = 0;
21
+ this.udsScanAddrSpan = 0x10;
22
+ this.udsScanAddrRange = [0x680, 0x6a0, 0x6c0, 0x6e0];
23
+ this.cntUdsScansActive = 0;
24
+ this.udsScanDids = {};
25
+ this.udsScanDidsCntSuccess = 0;
26
+ this.udsScanDidsCntTotal = 0;
27
+ this.udsScanDidsCntDone = 0;
28
+ this.udsScanDidsCntRetries = 0;
29
+ }
30
+
31
+ async scanDevCallback(ctx, ctxWorker, _args) {
32
+ async function mergeDev(dev) {
33
+ let newDev = true;
34
+ await ctx.log.silly('UDS device scan found device: '+String(dev.devStateName));
35
+ for (const d of Object(ctx.udsDevices).values()) {
36
+ if ( (d.devName == dev.devStateName.slice(0,-6)) && (d.devAddr == dev.devAddr) ) {
37
+ await ctx.log.silly('Device is already known. No change applied.');
38
+ newDev = false;
39
+ dev.devStateName = d.devStateName;
40
+ dev.collectCanId = d.collectCanId;
41
+ break;
42
+ }
43
+ }
44
+ if (newDev) {
45
+ await ctx.log.info('UDS device scan found NEW device: '+String(dev.devStateName));
46
+ await ctx.udsDevices.push(dev);
47
+ ctx.udsScanWorker.udsCntNewDevs += 1;
48
+ }
49
+ }
50
+ const response = _args[0];
51
+ const result = _args[1];
52
+ switch (response) {
53
+ case 'ok':
54
+ if (result) {
55
+ const devName = result.val.DeviceProperty.Text;
56
+ await mergeDev ({
57
+ 'devName': devName,
58
+ 'devStateName': devName+'_'+String(ctxWorker.canIDhex),
59
+ 'devAddr': String(ctxWorker.canIDhex),
60
+ 'collectCanId': (devName in ctx.udsScanWorker.udsDevName2CanId ? ctx.udsScanWorker.udsDevName2CanId[devName] : '')
61
+ });
62
+ } else {
63
+ await ctx.log.error('UDS Scan: '+String(ctxWorker.canIDhex)+' got ok, but empty response!');
64
+ }
65
+ break;
66
+ case 'timeout':
67
+ if (ctxWorker.stat.cntCommTimeout < ctx.udsScanWorker.udsMaxTrialsDevScan) {
68
+ await ctxWorker.pushCmnd(ctx, 'read', [ctx.udsDidForScan]);
69
+ ctx.udsScanWorker.udsScanDidsCntRetries += 1;
70
+ }
71
+ await ctx.log.silly('UDS Scan: '+String(ctxWorker.canIDhex)+' '+response);
72
+ break;
73
+ default:
74
+ await ctx.log.silly('UDS Scan: '+String(ctxWorker.canIDhex)+' '+response);
75
+ }
76
+ if (ctxWorker.cmndsQueue.length == 0) {
77
+ await ctxWorker.setCallback(null); // Scan worker completed. Reset callback.
78
+ ctx.udsScanWorker.cntUdsScansActive -= 1;
79
+ }
80
+ }
81
+
82
+ async scanDidsCallback(ctx, ctxWorker, _args) {
83
+ if (ctx.udsScanWorker.callbackBusy) {
84
+ ctx.log.error('UDS dids scan: '+ctxWorker.config.stateBase+' - callback busy!');
85
+ return;
86
+ }
87
+ ctx.udsScanWorker.callbackBusy = true;
88
+ const response = _args[0];
89
+ const result = _args[1];
90
+ if (result) {
91
+ const did = await Number(result.did);
92
+ switch (response) {
93
+ case 'ok':
94
+ case 'negative response':
95
+ if (response == 'ok') {
96
+ if (result.common) {
97
+ ctxWorker.storage.storageDids.didsDictDevCom[did] = result.didInfo;
98
+ } else {
99
+ // Device specific did. Do not override previous data
100
+ if (!(did in ctxWorker.storage.storageDids.didsDictDevSpec)) {
101
+ ctxWorker.storage.storageDids.didsDictDevSpec[did] = result.didInfo;
102
+ }
103
+ }
104
+ if (did in E3DidsWritable) {
105
+ // Did is writable. Add it to device specific list of writable dids:
106
+ ctxWorker.storage.storageDids.didsWritable[did] = E3DidsWritable[did];
107
+ }
108
+ ctx.udsScanWorker.udsScanDidsCntSuccess += 1;
109
+ }
110
+ ctx.udsScanWorker.udsScanDidsCntDone += 1;
111
+ ctx.udsScanWorker.udsScanDids[ctxWorker.canIDhex] -= 1;
112
+ await ctx.log.silly('UDS dids scan: '+ctxWorker.config.stateBase+'.'+String(ctxWorker.data.did)+': '+response);
113
+ break;
114
+ case 'timeout':
115
+ case 'did mismatch SF':
116
+ case 'did mismatch MF':
117
+ case 'bad MF frame':
118
+ case 'bad CF frame':
119
+ if (response == 'timeout') {
120
+ ctx.log.silly('UDS dids scan: Timeout on '+ctxWorker.config.stateBase+'.'+String(ctxWorker.data.did)+' cnt: '+String(ctxWorker.stat.cntCommTimeoutPerDid[ctxWorker.data.did]));
121
+ if (ctxWorker.stat.cntCommTimeoutPerDid[ctxWorker.data.did] < ctx.udsScanWorker.udsMaxTrialsDidScan) {
122
+ ctxWorker.pushCmnd(ctx, 'read', [ctxWorker.data.did]);
123
+ ctx.udsScanWorker.udsScanDidsCntRetries += 1;
124
+ }
125
+ await ctx.log.silly('UDS dids scan: '+ctxWorker.config.stateBase+' '+response);
126
+ } else {
127
+ ctx.log.debug('UDS dids scan: '+ctxWorker.config.stateBase+' - communication failed: '+response);
128
+ }
129
+ break;
130
+ default:
131
+ await ctx.log.warn('UDS dids scan: '+ctxWorker.config.stateBase+' - callback received unknown status: '+String(response));
132
+ }
133
+ if (ctx.udsScanWorker.udsScanDids[ctxWorker.canIDhex] == 0) ctx.udsScanWorker.cntUdsScansActive -= 1;
134
+ if (ctx.udsScanWorker.cntUdsScansActive == -1) {
135
+ await ctx.log.error('UDS dids scan: '+ctxWorker.config.stateBase+' - number of active dids got negative - this should not happen.');
136
+ }
137
+ if (ctx.udsScanWorker.udsScanDids[ctxWorker.canIDhex] == -1) {
138
+ await ctx.log.error('UDS dids scan: '+ctxWorker.config.stateBase+' - number of remaining dids got negative - this should not happen.');
139
+ }
140
+ } else {
141
+ await ctx.log.error('UDS dids scan: '+ctxWorker.config.stateBase+' - callback received empty result. response='+String(response));
142
+ }
143
+ ctx.udsScanWorker.callbackBusy = false;
144
+ }
145
+
146
+ async startupUdsWorker(ctx, worker, opMode) {
147
+ const rxAddr = Number(worker.config.canID) + Number(0x10);
148
+ ctx.udsScanWorker.workers[rxAddr] = worker;
149
+ await worker.initStates(ctx, opMode);
150
+ await worker.startup(ctx, opMode);
151
+ //await ctx.log.debug(ctx.udsScanWorker.workers[rxAddr].canIDhex+': '+await this.workers[rxAddr].storage.getOpMode());
152
+ }
153
+
154
+ async startupScanUdsDevice(ctx, addr) {
155
+ const udsWorker = new uds.uds(
156
+ { 'canID' : Number(addr),
157
+ 'stateBase': 'udsScanAddr',
158
+ 'device' : 'common',
159
+ 'delay' : 0,
160
+ 'active' : true,
161
+ 'channel' : ctx.channelExt,
162
+ 'timeout' : this.udsTimeoutDevScan
163
+ });
164
+ await udsWorker.initStates(ctx, 'udsDevScan');
165
+ await udsWorker.setCallback(this.scanDevCallback);
166
+ await this.startupUdsWorker(ctx, udsWorker, 'udsDevScan');
167
+ await udsWorker.pushCmnd(ctx, 'read', [ctx.udsDidForScan]);
168
+ this.cntUdsScansActive += 1;
169
+ }
170
+
171
+ async scanUdsDevices(ctx) {
172
+ function range(size, startAt = 0) {
173
+ return [...Array(size).keys()].map(i => i + startAt);
174
+ }
175
+
176
+ await ctx.log.info('UDS device scan - start');
177
+ this.workers = {};
178
+ this.udsCntNewDevs = 0;
179
+ ctx.udsDevices = ctx.config.tableUdsDevices;
180
+
181
+ // Stop all running workers to avoid communication conflicts:
182
+ for (const worker of Object.values(ctx.E3UdsWorkers)) {
183
+ await worker.stop(ctx);
184
+ }
185
+
186
+ // @ts-ignore
187
+ const canExtActivated = ctx.config.canExtActivated;
188
+ // @ts-ignore
189
+ const canExtName = ctx.config.canExtName;
190
+
191
+ // Startup CAN:
192
+ if (canExtActivated) {
193
+ if ((ctx.channelExt) &&
194
+ (ctx.channelExtName != canExtName) ) {
195
+ // CAN is different from running CAN. Stop actual CAN first.
196
+ [ctx.channelExt, ctx.channelExtName] = await ctx.disconnectFromCan(ctx.channelExt, ctx.channelExtName);
197
+ }
198
+ [ctx.channelExt, ctx.channelExtName] = await ctx.connectToCan(ctx.channelExt, canExtName, ctx.onCanMsgExt);
199
+ if (!ctx.channelExt) {
200
+ await ctx.log.error('UDS device scan: Could not connect to CAN Adapter '+canExtName+'. Aborting.');
201
+ return(false);
202
+ }
203
+ } else {
204
+ await ctx.log.error('UDS device scan: External CAN not activated! Aborting.');
205
+ return(false);
206
+ }
207
+
208
+ this.udsScanDidsCntRetries = 0;
209
+ this.cntUdsScansActive = 0;
210
+ for (const baseAddr of Object(this.udsScanAddrRange).values()) {
211
+ for (const addr of Object(range(Number(this.udsScanAddrSpan), Number(baseAddr))).values()) {
212
+ await this.startupScanUdsDevice(ctx, addr);
213
+ await this.sleep(50);
214
+ }
215
+ }
216
+ // eslint-disable-next-line no-case-declarations
217
+ const tsAbort = new Date().getTime() + this.udsMaxTrialsDevScan*this.udsTimeoutDevScan+250;
218
+ await ctx.log.info('UDS device scan: Waiting for scans to complete.');
219
+ while ( (this.cntUdsScansActive > 0) && (new Date().getTime() < tsAbort) ) {
220
+ await this.sleep(100);
221
+ }
222
+
223
+ // Stop all scan workers:
224
+ for (const worker of Object.values(this.workers)) {
225
+ await worker.stop(ctx);
226
+ }
227
+ this.workers = {};
228
+
229
+ // Restart all previously running workers:
230
+ for (const worker of Object.values(ctx.E3UdsWorkers)) {
231
+ await worker.startup(ctx,'normal');
232
+ }
233
+
234
+ if (this.cntUdsScansActive < 0) await ctx.log.warn('UDS scan finished. Number of retries / active UDS scans (should be 0): '+String(this.udsScanDidsCntRetries)+' / '+String(this.cntUdsScansActive));
235
+ await ctx.log.info('UDS device scan found '+
236
+ String(this.udsCntNewDevs)+
237
+ ' new of total '+
238
+ String(ctx.udsDevices.length)+
239
+ ' devices: '+
240
+ JSON.stringify(ctx.udsDevices)
241
+ );
242
+
243
+ await ctx.log.info('UDS device scan - done');
244
+
245
+ return(true);
246
+ }
247
+
248
+ async startupScanUdsDids(ctx, addr, dids) {
249
+ const hexAddr = '0x'+Number(addr).toString(16);
250
+ // @ts-ignore
251
+ const devInfo = ctx.config.tableUdsDevices.filter(item => item.devAddr == hexAddr);
252
+ let devName = '';
253
+ if (devInfo.length > 0) {
254
+ devName = devInfo[0].devStateName;
255
+ } else {
256
+ devName = hexAddr;
257
+ }
258
+ const udsWorker = await new uds.uds(
259
+ { 'canID' : Number(addr),
260
+ 'stateBase': devName,
261
+ 'device' : 'common',
262
+ 'delay' : 0,
263
+ 'active' : true,
264
+ 'channel' : ctx.channelExt,
265
+ 'timeout' : this.udsTimeoutDidScan
266
+ });
267
+ await udsWorker.setCallback(this.scanDidsCallback);
268
+ this.udsScanDids[udsWorker.canIDhex] = dids.length;
269
+ this.udsScanDidsCntTotal += dids.length;
270
+ await this.startupUdsWorker(ctx, udsWorker, 'udsDidScan');
271
+ this.cntUdsScansActive += 1;
272
+ await udsWorker.pushCmnd(ctx, 'read', dids);
273
+ }
274
+
275
+ async scanUdsDids(ctx, udsAddrs, udsMaxCntDids) {
276
+ function range(size, startAt = 0) {
277
+ return [...Array(size).keys()].map(i => i + startAt);
278
+ }
279
+ async function logStatus(ctx, ctxScan) {
280
+ await ctx.log.info('UDS dids scan status (retries/found/done/total): ('+
281
+ String(ctxScan.udsScanDidsCntRetries)+'/'+
282
+ String(ctxScan.udsScanDidsCntSuccess)+'/'+
283
+ String(ctxScan.udsScanDidsCntDone)+'/'+
284
+ String(ctxScan.udsScanDidsCntTotal)+
285
+ '), remaining: '+
286
+ JSON.stringify(ctxScan.udsScanDids)
287
+ );
288
+ }
289
+
290
+ await ctx.log.info('UDS dids scan - start');
291
+ this.workers = {};
292
+ this.udsScanDids = {};
293
+
294
+ // Stop all running workers to avoid communication conflicts:
295
+ for (const worker of Object.values(ctx.E3UdsWorkers)) {
296
+ await worker.stop(ctx);
297
+ }
298
+
299
+ this.cntUdsScansActive = 0;
300
+ this.udsScanDidsCntTotal = 0;
301
+ this.udsScanDidsCntRetries = 0;
302
+ this.udsScanDidsCntSuccess = 0;
303
+ this.udsScanDidsCntDone = 0;
304
+ const dids = range(udsMaxCntDids, 256);
305
+ for (const addr of Object(udsAddrs).values()) {
306
+ await this.startupScanUdsDids(ctx, addr, dids);
307
+ await this.sleep(50);
308
+ }
309
+ let cntDoneLast = -1;
310
+ await logStatus(ctx, this);
311
+ let ts = new Date().getTime();
312
+ const tsAbort = ts + this.udsScanDidsCntTotal*500;
313
+ while ( (this.cntUdsScansActive > 0) && (new Date().getTime() < tsAbort) ) {
314
+ await this.sleep(100);
315
+ if ((new Date().getTime() - ts) >= 10000) {
316
+ ts += 10000;
317
+ await logStatus(ctx, this);
318
+ const cnt = this.udsScanDidsCntDone+this.udsScanDidsCntRetries;
319
+ if (cntDoneLast == cnt) {
320
+ // No progress in last 10 seconds
321
+ await ctx.log.warn('UDS dids scan stalled. Aborting');
322
+ break;
323
+ }
324
+ cntDoneLast = cnt;
325
+ }
326
+ }
327
+ await logStatus(ctx, this);
328
+
329
+ // Store dids found and stop all scan workers:
330
+ for (const worker of Object.values(this.workers)) {
331
+ await worker.storage.storageDids.storeKnownDids(ctx);
332
+ await worker.stop(ctx);
333
+ }
334
+ this.workers = {};
335
+
336
+ if (this.cntUdsScansActive < 0) await ctx.log.warn('UDS dids scan finished. Number of active UDS scans (should be 0): '+String(this.cntUdsScansActive));
337
+ await ctx.log.info('UDS dids scan found '+String(this.udsScanDidsCntSuccess)+' dids. See channel "info" at device objects for details.');
338
+
339
+ // Restart all previously running workers:
340
+ for (const worker of Object.values(ctx.E3UdsWorkers)) {
341
+ await worker.startup(ctx,'normal');
342
+ }
343
+ }
344
+
345
+ sleep(milliseconds) {
346
+ return new Promise(resolve => setTimeout(resolve, milliseconds));
347
+ }
348
+
349
+ }
350
+
351
+ module.exports = {
352
+ udsScan
353
+ };