@amigo9090/ih-libiec61850-node 1.0.41 → 1.0.43
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/builds/linux_arm/addon_iec61850.node +0 -0
- package/builds/linux_arm64/addon_iec61850.node +0 -0
- package/builds/linux_x64/addon_iec61850.node +0 -0
- package/builds/macos_arm64/addon_iec61850.node +0 -0
- package/builds/macos_x64/addon_iec61850.node +0 -0
- package/builds/windows_x64/addon_iec61850.node +0 -0
- package/examples/index_iec61850_client2.js +534 -15
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -4,11 +4,32 @@ const util = require('util');
|
|
|
4
4
|
const client = new MmsClient((event, data) => {
|
|
5
5
|
console.log(`Event: ${event}, Data: ${util.inspect(data, { depth: null })}`);
|
|
6
6
|
|
|
7
|
-
if (event === 'conn' && data.event === 'opened') {
|
|
7
|
+
/*if (event === 'conn' && data.event === 'opened') {
|
|
8
8
|
console.log('Connection opened, browsing data model...');
|
|
9
|
-
handleConnectionOpened();
|
|
9
|
+
//handleConnectionOpened();
|
|
10
|
+
handleConnectionOpened2();
|
|
11
|
+
}*/
|
|
12
|
+
|
|
13
|
+
if (event === 'conn' && data.event === 'opened') {
|
|
14
|
+
console.log('Connection opened');
|
|
15
|
+
// Теперь пользователь сам решит, как исследовать модель
|
|
16
|
+
console.log('\n=== Примеры использования: ===');
|
|
17
|
+
console.log('1. Получить корневые узлы:');
|
|
18
|
+
console.log(' client.browseDataModel()');
|
|
19
|
+
console.log('\n2. Получить DataObjects конкретного узла:');
|
|
20
|
+
console.log(' client.browseDataModel("WAGO61850ServerDevice/LLN0")');
|
|
21
|
+
console.log('\n3. Получить атрибуты DataObject:');
|
|
22
|
+
console.log(' client.browseDataModel("WAGO61850ServerDevice/XCBR1.Pos")');
|
|
23
|
+
console.log('\n4. Получить члены DataSet:');
|
|
24
|
+
console.log(' client.browseDataModel("WAGO61850ServerDevice/LLN0.DataSet01")');
|
|
25
|
+
console.log('\n5. Получить информацию об отчете:');
|
|
26
|
+
console.log(' client.browseDataModel("WAGO61850ServerDevice/LLN0.RP$ReportBlock0101")');
|
|
27
|
+
|
|
28
|
+
// Автоматически получаем корневые узлы для примера
|
|
29
|
+
exploreModel();
|
|
10
30
|
}
|
|
11
31
|
|
|
32
|
+
|
|
12
33
|
if (event === 'data' && data.type === 'data') {
|
|
13
34
|
if (data.event === 'logicalDevices') {
|
|
14
35
|
console.log(`Logical Devices received: ${util.inspect(data.logicalDevices, { depth: null })}`);
|
|
@@ -85,7 +106,7 @@ const client = new MmsClient((event, data) => {
|
|
|
85
106
|
|
|
86
107
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
87
108
|
|
|
88
|
-
async function handleConnectionOpened() {
|
|
109
|
+
/*async function handleConnectionOpened() {
|
|
89
110
|
try {
|
|
90
111
|
const dataModel = await client.browseDataModel();
|
|
91
112
|
console.log('Data Model:', util.inspect(dataModel, { depth: null }));
|
|
@@ -118,7 +139,7 @@ async function handleConnectionOpened() {
|
|
|
118
139
|
console.log(` Значений: ${res.count}, Удаляемые: ${res.isDeletable}`);
|
|
119
140
|
|
|
120
141
|
// Функция для рекурсивного вывода вложенных структур
|
|
121
|
-
const printValue = (value, indent = ' ') => {
|
|
142
|
+
/*const printValue = (value, indent = ' ') => {
|
|
122
143
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
123
144
|
Object.entries(value).forEach(([key, val]) => {
|
|
124
145
|
if (val && typeof val === 'object' && !Array.isArray(val)) {
|
|
@@ -145,9 +166,50 @@ async function handleConnectionOpened() {
|
|
|
145
166
|
console.log(` ${ref}:`);
|
|
146
167
|
printValue(value, ' ');
|
|
147
168
|
});
|
|
148
|
-
|
|
169
|
+
|
|
149
170
|
|
|
150
|
-
|
|
171
|
+
// Функция для рекурсивного вывода с полным отображением всех элементов
|
|
172
|
+
const printModel = (model, indent = '') => {
|
|
173
|
+
if (Array.isArray(model)) {
|
|
174
|
+
model.forEach((item, index) => {
|
|
175
|
+
console.log(`${indent}[${index}]:`);
|
|
176
|
+
printModel(item, indent + ' ');
|
|
177
|
+
});
|
|
178
|
+
} else if (model && typeof model === 'object') {
|
|
179
|
+
Object.entries(model).forEach(([key, value]) => {
|
|
180
|
+
if (key === 'dataObjects' && Array.isArray(value)) {
|
|
181
|
+
console.log(`${indent}${key}: [`);
|
|
182
|
+
value.forEach((doObj, idx) => {
|
|
183
|
+
console.log(`${indent} [${idx}]:`);
|
|
184
|
+
printModel(doObj, indent + ' ');
|
|
185
|
+
});
|
|
186
|
+
console.log(`${indent}]`);
|
|
187
|
+
} else if (key === 'attributes' && typeof value === 'object') {
|
|
188
|
+
console.log(`${indent}${key}: {`);
|
|
189
|
+
printModel(value, indent + ' ');
|
|
190
|
+
console.log(`${indent}}`);
|
|
191
|
+
} else if (Array.isArray(value)) {
|
|
192
|
+
console.log(`${indent}${key}: [`);
|
|
193
|
+
value.forEach((item, idx) => {
|
|
194
|
+
console.log(`${indent} [${idx}]: ${item}`);
|
|
195
|
+
});
|
|
196
|
+
console.log(`${indent}]`);
|
|
197
|
+
} else if (typeof value === 'object') {
|
|
198
|
+
console.log(`${indent}${key}: {`);
|
|
199
|
+
printModel(value, indent + ' ');
|
|
200
|
+
console.log(`${indent}}`);
|
|
201
|
+
} else {
|
|
202
|
+
console.log(`${indent}${key}: ${value}`);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
} else {
|
|
206
|
+
console.log(`${indent}${model}`);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
console.log('Reading data...');
|
|
151
213
|
const dataRefs = [
|
|
152
214
|
'WAGO61850ServerDevice/XCBR1.Pos[ST]',
|
|
153
215
|
'WAGO61850ServerDevice/GGIO1.Ind1.stVal',
|
|
@@ -159,23 +221,23 @@ async function handleConnectionOpened() {
|
|
|
159
221
|
const rcbRef2 = 'WAGO61850ServerDevice/LLN0.RP.ReportBlock0201';
|
|
160
222
|
const dataSetRef2 = 'WAGO61850ServerDevice/LLN0.DataSet02';
|
|
161
223
|
console.log(`Enabling reporting for ${rcbRef2} with dataset ${dataSetRef2}`);
|
|
162
|
-
await client.enableReporting(rcbRef2, dataSetRef2)
|
|
224
|
+
await client.enableReporting(rcbRef2, dataSetRef2);
|
|
163
225
|
|
|
164
|
-
console.log('Reading data...');
|
|
226
|
+
/*console.log('Reading data...');
|
|
165
227
|
const dataRefs = [
|
|
166
228
|
'A01LD0/Q1_XCBR1.Pos[ST]',
|
|
167
229
|
'A01LD0/In_GGIO1.Ind1',
|
|
168
230
|
'A01LD0/CALH1.GrAlm.stVal'
|
|
169
231
|
];
|
|
170
232
|
const readRefResult = await client.readData(dataRefs);
|
|
171
|
-
console.log("readRefResult " + util.inspect(readRefResult, { depth: null }))
|
|
233
|
+
console.log("readRefResult " + util.inspect(readRefResult, { depth: null }));*/
|
|
172
234
|
|
|
173
235
|
/* const rcbRef = 'A01LD0/LLN0.RP.repTI1';
|
|
174
236
|
const dataSetRef = 'A01LD0/LLN0.TI_ASU';
|
|
175
237
|
console.log(`Enabling reporting for ${rcbRef} with dataset ${dataSetRef}`);
|
|
176
238
|
await client.enableReporting(rcbRef, dataSetRef);*/
|
|
177
239
|
|
|
178
|
-
const rcbRef2 = 'A01LD0/LLN0.BR.repTS1';
|
|
240
|
+
/*const rcbRef2 = 'A01LD0/LLN0.BR.repTS1';
|
|
179
241
|
const dataSetRef2 = 'A01LD0/LLN0.TS_ASU';
|
|
180
242
|
console.log(`Enabling reporting for ${rcbRef2} with dataset ${dataSetRef2}`);
|
|
181
243
|
await client.enableReporting(rcbRef2, dataSetRef2);
|
|
@@ -183,13 +245,470 @@ async function handleConnectionOpened() {
|
|
|
183
245
|
} catch (err) {
|
|
184
246
|
console.error('Error in handleConnectionOpened:', err.message);
|
|
185
247
|
}
|
|
248
|
+
}*/
|
|
249
|
+
|
|
250
|
+
async function handleConnectionOpened() {
|
|
251
|
+
try {
|
|
252
|
+
const dataModel = await client.browseDataModel();
|
|
253
|
+
|
|
254
|
+
const dataSets = [];
|
|
255
|
+
const reports = [];
|
|
256
|
+
|
|
257
|
+
dataModel.forEach(ld => {
|
|
258
|
+
console.log(`\nLogical Device: ${ld.name}`);
|
|
259
|
+
|
|
260
|
+
ld.logicalNodes.forEach(ln => {
|
|
261
|
+
console.log(` Logical Node: ${ln.name} (${ln.reference})`);
|
|
262
|
+
|
|
263
|
+
// Вывод DataSets
|
|
264
|
+
if (ln.dataSets && ln.dataSets.length > 0) {
|
|
265
|
+
console.log(` Datasets (${ln.dataSets.length}):`);
|
|
266
|
+
ln.dataSets.forEach(ds => {
|
|
267
|
+
console.log(` - ${ds.reference} (Deletable: ${ds.isDeletable})`);
|
|
268
|
+
dataSets.push(ds);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Вывод отчетов (Report Control Blocks)
|
|
273
|
+
if (ln.reports && ln.reports.length > 0) {
|
|
274
|
+
console.log(` Reports (${ln.reports.length}):`);
|
|
275
|
+
ln.reports.forEach((report, index) => {
|
|
276
|
+
console.log(` [${index + 1}] ${report.reference}`);
|
|
277
|
+
console.log(` Type: ${report.type} (${report.description || 'N/A'})`);
|
|
278
|
+
if (report.datasetRef) {
|
|
279
|
+
console.log(` Dataset: ${report.datasetRef}`);
|
|
280
|
+
}
|
|
281
|
+
if (report.reportId) {
|
|
282
|
+
console.log(` Report ID: ${report.reportId}`);
|
|
283
|
+
}
|
|
284
|
+
console.log(` Enabled: ${report.enabled !== undefined ? report.enabled : 'Unknown'}`);
|
|
285
|
+
reports.push(report);
|
|
286
|
+
});
|
|
287
|
+
} else {
|
|
288
|
+
console.log(` No reports found`);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
console.log('\n=== SUMMARY ===');
|
|
294
|
+
console.log(`Total Logical Devices: ${dataModel.length}`);
|
|
295
|
+
|
|
296
|
+
let totalDataSets = 0;
|
|
297
|
+
let totalReports = 0;
|
|
298
|
+
|
|
299
|
+
dataModel.forEach(ld => {
|
|
300
|
+
ld.logicalNodes.forEach(ln => {
|
|
301
|
+
totalDataSets += (ln.dataSets ? ln.dataSets.length : 0);
|
|
302
|
+
totalReports += (ln.reports ? ln.reports.length : 0);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
console.log(`Total Datasets found: ${totalDataSets}`);
|
|
307
|
+
console.log(`Total Reports found: ${totalReports}`);
|
|
308
|
+
|
|
309
|
+
// Выводим все найденные отчеты для удобства
|
|
310
|
+
console.log('\n=== ALL FOUND REPORTS ===');
|
|
311
|
+
reports.forEach((report, index) => {
|
|
312
|
+
const enabledStatus = report.enabled !== undefined ?
|
|
313
|
+
(report.enabled ? 'ENABLED' : 'DISABLED') : 'UNKNOWN';
|
|
314
|
+
console.log(`${index + 1}. ${report.reference} (${report.type}) - ${enabledStatus}`);
|
|
315
|
+
if (report.datasetRef) {
|
|
316
|
+
console.log(` Dataset: ${report.datasetRef}`);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
console.log('\n=== RECOMMENDED REPORTS FOR ENABLING ===');
|
|
321
|
+
// Ищем отчеты с DataSet02 (как в примере)
|
|
322
|
+
const recommendedReports = reports.filter(r =>
|
|
323
|
+
r.datasetRef && r.datasetRef.includes('DataSet02')
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
if (recommendedReports.length > 0) {
|
|
327
|
+
recommendedReports.forEach((report, index) => {
|
|
328
|
+
console.log(`${index + 1}. ${report.reference}`);
|
|
329
|
+
console.log(` Dataset: ${report.datasetRef}`);
|
|
330
|
+
console.log(` To enable: client.enableReporting("${report.reference}", "${report.datasetRef}")`);
|
|
331
|
+
});
|
|
332
|
+
} else {
|
|
333
|
+
console.log('No reports with DataSet02 found.');
|
|
334
|
+
// Предлагаем другие отчеты
|
|
335
|
+
if (reports.length > 0) {
|
|
336
|
+
console.log('\nAvailable reports with datasets:');
|
|
337
|
+
reports.filter(r => r.datasetRef).forEach((report, index) => {
|
|
338
|
+
console.log(`${index + 1}. ${report.reference} -> ${report.datasetRef}`);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
} catch (err) {
|
|
343
|
+
console.error('Error in handleConnectionOpened:', err.message);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function handleConnectionOpened2() {
|
|
348
|
+
try {
|
|
349
|
+
const dataModel = await client.browseDataModel();
|
|
350
|
+
console.log('Data Model:', util.inspect(dataModel, { depth: null }));
|
|
351
|
+
|
|
352
|
+
const dataSets = [];
|
|
353
|
+
const reports = [];
|
|
354
|
+
|
|
355
|
+
dataModel.forEach(ld => {
|
|
356
|
+
console.log(`\nLogical Device: ${ld.name}`);
|
|
357
|
+
|
|
358
|
+
ld.logicalNodes.forEach(ln => {
|
|
359
|
+
console.log(` Logical Node: ${ln.name}`);
|
|
360
|
+
|
|
361
|
+
// Вывод DataSets
|
|
362
|
+
if (ln.dataSets && ln.dataSets.length > 0) {
|
|
363
|
+
ln.dataSets.forEach(ds => {
|
|
364
|
+
console.log(` Dataset: ${ds.reference} (Deletable: ${ds.isDeletable})`);
|
|
365
|
+
dataSets.push(ds);
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Вывод отчетов (RCB - Report Control Blocks)
|
|
370
|
+
if (ln.reports && ln.reports.length > 0) {
|
|
371
|
+
console.log(` Reports (${ln.reports.length}):`);
|
|
372
|
+
ln.reports.forEach((report, index) => {
|
|
373
|
+
console.log(` [${index + 1}] ${report.reference}`);
|
|
374
|
+
console.log(` Type: ${report.type} (${report.description})`);
|
|
375
|
+
if (report.datasetRef) {
|
|
376
|
+
console.log(` Dataset: ${report.datasetRef}`);
|
|
377
|
+
}
|
|
378
|
+
if (report.reportId) {
|
|
379
|
+
console.log(` Report ID: ${report.reportId}`);
|
|
380
|
+
}
|
|
381
|
+
console.log(` Enabled: ${report.enabled}`);
|
|
382
|
+
reports.push(report);
|
|
383
|
+
});
|
|
384
|
+
} else {
|
|
385
|
+
console.log(` No reports found for ${ln.reference}`);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
console.log('\n=== SUMMARY ===');
|
|
391
|
+
console.log(`Total Logical Devices: ${dataModel.length}`);
|
|
392
|
+
console.log(`Total Datasets found: ${dataSets.length}`);
|
|
393
|
+
console.log(`Total Reports found: ${reports.length}`);
|
|
394
|
+
|
|
395
|
+
// Выводим все найденные отчеты для удобства
|
|
396
|
+
console.log('\n=== ALL FOUND REPORTS ===');
|
|
397
|
+
reports.forEach((report, index) => {
|
|
398
|
+
console.log(`${index + 1}. ${report.reference} (${report.type})`);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// 1. Читаем модель устройства
|
|
402
|
+
/*const dataModel = await client.browseDataModel();
|
|
403
|
+
console.log('Data Model:', util.inspect(dataModel, { depth: null }));
|
|
404
|
+
|
|
405
|
+
const dataSets = [];
|
|
406
|
+
dataModel.forEach(ld => {
|
|
407
|
+
ld.logicalNodes.forEach(ln => {
|
|
408
|
+
ln.dataSets.forEach(ds => {
|
|
409
|
+
console.log(`Found dataset: ${ds.reference}`);
|
|
410
|
+
dataSets.push(ds);
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
});*/
|
|
414
|
+
|
|
415
|
+
// 2. Читаем одиночные значения
|
|
416
|
+
console.log('Reading single values...');
|
|
417
|
+
const dataRefs = [
|
|
418
|
+
'WAGO61850ServerDevice/XCBR1.Pos[ST]',
|
|
419
|
+
'WAGO61850ServerDevice/GGIO1.Ind1[ST]',
|
|
420
|
+
'WAGO61850ServerDevice/CALH12.GrAlm.stVal'
|
|
421
|
+
];
|
|
422
|
+
const readRefResult = await client.readData(dataRefs);
|
|
423
|
+
console.log("readRefResult " + util.inspect(readRefResult, { depth: null }));
|
|
424
|
+
|
|
425
|
+
// 3. Читаем и кэшируем структуры (делаем это один раз)
|
|
426
|
+
console.log('First read (with caching)...');
|
|
427
|
+
const firstRead = await client.readDataSetModel([
|
|
428
|
+
'WAGO61850ServerDevice/LLN0.DataSet01',
|
|
429
|
+
'WAGO61850ServerDevice/LLN0.DataSet02',
|
|
430
|
+
'WAGO61850ServerDevice/LLN0.DataSet03'
|
|
431
|
+
]);
|
|
432
|
+
|
|
433
|
+
console.log('First read completed, structures cached');
|
|
434
|
+
|
|
435
|
+
// 4. Теперь можем использовать быстрый polling значений Dataset
|
|
436
|
+
console.log('\nStarting fast polling...');
|
|
437
|
+
|
|
438
|
+
for (let i = 0; i < 10; i++) {
|
|
439
|
+
console.log(`\n--- Poll ${i+1} ---`);
|
|
440
|
+
|
|
441
|
+
const startTime = Date.now();
|
|
442
|
+
|
|
443
|
+
const pollResults = await client.pollDataSetValues(['WAGO61850ServerDevice/LLN0.DataSet02']); // Быстрое чтение значений DataSet
|
|
444
|
+
|
|
445
|
+
const endTime = Date.now();
|
|
446
|
+
|
|
447
|
+
pollResults.forEach((result, idx) => {
|
|
448
|
+
if (result.isValid) {
|
|
449
|
+
console.log(`DataSet ${result.datasetRef}: ${result.count} values`);
|
|
450
|
+
console.log(` Read time: ${result.readTimeMicros} µs`);
|
|
451
|
+
console.log(` Process time: ${result.processTimeMicros} µs`);
|
|
452
|
+
|
|
453
|
+
// Выводим значения
|
|
454
|
+
Object.entries(result.values).forEach(([ref, value]) => {
|
|
455
|
+
console.log(` ${ref}:`, util.inspect(value, { depth: null }));
|
|
456
|
+
});
|
|
457
|
+
} else {
|
|
458
|
+
console.error(`Error: ${result.errorReason}`);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
console.log(`Total poll time: ${endTime - startTime} ms`);
|
|
463
|
+
|
|
464
|
+
// Ждем перед следующим опросом
|
|
465
|
+
await sleep(1000);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
} catch (err) {
|
|
469
|
+
console.error('Error in handleConnectionOpened2:', err.message);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/*async function exploreModel() {
|
|
474
|
+
try {
|
|
475
|
+
console.log('\n=== 1. Получение корневых узлов ===');
|
|
476
|
+
const rootNodes = await client.browseDataModel();
|
|
477
|
+
|
|
478
|
+
console.log('\nНайдено Logical Nodes:');
|
|
479
|
+
rootNodes.forEach((ln, index) => {
|
|
480
|
+
console.log(`\n${index + 1}. ${ln.name} (${ln.reference})`);
|
|
481
|
+
|
|
482
|
+
// Выводим ВСЕ датасеты
|
|
483
|
+
if (ln.dataSets && ln.dataSets.length > 0) {
|
|
484
|
+
console.log(` Datasets (${ln.dataSets.length}):`);
|
|
485
|
+
ln.dataSets.forEach((ds, idx) => {
|
|
486
|
+
console.log(` ${idx + 1}. ${ds.name}: ${ds.reference}`);
|
|
487
|
+
});
|
|
488
|
+
} else {
|
|
489
|
+
console.log(` Datasets: 0`);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Выводим ВСЕ отчеты
|
|
493
|
+
if (ln.reports && ln.reports.length > 0) {
|
|
494
|
+
console.log(` Reports (${ln.reports.length}):`);
|
|
495
|
+
ln.reports.forEach((report, idx) => {
|
|
496
|
+
const typeDesc = report.type === 'RP' ? 'Unbuffered' : 'Buffered';
|
|
497
|
+
console.log(` ${idx + 1}. ${report.name} (${report.type} - ${typeDesc}): ${report.reference}`);
|
|
498
|
+
});
|
|
499
|
+
} else {
|
|
500
|
+
console.log(` Reports: 0`);
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// Выбираем LLN0 для дальнейшего исследования
|
|
505
|
+
const lln0 = rootNodes.find(ln => ln.name === 'LLN0');
|
|
506
|
+
if (lln0) {
|
|
507
|
+
console.log('\n=== 2. Исследуем LLN0 ===');
|
|
508
|
+
const lln0Details = await client.browseDataModel(lln0.reference);
|
|
509
|
+
|
|
510
|
+
console.log(`\nDataObjects в ${lln0Details.reference}: ${llln0Details.dataObjectsCount}`);
|
|
511
|
+
console.log(`DataSets в ${lln0Details.reference}: ${llln0Details.dataSetsCount}`);
|
|
512
|
+
|
|
513
|
+
// Показываем ВСЕ DataObjects
|
|
514
|
+
console.log('\nВсе DataObjects:');
|
|
515
|
+
lln0Details.dataObjects.forEach((doObj, index) => {
|
|
516
|
+
console.log(`${index + 1}. ${doObj.name} (${doObj.cdc || 'Unknown'}) - ${doObj.reference}`);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// Выбираем первый DataSet для кэширования
|
|
520
|
+
if (lln0Details.dataSets.length > 0) {
|
|
521
|
+
const firstDataSet = lln0Details.dataSets[0];
|
|
522
|
+
console.log(`\n=== 3. Кэшируем DataSet ${firstDataSet.reference} ===`);
|
|
523
|
+
const dsDetails = await client.browseDataModel(firstDataSet.reference);
|
|
524
|
+
|
|
525
|
+
console.log(`DataSet ${dsDetails.reference}:`);
|
|
526
|
+
console.log(` Удаляемый: ${dsDetails.isDeletable}`);
|
|
527
|
+
console.log(` Членов: ${dsDetails.memberCount}`);
|
|
528
|
+
console.log('\n Первые члены:');
|
|
529
|
+
dsDetails.members.slice(0, 5).forEach((member, index) => {
|
|
530
|
+
console.log(` ${index + 1}. ${member.reference}`);
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
// Теперь можем быстро читать этот DataSet
|
|
534
|
+
console.log('\n=== 4. Быстрое чтение DataSet ===');
|
|
535
|
+
const pollResults = await client.pollDataSetValues([firstDataSet.reference]);
|
|
536
|
+
console.log('Poll results:', util.inspect(pollResults, { depth: 2 }));
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Выбираем первый отчет для кэширования
|
|
540
|
+
if (lln0.reports.length > 0) {
|
|
541
|
+
const firstReport = lln0.reports.find(r => r.reference.includes('ReportBlock0101'));
|
|
542
|
+
if (firstReport) {
|
|
543
|
+
console.log(`\n=== 5. Кэшируем отчет ${firstReport.reference} ===`);
|
|
544
|
+
const reportDetails = await client.browseDataModel(firstReport.reference);
|
|
545
|
+
|
|
546
|
+
console.log(`Отчет ${reportDetails.reference}:`);
|
|
547
|
+
console.log(` Тип: ${reportDetails.reportType}`);
|
|
548
|
+
console.log(` DataSet: ${reportDetails.datasetRef}`);
|
|
549
|
+
console.log(` Включен: ${reportDetails.enabled}`);
|
|
550
|
+
console.log(` Report ID: ${reportDetails.reportId}`);
|
|
551
|
+
|
|
552
|
+
// Подписываемся на отчет
|
|
553
|
+
console.log(`\n=== 6. Подписываемся на отчет ===`);
|
|
554
|
+
await client.enableReporting(firstReport.reference, reportDetails.datasetRef);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Пример чтения одиночного значения
|
|
560
|
+
console.log('\n=== 7. Чтение одиночных значений ===');
|
|
561
|
+
const singleValues = await client.readData([
|
|
562
|
+
'WAGO61850ServerDevice/XCBR1.Pos[ST]',
|
|
563
|
+
'WAGO61850ServerDevice/GGIO1.Ind1[ST]'
|
|
564
|
+
]);
|
|
565
|
+
console.log('Single values:', util.inspect(singleValues, { depth: 2 }));
|
|
566
|
+
|
|
567
|
+
} catch (err) {
|
|
568
|
+
console.error('Error in exploreModel:', err.message);
|
|
569
|
+
}
|
|
570
|
+
}*/
|
|
571
|
+
|
|
572
|
+
async function exploreModel() {
|
|
573
|
+
try {
|
|
574
|
+
console.log('\n=== 1. Получение корневых узлов ===');
|
|
575
|
+
const rootNodes = await client.browseDataModel();
|
|
576
|
+
|
|
577
|
+
console.log('\nНайдено Logical Nodes:');
|
|
578
|
+
rootNodes.forEach((ln, index) => {
|
|
579
|
+
console.log(`\n${index + 1}. ${ln.name} (${ln.reference})`);
|
|
580
|
+
|
|
581
|
+
// Выводим ВСЕ датасеты
|
|
582
|
+
if (ln.dataSets && ln.dataSets.length > 0) {
|
|
583
|
+
console.log(` Datasets (${ln.dataSets.length}):`);
|
|
584
|
+
ln.dataSets.forEach((ds, idx) => {
|
|
585
|
+
console.log(` ${idx + 1}. ${ds.name}: ${ds.reference}`);
|
|
586
|
+
});
|
|
587
|
+
} else {
|
|
588
|
+
console.log(` Datasets: 0`);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Выводим ВСЕ отчеты
|
|
592
|
+
if (ln.reports && ln.reports.length > 0) {
|
|
593
|
+
console.log(` Reports (${ln.reports.length}):`);
|
|
594
|
+
ln.reports.forEach((report, idx) => {
|
|
595
|
+
const typeDesc = report.type === 'RP' ? 'Unbuffered' : 'Buffered';
|
|
596
|
+
console.log(` ${idx + 1}. ${report.name} (${report.type} - ${typeDesc}): ${report.reference}`);
|
|
597
|
+
});
|
|
598
|
+
} else {
|
|
599
|
+
console.log(` Reports: 0`);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// Выбираем LLN0 для дальнейшего исследования
|
|
604
|
+
const lln0 = rootNodes.find(ln => ln.name === 'LLN0');
|
|
605
|
+
if (lln0) {
|
|
606
|
+
console.log('\n=== 2. Исследуем LLN0 ===');
|
|
607
|
+
const lln0Details = await client.browseDataModel(lln0.reference);
|
|
608
|
+
|
|
609
|
+
console.log(`\nDataObjects в ${lln0Details.reference}: ${lln0Details.dataObjectsCount}`);
|
|
610
|
+
console.log(`DataSets в ${lln0Details.reference}: ${lln0Details.dataSetsCount}`);
|
|
611
|
+
|
|
612
|
+
// Показываем ВСЕ DataObjects
|
|
613
|
+
console.log('\nВсе DataObjects:');
|
|
614
|
+
lln0Details.dataObjects.forEach((doObj, index) => {
|
|
615
|
+
console.log(`${index + 1}. ${doObj.name} (${doObj.cdc || 'Unknown'}) - ${doObj.reference}`);
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
// Выбираем первый DataSet для кэширования
|
|
619
|
+
if (lln0Details.dataSets.length > 0) {
|
|
620
|
+
const firstDataSet = lln0Details.dataSets[0];
|
|
621
|
+
console.log(`\n=== 3. Кэшируем DataSet ${firstDataSet.reference} ===`);
|
|
622
|
+
const dsDetails = await client.browseDataModel(firstDataSet.reference);
|
|
623
|
+
|
|
624
|
+
console.log(`\nDataSet ${dsDetails.reference}:`);
|
|
625
|
+
console.log(` Удаляемый: ${dsDetails.isDeletable}`);
|
|
626
|
+
console.log(` Членов: ${dsDetails.memberCount}`);
|
|
627
|
+
console.log('\n Все члены:');
|
|
628
|
+
dsDetails.members.forEach((member, index) => {
|
|
629
|
+
console.log(` ${index + 1}. ${member.reference}`);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
// Теперь можем быстро читать этот DataSet
|
|
633
|
+
console.log('\n=== 4. Быстрое чтение DataSet ===');
|
|
634
|
+
const pollResults = await client.pollDataSetValues([firstDataSet.reference]);
|
|
635
|
+
|
|
636
|
+
console.log('\nPoll results:');
|
|
637
|
+
pollResults.forEach((result, idx) => {
|
|
638
|
+
if (result.isValid) {
|
|
639
|
+
console.log(`\nDataSet ${result.datasetRef}: ${result.count} значений`);
|
|
640
|
+
console.log(` Read time: ${result.readTimeMicros} µs`);
|
|
641
|
+
console.log(` Process time: ${result.processTimeMicros} µs`);
|
|
642
|
+
|
|
643
|
+
// Выводим ВСЕ значения
|
|
644
|
+
console.log('\n Значения:');
|
|
645
|
+
Object.entries(result.values).forEach(([ref, value], index) => {
|
|
646
|
+
console.log(` [${index + 1}] ${ref}:`, util.inspect(value, {
|
|
647
|
+
depth: null,
|
|
648
|
+
colors: true,
|
|
649
|
+
maxArrayLength: 10
|
|
650
|
+
}));
|
|
651
|
+
});
|
|
652
|
+
} else {
|
|
653
|
+
console.error(` Error: ${result.errorReason}`);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Выбираем первый отчет для кэширования
|
|
659
|
+
if (lln0.reports.length > 0) {
|
|
660
|
+
const firstReport = lln0.reports.find(r => r.reference.includes('ReportBlock0101'));
|
|
661
|
+
if (firstReport) {
|
|
662
|
+
console.log(`\n=== 5. Кэшируем отчет ${firstReport.reference} ===`);
|
|
663
|
+
const reportDetails = await client.browseDataModel(firstReport.reference);
|
|
664
|
+
|
|
665
|
+
console.log(`\nОтчет ${reportDetails.reference}:`);
|
|
666
|
+
console.log(` Тип: ${reportDetails.reportType}`);
|
|
667
|
+
console.log(` DataSet: ${reportDetails.datasetRef}`);
|
|
668
|
+
console.log(` Включен: ${reportDetails.enabled}`);
|
|
669
|
+
console.log(` Report ID: ${reportDetails.reportId || 'N/A'}`);
|
|
670
|
+
console.log(` Triggers: ${reportDetails.trgOps}`);
|
|
671
|
+
console.log(` Integrity Period: ${reportDetails.intgPd} ms`);
|
|
672
|
+
console.log(` Buffer Time: ${reportDetails.bufTm} ms`);
|
|
673
|
+
console.log(` GI: ${reportDetails.gi}`);
|
|
674
|
+
|
|
675
|
+
// Подписываемся на отчет
|
|
676
|
+
console.log(`\n=== 6. Подписываемся на отчет ===`);
|
|
677
|
+
await client.enableReporting(firstReport.reference, reportDetails.datasetRef);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Пример чтения одиночного значения
|
|
683
|
+
console.log('\n=== 7. Чтение одиночных значений ===');
|
|
684
|
+
const singleValues = await client.readData([
|
|
685
|
+
'WAGO61850ServerDevice/XCBR1.Pos[ST]',
|
|
686
|
+
'WAGO61850ServerDevice/GGIO1.Ind1[ST]'
|
|
687
|
+
]);
|
|
688
|
+
|
|
689
|
+
console.log('\nSingle values:');
|
|
690
|
+
singleValues.forEach((result, index) => {
|
|
691
|
+
if (result.isValid) {
|
|
692
|
+
console.log(`[${index + 1}] ${result.dataRef}:`, util.inspect(result.value, {
|
|
693
|
+
depth: null,
|
|
694
|
+
colors: true
|
|
695
|
+
}));
|
|
696
|
+
} else {
|
|
697
|
+
console.log(`[${index + 1}] ${result.dataRef}: ERROR - ${result.errorReason}`);
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
} catch (err) {
|
|
702
|
+
console.error('Error in exploreModel:', err.message);
|
|
703
|
+
console.error(err.stack);
|
|
704
|
+
}
|
|
186
705
|
}
|
|
187
706
|
|
|
188
707
|
async function main() {
|
|
189
708
|
try {
|
|
190
709
|
console.log('Starting client...');
|
|
191
710
|
await client.connect({
|
|
192
|
-
ip: '192.168.0.
|
|
711
|
+
ip: '192.168.0.106',
|
|
193
712
|
port: 102,
|
|
194
713
|
clientID: 'mms_client1',
|
|
195
714
|
reconnectDelay: 2,
|
|
@@ -201,13 +720,13 @@ async function main() {
|
|
|
201
720
|
console.log('Waiting for data and reports...');
|
|
202
721
|
await sleep(30000);
|
|
203
722
|
|
|
204
|
-
const rcbRef = 'A01LD0/LLN0.BR.repTS1';
|
|
723
|
+
/*const rcbRef = 'A01LD0/LLN0.BR.repTS1';
|
|
205
724
|
console.log(`Disabling reporting for ${rcbRef}`);
|
|
206
|
-
await client.disableReporting(rcbRef)
|
|
725
|
+
await client.disableReporting(rcbRef);*/
|
|
207
726
|
|
|
208
|
-
|
|
727
|
+
const rcbRef2 = 'WAGO61850ServerDevice/LLN0.RP.ReportBlock0101';
|
|
209
728
|
console.log(`Disabling reporting for ${rcbRef2}`);
|
|
210
|
-
await client.disableReporting(rcbRef2)
|
|
729
|
+
await client.disableReporting(rcbRef2);
|
|
211
730
|
|
|
212
731
|
console.log('Client status:', client.getStatus());
|
|
213
732
|
console.log('Closing client...');
|