@amigo9090/ih-libiec61850-node 1.0.50 → 1.0.52

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/examples/test2.js CHANGED
@@ -1,229 +1,193 @@
1
1
  const { MmsClient } = require('../build/Release/addon_iec61850');
2
2
  const util = require('util');
3
3
 
4
- const client = new MmsClient((event, data) => {
5
- console.log(`Event: ${event}, Data: ${util.inspect(data, { depth: null })}`);
4
+ // Вспомогательные функции
5
+ const now = () => Date.now();
6
6
 
7
- if (event === 'conn' && data.event === 'opened') {
8
- console.log('Connection opened, browsing data model...');
9
- handleConnectionOpened();
10
- }
7
+ function logWithTime(...args) {
8
+ const timestamp = new Date().toISOString();
9
+ console.log(`[${timestamp}]`, ...args);
10
+ }
11
11
 
12
- if (event === 'data' && data.type === 'data') {
13
- if (data.event === 'logicalDevices') {
14
- console.log(`Logical Devices received: ${util.inspect(data.logicalDevices, { depth: null })}`);
15
- } else if (data.event === 'dataSetDirectory') {
16
- console.log(`DataSet Directory for ${data.logicalNodeRef}: ${util.inspect(data.dataSets, { depth: null })}`);
17
- } else if (data.event === 'dataModel') {
18
- console.log(`Data Model received: ${util.inspect(data.dataModel, { depth: null })}`);
19
- } else if (data.event === 'dataSet') {
20
- console.log(`DataSet received for ${data.datasetRef}: ${util.inspect(data.value, { depth: null })}`);
21
- } else if (data.event === 'multipleDataSets') {
22
- console.log(`Multiple DataSets received for ${util.inspect(data.datasetRefs, { depth: null })}:`);
23
- data.values.forEach((value, index) => {
24
- if (value.isValid !== false) {
25
- console.log(` DataSet[${index}]: ${data.datasetRefs[index]}, Value: ${util.inspect(value, { depth: null })}`);
26
- } else {
27
- console.log(` DataSet[${index}]: ${data.datasetRefs[index]}, Error: ${value.errorReason}`);
28
- }
29
- });
30
- } else if (data.event === 'report') {
31
- console.log(`Report received for ${data.rcbRef} (rptId: ${data.rptId}):`);
32
- if (data.timestamp) {
33
- console.log(` Timestamp: ${data.timestamp}`);
34
- }
35
- Object.entries(data.values).forEach(([ref, value], index) => {
36
- const reason = data.reasons[ref];
37
- if (reason && reason !== 0) {
38
- console.log(` ${ref}: ${util.inspect(value, { depth: null })}, Reason: ${reason}`);
39
- }
40
- });
41
- } else if (data.event === 'batchData') {
42
- console.log(`Batch Data received for ${util.inspect(data.dataRefs, { depth: null })}:`);
43
- data.values.forEach((result, index) => {
44
- if (result.isValid) {
45
- console.log(` dataRef[${index}]: ${data.dataRefs[index]}, Value: ${util.inspect(result.value, { depth: null })}`);
46
- } else {
47
- console.log(` dataRef[${index}]: ${data.dataRefs[index]}, Error: ${result.errorReason}`);
48
- }
12
+ const client = new MmsClient((event, data) => {
13
+ const eventTime = now();
14
+ logWithTime(`Event: ${event}, Data type: ${data.type || 'N/A'}, event: ${data.event || 'N/A'}`);
15
+
16
+ if (event === 'data' && data.event === 'report') {
17
+ logWithTime(`>>> REPORT RECEIVED for ${data.rcbRef}, timestamp: ${data.timestamp || 'none'}`);
18
+ if (data.values) {
19
+ const refs = Object.keys(data.values);
20
+ logWithTime(` Report contains ${refs.length} elements`);
21
+ refs.slice(0, 3).forEach(ref => {
22
+ logWithTime(` ${ref}: ${util.inspect(data.values[ref], { depth: 1 })}`);
49
23
  });
50
- } else {
51
- console.log(`Data received for ${data.dataRef || 'undefined'}: ${util.inspect(data.value, { depth: null })}`);
52
24
  }
53
25
  }
54
26
 
55
- if (event === 'data' && data.type === 'error') {
56
- console.error(`Error received: ${data.reason}`);
27
+ if (event === 'data' && data.event === 'multipleDataSets') {
28
+ logWithTime(`>>> POLL RESULT for ${data.datasetRefs}`);
57
29
  }
58
30
 
59
- if (event === 'conn' && data.event === 'reconnecting') {
60
- console.error(`Reconnection failed: ${data.reason}`);
61
- if (data.reason.includes('attempt 3')) {
62
- throw new Error('Max reconnection attempts reached');
63
- }
31
+ if (event === 'conn' && data.event === 'opened') {
32
+ logWithTime('Connection opened, starting diagnostics...');
33
+ runDiagnostics().catch(err => logWithTime('Diagnostics error:', err));
64
34
  }
65
35
 
66
- if (event === 'data' && data.type === 'control') {
67
- if (data.event === 'reportingEnabled') {
68
- console.log(`Reporting enabled for ${data.rcbRef}`);
69
- } else if (data.event === 'reportingDisabled') {
70
- console.log(`Reporting disabled for ${data.rcbRef}`);
71
- } else if (data.event === 'dataSetCreated') {
72
- console.log(`DataSet created: ${data.datasetRef}`);
73
- } else if (data.event === 'dataSetDeleted') {
74
- console.log(`DataSet deleted: ${data.datasetRef}`);
75
- } else if (data.event === 'stateChanged') {
76
- console.log(`Connection state changed: ${data.state}, isConnected: ${data.isConnected}`);
77
- }
36
+ if (event === 'data' && data.type === 'error') {
37
+ logWithTime(`!!! ERROR: ${data.reason}`);
78
38
  }
79
39
 
80
40
  if (event === 'conn' && data.event === 'stateChanged') {
81
- console.log(`Connection state changed: ${data.state}, isConnected: ${data.isConnected}`);
41
+ logWithTime(`Connection state changed: ${data.state}, isConnected: ${data.isConnected}`);
82
42
  }
83
43
  });
84
44
 
85
45
  const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
86
46
 
87
- async function handleConnectionOpened() {
47
+ async function runDiagnostics() {
88
48
  try {
49
+ // 1. Получаем модель данных
50
+ logWithTime('Step 1: Browsing data model...');
51
+ const browseStart = now();
89
52
  const dataModel = await client.browseDataModel();
90
- console.log('Data Model:', util.inspect(dataModel, { depth: null }));
91
-
92
- const dataSets = [];
93
- dataModel.forEach(ld => {
94
- ld.logicalNodes.forEach(ln => {
95
- ln.dataSets.forEach(ds => {
96
- console.log(`Found dataset: ${ds.reference}`);
97
- dataSets.push(ds);
98
- });
99
- });
100
- });
101
-
102
- //для теста
103
- const dataModel2 = await client.TestDebug();
104
-
105
- console.log('\nЧтение значений DataSet...');
106
- const datasetRefs = dataSets.map(ds => ds.reference);
107
- console.log('Calling readDataSetValues...');
108
- const readResults = await client.readDataSetValues(datasetRefs);
109
- console.log('readDataSetValues returned');
110
-
111
-
112
- readResults.forEach((res, idx) => {
113
- const ds = dataSets[idx];
114
- console.log(`\nDataSet: ${res.datasetRef}`);
115
- if (!res.isValid) {
116
- console.error(' Ошибка:', res.errorReason);
117
- return;
53
+ logWithTime(`Browse completed in ${now() - browseStart} ms`);
54
+
55
+ // Диагностический вывод структуры
56
+ logWithTime('Data model structure (first 2 nodes):',
57
+ util.inspect(dataModel.slice(0, 2), { depth: 4, colors: true }));
58
+
59
+ // 2. Ищем подходящие DataSet и отчеты
60
+ let targetDataset = null;
61
+ let targetReport = null;
62
+
63
+ // Сначала ищем DataSet (берём первый)
64
+ for (const ln of dataModel) {
65
+ if (ln.dataSets && ln.dataSets.length > 0) {
66
+ targetDataset = ln.dataSets[0];
67
+ logWithTime(`Found dataset: ${targetDataset.reference}`);
68
+ break;
118
69
  }
70
+ }
71
+
72
+ if (!targetDataset) {
73
+ logWithTime('No datasets found, cannot proceed.');
74
+ return;
75
+ }
119
76
 
120
- console.log(` Значений: ${res.count}, Удаляемые: ${res.isDeletable}`);
77
+ // Теперь ищем отчёт, который ссылается на этот DataSet
78
+ for (const ln of dataModel) {
79
+ if (!ln.reports || ln.reports.length === 0) continue;
121
80
 
122
- // Функция для рекурсивного вывода вложенных структур
123
- const printValue = (value, indent = ' ') => {
124
- if (value && typeof value === 'object' && !Array.isArray(value)) {
125
- Object.entries(value).forEach(([key, val]) => {
126
- if (val && typeof val === 'object' && !Array.isArray(val)) {
127
- console.log(`${indent}${key}: {`);
128
- printValue(val, indent + ' ');
129
- console.log(`${indent}}`);
130
- } else if (Array.isArray(val)) {
131
- console.log(`${indent}${key}: [`);
132
- val.forEach((item, i) => {
133
- console.log(`${indent} [${i}]:`);
134
- printValue(item, indent + ' ');
135
- });
136
- console.log(`${indent}]`);
137
- } else {
138
- console.log(`${indent}${key}: ${util.inspect(val, { colors: true })}`);
139
- }
140
- });
141
- } else {
142
- console.log(`${indent}${util.inspect(value, { colors: true })}`);
81
+ for (const report of ln.reports) {
82
+ logWithTime(`Getting details for report ${report.reference}...`);
83
+ try {
84
+ const details = await client.browseDataModel(report.reference);
85
+ if (details.datasetRef === targetDataset.reference) {
86
+ targetReport = details;
87
+ logWithTime(`Found matching report: ${report.reference} (dataset: ${details.datasetRef})`);
88
+ break;
89
+ }
90
+ } catch (err) {
91
+ logWithTime(`Error getting details for report ${report.reference}: ${err.message}`);
143
92
  }
144
- };
145
-
146
- Object.entries(res.values).forEach(([ref, value]) => {
147
- console.log(` ${ref}:`);
148
- printValue(value, ' ');
149
- });
150
- });
151
-
152
- console.log('Reading data...');
153
- const dataRefs = [
154
- 'WAGO61850ServerDevice/XCBR1.Pos[ST]',
155
- 'WAGO61850ServerDevice/GGIO1.Ind1.stVal',
156
- 'WAGO61850ServerDevice/CALH12.GrAlm.stVal'
157
- ];
158
- const readRefResult = await client.readData(dataRefs);
159
- console.log("readRefResult " + util.inspect(readRefResult, { depth: null }));
160
-
161
- /*const rcbRef = 'WAGO61850ServerDevice/LLN0.RP.ReportBlock0101';
162
- const dataSetRef = 'WAGO61850ServerDevice/LLN0.DataSet01';
163
- console.log(`Enabling reporting for ${rcbRef} with dataset ${dataSetRef}`);
164
- await client.enableReporting(rcbRef, dataSetRef);*/
165
-
166
- const rcbRef2 = 'WAGO61850ServerDevice/LLN0.RP.ReportBlock0201';
167
- const dataSetRef2 = 'WAGO61850ServerDevice/LLN0.DataSet02';
168
- console.log(`Enabling reporting for ${rcbRef2} with dataset ${dataSetRef2}`);
169
- await client.enableReporting(rcbRef2, dataSetRef2);
170
-
171
- /*console.log('Reading data...');
172
- const dataRefs = [
173
- 'A01LD0/Q1_XCBR1.Pos[ST]',
174
- 'A01LD0/In_GGIO1.Ind1',
175
- 'A01LD0/CALH1.GrAlm.stVal'
176
- ];
177
- const readRefResult = await client.readData(dataRefs);
178
- console.log("readRefResult " + util.inspect(readRefResult, { depth: null }));
179
-
180
- const rcbRef = 'A01LD0/LLN0.RP.repTI1';
181
- const dataSetRef = 'A01LD0/LLN0.TI_ASU';
182
- console.log(`Enabling reporting for ${rcbRef} with dataset ${dataSetRef}`);
183
- await client.enableReporting(rcbRef, dataSetRef);
184
-
185
- const rcbRef2 = 'A01LD0/LLN0.BR.repTS1';
186
- const dataSetRef2 = 'A01LD0/LLN0.TS_ASU';
187
- console.log(`Enabling reporting for ${rcbRef2} with dataset ${dataSetRef2}`);
188
- await client.enableReporting(rcbRef2, dataSetRef2);*/
189
-
190
- } catch (err) {
191
- console.error('Error in handleConnectionOpened:', err.message);
192
- }
193
- }
93
+ }
94
+ if (targetReport) break;
95
+ }
194
96
 
195
- async function main() {
196
- try {
197
- console.log('Starting client...');
198
- await client.connect({
199
- ip: '192.168.0.142',
200
- port: 102,
201
- clientID: 'mms_client1',
202
- reconnectDelay: 2
203
- });
97
+ // Если не нашли подходящий, возьмём любой отчёт с datasetRef
98
+ if (!targetReport) {
99
+ for (const ln of dataModel) {
100
+ if (!ln.reports || ln.reports.length === 0) continue;
101
+ for (const report of ln.reports) {
102
+ try {
103
+ const details = await client.browseDataModel(report.reference);
104
+ if (details.datasetRef) {
105
+ targetReport = details;
106
+ logWithTime(`Found any report with dataset: ${report.reference} (dataset: ${details.datasetRef})`);
107
+ break;
108
+ }
109
+ } catch (err) {
110
+ logWithTime(`Error getting details for report ${report.reference}: ${err.message}`);
111
+ }
112
+ }
113
+ if (targetReport) break;
114
+ }
115
+ }
204
116
 
205
- await sleep(5000);
117
+ // 3. Первичное чтение и кэширование DataSet (readDataSetModel)
118
+ logWithTime(`Step 2: Caching dataset ${targetDataset.reference}...`);
119
+ const cacheStart = now();
120
+ await client.readDataSetModel([targetDataset.reference]);
121
+ logWithTime(`Caching completed in ${now() - cacheStart} ms`);
122
+
123
+ // 4. Включаем отчёт, если найден
124
+ if (targetReport) {
125
+ logWithTime(`Step 3: Enabling report ${targetReport.reference} with dataset ${targetReport.datasetRef}...`);
126
+ const enableStart = now();
127
+ await client.enableReporting(targetReport.reference, targetReport.datasetRef);
128
+ logWithTime(`Reporting enabled in ${now() - enableStart} ms`);
129
+ } else {
130
+ logWithTime('No suitable report found, skipping report enabling.');
131
+ }
206
132
 
207
- console.log('Waiting for data and reports...');
208
- await sleep(15000);
133
+ // 5. Запускаем цикл поллинга (10 итераций с интервалом 2 сек)
134
+ logWithTime('Step 4: Starting polling loop (10 iterations, 2 sec interval)...');
135
+ for (let i = 0; i < 10; i++) {
136
+ logWithTime(`--- Poll iteration ${i+1} ---`);
137
+
138
+ const pollStart = now();
139
+ try {
140
+ const pollResults = await client.pollDataSetValues([targetDataset.reference]);
141
+ const pollEnd = now();
142
+ logWithTime(`Poll completed in ${pollEnd - pollStart} ms`);
143
+
144
+ if (pollResults && pollResults[0]) {
145
+ const res = pollResults[0];
146
+ if (res.isValid) {
147
+ logWithTime(` Read time (µs): ${res.readTimeMicros}, Process time (µs): ${res.processTimeMicros}`);
148
+ logWithTime(` Values count: ${res.count}`);
149
+ // Выводим первые 3 значения для проверки
150
+ const entries = Object.entries(res.values).slice(0, 3);
151
+ entries.forEach(([ref, val]) => {
152
+ logWithTime(` ${ref}: ${util.inspect(val, { depth: 2 })}`);
153
+ });
154
+ } else {
155
+ logWithTime(` Poll error: ${res.errorReason}`);
156
+ }
157
+ }
158
+ } catch (err) {
159
+ logWithTime(`Poll exception: ${err.message}`);
160
+ }
209
161
 
210
- /*const rcbRef = 'WAGO61850ServerDevice/LLN0.RP.ReportBlock0101';
211
- console.log(`Disabling reporting for ${rcbRef}`);
212
- await client.disableReporting(rcbRef);*/
162
+ await sleep(2000);
163
+ }
213
164
 
214
- const rcbRef2 = 'WAGO61850ServerDevice/LLN0.RP.ReportBlock0201';
215
- console.log(`Disabling reporting for ${rcbRef2}`);
216
- await client.disableReporting(rcbRef2);
165
+ // 6. Отключаем отчёт
166
+ if (targetReport) {
167
+ logWithTime('Step 5: Disabling report...');
168
+ const disableStart = now();
169
+ await client.disableReporting(targetReport.reference);
170
+ logWithTime(`Report disabled in ${now() - disableStart} ms`);
171
+ }
217
172
 
218
- console.log('Client status:', client.getStatus());
219
- console.log('Closing client...');
173
+ logWithTime('Diagnostics completed. Closing client...');
220
174
  await client.close();
221
- console.log('Client closed.');
175
+ logWithTime('Client closed.');
222
176
 
223
177
  } catch (err) {
224
- console.error('Main error:', err.message);
225
- await client.close().catch(e => console.error('Close error:', e.message));
178
+ logWithTime(`Fatal error in diagnostics: ${err.stack}`);
226
179
  }
227
180
  }
228
181
 
229
- main().catch(err => console.error('Fatal error:', err.message));
182
+ // Подключаемся к устройству (без .catch, так как connect не возвращает Promise)
183
+ logWithTime('Starting client...');
184
+ try {
185
+ client.connect({
186
+ ip: '192.168.0.106',
187
+ port: 102,
188
+ clientID: 'mms_client1',
189
+ reconnectDelay: 2
190
+ });
191
+ } catch (err) {
192
+ logWithTime('Connection error:', err);
193
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amigo9090/ih-libiec61850-node",
3
- "version": "1.0.50",
3
+ "version": "1.0.52",
4
4
  "description": "Node.js addon for IEC 61850 client (MMS, GOOSE) using libiec61850",
5
5
  "main": "index.js",
6
6
  "keywords": [