@amigo9090/ih-libiec61850-node 1.0.42 → 1.0.44
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 +485 -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 }));
|
|
@@ -144,7 +165,7 @@ async function handleConnectionOpened() {
|
|
|
144
165
|
Object.entries(res.values).forEach(([ref, value]) => {
|
|
145
166
|
console.log(` ${ref}:`);
|
|
146
167
|
printValue(value, ' ');
|
|
147
|
-
})
|
|
168
|
+
});
|
|
148
169
|
|
|
149
170
|
|
|
150
171
|
// Функция для рекурсивного вывода с полным отображением всех элементов
|
|
@@ -185,15 +206,7 @@ async function handleConnectionOpened() {
|
|
|
185
206
|
console.log(`${indent}${model}`);
|
|
186
207
|
}
|
|
187
208
|
};
|
|
188
|
-
|
|
189
|
-
console.log('\n=== Полная модель устройства ===');
|
|
190
|
-
printModel(dataModel);
|
|
191
|
-
console.log('=== Конец модели устройства ===\n');
|
|
192
|
-
|
|
193
|
-
// Также можно сохранить модель в файл для анализа
|
|
194
|
-
//const fs = require('fs');
|
|
195
|
-
//fs.writeFileSync('device_model.json', JSON.stringify(dataModel, null, 2));
|
|
196
|
-
//console.log('Модель устройства сохранена в device_model.json');
|
|
209
|
+
|
|
197
210
|
});
|
|
198
211
|
|
|
199
212
|
console.log('Reading data...');
|
|
@@ -227,11 +240,468 @@ async function handleConnectionOpened() {
|
|
|
227
240
|
/*const rcbRef2 = 'A01LD0/LLN0.BR.repTS1';
|
|
228
241
|
const dataSetRef2 = 'A01LD0/LLN0.TS_ASU';
|
|
229
242
|
console.log(`Enabling reporting for ${rcbRef2} with dataset ${dataSetRef2}`);
|
|
230
|
-
await client.enableReporting(rcbRef2, dataSetRef2)
|
|
243
|
+
await client.enableReporting(rcbRef2, dataSetRef2);
|
|
231
244
|
|
|
232
245
|
} catch (err) {
|
|
233
246
|
console.error('Error in handleConnectionOpened:', err.message);
|
|
234
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
|
+
}
|
|
235
705
|
}
|
|
236
706
|
|
|
237
707
|
async function main() {
|
|
@@ -254,7 +724,7 @@ async function main() {
|
|
|
254
724
|
console.log(`Disabling reporting for ${rcbRef}`);
|
|
255
725
|
await client.disableReporting(rcbRef);*/
|
|
256
726
|
|
|
257
|
-
const rcbRef2 = 'WAGO61850ServerDevice/LLN0.RP.
|
|
727
|
+
const rcbRef2 = 'WAGO61850ServerDevice/LLN0.RP.ReportBlock0101';
|
|
258
728
|
console.log(`Disabling reporting for ${rcbRef2}`);
|
|
259
729
|
await client.disableReporting(rcbRef2);
|
|
260
730
|
|