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/LICENSE +21 -0
- package/README.md +129 -0
- package/admin/e3oncan.png +0 -0
- package/admin/e3oncan_small.png +0 -0
- package/admin/i18n/de/translations.json +60 -0
- package/admin/i18n/en/translations.json +60 -0
- package/admin/i18n/es/translations.json +62 -0
- package/admin/i18n/fr/translations.json +62 -0
- package/admin/i18n/it/translations.json +62 -0
- package/admin/i18n/nl/translations.json +62 -0
- package/admin/i18n/pl/translations.json +62 -0
- package/admin/i18n/pt/translations.json +62 -0
- package/admin/i18n/ru/translations.json +62 -0
- package/admin/i18n/uk/translations.json +62 -0
- package/admin/i18n/zh-cn/translations.json +62 -0
- package/admin/jsonConfig.json +594 -0
- package/admin/udsDevices.json +37 -0
- package/io-package.json +164 -0
- package/lib/adapter-config.d.ts +19 -0
- package/lib/canCollect.js +199 -0
- package/lib/canUds.js +715 -0
- package/lib/codecs.js +709 -0
- package/lib/didsE3.json +29025 -0
- package/lib/didsE380.json +275 -0
- package/lib/didsE3Writables.json +396 -0
- package/lib/enums.js +2223 -0
- package/lib/storage.js +396 -0
- package/lib/udsScan.js +353 -0
- package/main.js +490 -0
- package/package.json +80 -0
package/lib/storage.js
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
|
|
2
|
+
const E3 = require('./codecs');
|
|
3
|
+
const E3DidsDict = require('./didsE3.json');
|
|
4
|
+
const E380DidsDict = require('./didsE380.json');
|
|
5
|
+
|
|
6
|
+
class storageDids {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.didsWritablesId = 'udsDidsWritable';
|
|
10
|
+
this.didsCommonId = 'udsDidsCommon';
|
|
11
|
+
this.didsSpecId = 'udsDidsSpecific';
|
|
12
|
+
this.didsWritable = {};
|
|
13
|
+
this.didsDictE3 = {}; // Common dids imported from project open3e
|
|
14
|
+
this.didsDictDevCom = {}; // Dids of this device matching the E3 common list
|
|
15
|
+
this.didsDictDevSpec = {}; // Dids specific for this device
|
|
16
|
+
this.dids = {}; // Consolidated list of dids available for this device
|
|
17
|
+
this.didsDevSpecAvail= false; // true, if device specific dids are available
|
|
18
|
+
}
|
|
19
|
+
async initStates(ctx, opMode) {
|
|
20
|
+
if (['standby','normal','udsDidScan'].includes(opMode)) {
|
|
21
|
+
if (this.config.device != 'e380') {
|
|
22
|
+
await ctx.setObjectNotExistsAsync(this.config.stateBase+'.info.'+this.didsWritablesId, {
|
|
23
|
+
type: 'state',
|
|
24
|
+
common: {
|
|
25
|
+
name: this.config.stateBase+' list of datapoints writable via WriteByDid',
|
|
26
|
+
role: 'state',
|
|
27
|
+
type: 'json',
|
|
28
|
+
read: true,
|
|
29
|
+
write: true,
|
|
30
|
+
def: JSON.stringify({})
|
|
31
|
+
},
|
|
32
|
+
native: {},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await ctx.setObjectNotExistsAsync(this.config.stateBase+'.info.'+this.didsCommonId, {
|
|
36
|
+
type: 'state',
|
|
37
|
+
common: {
|
|
38
|
+
name: this.config.stateBase+' all available datapoints',
|
|
39
|
+
role: 'state',
|
|
40
|
+
type: 'json',
|
|
41
|
+
read: true,
|
|
42
|
+
write: true,
|
|
43
|
+
def: JSON.stringify({})
|
|
44
|
+
},
|
|
45
|
+
native: {},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await ctx.setObjectNotExistsAsync(this.config.stateBase+'.info.'+this.didsSpecId, {
|
|
49
|
+
type: 'state',
|
|
50
|
+
common: {
|
|
51
|
+
name: this.config.stateBase+' available datapoints specific to this device',
|
|
52
|
+
role: 'state',
|
|
53
|
+
type: 'json',
|
|
54
|
+
read: true,
|
|
55
|
+
write: true,
|
|
56
|
+
def: JSON.stringify({})
|
|
57
|
+
},
|
|
58
|
+
native: {},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async mergeDids(didsCommon, didsDevSpecific) {
|
|
64
|
+
const dids = await JSON.parse(JSON.stringify(didsCommon));
|
|
65
|
+
for (const [id, did] of Object.entries(didsDevSpecific)) {
|
|
66
|
+
dids[id] = did;
|
|
67
|
+
}
|
|
68
|
+
return(dids);
|
|
69
|
+
}
|
|
70
|
+
async readKnownDids(ctx, opMode) {
|
|
71
|
+
// Read common and devive specific dids list known from dids scan
|
|
72
|
+
// If scanned dids are avalilable: return complete list of dids for this device
|
|
73
|
+
// else return list of common dids for all devices and list of writable dids
|
|
74
|
+
if (this.config.device != 'e380') {
|
|
75
|
+
if (opMode != 'udsDevScan') {
|
|
76
|
+
try {
|
|
77
|
+
const baseId = this.config.stateBase+'.info.';
|
|
78
|
+
this.didsWritable = await JSON.parse((await ctx.getStateAsync(baseId+this.didsWritablesId)).val);
|
|
79
|
+
this.didsDictDevCom = await JSON.parse((await ctx.getStateAsync(baseId+this.didsCommonId)).val);
|
|
80
|
+
this.didsDictDevSpec = await JSON.parse((await ctx.getStateAsync(baseId+this.didsSpecId)).val);
|
|
81
|
+
this.didsDevSpecAvail= true;
|
|
82
|
+
} catch(e) {
|
|
83
|
+
// Device specific data not available yet
|
|
84
|
+
this.didsWritable = {};
|
|
85
|
+
this.didsDictDevCom = E3DidsDict;
|
|
86
|
+
this.didsDictDevSpec = {};
|
|
87
|
+
this.didsDevSpecAvail= false;
|
|
88
|
+
}
|
|
89
|
+
if (Object.keys(this.didsDictDevCom).length == 0) {
|
|
90
|
+
// No dids scan results available yet
|
|
91
|
+
this.didsDictDevCom = E3DidsDict;
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
// UDS device scan
|
|
95
|
+
this.didsWritable = {};
|
|
96
|
+
this.didsDictDevCom[ctx.udsDidForScan] = E3DidsDict[ctx.udsDidForScan];
|
|
97
|
+
this.didsDictDevSpec = {};
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
this.didsWritable = {};
|
|
101
|
+
this.didsDictDevCom = {};
|
|
102
|
+
this.didsDictDevSpec = E380DidsDict;
|
|
103
|
+
}
|
|
104
|
+
if (opMode == 'udsDidScan') {
|
|
105
|
+
this.didsDictDevCom = {};
|
|
106
|
+
this.dids = E3DidsDict;
|
|
107
|
+
} else {
|
|
108
|
+
this.dids = await this.mergeDids(this.didsDictDevCom,this.didsDictDevSpec);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async storeKnownDids(ctx) {
|
|
112
|
+
if (this.config.device != 'e380') {
|
|
113
|
+
const baseId = this.config.stateBase+'.info.';
|
|
114
|
+
await ctx.setStateAsync(baseId+this.didsWritablesId, {val: JSON.stringify(this.didsWritable), ack: true});
|
|
115
|
+
await ctx.setStateAsync(baseId+this.didsCommonId, {val: JSON.stringify(this.didsDictDevCom), ack: true});
|
|
116
|
+
await ctx.setStateAsync(baseId+this.didsSpecId, {val: JSON.stringify(this.didsDictDevSpec), ack: true});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
class storage {
|
|
122
|
+
constructor(config) {
|
|
123
|
+
this.config = config;
|
|
124
|
+
this.storageDids = new storageDids({
|
|
125
|
+
stateBase:this.config.stateBase,
|
|
126
|
+
device:this.config.device
|
|
127
|
+
});
|
|
128
|
+
this.opModes = ['standby','udsDevScan','udsDidScan','normal','TEST'];
|
|
129
|
+
this.opMode = this.opModes[0];
|
|
130
|
+
this.udsScanResult = null;
|
|
131
|
+
}
|
|
132
|
+
async initStates(ctx, opMode) {
|
|
133
|
+
await this.setOpMode(opMode);
|
|
134
|
+
if (opMode != 'udsDevScan') {
|
|
135
|
+
await ctx.setObjectNotExistsAsync(this.config.stateBase, {
|
|
136
|
+
type: 'device',
|
|
137
|
+
common: {
|
|
138
|
+
name: this.config.stateBase,
|
|
139
|
+
role: 'device'
|
|
140
|
+
},
|
|
141
|
+
native: {},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await ctx.setObjectNotExistsAsync(this.config.stateBase+'.info', {
|
|
145
|
+
type: 'channel',
|
|
146
|
+
common: {
|
|
147
|
+
name: this.config.stateBase+' informations',
|
|
148
|
+
role: 'channel'
|
|
149
|
+
},
|
|
150
|
+
native: {},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await ctx.setObjectNotExistsAsync(this.config.stateBase+'.info.'+this.config.statId, {
|
|
154
|
+
type: 'state',
|
|
155
|
+
common: {
|
|
156
|
+
name: this.config.stateBase+' statistics about communication',
|
|
157
|
+
role: 'state',
|
|
158
|
+
type: 'json',
|
|
159
|
+
read: true,
|
|
160
|
+
write: true,
|
|
161
|
+
def: JSON.stringify({})
|
|
162
|
+
},
|
|
163
|
+
native: {},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
await this.storageDids.initStates(ctx, opMode);
|
|
167
|
+
|
|
168
|
+
await ctx.setObjectNotExistsAsync(this.config.stateBase+'.json', {
|
|
169
|
+
type: 'channel',
|
|
170
|
+
common: {
|
|
171
|
+
name: this.config.stateBase+' JSON',
|
|
172
|
+
role: 'channel for json data'
|
|
173
|
+
},
|
|
174
|
+
native: {},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await ctx.setObjectNotExistsAsync(this.config.stateBase+'.tree', {
|
|
178
|
+
type: 'channel',
|
|
179
|
+
common: {
|
|
180
|
+
name: this.config.stateBase+' TREE',
|
|
181
|
+
role: 'channel for tree data'
|
|
182
|
+
},
|
|
183
|
+
native: {},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await ctx.setObjectNotExistsAsync(this.config.stateBase+'.raw', {
|
|
187
|
+
type: 'channel',
|
|
188
|
+
common: {
|
|
189
|
+
name: this.config.stateBase+' RAW',
|
|
190
|
+
role: 'channel for raw data'
|
|
191
|
+
},
|
|
192
|
+
native: {},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
await this.storageDids.readKnownDids(ctx, opMode);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async setOpMode(mode) {
|
|
199
|
+
if (this.opModes.includes(mode)) {
|
|
200
|
+
this.opMode = mode;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async getOpMode() {
|
|
205
|
+
return(this.opMode);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
toHex(d) {
|
|
209
|
+
// Convert integer to hex string of length len
|
|
210
|
+
return ('00'+(Number(d).toString(16))).slice(-2);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
arr2Hex(arr) {
|
|
214
|
+
// Convert byte array to hex string
|
|
215
|
+
let hs = '';
|
|
216
|
+
for (const v in arr) { hs += this.toHex(arr[v]); }
|
|
217
|
+
return hs;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
toByteArray(hs) {
|
|
221
|
+
// Convert hex string, e.g. '21A8' to byte array: [33,168]
|
|
222
|
+
const ba = [];
|
|
223
|
+
for (let i=0; i<hs.length/2; i++) {
|
|
224
|
+
ba.push(parseInt(hs.slice(2*i,2*i+2), 16));
|
|
225
|
+
}
|
|
226
|
+
return ba;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async storeStatistics(ctx, ctxWorker, forceStore) {
|
|
230
|
+
if (['standby','normal','udsDidScan'].includes(await this.getOpMode())) {
|
|
231
|
+
const ts = new Date().getTime();
|
|
232
|
+
if ( (!forceStore) && (ts < ctxWorker.stat.nextTs) ) return; // Min. time step not reached. Do not store.
|
|
233
|
+
if (ctxWorker.stat) await ctx.setStateAsync(ctxWorker.config.stateBase+'.info.'+this.config.statId, JSON.stringify(ctxWorker.stat), true);
|
|
234
|
+
ctxWorker.stat.nextTs = ts+ctxWorker.stat.tsMinStep;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async encodeDataCAN(ctx, ctxWorker, did, data) {
|
|
239
|
+
// Encode data for given did
|
|
240
|
+
let val;
|
|
241
|
+
if (did in this.storageDids.dids) {
|
|
242
|
+
const cdi = this.storageDids.dids[did]; // Infos about did codec
|
|
243
|
+
try {
|
|
244
|
+
const codec = await new E3.O3Ecodecs[cdi.codec](cdi.len, cdi.id, cdi.args);
|
|
245
|
+
val = await codec.encode(data);
|
|
246
|
+
} catch(e) {
|
|
247
|
+
await ctx.log.warn('encodeDataCAN(): Could not encode data for '+ctxWorker.config.stateBase+'.'+String(did)+'. err='+e.message);
|
|
248
|
+
val = null;
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
await ctx.log.warn('encodeDataCAN(): Did not found for '+ctxWorker.config.stateBase+'.'+String(did));
|
|
252
|
+
val = null;
|
|
253
|
+
}
|
|
254
|
+
return(val);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async decodeDataCAN(ctx, ctxWorker, did, data) {
|
|
258
|
+
async function storeObjectJson(ctx, stateId, obj) {
|
|
259
|
+
try {
|
|
260
|
+
await ctx.setObjectNotExistsAsync(stateId, {
|
|
261
|
+
type: 'state',
|
|
262
|
+
common: {
|
|
263
|
+
name: idStr,
|
|
264
|
+
type: 'string',
|
|
265
|
+
role: 'state',
|
|
266
|
+
read: true,
|
|
267
|
+
write: true,
|
|
268
|
+
},
|
|
269
|
+
native: {},
|
|
270
|
+
});
|
|
271
|
+
ctx.log.silly(String(stateId)+': '+JSON.stringify(obj));
|
|
272
|
+
await ctx.setStateAsync(stateId, JSON.stringify(obj), true);
|
|
273
|
+
}
|
|
274
|
+
catch(e) {
|
|
275
|
+
ctx.log.error('Storage of did '+stateId+'.'+String(did)+' failed. err='+e.message);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
async function storeObjectTree(ctx, stateId, obj) {
|
|
279
|
+
try {
|
|
280
|
+
if (typeof(obj) == 'object') {
|
|
281
|
+
if (Object.keys(obj).length <= 100) {
|
|
282
|
+
for (const [key, itm] of Object.entries(obj)) {
|
|
283
|
+
await storeObjectTree(ctx, String(stateId)+'.'+String(key),itm);
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
ctx.log.error('Did valuation aborted. Too many members ('+String(Object.keys(obj).length)+') '+stateId+'.'+String(did));
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
await storeObjectJson(ctx, stateId, obj);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch(e) {
|
|
293
|
+
ctx.log.error('Storage of did '+stateId+'.'+String(did)+' failed. err='+e.message);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (this.opMode == this.opModes[0]) { return; }
|
|
298
|
+
const raw = this.arr2Hex(data);
|
|
299
|
+
let idStr, val, common, cdi, codec;
|
|
300
|
+
if (did in this.storageDids.dids) {
|
|
301
|
+
cdi = this.storageDids.dids[did]; // Infos about did codec
|
|
302
|
+
if (cdi.len == data.length) {
|
|
303
|
+
try {
|
|
304
|
+
codec = await new E3.O3Ecodecs[cdi.codec](cdi.len, cdi.id, cdi.args);
|
|
305
|
+
} catch(e) {
|
|
306
|
+
ctx.log.warn('Could not retreive codec for '+ctxWorker.config.stateBase+'.'+String(did)+'. err='+e.message);
|
|
307
|
+
codec = 'RawCodec';
|
|
308
|
+
}
|
|
309
|
+
idStr = cdi.id;
|
|
310
|
+
common = true;
|
|
311
|
+
try {
|
|
312
|
+
val = await codec.decode(data);
|
|
313
|
+
}
|
|
314
|
+
catch(e) {
|
|
315
|
+
val = raw;
|
|
316
|
+
ctx.log.warn('Codec failed: '+ctxWorker.canIDhex+'.'+String(did)+'. err='+e.message);
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
idStr = 'DeviceSpecific';
|
|
320
|
+
val = raw;
|
|
321
|
+
common = false;
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
idStr = 'DeviceSpecific';
|
|
325
|
+
val = raw;
|
|
326
|
+
common = false;
|
|
327
|
+
}
|
|
328
|
+
if (val != null) {
|
|
329
|
+
const didStr = '000'+String(did);
|
|
330
|
+
const stateIdJson = this.config.stateBase+'.json.'+didStr.slice(-4)+'_'+idStr;
|
|
331
|
+
const stateIdTree = this.config.stateBase+'.tree.'+didStr.slice(-4)+'_'+idStr;
|
|
332
|
+
const stateIdRaw = this.config.stateBase+'.raw.'+didStr.slice(-4)+'_'+idStr;
|
|
333
|
+
let didInfo;
|
|
334
|
+
switch (this.opMode) {
|
|
335
|
+
case this.opModes[0]: // 'standby'
|
|
336
|
+
break;
|
|
337
|
+
case this.opModes[1]: // 'udsDevScan'
|
|
338
|
+
this.udsScanResult = {
|
|
339
|
+
did: did,
|
|
340
|
+
didInfo: {
|
|
341
|
+
id : idStr,
|
|
342
|
+
len: data.length
|
|
343
|
+
},
|
|
344
|
+
val: val,
|
|
345
|
+
common: common
|
|
346
|
+
};
|
|
347
|
+
if (ctxWorker.callback) await ctxWorker.callback(ctx, ctxWorker, ['ok', this.udsScanResult]);
|
|
348
|
+
break;
|
|
349
|
+
case this.opModes[2]: // 'udsDidScan'
|
|
350
|
+
if (common) {
|
|
351
|
+
didInfo = {
|
|
352
|
+
id : idStr,
|
|
353
|
+
len: cdi.len,
|
|
354
|
+
codec: cdi.codec,
|
|
355
|
+
args: cdi.args
|
|
356
|
+
};
|
|
357
|
+
} else {
|
|
358
|
+
didInfo = {
|
|
359
|
+
id : idStr,
|
|
360
|
+
len: data.length,
|
|
361
|
+
codec: 'RawCodec',
|
|
362
|
+
args : {}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
this.udsScanResult = {
|
|
366
|
+
did: did,
|
|
367
|
+
didInfo: didInfo,
|
|
368
|
+
val: val,
|
|
369
|
+
common: common
|
|
370
|
+
};
|
|
371
|
+
if (ctxWorker.callback) await ctxWorker.callback(ctx, ctxWorker, ['ok', this.udsScanResult]);
|
|
372
|
+
await storeObjectTree(ctx, stateIdTree, val);
|
|
373
|
+
await storeObjectJson(ctx, stateIdJson, val);
|
|
374
|
+
await storeObjectJson(ctx, stateIdRaw, raw);
|
|
375
|
+
await this.storeStatistics(ctx, ctxWorker, false);
|
|
376
|
+
break;
|
|
377
|
+
case this.opModes[3]: // 'normal'
|
|
378
|
+
await storeObjectTree(ctx, stateIdTree, val);
|
|
379
|
+
await storeObjectJson(ctx, stateIdJson, val);
|
|
380
|
+
await storeObjectJson(ctx, stateIdRaw, raw);
|
|
381
|
+
await this.storeStatistics(ctx, ctxWorker, false);
|
|
382
|
+
break;
|
|
383
|
+
case this.opModes[4]: // 'TEST'
|
|
384
|
+
return val;
|
|
385
|
+
default:
|
|
386
|
+
ctx.log.error('Invalid opMode at class storage. Change to "standby"');
|
|
387
|
+
this.opMode = this.opModes[0];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
module.exports = {
|
|
394
|
+
storageDids,
|
|
395
|
+
storage
|
|
396
|
+
};
|