ads-client 2.0.0-beta.4 → 2.0.0-beta.6
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 +76 -0
- package/README.md +1404 -539
- package/dist/ads-client.d.ts +88 -11
- package/dist/ads-client.js +318 -107
- package/dist/ads-client.js.map +1 -1
- package/dist/ads-commons.d.ts +24 -8
- package/dist/ads-commons.js +48 -9
- 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 +39 -2
- package/package.json +6 -6
package/dist/ads-client.js
CHANGED
|
@@ -39,13 +39,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
39
39
|
}) : function(o, v) {
|
|
40
40
|
o["default"] = v;
|
|
41
41
|
});
|
|
42
|
-
var __importStar = (this && this.__importStar) || function (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
};
|
|
42
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
43
|
+
var ownKeys = function(o) {
|
|
44
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
45
|
+
var ar = [];
|
|
46
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
47
|
+
return ar;
|
|
48
|
+
};
|
|
49
|
+
return ownKeys(o);
|
|
50
|
+
};
|
|
51
|
+
return function (mod) {
|
|
52
|
+
if (mod && mod.__esModule) return mod;
|
|
53
|
+
var result = {};
|
|
54
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
55
|
+
__setModuleDefault(result, mod);
|
|
56
|
+
return result;
|
|
57
|
+
};
|
|
58
|
+
})();
|
|
49
59
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
50
60
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
61
|
};
|
|
@@ -148,6 +158,7 @@ class Client extends events_1.default {
|
|
|
148
158
|
plcSymbols: {},
|
|
149
159
|
allPlcDataTypesCached: false,
|
|
150
160
|
plcDataTypes: {},
|
|
161
|
+
adsSymbolsUseUtf8: false
|
|
151
162
|
};
|
|
152
163
|
/**
|
|
153
164
|
* Buffer for received data.
|
|
@@ -233,7 +244,8 @@ class Client extends events_1.default {
|
|
|
233
244
|
allowHalfOpen: false,
|
|
234
245
|
rawClient: false,
|
|
235
246
|
disableCaching: false,
|
|
236
|
-
deleteUnknownSubscriptions: true
|
|
247
|
+
deleteUnknownSubscriptions: true,
|
|
248
|
+
forceUtf8ForAdsSymbols: false
|
|
237
249
|
};
|
|
238
250
|
/**
|
|
239
251
|
* Active connection information.
|
|
@@ -419,6 +431,8 @@ class Client extends events_1.default {
|
|
|
419
431
|
//When socket errors from now on, we will close the connection
|
|
420
432
|
this.socketErrorHandler = this.onSocketError.bind(this);
|
|
421
433
|
socket.on("error", this.socketErrorHandler);
|
|
434
|
+
//Force ADS UTF-8 (note: this is also set later at readPlcUploadInfo() if target is a PLC)
|
|
435
|
+
this.metaData.adsSymbolsUseUtf8 = this.settings.forceUtf8ForAdsSymbols;
|
|
422
436
|
//If rawClient setting is true, we are done here (just a connection is enough)
|
|
423
437
|
if (this.settings.rawClient !== true) {
|
|
424
438
|
try {
|
|
@@ -748,11 +762,9 @@ class Client extends events_1.default {
|
|
|
748
762
|
this.socketWrite(buffer);
|
|
749
763
|
}
|
|
750
764
|
catch (err) {
|
|
751
|
-
reject(err);
|
|
752
|
-
}
|
|
753
|
-
finally {
|
|
754
765
|
this.amsTcpCallback = undefined;
|
|
755
766
|
clearTimeout(this.portRegisterTimeoutTimer);
|
|
767
|
+
reject(err);
|
|
756
768
|
}
|
|
757
769
|
});
|
|
758
770
|
}
|
|
@@ -878,29 +890,38 @@ class Client extends events_1.default {
|
|
|
878
890
|
* @param subscription The subscription object (unused here)
|
|
879
891
|
*/
|
|
880
892
|
onPlcRuntimeStateChanged(data, subscription) {
|
|
881
|
-
const
|
|
893
|
+
const state = {};
|
|
882
894
|
let pos = 0;
|
|
883
895
|
//0..1 ADS state
|
|
884
|
-
|
|
885
|
-
|
|
896
|
+
state.adsState = data.value.readUInt16LE(pos);
|
|
897
|
+
state.adsStateStr = ADS.ADS_STATE.toString(state.adsState);
|
|
886
898
|
pos += 2;
|
|
887
899
|
//2..3 Device state
|
|
888
|
-
|
|
900
|
+
state.deviceState = data.value.readUInt16LE(pos);
|
|
889
901
|
pos += 2;
|
|
890
|
-
|
|
902
|
+
this.handlePlcRuntimeStateChange(state);
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Checks if PLC runtime state has changed, and if so, emits an event.
|
|
906
|
+
*
|
|
907
|
+
* Call from `onPlcRuntimeStateChanged()` and `readPlcRuntimeState()`.
|
|
908
|
+
*
|
|
909
|
+
* @param state Active PLC runtime state
|
|
910
|
+
*/
|
|
911
|
+
handlePlcRuntimeStateChange(state) {
|
|
891
912
|
let stateChanged = false;
|
|
892
|
-
if (this.metaData.plcRuntimeState === undefined || this.metaData.plcRuntimeState.adsState !==
|
|
893
|
-
this.debug(`
|
|
913
|
+
if (this.metaData.plcRuntimeState === undefined || this.metaData.plcRuntimeState.adsState !== state.adsState) {
|
|
914
|
+
this.debug(`handlePlcRuntimeStateChange(): PLC runtime state (adsState) changed from ${this.metaData.plcRuntimeState === undefined ? 'UNKNOWN' : this.metaData.plcRuntimeState.adsStateStr} to ${state.adsStateStr}`);
|
|
894
915
|
stateChanged = true;
|
|
895
916
|
}
|
|
896
|
-
if (this.metaData.plcRuntimeState === undefined || this.metaData.plcRuntimeState.deviceState !==
|
|
897
|
-
this.debug(`
|
|
917
|
+
if (this.metaData.plcRuntimeState === undefined || this.metaData.plcRuntimeState.deviceState !== state.deviceState) {
|
|
918
|
+
this.debug(`handlePlcRuntimeStateChange(): PLC runtime state (deviceState) changed from ${this.metaData.plcRuntimeState === undefined ? 'UNKNOWN' : this.metaData.plcRuntimeState.deviceState} to ${state.deviceState}`);
|
|
898
919
|
stateChanged = true;
|
|
899
920
|
}
|
|
900
921
|
let oldState = this.metaData.plcRuntimeState !== undefined
|
|
901
922
|
? { ...this.metaData.plcRuntimeState }
|
|
902
923
|
: undefined;
|
|
903
|
-
this.metaData.plcRuntimeState =
|
|
924
|
+
this.metaData.plcRuntimeState = state;
|
|
904
925
|
if (stateChanged) {
|
|
905
926
|
this.emit('plcRuntimeStateChange', this.metaData.plcRuntimeState, oldState);
|
|
906
927
|
}
|
|
@@ -1322,8 +1343,7 @@ class Client extends events_1.default {
|
|
|
1322
1343
|
ads.payload.versionBuild = data.readUInt16LE(pos);
|
|
1323
1344
|
pos += 2;
|
|
1324
1345
|
//8..24 Device name
|
|
1325
|
-
ads.payload.deviceName = ADS.decodePlcStringBuffer(data.subarray(pos, pos + 16));
|
|
1326
|
-
;
|
|
1346
|
+
ads.payload.deviceName = ADS.decodePlcStringBuffer(data.subarray(pos, pos + 16), this.metaData.adsSymbolsUseUtf8);
|
|
1327
1347
|
}
|
|
1328
1348
|
break;
|
|
1329
1349
|
case ADS.ADS_COMMAND.ReadState:
|
|
@@ -1698,7 +1718,7 @@ class Client extends events_1.default {
|
|
|
1698
1718
|
};
|
|
1699
1719
|
if (this.symbol) {
|
|
1700
1720
|
const dataType = await clientRef.getDataType(this.symbol.type, this.targetOpts);
|
|
1701
|
-
result.value = clientRef.convertBufferToObject(data, dataType);
|
|
1721
|
+
result.value = clientRef.convertBufferToObject(data, dataType, this.symbol.attributes);
|
|
1702
1722
|
}
|
|
1703
1723
|
this.latestData = result;
|
|
1704
1724
|
return result;
|
|
@@ -1877,13 +1897,13 @@ class Client extends events_1.default {
|
|
|
1877
1897
|
const commentLength = data.readUInt16LE(pos);
|
|
1878
1898
|
pos += 2;
|
|
1879
1899
|
//30.... Symbol name
|
|
1880
|
-
symbol.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
1900
|
+
symbol.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1881
1901
|
pos += nameLength + 1;
|
|
1882
1902
|
//.. Symbol type
|
|
1883
|
-
symbol.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1));
|
|
1903
|
+
symbol.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1884
1904
|
pos += typeLength + 1;
|
|
1885
1905
|
//.. Symbol comment
|
|
1886
|
-
symbol.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
1906
|
+
symbol.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1887
1907
|
pos += commentLength + 1;
|
|
1888
1908
|
//Array information
|
|
1889
1909
|
symbol.arrayInfo = [];
|
|
@@ -1915,10 +1935,10 @@ class Client extends events_1.default {
|
|
|
1915
1935
|
const valueLength = data.readUInt8(pos);
|
|
1916
1936
|
pos += 1;
|
|
1917
1937
|
//Name
|
|
1918
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
1938
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1919
1939
|
pos += nameLength + 1;
|
|
1920
1940
|
//Value
|
|
1921
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
1941
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1922
1942
|
pos += valueLength + 1;
|
|
1923
1943
|
symbol.attributes.push(attr);
|
|
1924
1944
|
}
|
|
@@ -1986,13 +2006,13 @@ class Client extends events_1.default {
|
|
|
1986
2006
|
const subItemCount = data.readUInt16LE(pos);
|
|
1987
2007
|
pos += 2;
|
|
1988
2008
|
//38.. Data type name
|
|
1989
|
-
dataType.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2009
|
+
dataType.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1990
2010
|
pos += nameLength + 1;
|
|
1991
2011
|
//.. Data type type
|
|
1992
|
-
dataType.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1));
|
|
2012
|
+
dataType.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1993
2013
|
pos += typeLength + 1;
|
|
1994
2014
|
//.. Data type comment
|
|
1995
|
-
dataType.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
2015
|
+
dataType.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1996
2016
|
pos += commentLength + 1;
|
|
1997
2017
|
//Array information
|
|
1998
2018
|
dataType.arrayInfos = [];
|
|
@@ -2076,13 +2096,13 @@ class Client extends events_1.default {
|
|
|
2076
2096
|
const parameterCount = data.readUInt16LE(pos);
|
|
2077
2097
|
pos += 2;
|
|
2078
2098
|
//56.. Name
|
|
2079
|
-
method.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2099
|
+
method.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2080
2100
|
pos += nameLength + 1;
|
|
2081
2101
|
//.. Return data type
|
|
2082
|
-
method.retunDataType = ADS.decodePlcStringBuffer(data.subarray(pos, pos + returnTypeLength + 1));
|
|
2102
|
+
method.retunDataType = ADS.decodePlcStringBuffer(data.subarray(pos, pos + returnTypeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2083
2103
|
pos += returnTypeLength + 1;
|
|
2084
2104
|
//.. Comment
|
|
2085
|
-
method.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
2105
|
+
method.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2086
2106
|
pos += commentLength + 1;
|
|
2087
2107
|
//Parameters
|
|
2088
2108
|
method.parameters = [];
|
|
@@ -2125,13 +2145,13 @@ class Client extends events_1.default {
|
|
|
2125
2145
|
const commentLength = data.readUInt16LE(pos);
|
|
2126
2146
|
pos += 2;
|
|
2127
2147
|
//38.. Data type name
|
|
2128
|
-
param.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2148
|
+
param.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2129
2149
|
pos += nameLength + 1;
|
|
2130
2150
|
//.. Data type type
|
|
2131
|
-
param.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1));
|
|
2151
|
+
param.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2132
2152
|
pos += typeLength + 1;
|
|
2133
2153
|
//.. Data type comment
|
|
2134
|
-
param.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
2154
|
+
param.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2135
2155
|
pos += commentLength + 1;
|
|
2136
2156
|
//Attributes
|
|
2137
2157
|
param.attributes = [];
|
|
@@ -2148,18 +2168,19 @@ class Client extends events_1.default {
|
|
|
2148
2168
|
const valueLength = data.readUInt8(pos);
|
|
2149
2169
|
pos += 1;
|
|
2150
2170
|
//Name
|
|
2151
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2171
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2152
2172
|
pos += nameLength + 1;
|
|
2153
2173
|
//Value
|
|
2154
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
2174
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2155
2175
|
pos += valueLength + 1;
|
|
2156
|
-
|
|
2176
|
+
param.attributes.push(attr);
|
|
2157
2177
|
}
|
|
2158
2178
|
}
|
|
2159
2179
|
if (pos - beginPosition > entryLength) {
|
|
2160
2180
|
//There is some additional data left
|
|
2161
2181
|
param.reserved2 = data.subarray(pos);
|
|
2162
2182
|
}
|
|
2183
|
+
pos = beginPosition + entryLength;
|
|
2163
2184
|
method.parameters.push(param);
|
|
2164
2185
|
}
|
|
2165
2186
|
//Attributes
|
|
@@ -2177,10 +2198,10 @@ class Client extends events_1.default {
|
|
|
2177
2198
|
const valueLength = data.readUInt8(pos);
|
|
2178
2199
|
pos += 1;
|
|
2179
2200
|
//Name
|
|
2180
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2201
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2181
2202
|
pos += nameLength + 1;
|
|
2182
2203
|
//Value
|
|
2183
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
2204
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2184
2205
|
pos += valueLength + 1;
|
|
2185
2206
|
method.attributes.push(attr);
|
|
2186
2207
|
}
|
|
@@ -2205,18 +2226,19 @@ class Client extends events_1.default {
|
|
|
2205
2226
|
const valueLength = data.readUInt8(pos);
|
|
2206
2227
|
pos += 1;
|
|
2207
2228
|
//Name
|
|
2208
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2229
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2209
2230
|
pos += nameLength + 1;
|
|
2210
2231
|
//Value
|
|
2211
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
2232
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2212
2233
|
pos += valueLength + 1;
|
|
2213
2234
|
dataType.attributes.push(attr);
|
|
2214
2235
|
}
|
|
2215
2236
|
}
|
|
2216
2237
|
//If flag EnumInfos set
|
|
2217
2238
|
dataType.enumInfos = [];
|
|
2239
|
+
let enumInfoCount = 0;
|
|
2218
2240
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.EnumInfos) === ADS.ADS_DATA_TYPE_FLAGS.EnumInfos) {
|
|
2219
|
-
|
|
2241
|
+
enumInfoCount = data.readUInt16LE(pos);
|
|
2220
2242
|
pos += 2;
|
|
2221
2243
|
for (let i = 0; i < enumInfoCount; i++) {
|
|
2222
2244
|
let enumInfo = {};
|
|
@@ -2224,7 +2246,7 @@ class Client extends events_1.default {
|
|
|
2224
2246
|
const nameLength = data.readUInt8(pos);
|
|
2225
2247
|
pos += 1;
|
|
2226
2248
|
//Enumeration name
|
|
2227
|
-
enumInfo.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2249
|
+
enumInfo.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2228
2250
|
pos += nameLength + 1;
|
|
2229
2251
|
//Enumeration value
|
|
2230
2252
|
enumInfo.value = data.subarray(pos, pos + dataType.size);
|
|
@@ -2251,9 +2273,42 @@ class Client extends events_1.default {
|
|
|
2251
2273
|
}
|
|
2252
2274
|
//If flag ExtendedEnumInfos set
|
|
2253
2275
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.ExtendedEnumInfos) === ADS.ADS_DATA_TYPE_FLAGS.ExtendedEnumInfos) {
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2276
|
+
for (let i = 0; i < enumInfoCount; i++) {
|
|
2277
|
+
let beginPosition = pos;
|
|
2278
|
+
//Total entry length (bytes)
|
|
2279
|
+
const entryLength = data.readUInt16LE(pos);
|
|
2280
|
+
pos += 2;
|
|
2281
|
+
//Comment length
|
|
2282
|
+
const commentLength = data.readUInt8(pos);
|
|
2283
|
+
pos += 1;
|
|
2284
|
+
//Attribute count
|
|
2285
|
+
const attributeCount = data.readUInt8(pos);
|
|
2286
|
+
pos += 1;
|
|
2287
|
+
//Enumeration comment
|
|
2288
|
+
dataType.enumInfos[i].comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2289
|
+
pos += commentLength + 1;
|
|
2290
|
+
//Enumeration attributes
|
|
2291
|
+
dataType.enumInfos[i].attributes = [];
|
|
2292
|
+
//Attributes
|
|
2293
|
+
for (let i = 0; i < attributeCount; i++) {
|
|
2294
|
+
const attr = {};
|
|
2295
|
+
//Name length
|
|
2296
|
+
const nameLength = data.readUInt8(pos);
|
|
2297
|
+
pos += 1;
|
|
2298
|
+
//Value length
|
|
2299
|
+
const valueLength = data.readUInt8(pos);
|
|
2300
|
+
pos += 1;
|
|
2301
|
+
//Name
|
|
2302
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2303
|
+
pos += nameLength + 1;
|
|
2304
|
+
//Value
|
|
2305
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2306
|
+
pos += valueLength + 1;
|
|
2307
|
+
dataType.enumInfos[i].attributes.push(attr);
|
|
2308
|
+
}
|
|
2309
|
+
//If there are some reserved bytes -> skip
|
|
2310
|
+
pos = beginPosition + entryLength;
|
|
2311
|
+
}
|
|
2257
2312
|
}
|
|
2258
2313
|
//If flag SoftwareProtectionLevels set
|
|
2259
2314
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.SoftwareProtectionLevels) === ADS.ADS_DATA_TYPE_FLAGS.SoftwareProtectionLevels) {
|
|
@@ -2437,6 +2492,8 @@ class Client extends events_1.default {
|
|
|
2437
2492
|
subItemType.hashValue = dataType.subItems[i].hashValue;
|
|
2438
2493
|
subItemType.offset = dataType.subItems[i].offset;
|
|
2439
2494
|
subItemType.comment = dataType.subItems[i].comment;
|
|
2495
|
+
//Let's keep attributes from parent as well as subitem's own attributes
|
|
2496
|
+
subItemType.attributes = [...subItemType.attributes, ...dataType.subItems[i].attributes];
|
|
2440
2497
|
}
|
|
2441
2498
|
builtType.subItems.push(subItemType);
|
|
2442
2499
|
}
|
|
@@ -2507,10 +2564,11 @@ class Client extends events_1.default {
|
|
|
2507
2564
|
*
|
|
2508
2565
|
* @param buffer The raw data to convert
|
|
2509
2566
|
* @param dataType Target data type
|
|
2567
|
+
* @param attributes Additional attributes of the symbol or data type to use for conversion
|
|
2510
2568
|
*/
|
|
2511
|
-
convertBufferToPrimitiveType(buffer, dataType) {
|
|
2569
|
+
convertBufferToPrimitiveType(buffer, dataType, attributes) {
|
|
2512
2570
|
if (dataType.adsDataType === ADS.ADS_DATA_TYPES.ADST_STRING) {
|
|
2513
|
-
return ADS.decodePlcStringBuffer(buffer);
|
|
2571
|
+
return ADS.decodePlcStringBuffer(buffer, this.getStringDataTypeEncoding(dataType, attributes) === 'UTF-8');
|
|
2514
2572
|
}
|
|
2515
2573
|
else if (dataType.adsDataType === ADS.ADS_DATA_TYPES.ADST_WSTRING) {
|
|
2516
2574
|
return ADS.decodePlcWstringBuffer(buffer);
|
|
@@ -2519,6 +2577,16 @@ class Client extends events_1.default {
|
|
|
2519
2577
|
return ADS.BASE_DATA_TYPES.fromBuffer(dataType.type, buffer, this.settings.convertDatesToJavascript);
|
|
2520
2578
|
}
|
|
2521
2579
|
}
|
|
2580
|
+
/**
|
|
2581
|
+
* Extracts TcEncoding attribute from the data datatype attributes or additional attributes
|
|
2582
|
+
*
|
|
2583
|
+
* @param dataType The data type
|
|
2584
|
+
* @param attributes Additional attributes of the symbol or data type
|
|
2585
|
+
*/
|
|
2586
|
+
getStringDataTypeEncoding(dataType, attributes) {
|
|
2587
|
+
return dataType.attributes.find((attr) => attr.name === 'TcEncoding')?.value
|
|
2588
|
+
|| attributes?.find((attr) => attr.name === 'TcEncoding')?.value;
|
|
2589
|
+
}
|
|
2522
2590
|
/**
|
|
2523
2591
|
* Converts raw data to Javascript object.
|
|
2524
2592
|
*
|
|
@@ -2526,9 +2594,10 @@ class Client extends events_1.default {
|
|
|
2526
2594
|
*
|
|
2527
2595
|
* @param data The raw data to convert
|
|
2528
2596
|
* @param dataType Target data type
|
|
2597
|
+
* @param attributes Additional attributes of the symbol or data type used for conversion
|
|
2529
2598
|
* @param isArrayItem If `true`, this is an array item (default: `false`)
|
|
2530
2599
|
*/
|
|
2531
|
-
convertBufferToObject(data, dataType, isArrayItem = false) {
|
|
2600
|
+
convertBufferToObject(data, dataType, attributes, isArrayItem = false) {
|
|
2532
2601
|
let result;
|
|
2533
2602
|
if ((dataType.arrayInfos.length === 0 || isArrayItem) && dataType.subItems.length > 0) {
|
|
2534
2603
|
//Struct or array item
|
|
@@ -2550,7 +2619,7 @@ class Client extends events_1.default {
|
|
|
2550
2619
|
}
|
|
2551
2620
|
else {
|
|
2552
2621
|
//Final dimension
|
|
2553
|
-
temp.push(this.convertBufferToObject(data, dataType, true));
|
|
2622
|
+
temp.push(this.convertBufferToObject(data, dataType, undefined, true));
|
|
2554
2623
|
data = data.subarray(dataType.size);
|
|
2555
2624
|
}
|
|
2556
2625
|
}
|
|
@@ -2596,7 +2665,7 @@ class Client extends events_1.default {
|
|
|
2596
2665
|
}
|
|
2597
2666
|
else {
|
|
2598
2667
|
//Primitive type
|
|
2599
|
-
result = this.convertBufferToPrimitiveType(data.subarray(dataType.offset, dataType.offset + dataType.size), dataType);
|
|
2668
|
+
result = this.convertBufferToPrimitiveType(data.subarray(dataType.offset, dataType.offset + dataType.size), dataType, attributes);
|
|
2600
2669
|
}
|
|
2601
2670
|
return result;
|
|
2602
2671
|
}
|
|
@@ -2605,10 +2674,11 @@ class Client extends events_1.default {
|
|
|
2605
2674
|
*
|
|
2606
2675
|
* @param value Javascript object to convert
|
|
2607
2676
|
* @param dataType Target data type
|
|
2677
|
+
* @param attributes Additional attributes of the symbol or data type used for conversion
|
|
2608
2678
|
* @param objectPath Object path that is passed forward when calling recursively. This is used for error reporting if a property is missing
|
|
2609
2679
|
* @param isArrayItem If `true`, this is an array item (default: `false`)
|
|
2610
2680
|
*/
|
|
2611
|
-
convertObjectToBuffer(value, dataType, objectPath = '', isArrayItem = false) {
|
|
2681
|
+
convertObjectToBuffer(value, dataType, attributes, objectPath = '', isArrayItem = false) {
|
|
2612
2682
|
let rawValue = Buffer.alloc(0);
|
|
2613
2683
|
const pathInfo = objectPath === '' && dataType.name === ''
|
|
2614
2684
|
? ''
|
|
@@ -2642,7 +2712,7 @@ class Client extends events_1.default {
|
|
|
2642
2712
|
};
|
|
2643
2713
|
}
|
|
2644
2714
|
//Converting subItem
|
|
2645
|
-
let res = this.convertObjectToBuffer(value[key], subItem, `${pathInfo}.${subItem.name}`);
|
|
2715
|
+
let res = this.convertObjectToBuffer(value[key], subItem, undefined, `${pathInfo}.${subItem.name}`);
|
|
2646
2716
|
if (res.missingProperty) {
|
|
2647
2717
|
//We have a missing property somewhere -> quit
|
|
2648
2718
|
return res;
|
|
@@ -2697,7 +2767,7 @@ class Client extends events_1.default {
|
|
|
2697
2767
|
return `${pathInfo}${path}[${child}]`;
|
|
2698
2768
|
}
|
|
2699
2769
|
//Converting array item
|
|
2700
|
-
let res = this.convertObjectToBuffer(value[child], dataType, `${pathInfo}.${path}[${child}]`, true);
|
|
2770
|
+
let res = this.convertObjectToBuffer(value[child], dataType, undefined, `${pathInfo}.${path}[${child}]`, true);
|
|
2701
2771
|
if (res.missingProperty) {
|
|
2702
2772
|
//We have a missing property somewhere -> quit
|
|
2703
2773
|
return res.missingProperty;
|
|
@@ -2748,11 +2818,11 @@ class Client extends events_1.default {
|
|
|
2748
2818
|
else {
|
|
2749
2819
|
throw new client_error_1.default(`Provided value ${JSON.stringify(value)}${pathInfo} is not valid value for this ENUM. Allowed values are a number and any of the following: ${allowedEnumValuesStr}`);
|
|
2750
2820
|
}
|
|
2751
|
-
this.convertPrimitiveTypeToBuffer(enumEntry.value, dataType, rawValue);
|
|
2821
|
+
this.convertPrimitiveTypeToBuffer(enumEntry.value, dataType, rawValue, attributes);
|
|
2752
2822
|
}
|
|
2753
2823
|
else if (ADS.BASE_DATA_TYPES.isKnownType(dataType.type)) {
|
|
2754
2824
|
rawValue = Buffer.alloc(dataType.size);
|
|
2755
|
-
this.convertPrimitiveTypeToBuffer(value, dataType, rawValue);
|
|
2825
|
+
this.convertPrimitiveTypeToBuffer(value, dataType, rawValue, attributes);
|
|
2756
2826
|
}
|
|
2757
2827
|
else if (dataType.adsDataType === ADS.ADS_DATA_TYPES.ADST_BIGTYPE && dataType.subItems.length === 0) {
|
|
2758
2828
|
//If size is 0, then it's empty struct -> rawValue is already OK
|
|
@@ -2787,15 +2857,17 @@ class Client extends events_1.default {
|
|
|
2787
2857
|
* @param value Javascript object to convert
|
|
2788
2858
|
* @param dataType Data type
|
|
2789
2859
|
* @param buffer Reference to Buffer object where to write the raw value
|
|
2860
|
+
* @param attributes Additional attributes of the symbol or data type used for conversion
|
|
2790
2861
|
*/
|
|
2791
|
-
convertPrimitiveTypeToBuffer(value, dataType, buffer) {
|
|
2862
|
+
convertPrimitiveTypeToBuffer(value, dataType, buffer, attributes) {
|
|
2792
2863
|
if (dataType.size === 0) {
|
|
2793
2864
|
return;
|
|
2794
2865
|
}
|
|
2795
2866
|
if (dataType.adsDataType === ADS.ADS_DATA_TYPES.ADST_STRING) {
|
|
2867
|
+
const encoded = ADS.encodeStringToPlcStringBuffer(value, this.getStringDataTypeEncoding(dataType, attributes) === 'UTF-8');
|
|
2796
2868
|
//If string is too long for target data type, truncate it
|
|
2797
|
-
const length = Math.min(dataType.size - 1,
|
|
2798
|
-
|
|
2869
|
+
const length = Math.min(dataType.size - 1, encoded.length);
|
|
2870
|
+
encoded.copy(buffer, 0, 0, length);
|
|
2799
2871
|
}
|
|
2800
2872
|
else if (dataType.adsDataType === ADS.ADS_DATA_TYPES.ADST_WSTRING) {
|
|
2801
2873
|
ADS.encodeStringToPlcWstringBuffer(value).copy(buffer);
|
|
@@ -3015,6 +3087,8 @@ class Client extends events_1.default {
|
|
|
3015
3087
|
* }
|
|
3016
3088
|
* ```
|
|
3017
3089
|
*
|
|
3090
|
+
* @param command The ADS command to send
|
|
3091
|
+
*
|
|
3018
3092
|
* @template T In Typescript, the type of the ADS response. If omitted, generic {@link AdsResponse} type is used.
|
|
3019
3093
|
*
|
|
3020
3094
|
* @throws Throws an error if sending the command fails or if target responds with an error
|
|
@@ -3100,6 +3174,82 @@ class Client extends events_1.default {
|
|
|
3100
3174
|
}
|
|
3101
3175
|
});
|
|
3102
3176
|
}
|
|
3177
|
+
/**
|
|
3178
|
+
* Sends a raw ADS command to the target with fallback. A wrapper for {@link Client.sendAdsCommand}().
|
|
3179
|
+
*
|
|
3180
|
+
* Calls `sendAdsCommand(command)` and if it fails with
|
|
3181
|
+
* ADS error 1793 or 1808 then calls the `sendAdsCommand(fallback)`.
|
|
3182
|
+
*
|
|
3183
|
+
* See {@link Client.readPlcUploadInfo}() for use case.
|
|
3184
|
+
*
|
|
3185
|
+
* The ideas is copied from TwinCAT.Ads.dll (`TwinCAT.Ads.AdsClientExtensions.ReadWithFallbackAsync()`).
|
|
3186
|
+
*
|
|
3187
|
+
* @example
|
|
3188
|
+
* ```js
|
|
3189
|
+
* try {
|
|
3190
|
+
* const data = Buffer.alloc(12);
|
|
3191
|
+
* //...code omitted...
|
|
3192
|
+
*
|
|
3193
|
+
* const command = {
|
|
3194
|
+
* adsCommand: ADS.ADS_COMMAND.Read,
|
|
3195
|
+
* targetAmsNetId: targetOpts.amsNetId,
|
|
3196
|
+
* targetAdsPort: targetOpts.adsPort,
|
|
3197
|
+
* payload: data
|
|
3198
|
+
* };
|
|
3199
|
+
*
|
|
3200
|
+
* const fbData = Buffer.alloc(12);
|
|
3201
|
+
* //...code omitted...
|
|
3202
|
+
*
|
|
3203
|
+
* const fallback = {
|
|
3204
|
+
* adsCommand: ADS.ADS_COMMAND.Read,
|
|
3205
|
+
* targetAmsNetId: targetOpts.amsNetId,
|
|
3206
|
+
* targetAdsPort: targetOpts.adsPort,
|
|
3207
|
+
* payload: fbData
|
|
3208
|
+
* };
|
|
3209
|
+
*
|
|
3210
|
+
* const { response: res, fallbackUsed } = await this.sendAdsCommandWithFallback<AdsReadResponse>(command, fallback);
|
|
3211
|
+
*
|
|
3212
|
+
* //If we are here, one of those commands was succcesful
|
|
3213
|
+
* if(fallbackUsed) {
|
|
3214
|
+
* console.log("Fallback was used. Result:", res.ads.payload);
|
|
3215
|
+
* } else {
|
|
3216
|
+
* console.log("Fallback was not used. Result:", res.ads.payload);
|
|
3217
|
+
* }
|
|
3218
|
+
*
|
|
3219
|
+
* } catch (err) {
|
|
3220
|
+
* console.log("Error:", err);
|
|
3221
|
+
* }
|
|
3222
|
+
* ```
|
|
3223
|
+
*
|
|
3224
|
+
* @param command The main ADS command to send
|
|
3225
|
+
* @param fallback The fallback ADS command to send
|
|
3226
|
+
*
|
|
3227
|
+
* @template T In Typescript, the type of the ADS response. If omitted, generic {@link AdsResponse} type is used.
|
|
3228
|
+
*
|
|
3229
|
+
* @throws Throws an error if sending the command fails or if target responds with an error
|
|
3230
|
+
*/
|
|
3231
|
+
async sendAdsCommandWithFallback(command, fallback) {
|
|
3232
|
+
try {
|
|
3233
|
+
this.debug(`sendAdsCommandWithFallback(): Sending ADS command with a fallback`);
|
|
3234
|
+
return {
|
|
3235
|
+
fallbackUsed: false,
|
|
3236
|
+
response: await this.sendAdsCommand(command)
|
|
3237
|
+
};
|
|
3238
|
+
}
|
|
3239
|
+
catch (err) {
|
|
3240
|
+
const clientErr = err;
|
|
3241
|
+
//If error is "Service is not supported by server" (1793) or "Symbol not found" (1808) then we should try the fallback
|
|
3242
|
+
if (clientErr.adsError?.errorCode === 1793 || clientErr.adsError?.errorCode === 1808) {
|
|
3243
|
+
this.debug(`sendAdsCommandWithFallback(): Sending ADS command failed with ADS error ${clientErr.adsError.errorCode} - trying fallback...`);
|
|
3244
|
+
return {
|
|
3245
|
+
fallbackUsed: true,
|
|
3246
|
+
response: await this.sendAdsCommand(fallback)
|
|
3247
|
+
};
|
|
3248
|
+
}
|
|
3249
|
+
this.debug(`sendAdsCommandWithFallback(): Sending ADS command failed - skipping fallback (ADS error is not 1793 or 1808)`);
|
|
3250
|
+
throw err;
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3103
3253
|
/**
|
|
3104
3254
|
* Sends an ADS `WriteControl` command to the target.
|
|
3105
3255
|
*
|
|
@@ -3191,6 +3341,8 @@ class Client extends events_1.default {
|
|
|
3191
3341
|
const state = await this.readPlcRuntimeState(targetOpts);
|
|
3192
3342
|
await this.writeControl("Run", state.deviceState, undefined, targetOpts);
|
|
3193
3343
|
this.debug(`startPlc(): PLC runtime started at ${this.targetToString(targetOpts)}`);
|
|
3344
|
+
//Reading runtime state to make sure metaData is updated asap
|
|
3345
|
+
await this.readPlcRuntimeState(targetOpts);
|
|
3194
3346
|
}
|
|
3195
3347
|
catch (err) {
|
|
3196
3348
|
this.debug(`startPlc(): Starting PLC runtime at ${this.targetToString(targetOpts)} failed: %o`, err);
|
|
@@ -3223,6 +3375,8 @@ class Client extends events_1.default {
|
|
|
3223
3375
|
const state = await this.readPlcRuntimeState(targetOpts);
|
|
3224
3376
|
await this.writeControl("Reset", state.deviceState, undefined, targetOpts);
|
|
3225
3377
|
this.debug(`resetPlc(): PLC runtime reset at ${this.targetToString(targetOpts)}`);
|
|
3378
|
+
//Reading runtime state to make sure metaData is updated asap
|
|
3379
|
+
await this.readPlcRuntimeState(targetOpts);
|
|
3226
3380
|
}
|
|
3227
3381
|
catch (err) {
|
|
3228
3382
|
this.debug(`resetPlc(): Resetting PLC runtime at ${this.targetToString(targetOpts)} failed: %o`, err);
|
|
@@ -3255,6 +3409,8 @@ class Client extends events_1.default {
|
|
|
3255
3409
|
const state = await this.readPlcRuntimeState(targetOpts);
|
|
3256
3410
|
await this.writeControl("Stop", state.deviceState, undefined, targetOpts);
|
|
3257
3411
|
this.debug(`stopPlc(): PLC runtime stopped at ${this.targetToString(targetOpts)}`);
|
|
3412
|
+
//Reading runtime state to make sure metaData is updated asap
|
|
3413
|
+
await this.readPlcRuntimeState(targetOpts);
|
|
3258
3414
|
}
|
|
3259
3415
|
catch (err) {
|
|
3260
3416
|
this.debug(`stopPlc(): Stopping PLC runtime at ${this.targetToString(targetOpts)} failed: %o`, err);
|
|
@@ -3602,8 +3758,8 @@ class Client extends events_1.default {
|
|
|
3602
3758
|
});
|
|
3603
3759
|
this.debug(`readPlcRuntimeState(): Runtime state read successfully. State is %o`, res.ads.payload);
|
|
3604
3760
|
if (!targetOpts.adsPort && !targetOpts.amsNetId) {
|
|
3605
|
-
//Target is not overridden ->
|
|
3606
|
-
this.
|
|
3761
|
+
//Target is not overridden -> handle the result (check for change and update metadata)
|
|
3762
|
+
this.handlePlcRuntimeStateChange(res.ads.payload);
|
|
3607
3763
|
}
|
|
3608
3764
|
return res.ads.payload;
|
|
3609
3765
|
}
|
|
@@ -4096,36 +4252,90 @@ class Client extends events_1.default {
|
|
|
4096
4252
|
data.writeUInt32LE(0, pos);
|
|
4097
4253
|
pos += 4;
|
|
4098
4254
|
//8..11 Read data length
|
|
4099
|
-
data.writeUInt32LE(
|
|
4255
|
+
data.writeUInt32LE(64, pos); //64 = upload info version 3 size (works also for lower versions)
|
|
4256
|
+
pos += 4;
|
|
4257
|
+
const command = {
|
|
4258
|
+
adsCommand: ADS.ADS_COMMAND.Read,
|
|
4259
|
+
targetAmsNetId: targetOpts.amsNetId,
|
|
4260
|
+
targetAdsPort: targetOpts.adsPort,
|
|
4261
|
+
payload: data
|
|
4262
|
+
};
|
|
4263
|
+
//Fallback read command for version 1
|
|
4264
|
+
//Allocating bytes for request
|
|
4265
|
+
const fbData = Buffer.alloc(12);
|
|
4266
|
+
pos = 0;
|
|
4267
|
+
//0..3 IndexGroup
|
|
4268
|
+
fbData.writeUInt32LE(ADS.ADS_RESERVED_INDEX_GROUPS.SymbolUploadInfo, pos);
|
|
4269
|
+
pos += 4;
|
|
4270
|
+
//4..7 IndexOffset
|
|
4271
|
+
fbData.writeUInt32LE(0, pos);
|
|
4272
|
+
pos += 4;
|
|
4273
|
+
//8..11 Read data length
|
|
4274
|
+
fbData.writeUInt32LE(8, pos); //8 = upload info version 1 size
|
|
4100
4275
|
pos += 4;
|
|
4276
|
+
const fallback = {
|
|
4277
|
+
adsCommand: ADS.ADS_COMMAND.Read,
|
|
4278
|
+
targetAmsNetId: targetOpts.amsNetId,
|
|
4279
|
+
targetAdsPort: targetOpts.adsPort,
|
|
4280
|
+
payload: fbData
|
|
4281
|
+
};
|
|
4101
4282
|
try {
|
|
4102
|
-
const res = await this.
|
|
4103
|
-
|
|
4104
|
-
targetAmsNetId: targetOpts.amsNetId,
|
|
4105
|
-
targetAdsPort: targetOpts.adsPort,
|
|
4106
|
-
payload: data
|
|
4107
|
-
});
|
|
4283
|
+
const { response: res, fallbackUsed } = await this.sendAdsCommandWithFallback(command, fallback);
|
|
4284
|
+
this.debug(`readPlcUploadInfo(): Upload info read - fallback was needed: ${fallbackUsed}`);
|
|
4108
4285
|
const uploadInfo = {};
|
|
4109
4286
|
let pos = 0;
|
|
4110
4287
|
const response = res.ads.payload;
|
|
4288
|
+
//If we are here, either the command or fallback was successful
|
|
4289
|
+
//Detect version from byte count
|
|
4290
|
+
if (response.byteLength === 8) {
|
|
4291
|
+
uploadInfo.version = 1;
|
|
4292
|
+
}
|
|
4293
|
+
else if (response.byteLength === 24) {
|
|
4294
|
+
uploadInfo.version = 2;
|
|
4295
|
+
}
|
|
4296
|
+
else if (response.byteLength === 64) {
|
|
4297
|
+
uploadInfo.version = 3;
|
|
4298
|
+
}
|
|
4299
|
+
else {
|
|
4300
|
+
//Shouldn't happen as we request max 64 bytes
|
|
4301
|
+
throw new Error(`Upload info version is unknown (received ${response.byteLength} bytes) - create a GitHub issue`);
|
|
4302
|
+
}
|
|
4111
4303
|
//0..3 Symbol count
|
|
4112
4304
|
uploadInfo.symbolCount = response.readUInt32LE(pos);
|
|
4113
4305
|
pos += 4;
|
|
4114
4306
|
//4..7 Symbol length
|
|
4115
4307
|
uploadInfo.symbolLength = response.readUInt32LE(pos);
|
|
4116
4308
|
pos += 4;
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4309
|
+
if (uploadInfo.version >= 2) {
|
|
4310
|
+
//8..11 Data type count
|
|
4311
|
+
uploadInfo.dataTypeCount = response.readUInt32LE(pos);
|
|
4312
|
+
pos += 4;
|
|
4313
|
+
//12..15 Data type length
|
|
4314
|
+
uploadInfo.dataTypeLength = response.readUInt32LE(pos);
|
|
4315
|
+
pos += 4;
|
|
4316
|
+
//16..19 Max. allowed dynamic symbol count
|
|
4317
|
+
uploadInfo.maxDynamicSymbolCount = response.readUInt32LE(pos);
|
|
4318
|
+
pos += 4;
|
|
4319
|
+
//20..23 Number of dynamic symbols used (version >= 2)
|
|
4320
|
+
uploadInfo.dynamicSymbolCount = response.readUInt32LE(pos);
|
|
4321
|
+
pos += 4;
|
|
4322
|
+
if (uploadInfo.version >= 3) {
|
|
4323
|
+
//24..27 Invalid dynamic symbol count
|
|
4324
|
+
uploadInfo.invalidDynamicSymbolCount = response.readUInt32LE(pos);
|
|
4325
|
+
pos += 4;
|
|
4326
|
+
//28..31 Encoding code page used for STRING encoding
|
|
4327
|
+
uploadInfo.encodingCodePage = response.readUInt32LE(pos);
|
|
4328
|
+
pos += 4;
|
|
4329
|
+
//32..35 Upload info flags
|
|
4330
|
+
uploadInfo.flags = response.readUInt32LE(pos);
|
|
4331
|
+
uploadInfo.flagsStr = ADS.ADS_UPLOAD_INFO_FLAGS.toStringArray(uploadInfo.flags);
|
|
4332
|
+
pos += 4;
|
|
4333
|
+
//36..63 Reserved
|
|
4334
|
+
uploadInfo.reserved = response.subarray(pos);
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4337
|
+
//Target has UTF-8 encoded ADS symbols if encodingCodePage is 65001 (or if forced from settings)
|
|
4338
|
+
this.metaData.adsSymbolsUseUtf8 = uploadInfo.encodingCodePage === 65001 || this.settings.forceUtf8ForAdsSymbols;
|
|
4129
4339
|
if (!targetOpts.adsPort && !targetOpts.amsNetId) {
|
|
4130
4340
|
//Target is not overridden -> save to metadata
|
|
4131
4341
|
this.metaData.plcUploadInfo = uploadInfo;
|
|
@@ -4314,7 +4524,7 @@ class Client extends events_1.default {
|
|
|
4314
4524
|
data.writeUInt32LE(0, pos);
|
|
4315
4525
|
pos += 4;
|
|
4316
4526
|
//8..11 Read data length
|
|
4317
|
-
data.writeUInt32LE(uploadInfo.dataTypeLength, pos);
|
|
4527
|
+
data.writeUInt32LE(uploadInfo.dataTypeLength ?? 0xFFFFFFFF, pos); //Using 0xFFFFFFFF is upload info is version 1 (= we don't know the length)
|
|
4318
4528
|
pos += 4;
|
|
4319
4529
|
try {
|
|
4320
4530
|
const res = await this.sendAdsCommand({
|
|
@@ -4826,11 +5036,11 @@ class Client extends events_1.default {
|
|
|
4826
5036
|
*
|
|
4827
5037
|
* //Writing a POINTER value (Note the dereference operator ^)
|
|
4828
5038
|
* const ptrValue = ...
|
|
4829
|
-
* await client.writeRawByPath('
|
|
5039
|
+
* await client.writeRawByPath('GVL_Write.ComplexTypes.POINTER_^', ptrValue);
|
|
4830
5040
|
*
|
|
4831
5041
|
* //Writing a REFERENCE value
|
|
4832
5042
|
* const refValue = ...
|
|
4833
|
-
* await client.
|
|
5043
|
+
* await client.writeRawByPath('GVL_Write.ComplexTypes.REFERENCE_');
|
|
4834
5044
|
*
|
|
4835
5045
|
* } catch (err) {
|
|
4836
5046
|
* console.log("Error:", err);
|
|
@@ -5133,11 +5343,11 @@ class Client extends events_1.default {
|
|
|
5133
5343
|
this.debug(`readValue(): Reading raw value from ${path} failed: %o`, err);
|
|
5134
5344
|
throw new client_error_1.default(`readValue(): Reading raw value from ${path} failed`, err);
|
|
5135
5345
|
}
|
|
5136
|
-
//Converting byte data to javascript object
|
|
5346
|
+
//Converting byte data to javascript object
|
|
5137
5347
|
let value;
|
|
5138
5348
|
try {
|
|
5139
5349
|
this.debugD(`readValue(): Converting raw value to object for ${path}`);
|
|
5140
|
-
value = await this.convertBufferToObject(rawValue, dataType);
|
|
5350
|
+
value = await this.convertBufferToObject(rawValue, dataType, symbol.attributes);
|
|
5141
5351
|
}
|
|
5142
5352
|
catch (err) {
|
|
5143
5353
|
this.debug(`readValue(): Converting raw value to object for ${path} failed: %o`, err);
|
|
@@ -5204,11 +5414,9 @@ class Client extends events_1.default {
|
|
|
5204
5414
|
* @example
|
|
5205
5415
|
* ```js
|
|
5206
5416
|
* try {
|
|
5207
|
-
* const
|
|
5208
|
-
*
|
|
5209
|
-
* };
|
|
5417
|
+
* const res = await client.writeValue('GVL_Write.StandardTypes.INT_', 32767);
|
|
5418
|
+
* console.log('Value written:', res.value);
|
|
5210
5419
|
*
|
|
5211
|
-
* const res = await client.writeValue('GVL_Read.StandardTypes.INT_', value);
|
|
5212
5420
|
* } catch (err) {
|
|
5213
5421
|
* console.log("Error:", err);
|
|
5214
5422
|
* }
|
|
@@ -5251,7 +5459,7 @@ class Client extends events_1.default {
|
|
|
5251
5459
|
//Creating raw data from object
|
|
5252
5460
|
let rawValue;
|
|
5253
5461
|
try {
|
|
5254
|
-
let res = this.convertObjectToBuffer(value, dataType);
|
|
5462
|
+
let res = this.convertObjectToBuffer(value, dataType, symbol.attributes);
|
|
5255
5463
|
if (res.missingProperty && autoFill) {
|
|
5256
5464
|
//Some fields are missing and autoFill is used -> try to auto fill missing values
|
|
5257
5465
|
this.debug(`writeValue(): Autofilling missing fields with active values`);
|
|
@@ -5260,7 +5468,7 @@ class Client extends events_1.default {
|
|
|
5260
5468
|
this.debugD(`writeValue(): Merging objects (adding missing fields)`);
|
|
5261
5469
|
value = this.deepMergeObjects(false, valueNow.value, value);
|
|
5262
5470
|
//Try conversion again - should work now
|
|
5263
|
-
res = this.convertObjectToBuffer(value, dataType);
|
|
5471
|
+
res = this.convertObjectToBuffer(value, dataType, symbol.attributes);
|
|
5264
5472
|
if (res.missingProperty) {
|
|
5265
5473
|
//This shouldn't really happen
|
|
5266
5474
|
//However, if it happened, the merge isn't working as it should
|
|
@@ -5356,12 +5564,13 @@ class Client extends events_1.default {
|
|
|
5356
5564
|
* ```
|
|
5357
5565
|
*
|
|
5358
5566
|
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or data type object (acquired using {@link getDataType}())
|
|
5567
|
+
* @param attributes Additional attributes of the symbol or data type used for conversion
|
|
5359
5568
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5360
5569
|
*
|
|
5361
5570
|
* @template T Typescript data type of the PLC data, for example `getDefaultPlcObject<number>(...)` or `getDefaultPlcObject<ST_TypedStruct>(...)`
|
|
5362
5571
|
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5363
5572
|
*/
|
|
5364
|
-
async getDefaultPlcObject(dataType, targetOpts = {}) {
|
|
5573
|
+
async getDefaultPlcObject(dataType, attributes, targetOpts = {}) {
|
|
5365
5574
|
if (!this.connection.connected) {
|
|
5366
5575
|
throw new client_error_1.default(`getDefaultPlcObject(): Client is not connected. Use connect() to connect to the target first.`);
|
|
5367
5576
|
}
|
|
@@ -5378,7 +5587,7 @@ class Client extends events_1.default {
|
|
|
5378
5587
|
}
|
|
5379
5588
|
}
|
|
5380
5589
|
//Converting raw byte data to Javascript object
|
|
5381
|
-
let value = await this.convertFromRaw(Buffer.alloc(dataType.size), dataType, targetOpts);
|
|
5590
|
+
let value = await this.convertFromRaw(Buffer.alloc(dataType.size), dataType, attributes, targetOpts);
|
|
5382
5591
|
this.debug(`getDefaultPlcObject(): Empty default object created for ${dataType.type}`);
|
|
5383
5592
|
return value;
|
|
5384
5593
|
}
|
|
@@ -5401,12 +5610,13 @@ class Client extends events_1.default {
|
|
|
5401
5610
|
*
|
|
5402
5611
|
* @param data Raw data (acquired for example using {@link readRaw}())
|
|
5403
5612
|
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or data type object (acquired using {@link getDataType}())
|
|
5613
|
+
* @param attributes Additional attributes of the symbol or data type used for conversion
|
|
5404
5614
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5405
5615
|
*
|
|
5406
5616
|
* @template T Typescript data type of the PLC data, for example `convertFromRaw<number>(...)` or `convertFromRaw<ST_TypedStruct>(...)`
|
|
5407
5617
|
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5408
5618
|
*/
|
|
5409
|
-
async convertFromRaw(data, dataType, targetOpts = {}) {
|
|
5619
|
+
async convertFromRaw(data, dataType, attributes, targetOpts = {}) {
|
|
5410
5620
|
if (!this.connection.connected) {
|
|
5411
5621
|
throw new client_error_1.default(`convertFromRaw(): Client is not connected. Use connect() to connect to the target first.`);
|
|
5412
5622
|
}
|
|
@@ -5426,7 +5636,7 @@ class Client extends events_1.default {
|
|
|
5426
5636
|
let value;
|
|
5427
5637
|
try {
|
|
5428
5638
|
this.debugD(`convertFromRaw(): Converting raw value to object for ${dataType.type}`);
|
|
5429
|
-
value = await this.convertBufferToObject(data, dataType);
|
|
5639
|
+
value = await this.convertBufferToObject(data, dataType, attributes);
|
|
5430
5640
|
}
|
|
5431
5641
|
catch (err) {
|
|
5432
5642
|
this.debug(`convertFromRaw(): Converting raw value to object for ${dataType.type} failed: %o`, err);
|
|
@@ -5454,11 +5664,12 @@ class Client extends events_1.default {
|
|
|
5454
5664
|
* @param value Value to convert
|
|
5455
5665
|
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or data type object (acquired using {@link getDataType}())
|
|
5456
5666
|
* @param autoFill autoFill If set and the data type is a container (`STRUCT`, `FUNCTION_BLOCK` etc.), missing properties are automatically set to default values (`0` or empty string).
|
|
5667
|
+
* @param attributes Additional attributes of the symbol or data type used for conversion
|
|
5457
5668
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5458
5669
|
*
|
|
5459
5670
|
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5460
5671
|
*/
|
|
5461
|
-
async convertToRaw(value, dataType, autoFill = false, targetOpts = {}) {
|
|
5672
|
+
async convertToRaw(value, dataType, autoFill = false, attributes, targetOpts = {}) {
|
|
5462
5673
|
if (!this.connection.connected) {
|
|
5463
5674
|
throw new client_error_1.default(`convertToRaw(): Client is not connected. Use connect() to connect to the target first.`);
|
|
5464
5675
|
}
|
|
@@ -5477,16 +5688,16 @@ class Client extends events_1.default {
|
|
|
5477
5688
|
//Converting Javascript object to raw byte data
|
|
5478
5689
|
let rawValue;
|
|
5479
5690
|
try {
|
|
5480
|
-
let res = this.convertObjectToBuffer(value, dataType);
|
|
5691
|
+
let res = this.convertObjectToBuffer(value, dataType, attributes);
|
|
5481
5692
|
if (res.missingProperty && autoFill) {
|
|
5482
5693
|
//Some fields are missing and autoFill is used -> try to auto fill missing values
|
|
5483
5694
|
this.debug(`convertToRaw(): Autofilling missing fields with default/zero values`);
|
|
5484
5695
|
this.debugD(`convertToRaw(): Creating default object`);
|
|
5485
|
-
const defaultValue = await this.getDefaultPlcObject(dataType, targetOpts);
|
|
5696
|
+
const defaultValue = await this.getDefaultPlcObject(dataType, attributes, targetOpts);
|
|
5486
5697
|
this.debugD(`convertToRaw(): Merging objects (adding missing fields)`);
|
|
5487
5698
|
value = this.deepMergeObjects(false, defaultValue, value);
|
|
5488
5699
|
//Try conversion again - should work now
|
|
5489
|
-
res = this.convertObjectToBuffer(value, dataType);
|
|
5700
|
+
res = this.convertObjectToBuffer(value, dataType, attributes);
|
|
5490
5701
|
if (res.missingProperty) {
|
|
5491
5702
|
//This shouldn't really happen
|
|
5492
5703
|
//However, if it happened, the merge isn't working as it should
|
|
@@ -5592,7 +5803,7 @@ class Client extends events_1.default {
|
|
|
5592
5803
|
let dataTypeLength = response.readUInt16LE(pos);
|
|
5593
5804
|
pos += 2;
|
|
5594
5805
|
//10..n Data type
|
|
5595
|
-
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1));
|
|
5806
|
+
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
5596
5807
|
this.debug(`createVariableHandle(): Variable handle created to ${path}`);
|
|
5597
5808
|
return result;
|
|
5598
5809
|
}
|
|
@@ -5730,7 +5941,7 @@ class Client extends events_1.default {
|
|
|
5730
5941
|
let dataTypeLength = response.readUInt16LE(pos);
|
|
5731
5942
|
pos += 2;
|
|
5732
5943
|
//10..n Data type
|
|
5733
|
-
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1));
|
|
5944
|
+
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
5734
5945
|
pos += dataTypeLength + 1;
|
|
5735
5946
|
results[i].handle = result;
|
|
5736
5947
|
}
|
|
@@ -6123,8 +6334,8 @@ class Client extends events_1.default {
|
|
|
6123
6334
|
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
6335
|
}
|
|
6125
6336
|
//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);
|
|
6337
|
+
const inputs = rpcMethod.parameters.filter(p => (p.flags & ADS.ADS_RCP_METHOD_PARAM_FLAGS.In) === ADS.ADS_RCP_METHOD_PARAM_FLAGS.In);
|
|
6338
|
+
const outputs = rpcMethod.parameters.filter(p => (p.flags & ADS.ADS_RCP_METHOD_PARAM_FLAGS.Out) === ADS.ADS_RCP_METHOD_PARAM_FLAGS.Out);
|
|
6128
6339
|
//Creating data buffer for inputs
|
|
6129
6340
|
let inputsData = Buffer.alloc(0);
|
|
6130
6341
|
for (const input of inputs) {
|