@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/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/test2.js +154 -190
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
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
|
-
|
|
5
|
-
|
|
4
|
+
// Вспомогательные функции
|
|
5
|
+
const now = () => Date.now();
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
function logWithTime(...args) {
|
|
8
|
+
const timestamp = new Date().toISOString();
|
|
9
|
+
console.log(`[${timestamp}]`, ...args);
|
|
10
|
+
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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.
|
|
56
|
-
|
|
27
|
+
if (event === 'data' && data.event === 'multipleDataSets') {
|
|
28
|
+
logWithTime(`>>> POLL RESULT for ${data.datasetRefs}`);
|
|
57
29
|
}
|
|
58
30
|
|
|
59
|
-
if (event === 'conn' && data.event === '
|
|
60
|
-
|
|
61
|
-
|
|
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 === '
|
|
67
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
77
|
+
// Теперь ищем отчёт, который ссылается на этот DataSet
|
|
78
|
+
for (const ln of dataModel) {
|
|
79
|
+
if (!ln.reports || ln.reports.length === 0) continue;
|
|
121
80
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
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
|
-
|
|
208
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
await client.disableReporting(rcbRef);*/
|
|
162
|
+
await sleep(2000);
|
|
163
|
+
}
|
|
213
164
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
219
|
-
console.log('Closing client...');
|
|
173
|
+
logWithTime('Diagnostics completed. Closing client...');
|
|
220
174
|
await client.close();
|
|
221
|
-
|
|
175
|
+
logWithTime('Client closed.');
|
|
222
176
|
|
|
223
177
|
} catch (err) {
|
|
224
|
-
|
|
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
|
-
|
|
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
|
+
}
|