ads-client 2.0.0-beta.4 → 2.0.0-beta.5
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/CHANGELOG.md +22 -0
- package/README.md +1043 -57
- package/dist/ads-client.d.ts +71 -8
- package/dist/ads-client.js +257 -75
- package/dist/ads-client.js.map +1 -1
- package/dist/ads-commons.d.ts +17 -2
- package/dist/ads-commons.js +46 -8
- package/dist/ads-commons.js.map +1 -1
- package/dist/types/ads-client-types.d.ts +22 -22
- package/dist/types/ads-client-types.js.map +1 -1
- package/dist/types/ads-protocol-types.d.ts +37 -0
- package/package.json +1 -1
package/dist/ads-client.js
CHANGED
|
@@ -148,6 +148,7 @@ class Client extends events_1.default {
|
|
|
148
148
|
plcSymbols: {},
|
|
149
149
|
allPlcDataTypesCached: false,
|
|
150
150
|
plcDataTypes: {},
|
|
151
|
+
adsSymbolsUseUtf8: false
|
|
151
152
|
};
|
|
152
153
|
/**
|
|
153
154
|
* Buffer for received data.
|
|
@@ -233,7 +234,8 @@ class Client extends events_1.default {
|
|
|
233
234
|
allowHalfOpen: false,
|
|
234
235
|
rawClient: false,
|
|
235
236
|
disableCaching: false,
|
|
236
|
-
deleteUnknownSubscriptions: true
|
|
237
|
+
deleteUnknownSubscriptions: true,
|
|
238
|
+
forceUtf8ForAdsSymbols: false
|
|
237
239
|
};
|
|
238
240
|
/**
|
|
239
241
|
* Active connection information.
|
|
@@ -419,6 +421,8 @@ class Client extends events_1.default {
|
|
|
419
421
|
//When socket errors from now on, we will close the connection
|
|
420
422
|
this.socketErrorHandler = this.onSocketError.bind(this);
|
|
421
423
|
socket.on("error", this.socketErrorHandler);
|
|
424
|
+
//Force ADS UTF-8 (note: this is also set later at readPlcUploadInfo() if target is a PLC)
|
|
425
|
+
this.metaData.adsSymbolsUseUtf8 = this.settings.forceUtf8ForAdsSymbols;
|
|
422
426
|
//If rawClient setting is true, we are done here (just a connection is enough)
|
|
423
427
|
if (this.settings.rawClient !== true) {
|
|
424
428
|
try {
|
|
@@ -748,11 +752,9 @@ class Client extends events_1.default {
|
|
|
748
752
|
this.socketWrite(buffer);
|
|
749
753
|
}
|
|
750
754
|
catch (err) {
|
|
751
|
-
reject(err);
|
|
752
|
-
}
|
|
753
|
-
finally {
|
|
754
755
|
this.amsTcpCallback = undefined;
|
|
755
756
|
clearTimeout(this.portRegisterTimeoutTimer);
|
|
757
|
+
reject(err);
|
|
756
758
|
}
|
|
757
759
|
});
|
|
758
760
|
}
|
|
@@ -878,29 +880,38 @@ class Client extends events_1.default {
|
|
|
878
880
|
* @param subscription The subscription object (unused here)
|
|
879
881
|
*/
|
|
880
882
|
onPlcRuntimeStateChanged(data, subscription) {
|
|
881
|
-
const
|
|
883
|
+
const state = {};
|
|
882
884
|
let pos = 0;
|
|
883
885
|
//0..1 ADS state
|
|
884
|
-
|
|
885
|
-
|
|
886
|
+
state.adsState = data.value.readUInt16LE(pos);
|
|
887
|
+
state.adsStateStr = ADS.ADS_STATE.toString(state.adsState);
|
|
886
888
|
pos += 2;
|
|
887
889
|
//2..3 Device state
|
|
888
|
-
|
|
890
|
+
state.deviceState = data.value.readUInt16LE(pos);
|
|
889
891
|
pos += 2;
|
|
890
|
-
|
|
892
|
+
this.handlePlcRuntimeStateChange(state);
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Checks if PLC runtime state has changed, and if so, emits an event.
|
|
896
|
+
*
|
|
897
|
+
* Call from `onPlcRuntimeStateChanged()` and `readPlcRuntimeState()`.
|
|
898
|
+
*
|
|
899
|
+
* @param state Active PLC runtime state
|
|
900
|
+
*/
|
|
901
|
+
handlePlcRuntimeStateChange(state) {
|
|
891
902
|
let stateChanged = false;
|
|
892
|
-
if (this.metaData.plcRuntimeState === undefined || this.metaData.plcRuntimeState.adsState !==
|
|
893
|
-
this.debug(`
|
|
903
|
+
if (this.metaData.plcRuntimeState === undefined || this.metaData.plcRuntimeState.adsState !== state.adsState) {
|
|
904
|
+
this.debug(`handlePlcRuntimeStateChange(): PLC runtime state (adsState) changed from ${this.metaData.plcRuntimeState === undefined ? 'UNKNOWN' : this.metaData.plcRuntimeState.adsStateStr} to ${state.adsStateStr}`);
|
|
894
905
|
stateChanged = true;
|
|
895
906
|
}
|
|
896
|
-
if (this.metaData.plcRuntimeState === undefined || this.metaData.plcRuntimeState.deviceState !==
|
|
897
|
-
this.debug(`
|
|
907
|
+
if (this.metaData.plcRuntimeState === undefined || this.metaData.plcRuntimeState.deviceState !== state.deviceState) {
|
|
908
|
+
this.debug(`handlePlcRuntimeStateChange(): PLC runtime state (deviceState) changed from ${this.metaData.plcRuntimeState === undefined ? 'UNKNOWN' : this.metaData.plcRuntimeState.deviceState} to ${state.deviceState}`);
|
|
898
909
|
stateChanged = true;
|
|
899
910
|
}
|
|
900
911
|
let oldState = this.metaData.plcRuntimeState !== undefined
|
|
901
912
|
? { ...this.metaData.plcRuntimeState }
|
|
902
913
|
: undefined;
|
|
903
|
-
this.metaData.plcRuntimeState =
|
|
914
|
+
this.metaData.plcRuntimeState = state;
|
|
904
915
|
if (stateChanged) {
|
|
905
916
|
this.emit('plcRuntimeStateChange', this.metaData.plcRuntimeState, oldState);
|
|
906
917
|
}
|
|
@@ -1322,8 +1333,7 @@ class Client extends events_1.default {
|
|
|
1322
1333
|
ads.payload.versionBuild = data.readUInt16LE(pos);
|
|
1323
1334
|
pos += 2;
|
|
1324
1335
|
//8..24 Device name
|
|
1325
|
-
ads.payload.deviceName = ADS.decodePlcStringBuffer(data.subarray(pos, pos + 16));
|
|
1326
|
-
;
|
|
1336
|
+
ads.payload.deviceName = ADS.decodePlcStringBuffer(data.subarray(pos, pos + 16), this.metaData.adsSymbolsUseUtf8);
|
|
1327
1337
|
}
|
|
1328
1338
|
break;
|
|
1329
1339
|
case ADS.ADS_COMMAND.ReadState:
|
|
@@ -1877,13 +1887,13 @@ class Client extends events_1.default {
|
|
|
1877
1887
|
const commentLength = data.readUInt16LE(pos);
|
|
1878
1888
|
pos += 2;
|
|
1879
1889
|
//30.... Symbol name
|
|
1880
|
-
symbol.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
1890
|
+
symbol.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1881
1891
|
pos += nameLength + 1;
|
|
1882
1892
|
//.. Symbol type
|
|
1883
|
-
symbol.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1));
|
|
1893
|
+
symbol.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1884
1894
|
pos += typeLength + 1;
|
|
1885
1895
|
//.. Symbol comment
|
|
1886
|
-
symbol.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
1896
|
+
symbol.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1887
1897
|
pos += commentLength + 1;
|
|
1888
1898
|
//Array information
|
|
1889
1899
|
symbol.arrayInfo = [];
|
|
@@ -1915,10 +1925,10 @@ class Client extends events_1.default {
|
|
|
1915
1925
|
const valueLength = data.readUInt8(pos);
|
|
1916
1926
|
pos += 1;
|
|
1917
1927
|
//Name
|
|
1918
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
1928
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1919
1929
|
pos += nameLength + 1;
|
|
1920
1930
|
//Value
|
|
1921
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
1931
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1922
1932
|
pos += valueLength + 1;
|
|
1923
1933
|
symbol.attributes.push(attr);
|
|
1924
1934
|
}
|
|
@@ -1986,13 +1996,13 @@ class Client extends events_1.default {
|
|
|
1986
1996
|
const subItemCount = data.readUInt16LE(pos);
|
|
1987
1997
|
pos += 2;
|
|
1988
1998
|
//38.. Data type name
|
|
1989
|
-
dataType.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
1999
|
+
dataType.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1990
2000
|
pos += nameLength + 1;
|
|
1991
2001
|
//.. Data type type
|
|
1992
|
-
dataType.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1));
|
|
2002
|
+
dataType.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1993
2003
|
pos += typeLength + 1;
|
|
1994
2004
|
//.. Data type comment
|
|
1995
|
-
dataType.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
2005
|
+
dataType.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1996
2006
|
pos += commentLength + 1;
|
|
1997
2007
|
//Array information
|
|
1998
2008
|
dataType.arrayInfos = [];
|
|
@@ -2076,13 +2086,13 @@ class Client extends events_1.default {
|
|
|
2076
2086
|
const parameterCount = data.readUInt16LE(pos);
|
|
2077
2087
|
pos += 2;
|
|
2078
2088
|
//56.. Name
|
|
2079
|
-
method.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2089
|
+
method.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2080
2090
|
pos += nameLength + 1;
|
|
2081
2091
|
//.. Return data type
|
|
2082
|
-
method.retunDataType = ADS.decodePlcStringBuffer(data.subarray(pos, pos + returnTypeLength + 1));
|
|
2092
|
+
method.retunDataType = ADS.decodePlcStringBuffer(data.subarray(pos, pos + returnTypeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2083
2093
|
pos += returnTypeLength + 1;
|
|
2084
2094
|
//.. Comment
|
|
2085
|
-
method.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
2095
|
+
method.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2086
2096
|
pos += commentLength + 1;
|
|
2087
2097
|
//Parameters
|
|
2088
2098
|
method.parameters = [];
|
|
@@ -2125,13 +2135,13 @@ class Client extends events_1.default {
|
|
|
2125
2135
|
const commentLength = data.readUInt16LE(pos);
|
|
2126
2136
|
pos += 2;
|
|
2127
2137
|
//38.. Data type name
|
|
2128
|
-
param.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2138
|
+
param.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2129
2139
|
pos += nameLength + 1;
|
|
2130
2140
|
//.. Data type type
|
|
2131
|
-
param.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1));
|
|
2141
|
+
param.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2132
2142
|
pos += typeLength + 1;
|
|
2133
2143
|
//.. Data type comment
|
|
2134
|
-
param.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
2144
|
+
param.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2135
2145
|
pos += commentLength + 1;
|
|
2136
2146
|
//Attributes
|
|
2137
2147
|
param.attributes = [];
|
|
@@ -2148,18 +2158,19 @@ class Client extends events_1.default {
|
|
|
2148
2158
|
const valueLength = data.readUInt8(pos);
|
|
2149
2159
|
pos += 1;
|
|
2150
2160
|
//Name
|
|
2151
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2161
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2152
2162
|
pos += nameLength + 1;
|
|
2153
2163
|
//Value
|
|
2154
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
2164
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2155
2165
|
pos += valueLength + 1;
|
|
2156
|
-
|
|
2166
|
+
param.attributes.push(attr);
|
|
2157
2167
|
}
|
|
2158
2168
|
}
|
|
2159
2169
|
if (pos - beginPosition > entryLength) {
|
|
2160
2170
|
//There is some additional data left
|
|
2161
2171
|
param.reserved2 = data.subarray(pos);
|
|
2162
2172
|
}
|
|
2173
|
+
pos = beginPosition + entryLength;
|
|
2163
2174
|
method.parameters.push(param);
|
|
2164
2175
|
}
|
|
2165
2176
|
//Attributes
|
|
@@ -2177,10 +2188,10 @@ class Client extends events_1.default {
|
|
|
2177
2188
|
const valueLength = data.readUInt8(pos);
|
|
2178
2189
|
pos += 1;
|
|
2179
2190
|
//Name
|
|
2180
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2191
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2181
2192
|
pos += nameLength + 1;
|
|
2182
2193
|
//Value
|
|
2183
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
2194
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2184
2195
|
pos += valueLength + 1;
|
|
2185
2196
|
method.attributes.push(attr);
|
|
2186
2197
|
}
|
|
@@ -2205,18 +2216,19 @@ class Client extends events_1.default {
|
|
|
2205
2216
|
const valueLength = data.readUInt8(pos);
|
|
2206
2217
|
pos += 1;
|
|
2207
2218
|
//Name
|
|
2208
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2219
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2209
2220
|
pos += nameLength + 1;
|
|
2210
2221
|
//Value
|
|
2211
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
2222
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2212
2223
|
pos += valueLength + 1;
|
|
2213
2224
|
dataType.attributes.push(attr);
|
|
2214
2225
|
}
|
|
2215
2226
|
}
|
|
2216
2227
|
//If flag EnumInfos set
|
|
2217
2228
|
dataType.enumInfos = [];
|
|
2229
|
+
let enumInfoCount = 0;
|
|
2218
2230
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.EnumInfos) === ADS.ADS_DATA_TYPE_FLAGS.EnumInfos) {
|
|
2219
|
-
|
|
2231
|
+
enumInfoCount = data.readUInt16LE(pos);
|
|
2220
2232
|
pos += 2;
|
|
2221
2233
|
for (let i = 0; i < enumInfoCount; i++) {
|
|
2222
2234
|
let enumInfo = {};
|
|
@@ -2224,7 +2236,7 @@ class Client extends events_1.default {
|
|
|
2224
2236
|
const nameLength = data.readUInt8(pos);
|
|
2225
2237
|
pos += 1;
|
|
2226
2238
|
//Enumeration name
|
|
2227
|
-
enumInfo.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2239
|
+
enumInfo.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2228
2240
|
pos += nameLength + 1;
|
|
2229
2241
|
//Enumeration value
|
|
2230
2242
|
enumInfo.value = data.subarray(pos, pos + dataType.size);
|
|
@@ -2251,9 +2263,43 @@ class Client extends events_1.default {
|
|
|
2251
2263
|
}
|
|
2252
2264
|
//If flag ExtendedEnumInfos set
|
|
2253
2265
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.ExtendedEnumInfos) === ADS.ADS_DATA_TYPE_FLAGS.ExtendedEnumInfos) {
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2266
|
+
for (let i = 0; i < enumInfoCount; i++) {
|
|
2267
|
+
let beginPosition = pos;
|
|
2268
|
+
//Total entry length (bytes)
|
|
2269
|
+
const entryLength = data.readUInt16LE(pos);
|
|
2270
|
+
pos += 2;
|
|
2271
|
+
//Comment length
|
|
2272
|
+
const commentLength = data.readUInt8(pos);
|
|
2273
|
+
pos += 1;
|
|
2274
|
+
//Attribute count
|
|
2275
|
+
const attributeCount = data.readUInt8(pos);
|
|
2276
|
+
pos += 1;
|
|
2277
|
+
//Enumeration comment
|
|
2278
|
+
dataType.enumInfos[i].comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2279
|
+
pos += commentLength + 1;
|
|
2280
|
+
//Enumeration attributes
|
|
2281
|
+
dataType.enumInfos[i].attributes = [];
|
|
2282
|
+
//console.log(enumInfoCount, num1, commentLength, attributeCount, test, attributes);
|
|
2283
|
+
//Attributes
|
|
2284
|
+
for (let i = 0; i < attributeCount; i++) {
|
|
2285
|
+
const attr = {};
|
|
2286
|
+
//Name length
|
|
2287
|
+
const nameLength = data.readUInt8(pos);
|
|
2288
|
+
pos += 1;
|
|
2289
|
+
//Value length
|
|
2290
|
+
const valueLength = data.readUInt8(pos);
|
|
2291
|
+
pos += 1;
|
|
2292
|
+
//Name
|
|
2293
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2294
|
+
pos += nameLength + 1;
|
|
2295
|
+
//Value
|
|
2296
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2297
|
+
pos += valueLength + 1;
|
|
2298
|
+
dataType.enumInfos[i].attributes.push(attr);
|
|
2299
|
+
}
|
|
2300
|
+
//If there are some reserved bytes -> skip
|
|
2301
|
+
pos = beginPosition + entryLength;
|
|
2302
|
+
}
|
|
2257
2303
|
}
|
|
2258
2304
|
//If flag SoftwareProtectionLevels set
|
|
2259
2305
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.SoftwareProtectionLevels) === ADS.ADS_DATA_TYPE_FLAGS.SoftwareProtectionLevels) {
|
|
@@ -2510,7 +2556,7 @@ class Client extends events_1.default {
|
|
|
2510
2556
|
*/
|
|
2511
2557
|
convertBufferToPrimitiveType(buffer, dataType) {
|
|
2512
2558
|
if (dataType.adsDataType === ADS.ADS_DATA_TYPES.ADST_STRING) {
|
|
2513
|
-
return ADS.decodePlcStringBuffer(buffer);
|
|
2559
|
+
return ADS.decodePlcStringBuffer(buffer); //TODO: If symbol has {attribute 'TcEncoding':='UTF-8'}
|
|
2514
2560
|
}
|
|
2515
2561
|
else if (dataType.adsDataType === ADS.ADS_DATA_TYPES.ADST_WSTRING) {
|
|
2516
2562
|
return ADS.decodePlcWstringBuffer(buffer);
|
|
@@ -3015,6 +3061,8 @@ class Client extends events_1.default {
|
|
|
3015
3061
|
* }
|
|
3016
3062
|
* ```
|
|
3017
3063
|
*
|
|
3064
|
+
* @param command The ADS command to send
|
|
3065
|
+
*
|
|
3018
3066
|
* @template T In Typescript, the type of the ADS response. If omitted, generic {@link AdsResponse} type is used.
|
|
3019
3067
|
*
|
|
3020
3068
|
* @throws Throws an error if sending the command fails or if target responds with an error
|
|
@@ -3100,6 +3148,82 @@ class Client extends events_1.default {
|
|
|
3100
3148
|
}
|
|
3101
3149
|
});
|
|
3102
3150
|
}
|
|
3151
|
+
/**
|
|
3152
|
+
* Sends a raw ADS command to the target with fallback. A wrapper for {@link Client.sendAdsCommand}().
|
|
3153
|
+
*
|
|
3154
|
+
* Calls `sendAdsCommand(command)` and if it fails with
|
|
3155
|
+
* ADS error 1793 or 1808 then calls the `sendAdsCommand(fallback)`.
|
|
3156
|
+
*
|
|
3157
|
+
* See {@link Client.readPlcUploadInfo}() for use case.
|
|
3158
|
+
*
|
|
3159
|
+
* The ideas is copied from TwinCAT.Ads.dll (`TwinCAT.Ads.AdsClientExtensions.ReadWithFallbackAsync()`).
|
|
3160
|
+
*
|
|
3161
|
+
* @example
|
|
3162
|
+
* ```js
|
|
3163
|
+
* try {
|
|
3164
|
+
* const data = Buffer.alloc(12);
|
|
3165
|
+
* //...code omitted...
|
|
3166
|
+
*
|
|
3167
|
+
* const command = {
|
|
3168
|
+
* adsCommand: ADS.ADS_COMMAND.Read,
|
|
3169
|
+
* targetAmsNetId: targetOpts.amsNetId,
|
|
3170
|
+
* targetAdsPort: targetOpts.adsPort,
|
|
3171
|
+
* payload: data
|
|
3172
|
+
* };
|
|
3173
|
+
*
|
|
3174
|
+
* const fbData = Buffer.alloc(12);
|
|
3175
|
+
* //...code omitted...
|
|
3176
|
+
*
|
|
3177
|
+
* const fallback = {
|
|
3178
|
+
* adsCommand: ADS.ADS_COMMAND.Read,
|
|
3179
|
+
* targetAmsNetId: targetOpts.amsNetId,
|
|
3180
|
+
* targetAdsPort: targetOpts.adsPort,
|
|
3181
|
+
* payload: fbData
|
|
3182
|
+
* };
|
|
3183
|
+
*
|
|
3184
|
+
* const { response: res, fallbackUsed } = await this.sendAdsCommandWithFallback<AdsReadResponse>(command, fallback);
|
|
3185
|
+
*
|
|
3186
|
+
* //If we are here, one of those commands was succcesful
|
|
3187
|
+
* if(fallbackUsed) {
|
|
3188
|
+
* console.log("Fallback was used. Result:", res.ads.payload);
|
|
3189
|
+
* } else {
|
|
3190
|
+
* console.log("Fallback was not used. Result:", res.ads.payload);
|
|
3191
|
+
* }
|
|
3192
|
+
*
|
|
3193
|
+
* } catch (err) {
|
|
3194
|
+
* console.log("Error:", err);
|
|
3195
|
+
* }
|
|
3196
|
+
* ```
|
|
3197
|
+
*
|
|
3198
|
+
* @param command The main ADS command to send
|
|
3199
|
+
* @param fallback The fallback ADS command to send
|
|
3200
|
+
*
|
|
3201
|
+
* @template T In Typescript, the type of the ADS response. If omitted, generic {@link AdsResponse} type is used.
|
|
3202
|
+
*
|
|
3203
|
+
* @throws Throws an error if sending the command fails or if target responds with an error
|
|
3204
|
+
*/
|
|
3205
|
+
async sendAdsCommandWithFallback(command, fallback) {
|
|
3206
|
+
try {
|
|
3207
|
+
this.debug(`sendAdsCommandWithFallback(): Sending ADS command with a fallback`);
|
|
3208
|
+
return {
|
|
3209
|
+
fallbackUsed: false,
|
|
3210
|
+
response: await this.sendAdsCommand(command)
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
catch (err) {
|
|
3214
|
+
const clientErr = err;
|
|
3215
|
+
//If error is "Service is not supported by server" (1793) or "Symbol not found" (1808) then we should try the fallback
|
|
3216
|
+
if (clientErr.adsError?.errorCode === 1793 || clientErr.adsError?.errorCode === 1808) {
|
|
3217
|
+
this.debug(`sendAdsCommandWithFallback(): Sending ADS command failed with ADS error ${clientErr.adsError.errorCode} - trying fallback...`);
|
|
3218
|
+
return {
|
|
3219
|
+
fallbackUsed: true,
|
|
3220
|
+
response: await this.sendAdsCommand(fallback)
|
|
3221
|
+
};
|
|
3222
|
+
}
|
|
3223
|
+
this.debug(`sendAdsCommandWithFallback(): Sending ADS command failed - skipping fallback (ADS error is not 1793 or 1808)`);
|
|
3224
|
+
throw err;
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3103
3227
|
/**
|
|
3104
3228
|
* Sends an ADS `WriteControl` command to the target.
|
|
3105
3229
|
*
|
|
@@ -3191,6 +3315,8 @@ class Client extends events_1.default {
|
|
|
3191
3315
|
const state = await this.readPlcRuntimeState(targetOpts);
|
|
3192
3316
|
await this.writeControl("Run", state.deviceState, undefined, targetOpts);
|
|
3193
3317
|
this.debug(`startPlc(): PLC runtime started at ${this.targetToString(targetOpts)}`);
|
|
3318
|
+
//Reading runtime state to make sure metaData is updated asap
|
|
3319
|
+
await this.readPlcRuntimeState(targetOpts);
|
|
3194
3320
|
}
|
|
3195
3321
|
catch (err) {
|
|
3196
3322
|
this.debug(`startPlc(): Starting PLC runtime at ${this.targetToString(targetOpts)} failed: %o`, err);
|
|
@@ -3223,6 +3349,8 @@ class Client extends events_1.default {
|
|
|
3223
3349
|
const state = await this.readPlcRuntimeState(targetOpts);
|
|
3224
3350
|
await this.writeControl("Reset", state.deviceState, undefined, targetOpts);
|
|
3225
3351
|
this.debug(`resetPlc(): PLC runtime reset at ${this.targetToString(targetOpts)}`);
|
|
3352
|
+
//Reading runtime state to make sure metaData is updated asap
|
|
3353
|
+
await this.readPlcRuntimeState(targetOpts);
|
|
3226
3354
|
}
|
|
3227
3355
|
catch (err) {
|
|
3228
3356
|
this.debug(`resetPlc(): Resetting PLC runtime at ${this.targetToString(targetOpts)} failed: %o`, err);
|
|
@@ -3255,6 +3383,8 @@ class Client extends events_1.default {
|
|
|
3255
3383
|
const state = await this.readPlcRuntimeState(targetOpts);
|
|
3256
3384
|
await this.writeControl("Stop", state.deviceState, undefined, targetOpts);
|
|
3257
3385
|
this.debug(`stopPlc(): PLC runtime stopped at ${this.targetToString(targetOpts)}`);
|
|
3386
|
+
//Reading runtime state to make sure metaData is updated asap
|
|
3387
|
+
await this.readPlcRuntimeState(targetOpts);
|
|
3258
3388
|
}
|
|
3259
3389
|
catch (err) {
|
|
3260
3390
|
this.debug(`stopPlc(): Stopping PLC runtime at ${this.targetToString(targetOpts)} failed: %o`, err);
|
|
@@ -3602,8 +3732,8 @@ class Client extends events_1.default {
|
|
|
3602
3732
|
});
|
|
3603
3733
|
this.debug(`readPlcRuntimeState(): Runtime state read successfully. State is %o`, res.ads.payload);
|
|
3604
3734
|
if (!targetOpts.adsPort && !targetOpts.amsNetId) {
|
|
3605
|
-
//Target is not overridden ->
|
|
3606
|
-
this.
|
|
3735
|
+
//Target is not overridden -> handle the result (check for change and update metadata)
|
|
3736
|
+
this.handlePlcRuntimeStateChange(res.ads.payload);
|
|
3607
3737
|
}
|
|
3608
3738
|
return res.ads.payload;
|
|
3609
3739
|
}
|
|
@@ -4096,36 +4226,90 @@ class Client extends events_1.default {
|
|
|
4096
4226
|
data.writeUInt32LE(0, pos);
|
|
4097
4227
|
pos += 4;
|
|
4098
4228
|
//8..11 Read data length
|
|
4099
|
-
data.writeUInt32LE(
|
|
4229
|
+
data.writeUInt32LE(64, pos); //64 = upload info version 3 size (works also for lower versions)
|
|
4230
|
+
pos += 4;
|
|
4231
|
+
const command = {
|
|
4232
|
+
adsCommand: ADS.ADS_COMMAND.Read,
|
|
4233
|
+
targetAmsNetId: targetOpts.amsNetId,
|
|
4234
|
+
targetAdsPort: targetOpts.adsPort,
|
|
4235
|
+
payload: data
|
|
4236
|
+
};
|
|
4237
|
+
//Fallback read command for version 1
|
|
4238
|
+
//Allocating bytes for request
|
|
4239
|
+
const fbData = Buffer.alloc(12);
|
|
4240
|
+
pos = 0;
|
|
4241
|
+
//0..3 IndexGroup
|
|
4242
|
+
fbData.writeUInt32LE(ADS.ADS_RESERVED_INDEX_GROUPS.SymbolUploadInfo, pos);
|
|
4243
|
+
pos += 4;
|
|
4244
|
+
//4..7 IndexOffset
|
|
4245
|
+
fbData.writeUInt32LE(0, pos);
|
|
4246
|
+
pos += 4;
|
|
4247
|
+
//8..11 Read data length
|
|
4248
|
+
fbData.writeUInt32LE(8, pos); //8 = upload info version 1 size
|
|
4100
4249
|
pos += 4;
|
|
4250
|
+
const fallback = {
|
|
4251
|
+
adsCommand: ADS.ADS_COMMAND.Read,
|
|
4252
|
+
targetAmsNetId: targetOpts.amsNetId,
|
|
4253
|
+
targetAdsPort: targetOpts.adsPort,
|
|
4254
|
+
payload: fbData
|
|
4255
|
+
};
|
|
4101
4256
|
try {
|
|
4102
|
-
const res = await this.
|
|
4103
|
-
|
|
4104
|
-
targetAmsNetId: targetOpts.amsNetId,
|
|
4105
|
-
targetAdsPort: targetOpts.adsPort,
|
|
4106
|
-
payload: data
|
|
4107
|
-
});
|
|
4257
|
+
const { response: res, fallbackUsed } = await this.sendAdsCommandWithFallback(command, fallback);
|
|
4258
|
+
this.debug(`readPlcUploadInfo(): Upload info read - fallback was needed: ${fallbackUsed}`);
|
|
4108
4259
|
const uploadInfo = {};
|
|
4109
4260
|
let pos = 0;
|
|
4110
4261
|
const response = res.ads.payload;
|
|
4262
|
+
//If we are here, either the command or fallback was successful
|
|
4263
|
+
//Detect version from byte count
|
|
4264
|
+
if (response.byteLength === 8) {
|
|
4265
|
+
uploadInfo.version = 1;
|
|
4266
|
+
}
|
|
4267
|
+
else if (response.byteLength === 24) {
|
|
4268
|
+
uploadInfo.version = 2;
|
|
4269
|
+
}
|
|
4270
|
+
else if (response.byteLength === 64) {
|
|
4271
|
+
uploadInfo.version = 3;
|
|
4272
|
+
}
|
|
4273
|
+
else {
|
|
4274
|
+
//Shouldn't happen as we request max 64 bytes
|
|
4275
|
+
throw new Error(`Upload info version is unknown (received ${response.byteLength} bytes) - create a GitHub issue`);
|
|
4276
|
+
}
|
|
4111
4277
|
//0..3 Symbol count
|
|
4112
4278
|
uploadInfo.symbolCount = response.readUInt32LE(pos);
|
|
4113
4279
|
pos += 4;
|
|
4114
4280
|
//4..7 Symbol length
|
|
4115
4281
|
uploadInfo.symbolLength = response.readUInt32LE(pos);
|
|
4116
4282
|
pos += 4;
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4283
|
+
if (uploadInfo.version >= 2) {
|
|
4284
|
+
//8..11 Data type count
|
|
4285
|
+
uploadInfo.dataTypeCount = response.readUInt32LE(pos);
|
|
4286
|
+
pos += 4;
|
|
4287
|
+
//12..15 Data type length
|
|
4288
|
+
uploadInfo.dataTypeLength = response.readUInt32LE(pos);
|
|
4289
|
+
pos += 4;
|
|
4290
|
+
//16..19 Max. allowed dynamic symbol count
|
|
4291
|
+
uploadInfo.maxDynamicSymbolCount = response.readUInt32LE(pos);
|
|
4292
|
+
pos += 4;
|
|
4293
|
+
//20..23 Number of dynamic symbols used (version >= 2)
|
|
4294
|
+
uploadInfo.dynamicSymbolCount = response.readUInt32LE(pos);
|
|
4295
|
+
pos += 4;
|
|
4296
|
+
if (uploadInfo.version >= 3) {
|
|
4297
|
+
//24..27 Invalid dynamic symbol count
|
|
4298
|
+
uploadInfo.invalidDynamicSymbolCount = response.readUInt32LE(pos);
|
|
4299
|
+
pos += 4;
|
|
4300
|
+
//28..31 Encoding code page used for STRING encoding
|
|
4301
|
+
uploadInfo.encodingCodePage = response.readUInt32LE(pos);
|
|
4302
|
+
pos += 4;
|
|
4303
|
+
//32..35 Upload info flags
|
|
4304
|
+
uploadInfo.flags = response.readUInt32LE(pos);
|
|
4305
|
+
uploadInfo.flagsStr = ADS.ADS_UPLOAD_INFO_FLAGS.toStringArray(uploadInfo.flags);
|
|
4306
|
+
pos += 4;
|
|
4307
|
+
//36..63 Reserved
|
|
4308
|
+
uploadInfo.reserved = response.subarray(pos);
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
//Target has UTF-8 encoded ADS symbols if encodingCodePage is 65001 (or if forced from settings)
|
|
4312
|
+
this.metaData.adsSymbolsUseUtf8 = uploadInfo.encodingCodePage === 65001 || this.settings.forceUtf8ForAdsSymbols;
|
|
4129
4313
|
if (!targetOpts.adsPort && !targetOpts.amsNetId) {
|
|
4130
4314
|
//Target is not overridden -> save to metadata
|
|
4131
4315
|
this.metaData.plcUploadInfo = uploadInfo;
|
|
@@ -4314,7 +4498,7 @@ class Client extends events_1.default {
|
|
|
4314
4498
|
data.writeUInt32LE(0, pos);
|
|
4315
4499
|
pos += 4;
|
|
4316
4500
|
//8..11 Read data length
|
|
4317
|
-
data.writeUInt32LE(uploadInfo.dataTypeLength, pos);
|
|
4501
|
+
data.writeUInt32LE(uploadInfo.dataTypeLength ?? 0xFFFFFFFF, pos); //Using 0xFFFFFFFF is upload info is version 1 (= we don't know the length)
|
|
4318
4502
|
pos += 4;
|
|
4319
4503
|
try {
|
|
4320
4504
|
const res = await this.sendAdsCommand({
|
|
@@ -4826,11 +5010,11 @@ class Client extends events_1.default {
|
|
|
4826
5010
|
*
|
|
4827
5011
|
* //Writing a POINTER value (Note the dereference operator ^)
|
|
4828
5012
|
* const ptrValue = ...
|
|
4829
|
-
* await client.writeRawByPath('
|
|
5013
|
+
* await client.writeRawByPath('GVL_Write.ComplexTypes.POINTER_^', ptrValue);
|
|
4830
5014
|
*
|
|
4831
5015
|
* //Writing a REFERENCE value
|
|
4832
5016
|
* const refValue = ...
|
|
4833
|
-
* await client.
|
|
5017
|
+
* await client.writeRawByPath('GVL_Write.ComplexTypes.REFERENCE_');
|
|
4834
5018
|
*
|
|
4835
5019
|
* } catch (err) {
|
|
4836
5020
|
* console.log("Error:", err);
|
|
@@ -5204,11 +5388,9 @@ class Client extends events_1.default {
|
|
|
5204
5388
|
* @example
|
|
5205
5389
|
* ```js
|
|
5206
5390
|
* try {
|
|
5207
|
-
* const
|
|
5208
|
-
*
|
|
5209
|
-
* };
|
|
5391
|
+
* const res = await client.writeValue('GVL_Write.StandardTypes.INT_', 32767);
|
|
5392
|
+
* console.log('Value written:', res.value);
|
|
5210
5393
|
*
|
|
5211
|
-
* const res = await client.writeValue('GVL_Read.StandardTypes.INT_', value);
|
|
5212
5394
|
* } catch (err) {
|
|
5213
5395
|
* console.log("Error:", err);
|
|
5214
5396
|
* }
|
|
@@ -5592,7 +5774,7 @@ class Client extends events_1.default {
|
|
|
5592
5774
|
let dataTypeLength = response.readUInt16LE(pos);
|
|
5593
5775
|
pos += 2;
|
|
5594
5776
|
//10..n Data type
|
|
5595
|
-
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1));
|
|
5777
|
+
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
5596
5778
|
this.debug(`createVariableHandle(): Variable handle created to ${path}`);
|
|
5597
5779
|
return result;
|
|
5598
5780
|
}
|
|
@@ -5730,7 +5912,7 @@ class Client extends events_1.default {
|
|
|
5730
5912
|
let dataTypeLength = response.readUInt16LE(pos);
|
|
5731
5913
|
pos += 2;
|
|
5732
5914
|
//10..n Data type
|
|
5733
|
-
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1));
|
|
5915
|
+
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
5734
5916
|
pos += dataTypeLength + 1;
|
|
5735
5917
|
results[i].handle = result;
|
|
5736
5918
|
}
|
|
@@ -6123,8 +6305,8 @@ class Client extends events_1.default {
|
|
|
6123
6305
|
throw new client_error_1.default(`invokeRpcMethod(): Method ${method} was not found from symbol ${path}. Available RPC methods are: ${dataType.rpcMethods.map(m => m.name).join(', ')}`);
|
|
6124
6306
|
}
|
|
6125
6307
|
//Method inputs and outputs
|
|
6126
|
-
const inputs = rpcMethod.parameters.filter(p => p.flags === ADS.ADS_RCP_METHOD_PARAM_FLAGS.In);
|
|
6127
|
-
const outputs = rpcMethod.parameters.filter(p => p.flags === ADS.ADS_RCP_METHOD_PARAM_FLAGS.Out);
|
|
6308
|
+
const inputs = rpcMethod.parameters.filter(p => (p.flags & ADS.ADS_RCP_METHOD_PARAM_FLAGS.In) === ADS.ADS_RCP_METHOD_PARAM_FLAGS.In);
|
|
6309
|
+
const outputs = rpcMethod.parameters.filter(p => (p.flags & ADS.ADS_RCP_METHOD_PARAM_FLAGS.Out) === ADS.ADS_RCP_METHOD_PARAM_FLAGS.Out);
|
|
6128
6310
|
//Creating data buffer for inputs
|
|
6129
6311
|
let inputsData = Buffer.alloc(0);
|
|
6130
6312
|
for (const input of inputs) {
|