ads-client 2.0.0-beta.3 → 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 +35 -0
- package/README.md +1536 -303
- package/dist/ads-client.d.ts +350 -76
- package/dist/ads-client.js +585 -147
- 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 +3 -4
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 {
|
|
@@ -659,7 +663,7 @@ class Client extends events_1.default {
|
|
|
659
663
|
};
|
|
660
664
|
this.socket?.once('error', errorHandler);
|
|
661
665
|
try {
|
|
662
|
-
|
|
666
|
+
this.socketWrite(packet);
|
|
663
667
|
}
|
|
664
668
|
catch (err) {
|
|
665
669
|
this.socket?.off('error', errorHandler);
|
|
@@ -715,13 +719,41 @@ class Client extends events_1.default {
|
|
|
715
719
|
//Sometimes close event is not received, so resolve already here
|
|
716
720
|
this.socket.once('end', () => {
|
|
717
721
|
this.debugD(`unregisterAdsPort(): Socket connection ended, connection closed.`);
|
|
722
|
+
clearTimeout(this.portRegisterTimeoutTimer);
|
|
723
|
+
this.amsTcpCallback = undefined;
|
|
718
724
|
this.socket?.destroy();
|
|
719
725
|
resolve();
|
|
720
726
|
});
|
|
727
|
+
this.amsTcpCallback = (res) => {
|
|
728
|
+
this.amsTcpCallback = undefined;
|
|
729
|
+
clearTimeout(this.portRegisterTimeoutTimer);
|
|
730
|
+
if (res.amsTcp.command === ADS.AMS_HEADER_FLAG.AMS_TCP_PORT_CLOSE) {
|
|
731
|
+
this.debug(`unregisterAdsPort(): ADS port unregistered`);
|
|
732
|
+
this.socket?.destroy();
|
|
733
|
+
resolve();
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
//Timeout (if no answer from router)
|
|
737
|
+
this.portRegisterTimeoutTimer = setTimeout(() => {
|
|
738
|
+
//Callback is no longer needed, delete it
|
|
739
|
+
this.amsTcpCallback = undefined;
|
|
740
|
+
//Create a custom "ads error" so that the info is passed onwards
|
|
741
|
+
const adsError = {
|
|
742
|
+
ads: {
|
|
743
|
+
error: true,
|
|
744
|
+
errorCode: -1,
|
|
745
|
+
errorStr: `Timeout - no response in ${this.settings.timeoutDelay} ms`
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
this.debug(`unregisterAdsPort(): Failed to unregister ADS port: Timeout - no response in ${this.settings.timeoutDelay} ms`);
|
|
749
|
+
return reject(new client_error_1.default(`unregisterAdsPort(): Timeout - no response in ${this.settings.timeoutDelay} ms`, adsError));
|
|
750
|
+
}, this.settings.timeoutDelay);
|
|
721
751
|
try {
|
|
722
|
-
|
|
752
|
+
this.socketWrite(buffer);
|
|
723
753
|
}
|
|
724
754
|
catch (err) {
|
|
755
|
+
this.amsTcpCallback = undefined;
|
|
756
|
+
clearTimeout(this.portRegisterTimeoutTimer);
|
|
725
757
|
reject(err);
|
|
726
758
|
}
|
|
727
759
|
});
|
|
@@ -848,29 +880,38 @@ class Client extends events_1.default {
|
|
|
848
880
|
* @param subscription The subscription object (unused here)
|
|
849
881
|
*/
|
|
850
882
|
onPlcRuntimeStateChanged(data, subscription) {
|
|
851
|
-
const
|
|
883
|
+
const state = {};
|
|
852
884
|
let pos = 0;
|
|
853
885
|
//0..1 ADS state
|
|
854
|
-
|
|
855
|
-
|
|
886
|
+
state.adsState = data.value.readUInt16LE(pos);
|
|
887
|
+
state.adsStateStr = ADS.ADS_STATE.toString(state.adsState);
|
|
856
888
|
pos += 2;
|
|
857
889
|
//2..3 Device state
|
|
858
|
-
|
|
890
|
+
state.deviceState = data.value.readUInt16LE(pos);
|
|
859
891
|
pos += 2;
|
|
860
|
-
|
|
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) {
|
|
861
902
|
let stateChanged = false;
|
|
862
|
-
if (this.metaData.plcRuntimeState === undefined || this.metaData.plcRuntimeState.adsState !==
|
|
863
|
-
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}`);
|
|
864
905
|
stateChanged = true;
|
|
865
906
|
}
|
|
866
|
-
if (this.metaData.plcRuntimeState === undefined || this.metaData.plcRuntimeState.deviceState !==
|
|
867
|
-
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}`);
|
|
868
909
|
stateChanged = true;
|
|
869
910
|
}
|
|
870
911
|
let oldState = this.metaData.plcRuntimeState !== undefined
|
|
871
912
|
? { ...this.metaData.plcRuntimeState }
|
|
872
913
|
: undefined;
|
|
873
|
-
this.metaData.plcRuntimeState =
|
|
914
|
+
this.metaData.plcRuntimeState = state;
|
|
874
915
|
if (stateChanged) {
|
|
875
916
|
this.emit('plcRuntimeStateChange', this.metaData.plcRuntimeState, oldState);
|
|
876
917
|
}
|
|
@@ -1292,8 +1333,7 @@ class Client extends events_1.default {
|
|
|
1292
1333
|
ads.payload.versionBuild = data.readUInt16LE(pos);
|
|
1293
1334
|
pos += 2;
|
|
1294
1335
|
//8..24 Device name
|
|
1295
|
-
ads.payload.deviceName = ADS.decodePlcStringBuffer(data.subarray(pos, pos + 16));
|
|
1296
|
-
;
|
|
1336
|
+
ads.payload.deviceName = ADS.decodePlcStringBuffer(data.subarray(pos, pos + 16), this.metaData.adsSymbolsUseUtf8);
|
|
1297
1337
|
}
|
|
1298
1338
|
break;
|
|
1299
1339
|
case ADS.ADS_COMMAND.ReadState:
|
|
@@ -1379,7 +1419,12 @@ class Client extends events_1.default {
|
|
|
1379
1419
|
//AMS port unregister
|
|
1380
1420
|
case ADS.AMS_HEADER_FLAG.AMS_TCP_PORT_CLOSE:
|
|
1381
1421
|
packet.amsTcp.commandStr = 'Port unregister';
|
|
1382
|
-
|
|
1422
|
+
if (this.amsTcpCallback) {
|
|
1423
|
+
this.amsTcpCallback(packet);
|
|
1424
|
+
}
|
|
1425
|
+
else {
|
|
1426
|
+
this.debug(`onAmsTcpPacketReceived(): Port unregister response received but no callback was assigned (${packet.amsTcp.commandStr})`);
|
|
1427
|
+
}
|
|
1383
1428
|
break;
|
|
1384
1429
|
//AMS port register
|
|
1385
1430
|
case ADS.AMS_HEADER_FLAG.AMS_TCP_PORT_CONNECT:
|
|
@@ -1842,13 +1887,13 @@ class Client extends events_1.default {
|
|
|
1842
1887
|
const commentLength = data.readUInt16LE(pos);
|
|
1843
1888
|
pos += 2;
|
|
1844
1889
|
//30.... Symbol name
|
|
1845
|
-
symbol.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
1890
|
+
symbol.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1846
1891
|
pos += nameLength + 1;
|
|
1847
1892
|
//.. Symbol type
|
|
1848
|
-
symbol.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1));
|
|
1893
|
+
symbol.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1849
1894
|
pos += typeLength + 1;
|
|
1850
1895
|
//.. Symbol comment
|
|
1851
|
-
symbol.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
1896
|
+
symbol.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1852
1897
|
pos += commentLength + 1;
|
|
1853
1898
|
//Array information
|
|
1854
1899
|
symbol.arrayInfo = [];
|
|
@@ -1880,10 +1925,10 @@ class Client extends events_1.default {
|
|
|
1880
1925
|
const valueLength = data.readUInt8(pos);
|
|
1881
1926
|
pos += 1;
|
|
1882
1927
|
//Name
|
|
1883
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
1928
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1884
1929
|
pos += nameLength + 1;
|
|
1885
1930
|
//Value
|
|
1886
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
1931
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1887
1932
|
pos += valueLength + 1;
|
|
1888
1933
|
symbol.attributes.push(attr);
|
|
1889
1934
|
}
|
|
@@ -1951,13 +1996,13 @@ class Client extends events_1.default {
|
|
|
1951
1996
|
const subItemCount = data.readUInt16LE(pos);
|
|
1952
1997
|
pos += 2;
|
|
1953
1998
|
//38.. Data type name
|
|
1954
|
-
dataType.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
1999
|
+
dataType.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1955
2000
|
pos += nameLength + 1;
|
|
1956
2001
|
//.. Data type type
|
|
1957
|
-
dataType.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1));
|
|
2002
|
+
dataType.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1958
2003
|
pos += typeLength + 1;
|
|
1959
2004
|
//.. Data type comment
|
|
1960
|
-
dataType.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
2005
|
+
dataType.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
1961
2006
|
pos += commentLength + 1;
|
|
1962
2007
|
//Array information
|
|
1963
2008
|
dataType.arrayInfos = [];
|
|
@@ -2041,13 +2086,13 @@ class Client extends events_1.default {
|
|
|
2041
2086
|
const parameterCount = data.readUInt16LE(pos);
|
|
2042
2087
|
pos += 2;
|
|
2043
2088
|
//56.. Name
|
|
2044
|
-
method.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2089
|
+
method.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2045
2090
|
pos += nameLength + 1;
|
|
2046
2091
|
//.. Return data type
|
|
2047
|
-
method.retunDataType = ADS.decodePlcStringBuffer(data.subarray(pos, pos + returnTypeLength + 1));
|
|
2092
|
+
method.retunDataType = ADS.decodePlcStringBuffer(data.subarray(pos, pos + returnTypeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2048
2093
|
pos += returnTypeLength + 1;
|
|
2049
2094
|
//.. Comment
|
|
2050
|
-
method.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
2095
|
+
method.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2051
2096
|
pos += commentLength + 1;
|
|
2052
2097
|
//Parameters
|
|
2053
2098
|
method.parameters = [];
|
|
@@ -2090,13 +2135,13 @@ class Client extends events_1.default {
|
|
|
2090
2135
|
const commentLength = data.readUInt16LE(pos);
|
|
2091
2136
|
pos += 2;
|
|
2092
2137
|
//38.. Data type name
|
|
2093
|
-
param.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2138
|
+
param.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2094
2139
|
pos += nameLength + 1;
|
|
2095
2140
|
//.. Data type type
|
|
2096
|
-
param.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1));
|
|
2141
|
+
param.type = ADS.decodePlcStringBuffer(data.subarray(pos, pos + typeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2097
2142
|
pos += typeLength + 1;
|
|
2098
2143
|
//.. Data type comment
|
|
2099
|
-
param.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1));
|
|
2144
|
+
param.comment = ADS.decodePlcStringBuffer(data.subarray(pos, pos + commentLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2100
2145
|
pos += commentLength + 1;
|
|
2101
2146
|
//Attributes
|
|
2102
2147
|
param.attributes = [];
|
|
@@ -2113,18 +2158,19 @@ class Client extends events_1.default {
|
|
|
2113
2158
|
const valueLength = data.readUInt8(pos);
|
|
2114
2159
|
pos += 1;
|
|
2115
2160
|
//Name
|
|
2116
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2161
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2117
2162
|
pos += nameLength + 1;
|
|
2118
2163
|
//Value
|
|
2119
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
2164
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2120
2165
|
pos += valueLength + 1;
|
|
2121
|
-
|
|
2166
|
+
param.attributes.push(attr);
|
|
2122
2167
|
}
|
|
2123
2168
|
}
|
|
2124
2169
|
if (pos - beginPosition > entryLength) {
|
|
2125
2170
|
//There is some additional data left
|
|
2126
2171
|
param.reserved2 = data.subarray(pos);
|
|
2127
2172
|
}
|
|
2173
|
+
pos = beginPosition + entryLength;
|
|
2128
2174
|
method.parameters.push(param);
|
|
2129
2175
|
}
|
|
2130
2176
|
//Attributes
|
|
@@ -2142,10 +2188,10 @@ class Client extends events_1.default {
|
|
|
2142
2188
|
const valueLength = data.readUInt8(pos);
|
|
2143
2189
|
pos += 1;
|
|
2144
2190
|
//Name
|
|
2145
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2191
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2146
2192
|
pos += nameLength + 1;
|
|
2147
2193
|
//Value
|
|
2148
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
2194
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2149
2195
|
pos += valueLength + 1;
|
|
2150
2196
|
method.attributes.push(attr);
|
|
2151
2197
|
}
|
|
@@ -2170,18 +2216,19 @@ class Client extends events_1.default {
|
|
|
2170
2216
|
const valueLength = data.readUInt8(pos);
|
|
2171
2217
|
pos += 1;
|
|
2172
2218
|
//Name
|
|
2173
|
-
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2219
|
+
attr.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2174
2220
|
pos += nameLength + 1;
|
|
2175
2221
|
//Value
|
|
2176
|
-
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1));
|
|
2222
|
+
attr.value = ADS.decodePlcStringBuffer(data.subarray(pos, pos + valueLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2177
2223
|
pos += valueLength + 1;
|
|
2178
2224
|
dataType.attributes.push(attr);
|
|
2179
2225
|
}
|
|
2180
2226
|
}
|
|
2181
2227
|
//If flag EnumInfos set
|
|
2182
2228
|
dataType.enumInfos = [];
|
|
2229
|
+
let enumInfoCount = 0;
|
|
2183
2230
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.EnumInfos) === ADS.ADS_DATA_TYPE_FLAGS.EnumInfos) {
|
|
2184
|
-
|
|
2231
|
+
enumInfoCount = data.readUInt16LE(pos);
|
|
2185
2232
|
pos += 2;
|
|
2186
2233
|
for (let i = 0; i < enumInfoCount; i++) {
|
|
2187
2234
|
let enumInfo = {};
|
|
@@ -2189,7 +2236,7 @@ class Client extends events_1.default {
|
|
|
2189
2236
|
const nameLength = data.readUInt8(pos);
|
|
2190
2237
|
pos += 1;
|
|
2191
2238
|
//Enumeration name
|
|
2192
|
-
enumInfo.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1));
|
|
2239
|
+
enumInfo.name = ADS.decodePlcStringBuffer(data.subarray(pos, pos + nameLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
2193
2240
|
pos += nameLength + 1;
|
|
2194
2241
|
//Enumeration value
|
|
2195
2242
|
enumInfo.value = data.subarray(pos, pos + dataType.size);
|
|
@@ -2216,9 +2263,43 @@ class Client extends events_1.default {
|
|
|
2216
2263
|
}
|
|
2217
2264
|
//If flag ExtendedEnumInfos set
|
|
2218
2265
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.ExtendedEnumInfos) === ADS.ADS_DATA_TYPE_FLAGS.ExtendedEnumInfos) {
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
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
|
+
}
|
|
2222
2303
|
}
|
|
2223
2304
|
//If flag SoftwareProtectionLevels set
|
|
2224
2305
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.SoftwareProtectionLevels) === ADS.ADS_DATA_TYPE_FLAGS.SoftwareProtectionLevels) {
|
|
@@ -2325,6 +2406,7 @@ class Client extends events_1.default {
|
|
|
2325
2406
|
if (err.adsError && err.adsError?.errorCode === 1808) {
|
|
2326
2407
|
//Type wasn't found.
|
|
2327
2408
|
//Might be TwinCAT 2 system that doesn't provide pseudo or base data types by ADS --> check if we know the type ourselves
|
|
2409
|
+
const originalName = name;
|
|
2328
2410
|
if (ADS.BASE_DATA_TYPES.isPseudoType(name)) {
|
|
2329
2411
|
//This converts e.g. PVOID to a primitive type
|
|
2330
2412
|
if (knownSize === undefined) {
|
|
@@ -2358,6 +2440,11 @@ class Client extends events_1.default {
|
|
|
2358
2440
|
extendedFlags: 0,
|
|
2359
2441
|
reserved: Buffer.alloc(0)
|
|
2360
2442
|
};
|
|
2443
|
+
//Adding to cache, even though it's not read from the target
|
|
2444
|
+
//With TwinCAT 2, this prevents trying to read base types again and again from the target (as there aren't any)
|
|
2445
|
+
if (!this.settings.disableCaching && !targetOpts.adsPort && !targetOpts.amsNetId) {
|
|
2446
|
+
this.metaData.plcDataTypes[originalName.toLowerCase()] = dataType;
|
|
2447
|
+
}
|
|
2361
2448
|
}
|
|
2362
2449
|
else {
|
|
2363
2450
|
//Type is unknown - we can't do anything
|
|
@@ -2401,7 +2488,7 @@ class Client extends events_1.default {
|
|
|
2401
2488
|
}
|
|
2402
2489
|
}
|
|
2403
2490
|
else if ((((dataType.type === '' && !ADS.BASE_DATA_TYPES.isPseudoType(dataType.name)) || ADS.BASE_DATA_TYPES.isKnownType(dataType.name))
|
|
2404
|
-
&& dataType.flagsStr.includes('DataType')
|
|
2491
|
+
&& (dataType.flagsStr.includes('DataType') || ADS.BASE_DATA_TYPES.isKnownType(dataType.name)) //isKnownType() call is for TwinCAT 2 support, as base types (like INT16) do not have DataType flag (but it's the final base type)
|
|
2405
2492
|
&& !dataType.flagsStr.includes('EnumInfos')
|
|
2406
2493
|
&& dataType.arrayDimension === 0)) {
|
|
2407
2494
|
//This is final form (no need to go deeper)
|
|
@@ -2409,8 +2496,10 @@ class Client extends events_1.default {
|
|
|
2409
2496
|
}
|
|
2410
2497
|
else if (dataType.arrayDimension > 0) {
|
|
2411
2498
|
//Data type is an array - get array subtype
|
|
2499
|
+
//TwinCAT 2 support - calculate size if it's array of pointer etc.
|
|
2500
|
+
const size = dataType.size / dataType.arrayInfos.reduce((total, dimension) => total * dimension.length, 1);
|
|
2412
2501
|
builtType = {
|
|
2413
|
-
...(await this.buildDataType(dataType.type, targetOpts, false))
|
|
2502
|
+
...(await this.buildDataType(dataType.type, targetOpts, false, size))
|
|
2414
2503
|
};
|
|
2415
2504
|
builtType.arrayInfos = [...dataType.arrayInfos, ...builtType.arrayInfos];
|
|
2416
2505
|
}
|
|
@@ -2467,7 +2556,7 @@ class Client extends events_1.default {
|
|
|
2467
2556
|
*/
|
|
2468
2557
|
convertBufferToPrimitiveType(buffer, dataType) {
|
|
2469
2558
|
if (dataType.adsDataType === ADS.ADS_DATA_TYPES.ADST_STRING) {
|
|
2470
|
-
return ADS.decodePlcStringBuffer(buffer);
|
|
2559
|
+
return ADS.decodePlcStringBuffer(buffer); //TODO: If symbol has {attribute 'TcEncoding':='UTF-8'}
|
|
2471
2560
|
}
|
|
2472
2561
|
else if (dataType.adsDataType === ADS.ADS_DATA_TYPES.ADST_WSTRING) {
|
|
2473
2562
|
return ADS.decodePlcWstringBuffer(buffer);
|
|
@@ -2972,6 +3061,8 @@ class Client extends events_1.default {
|
|
|
2972
3061
|
* }
|
|
2973
3062
|
* ```
|
|
2974
3063
|
*
|
|
3064
|
+
* @param command The ADS command to send
|
|
3065
|
+
*
|
|
2975
3066
|
* @template T In Typescript, the type of the ADS response. If omitted, generic {@link AdsResponse} type is used.
|
|
2976
3067
|
*
|
|
2977
3068
|
* @throws Throws an error if sending the command fails or if target responds with an error
|
|
@@ -3057,6 +3148,82 @@ class Client extends events_1.default {
|
|
|
3057
3148
|
}
|
|
3058
3149
|
});
|
|
3059
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
|
+
}
|
|
3060
3227
|
/**
|
|
3061
3228
|
* Sends an ADS `WriteControl` command to the target.
|
|
3062
3229
|
*
|
|
@@ -3148,6 +3315,8 @@ class Client extends events_1.default {
|
|
|
3148
3315
|
const state = await this.readPlcRuntimeState(targetOpts);
|
|
3149
3316
|
await this.writeControl("Run", state.deviceState, undefined, targetOpts);
|
|
3150
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);
|
|
3151
3320
|
}
|
|
3152
3321
|
catch (err) {
|
|
3153
3322
|
this.debug(`startPlc(): Starting PLC runtime at ${this.targetToString(targetOpts)} failed: %o`, err);
|
|
@@ -3180,6 +3349,8 @@ class Client extends events_1.default {
|
|
|
3180
3349
|
const state = await this.readPlcRuntimeState(targetOpts);
|
|
3181
3350
|
await this.writeControl("Reset", state.deviceState, undefined, targetOpts);
|
|
3182
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);
|
|
3183
3354
|
}
|
|
3184
3355
|
catch (err) {
|
|
3185
3356
|
this.debug(`resetPlc(): Resetting PLC runtime at ${this.targetToString(targetOpts)} failed: %o`, err);
|
|
@@ -3212,6 +3383,8 @@ class Client extends events_1.default {
|
|
|
3212
3383
|
const state = await this.readPlcRuntimeState(targetOpts);
|
|
3213
3384
|
await this.writeControl("Stop", state.deviceState, undefined, targetOpts);
|
|
3214
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);
|
|
3215
3388
|
}
|
|
3216
3389
|
catch (err) {
|
|
3217
3390
|
this.debug(`stopPlc(): Stopping PLC runtime at ${this.targetToString(targetOpts)} failed: %o`, err);
|
|
@@ -3381,7 +3554,7 @@ class Client extends events_1.default {
|
|
|
3381
3554
|
* @example
|
|
3382
3555
|
* ```js
|
|
3383
3556
|
* try {
|
|
3384
|
-
* const symbol = await client.getSymbol('
|
|
3557
|
+
* const symbol = await client.getSymbol('GVL_Read.StandardTypes.INT_');
|
|
3385
3558
|
* } catch (err) {
|
|
3386
3559
|
* console.log("Error:", err);
|
|
3387
3560
|
* }
|
|
@@ -3559,8 +3732,8 @@ class Client extends events_1.default {
|
|
|
3559
3732
|
});
|
|
3560
3733
|
this.debug(`readPlcRuntimeState(): Runtime state read successfully. State is %o`, res.ads.payload);
|
|
3561
3734
|
if (!targetOpts.adsPort && !targetOpts.amsNetId) {
|
|
3562
|
-
//Target is not overridden ->
|
|
3563
|
-
this.
|
|
3735
|
+
//Target is not overridden -> handle the result (check for change and update metadata)
|
|
3736
|
+
this.handlePlcRuntimeStateChange(res.ads.payload);
|
|
3564
3737
|
}
|
|
3565
3738
|
return res.ads.payload;
|
|
3566
3739
|
}
|
|
@@ -3677,7 +3850,7 @@ class Client extends events_1.default {
|
|
|
3677
3850
|
* //Checks if value has changed every 100ms
|
|
3678
3851
|
* //Callback is called only when the value has changed
|
|
3679
3852
|
* await client.subscribeValue(
|
|
3680
|
-
* '
|
|
3853
|
+
* 'GVL_Subscription.NumericValue_10ms',
|
|
3681
3854
|
* (data, subscription) => {
|
|
3682
3855
|
* console.log(`Value of ${subscription.symbol.name} has changed: ${data.value}`);
|
|
3683
3856
|
* },
|
|
@@ -3825,7 +3998,7 @@ class Client extends events_1.default {
|
|
|
3825
3998
|
* //Checks if value has changed every 100ms
|
|
3826
3999
|
* //Callback is called only when the value has changed
|
|
3827
4000
|
* await client.subscribe({
|
|
3828
|
-
* target: '
|
|
4001
|
+
* target: 'GVL_Subscription.NumericValue_10ms',
|
|
3829
4002
|
* callback: (data, subscription) => {
|
|
3830
4003
|
* console.log(`Value of ${subscription.symbol.name} has changed: ${data.value}`);
|
|
3831
4004
|
* },
|
|
@@ -4053,36 +4226,90 @@ class Client extends events_1.default {
|
|
|
4053
4226
|
data.writeUInt32LE(0, pos);
|
|
4054
4227
|
pos += 4;
|
|
4055
4228
|
//8..11 Read data length
|
|
4056
|
-
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);
|
|
4057
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
|
|
4249
|
+
pos += 4;
|
|
4250
|
+
const fallback = {
|
|
4251
|
+
adsCommand: ADS.ADS_COMMAND.Read,
|
|
4252
|
+
targetAmsNetId: targetOpts.amsNetId,
|
|
4253
|
+
targetAdsPort: targetOpts.adsPort,
|
|
4254
|
+
payload: fbData
|
|
4255
|
+
};
|
|
4058
4256
|
try {
|
|
4059
|
-
const res = await this.
|
|
4060
|
-
|
|
4061
|
-
targetAmsNetId: targetOpts.amsNetId,
|
|
4062
|
-
targetAdsPort: targetOpts.adsPort,
|
|
4063
|
-
payload: data
|
|
4064
|
-
});
|
|
4257
|
+
const { response: res, fallbackUsed } = await this.sendAdsCommandWithFallback(command, fallback);
|
|
4258
|
+
this.debug(`readPlcUploadInfo(): Upload info read - fallback was needed: ${fallbackUsed}`);
|
|
4065
4259
|
const uploadInfo = {};
|
|
4066
4260
|
let pos = 0;
|
|
4067
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
|
+
}
|
|
4068
4277
|
//0..3 Symbol count
|
|
4069
4278
|
uploadInfo.symbolCount = response.readUInt32LE(pos);
|
|
4070
4279
|
pos += 4;
|
|
4071
4280
|
//4..7 Symbol length
|
|
4072
4281
|
uploadInfo.symbolLength = response.readUInt32LE(pos);
|
|
4073
4282
|
pos += 4;
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
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;
|
|
4086
4313
|
if (!targetOpts.adsPort && !targetOpts.amsNetId) {
|
|
4087
4314
|
//Target is not overridden -> save to metadata
|
|
4088
4315
|
this.metaData.plcUploadInfo = uploadInfo;
|
|
@@ -4271,7 +4498,7 @@ class Client extends events_1.default {
|
|
|
4271
4498
|
data.writeUInt32LE(0, pos);
|
|
4272
4499
|
pos += 4;
|
|
4273
4500
|
//8..11 Read data length
|
|
4274
|
-
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)
|
|
4275
4502
|
pos += 4;
|
|
4276
4503
|
try {
|
|
4277
4504
|
const res = await this.sendAdsCommand({
|
|
@@ -4773,7 +5000,6 @@ class Client extends events_1.default {
|
|
|
4773
5000
|
*
|
|
4774
5001
|
* NOTE: Unlike with {@link readRawByPath}(), this command uses multiple ADS requests for the operation.
|
|
4775
5002
|
*
|
|
4776
|
-
*
|
|
4777
5003
|
* @example
|
|
4778
5004
|
* ```js
|
|
4779
5005
|
* try {
|
|
@@ -4784,11 +5010,11 @@ class Client extends events_1.default {
|
|
|
4784
5010
|
*
|
|
4785
5011
|
* //Writing a POINTER value (Note the dereference operator ^)
|
|
4786
5012
|
* const ptrValue = ...
|
|
4787
|
-
* await client.writeRawByPath('
|
|
5013
|
+
* await client.writeRawByPath('GVL_Write.ComplexTypes.POINTER_^', ptrValue);
|
|
4788
5014
|
*
|
|
4789
5015
|
* //Writing a REFERENCE value
|
|
4790
5016
|
* const refValue = ...
|
|
4791
|
-
* await client.
|
|
5017
|
+
* await client.writeRawByPath('GVL_Write.ComplexTypes.REFERENCE_');
|
|
4792
5018
|
*
|
|
4793
5019
|
* } catch (err) {
|
|
4794
5020
|
* console.log("Error:", err);
|
|
@@ -5042,7 +5268,7 @@ class Client extends events_1.default {
|
|
|
5042
5268
|
* @example
|
|
5043
5269
|
* ```js
|
|
5044
5270
|
* try {
|
|
5045
|
-
* const res = await client.readValue('
|
|
5271
|
+
* const res = await client.readValue('GVL_Read.StandardTypes.INT_');
|
|
5046
5272
|
* console.log(res.value);
|
|
5047
5273
|
* } catch (err) {
|
|
5048
5274
|
* console.log("Error:", err);
|
|
@@ -5053,6 +5279,7 @@ class Client extends events_1.default {
|
|
|
5053
5279
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5054
5280
|
*
|
|
5055
5281
|
* @template T In Typescript, the data type of the value, for example `readValue<number>(...)` or `readValue<ST_TypedStruct>(...)` (default: `any`)
|
|
5282
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5056
5283
|
*/
|
|
5057
5284
|
async readValue(path, targetOpts = {}) {
|
|
5058
5285
|
if (!this.connection.connected) {
|
|
@@ -5073,7 +5300,8 @@ class Client extends events_1.default {
|
|
|
5073
5300
|
let dataType;
|
|
5074
5301
|
try {
|
|
5075
5302
|
this.debugD(`readValue(): Getting data type for ${path}`);
|
|
5076
|
-
|
|
5303
|
+
//Also passing size for TC2 pointer/pseudo type support
|
|
5304
|
+
dataType = await this.buildDataType(symbol.type, targetOpts, true, symbol.size);
|
|
5077
5305
|
}
|
|
5078
5306
|
catch (err) {
|
|
5079
5307
|
this.debug(`readValue(): Getting data type for ${path} failed: %o`, err);
|
|
@@ -5108,7 +5336,7 @@ class Client extends events_1.default {
|
|
|
5108
5336
|
};
|
|
5109
5337
|
}
|
|
5110
5338
|
/**
|
|
5111
|
-
* Reads variable's value from the target system by a symbol object
|
|
5339
|
+
* Reads variable's value from the target system by a symbol object (acquired using `getSymbol()`)
|
|
5112
5340
|
* and returns the value as a Javascript object.
|
|
5113
5341
|
*
|
|
5114
5342
|
* Returns variable's
|
|
@@ -5122,7 +5350,7 @@ class Client extends events_1.default {
|
|
|
5122
5350
|
* @example
|
|
5123
5351
|
* ```js
|
|
5124
5352
|
* try {
|
|
5125
|
-
* const symbol = await client.getSymbol('
|
|
5353
|
+
* const symbol = await client.getSymbol('GVL_Read.StandardTypes.INT_');
|
|
5126
5354
|
*
|
|
5127
5355
|
* const res = await client.readValueBySymbol(symbol);
|
|
5128
5356
|
* console.log(res.value);
|
|
@@ -5131,10 +5359,11 @@ class Client extends events_1.default {
|
|
|
5131
5359
|
* }
|
|
5132
5360
|
* ```
|
|
5133
5361
|
*
|
|
5134
|
-
* @param symbol Symbol object
|
|
5362
|
+
* @param symbol Symbol object
|
|
5135
5363
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5136
5364
|
*
|
|
5137
5365
|
* @template T In Typescript, the data type of the value, for example `readValueBySymbol<number>(...)` or `readValueBySymbol<ST_TypedStruct>(...)` (default: `any`)
|
|
5366
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5138
5367
|
*/
|
|
5139
5368
|
async readValueBySymbol(symbol, targetOpts = {}) {
|
|
5140
5369
|
if (!this.connection.connected) {
|
|
@@ -5159,11 +5388,9 @@ class Client extends events_1.default {
|
|
|
5159
5388
|
* @example
|
|
5160
5389
|
* ```js
|
|
5161
5390
|
* try {
|
|
5162
|
-
* const
|
|
5163
|
-
*
|
|
5164
|
-
* };
|
|
5391
|
+
* const res = await client.writeValue('GVL_Write.StandardTypes.INT_', 32767);
|
|
5392
|
+
* console.log('Value written:', res.value);
|
|
5165
5393
|
*
|
|
5166
|
-
* const res = await client.writeValue('GVL_Test.ExampleStruct', value);
|
|
5167
5394
|
* } catch (err) {
|
|
5168
5395
|
* console.log("Error:", err);
|
|
5169
5396
|
* }
|
|
@@ -5172,9 +5399,10 @@ class Client extends events_1.default {
|
|
|
5172
5399
|
* @param path Variable path in the PLC (such as `GVL_Test.ExampleStruct`)
|
|
5173
5400
|
* @param value Value to write
|
|
5174
5401
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5175
|
-
* @param autoFill If set and the data type is a container (`STRUCT`, `FUNCTION_BLOCK` etc.), missing properties are automatically set to
|
|
5402
|
+
* @param autoFill If set and the data type is a container (`STRUCT`, `FUNCTION_BLOCK` etc.), missing properties are automatically set to active values read from target (kept as-is).
|
|
5176
5403
|
*
|
|
5177
5404
|
* @template T In Typescript, the data type of the value, for example `writeValue<number>(...)` or `writeValue<ST_TypedStruct>(...)`
|
|
5405
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5178
5406
|
*/
|
|
5179
5407
|
async writeValue(path, value, autoFill = false, targetOpts = {}) {
|
|
5180
5408
|
if (!this.connection.connected) {
|
|
@@ -5195,7 +5423,8 @@ class Client extends events_1.default {
|
|
|
5195
5423
|
let dataType;
|
|
5196
5424
|
try {
|
|
5197
5425
|
this.debugD(`writeValue(): Getting data type for ${path}`);
|
|
5198
|
-
|
|
5426
|
+
//Also passing size for TC2 pointer/pseudo type support
|
|
5427
|
+
dataType = await this.buildDataType(symbol.type, targetOpts, true, symbol.size);
|
|
5199
5428
|
}
|
|
5200
5429
|
catch (err) {
|
|
5201
5430
|
this.debug(`writeValue(): Getting data type for ${path} failed: %o`, err);
|
|
@@ -5253,20 +5482,37 @@ class Client extends events_1.default {
|
|
|
5253
5482
|
};
|
|
5254
5483
|
}
|
|
5255
5484
|
/**
|
|
5256
|
-
*
|
|
5485
|
+
* Writes variable's value to the target system by a symbol object (acquired using `getSymbol()`).
|
|
5486
|
+
* Converts the value from a Javascript object to a raw value.
|
|
5257
5487
|
*
|
|
5258
|
-
*
|
|
5488
|
+
* Returns variable's
|
|
5489
|
+
* - converted value
|
|
5490
|
+
* - raw value
|
|
5491
|
+
* - data type
|
|
5492
|
+
* - symbol
|
|
5259
5493
|
*
|
|
5260
|
-
*
|
|
5494
|
+
* **NOTE:** Do not use `autoFill` for `UNION` types, it doesn't work correctly.
|
|
5495
|
+
*
|
|
5496
|
+
* **NOTE:** This requires that the target is a PLC runtime or has equivalent ADS protocol support.
|
|
5261
5497
|
*
|
|
5262
|
-
*
|
|
5498
|
+
* @example
|
|
5499
|
+
* ```js
|
|
5500
|
+
* try {
|
|
5501
|
+
* const symbol = await client.getSymbol('GVL_Read.StandardTypes.INT_');
|
|
5263
5502
|
*
|
|
5264
|
-
*
|
|
5503
|
+
* const res = await client.writeValueBySymbol(symbol, 32767);
|
|
5504
|
+
* } catch (err) {
|
|
5505
|
+
* console.log("Error:", err);
|
|
5506
|
+
* }
|
|
5507
|
+
* ```
|
|
5508
|
+
*
|
|
5509
|
+
* @param symbol Symbol object
|
|
5265
5510
|
* @param value Value to write
|
|
5266
5511
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5267
|
-
* @param autoFill If
|
|
5512
|
+
* @param autoFill If set and the data type is a container (`STRUCT`, `FUNCTION_BLOCK` etc.), missing properties are automatically set to active values read from target (kept as-is).
|
|
5268
5513
|
*
|
|
5269
5514
|
* @template T In Typescript, the data type of the value, for example `writeValue<number>(...)` or `writeValue<ST_TypedStruct>(...)`
|
|
5515
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5270
5516
|
*/
|
|
5271
5517
|
async writeValueBySymbol(symbol, value, autoFill = false, targetOpts = {}) {
|
|
5272
5518
|
if (!this.connection.connected) {
|
|
@@ -5275,14 +5521,27 @@ class Client extends events_1.default {
|
|
|
5275
5521
|
return this.writeValue(symbol.name, value, autoFill, targetOpts);
|
|
5276
5522
|
}
|
|
5277
5523
|
/**
|
|
5278
|
-
* **TODO - DOCUMENTATION ONGOING**
|
|
5279
|
-
*
|
|
5280
5524
|
* Returns a default (empty) Javascript object representing provided PLC data type.
|
|
5281
5525
|
*
|
|
5282
|
-
* @
|
|
5526
|
+
* @example
|
|
5527
|
+
* ```js
|
|
5528
|
+
* try {
|
|
5529
|
+
* const res = await client.getDefaultPlcObject('INT');
|
|
5530
|
+
* console.log(res); //0
|
|
5531
|
+
|
|
5532
|
+
* const res2 = await client.getDefaultPlcObject('Tc2_Standard.TON');
|
|
5533
|
+
* console.log(res2); //{ IN: false, PT: 0, Q: false, ET: 0, M: false, StartTime: 0 }
|
|
5534
|
+
*
|
|
5535
|
+
* } catch (err) {
|
|
5536
|
+
* console.log("Error:", err);
|
|
5537
|
+
* }
|
|
5538
|
+
* ```
|
|
5539
|
+
*
|
|
5540
|
+
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or data type object (acquired using {@link getDataType}())
|
|
5283
5541
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5284
5542
|
*
|
|
5285
|
-
* @template T Typescript data type of the PLC data, for example `
|
|
5543
|
+
* @template T Typescript data type of the PLC data, for example `getDefaultPlcObject<number>(...)` or `getDefaultPlcObject<ST_TypedStruct>(...)`
|
|
5544
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5286
5545
|
*/
|
|
5287
5546
|
async getDefaultPlcObject(dataType, targetOpts = {}) {
|
|
5288
5547
|
if (!this.connection.connected) {
|
|
@@ -5306,15 +5565,28 @@ class Client extends events_1.default {
|
|
|
5306
5565
|
return value;
|
|
5307
5566
|
}
|
|
5308
5567
|
/**
|
|
5309
|
-
*
|
|
5568
|
+
* Converts raw data to a Javascript object by using the provided data type.
|
|
5569
|
+
*
|
|
5570
|
+
* @example
|
|
5571
|
+
* ```js
|
|
5572
|
+
* try {
|
|
5573
|
+
* const data = await client.readRaw(16448, 414816, 2);
|
|
5574
|
+
* console.log(data); //<Buffer ff 7f>
|
|
5310
5575
|
*
|
|
5311
|
-
*
|
|
5576
|
+
* const converted = await client.convertFromRaw(data, 'INT');
|
|
5577
|
+
* console.log(converted); //32767
|
|
5578
|
+
*
|
|
5579
|
+
* } catch (err) {
|
|
5580
|
+
* console.log("Error:", err);
|
|
5581
|
+
* }
|
|
5582
|
+
* ```
|
|
5312
5583
|
*
|
|
5313
|
-
* @param data Raw
|
|
5314
|
-
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or
|
|
5584
|
+
* @param data Raw data (acquired for example using {@link readRaw}())
|
|
5585
|
+
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or data type object (acquired using {@link getDataType}())
|
|
5315
5586
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5316
5587
|
*
|
|
5317
5588
|
* @template T Typescript data type of the PLC data, for example `convertFromRaw<number>(...)` or `convertFromRaw<ST_TypedStruct>(...)`
|
|
5589
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5318
5590
|
*/
|
|
5319
5591
|
async convertFromRaw(data, dataType, targetOpts = {}) {
|
|
5320
5592
|
if (!this.connection.connected) {
|
|
@@ -5346,18 +5618,27 @@ class Client extends events_1.default {
|
|
|
5346
5618
|
return value;
|
|
5347
5619
|
}
|
|
5348
5620
|
/**
|
|
5349
|
-
*
|
|
5621
|
+
* Converts a Javascript object to raw data by using the provided data type.
|
|
5350
5622
|
*
|
|
5351
|
-
*
|
|
5623
|
+
* **NOTE:** Do not use `autoFill` for `UNION` types, it doesn't work correctly.
|
|
5624
|
+
|
|
5625
|
+
* @example
|
|
5626
|
+
* ```js
|
|
5627
|
+
* try {
|
|
5628
|
+
* const data = await client.convertToRaw(32767, 'INT');
|
|
5629
|
+
* console.log(data); //<Buffer ff 7f>
|
|
5352
5630
|
*
|
|
5353
|
-
*
|
|
5631
|
+
* } catch (err) {
|
|
5632
|
+
* console.log("Error:", err);
|
|
5633
|
+
* }
|
|
5634
|
+
* ```
|
|
5354
5635
|
*
|
|
5355
|
-
* @param value
|
|
5356
|
-
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or
|
|
5357
|
-
* @param autoFill If
|
|
5636
|
+
* @param value Value to convert
|
|
5637
|
+
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or data type object (acquired using {@link getDataType}())
|
|
5638
|
+
* @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).
|
|
5358
5639
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5359
5640
|
*
|
|
5360
|
-
* @
|
|
5641
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5361
5642
|
*/
|
|
5362
5643
|
async convertToRaw(value, dataType, autoFill = false, targetOpts = {}) {
|
|
5363
5644
|
if (!this.connection.connected) {
|
|
@@ -5413,14 +5694,39 @@ class Client extends events_1.default {
|
|
|
5413
5694
|
}
|
|
5414
5695
|
}
|
|
5415
5696
|
/**
|
|
5416
|
-
*
|
|
5697
|
+
* Creates a handle to a variable at the target system by variable path (such as `GVL_Test.ExampleStruct`).
|
|
5417
5698
|
*
|
|
5418
|
-
*
|
|
5699
|
+
* The handle can be then used for reading and writing the value.
|
|
5419
5700
|
*
|
|
5420
|
-
*
|
|
5701
|
+
* Reading and writing dereferenced `POINTER` and `REFERENCE` values is also possible.
|
|
5702
|
+
* See {@link readRawByHandle}() and {@link writeRawByHandle}().
|
|
5421
5703
|
*
|
|
5422
|
-
*
|
|
5423
|
-
*
|
|
5704
|
+
* NOTE: The handle should be deleted after it's no longer needed,
|
|
5705
|
+
* as there is a limited amount of handles available. See {@link deleteVariableHandle}().
|
|
5706
|
+
*
|
|
5707
|
+
* @example
|
|
5708
|
+
* ```js
|
|
5709
|
+
* try {
|
|
5710
|
+
* //POINTER value (Note the dereference operator ^)
|
|
5711
|
+
* const handle1 = await client.createVariableHandle('GVL_Read.ComplexTypes.POINTER_^');
|
|
5712
|
+
* const value = await client.readRawByHandle(handle1);
|
|
5713
|
+
* await client.deleteVariableHandle(handle1);
|
|
5714
|
+
*
|
|
5715
|
+
* const handle2 = await client.createVariableHandle('GVL_Read.StandardTypes.INT_');
|
|
5716
|
+
* const value2 = await client.readRawByHandle(handle2);
|
|
5717
|
+
* await client.deleteVariableHandle(handle2);
|
|
5718
|
+
*
|
|
5719
|
+
* //Now you use convertFromRaw() to get actual values
|
|
5720
|
+
*
|
|
5721
|
+
* } catch (err) {
|
|
5722
|
+
* console.log("Error:", err);
|
|
5723
|
+
* }
|
|
5724
|
+
* ```
|
|
5725
|
+
*
|
|
5726
|
+
* @param path Variable path in the PLC to read (such as `GVL_Test.ExampleStruct`)
|
|
5727
|
+
* @param targetOpts Optional target settings that override values in `settings`
|
|
5728
|
+
*
|
|
5729
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5424
5730
|
*/
|
|
5425
5731
|
async createVariableHandle(path, targetOpts = {}) {
|
|
5426
5732
|
if (!this.connection.connected) {
|
|
@@ -5468,7 +5774,7 @@ class Client extends events_1.default {
|
|
|
5468
5774
|
let dataTypeLength = response.readUInt16LE(pos);
|
|
5469
5775
|
pos += 2;
|
|
5470
5776
|
//10..n Data type
|
|
5471
|
-
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1));
|
|
5777
|
+
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
5472
5778
|
this.debug(`createVariableHandle(): Variable handle created to ${path}`);
|
|
5473
5779
|
return result;
|
|
5474
5780
|
}
|
|
@@ -5478,18 +5784,44 @@ class Client extends events_1.default {
|
|
|
5478
5784
|
}
|
|
5479
5785
|
}
|
|
5480
5786
|
/**
|
|
5481
|
-
*
|
|
5787
|
+
* Sends multiple {@link createVariableHandle}() commands in one ADS packet.
|
|
5788
|
+
*
|
|
5789
|
+
* Creates a handle to a variable at the target system by variable path (such as `GVL_Test.ExampleStruct`).
|
|
5790
|
+
*
|
|
5791
|
+
* The handle can be then used for reading and writing the value.
|
|
5482
5792
|
*
|
|
5483
|
-
*
|
|
5793
|
+
* Reading and writing dereferenced `POINTER` and `REFERENCE` values is also possible.
|
|
5794
|
+
* See {@link readRawByHandle}() and {@link writeRawByHandle}().
|
|
5484
5795
|
*
|
|
5485
|
-
*
|
|
5796
|
+
* NOTE: The handle should be deleted after it's no longer needed,
|
|
5797
|
+
* as there is a limited amount of handles available. See {@link deleteVariableHandle}().
|
|
5486
5798
|
*
|
|
5487
|
-
*
|
|
5799
|
+
* Uses ADS sum command under the hood (better and faster performance).
|
|
5800
|
+
* See [Beckhoff Information System](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/9007199379576075.html&id=9180083787138954512) for more info.
|
|
5801
|
+
*
|
|
5802
|
+
* @example
|
|
5803
|
+
* ```js
|
|
5804
|
+
* try {
|
|
5805
|
+
* const results = await client.createVariableHandleMulti([
|
|
5806
|
+
* 'GVL_Read.StandardTypes.INT_',
|
|
5807
|
+
* 'GVL_Read.StandardTypes.REAL_'
|
|
5808
|
+
* ]);
|
|
5488
5809
|
*
|
|
5489
|
-
*
|
|
5810
|
+
* if(results[0].success) {
|
|
5811
|
+
* console.log(`First handle: ${results[0].handle}`);
|
|
5812
|
+
* } else {
|
|
5813
|
+
* console.log(`Creating first handle failed: ${results[0].errorStr}`);
|
|
5814
|
+
* }
|
|
5490
5815
|
*
|
|
5491
|
-
*
|
|
5816
|
+
* } catch (err) {
|
|
5817
|
+
* console.log("Error:", err);
|
|
5818
|
+
* }
|
|
5819
|
+
* ```
|
|
5820
|
+
*
|
|
5821
|
+
* @param paths Array of variable paths in the PLC to read (such as `GVL_Test.ExampleStruct`)
|
|
5492
5822
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5823
|
+
*
|
|
5824
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5493
5825
|
*/
|
|
5494
5826
|
async createVariableHandleMulti(paths, targetOpts = {}) {
|
|
5495
5827
|
if (!this.connection.connected) {
|
|
@@ -5580,7 +5912,7 @@ class Client extends events_1.default {
|
|
|
5580
5912
|
let dataTypeLength = response.readUInt16LE(pos);
|
|
5581
5913
|
pos += 2;
|
|
5582
5914
|
//10..n Data type
|
|
5583
|
-
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1));
|
|
5915
|
+
result.dataType = ADS.decodePlcStringBuffer(response.subarray(pos, pos + dataTypeLength + 1), this.metaData.adsSymbolsUseUtf8);
|
|
5584
5916
|
pos += dataTypeLength + 1;
|
|
5585
5917
|
results[i].handle = result;
|
|
5586
5918
|
}
|
|
@@ -5594,12 +5926,26 @@ class Client extends events_1.default {
|
|
|
5594
5926
|
}
|
|
5595
5927
|
}
|
|
5596
5928
|
/**
|
|
5597
|
-
*
|
|
5929
|
+
* Deletes a variable handle that was previously created
|
|
5930
|
+
* using {@link createVariableHandle}().
|
|
5598
5931
|
*
|
|
5599
|
-
*
|
|
5932
|
+
* @example
|
|
5933
|
+
* ```js
|
|
5934
|
+
* try {
|
|
5935
|
+
* const handle = createVariableHandle(...);
|
|
5936
|
+
*
|
|
5937
|
+
* //After use, deleting the handle
|
|
5938
|
+
* await client.deleteVariableHandle(handle);
|
|
5939
|
+
*
|
|
5940
|
+
* } catch (err) {
|
|
5941
|
+
* console.log("Error:", err);
|
|
5942
|
+
* }
|
|
5943
|
+
* ```
|
|
5600
5944
|
*
|
|
5601
5945
|
* @param handle Variable handle to delete
|
|
5602
|
-
* @param targetOpts Optional target settings that override values in `settings`
|
|
5946
|
+
* @param targetOpts Optional target settings that override values in `settings`
|
|
5947
|
+
*
|
|
5948
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5603
5949
|
*/
|
|
5604
5950
|
async deleteVariableHandle(handle, targetOpts = {}) {
|
|
5605
5951
|
if (!this.connection.connected) {
|
|
@@ -5637,16 +5983,38 @@ class Client extends events_1.default {
|
|
|
5637
5983
|
}
|
|
5638
5984
|
}
|
|
5639
5985
|
/**
|
|
5640
|
-
*
|
|
5986
|
+
* Sends multiple {@link deleteVariableHandle}() commands in one ADS packet.
|
|
5987
|
+
*
|
|
5988
|
+
* Deletes a variable handle that was previously created
|
|
5989
|
+
* using {@link createVariableHandle}().
|
|
5990
|
+
*
|
|
5991
|
+
* Uses ADS sum command under the hood (better and faster performance).
|
|
5992
|
+
* See [Beckhoff Information System](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/9007199379576075.html&id=9180083787138954512) for more info.
|
|
5641
5993
|
*
|
|
5642
|
-
*
|
|
5994
|
+
* @example
|
|
5995
|
+
* ```js
|
|
5996
|
+
* try {
|
|
5997
|
+
* const handle1 = createVariableHandle(...);
|
|
5998
|
+
* const handle2 = createVariableHandle(...);
|
|
5643
5999
|
*
|
|
5644
|
-
*
|
|
6000
|
+
* //After use, deleting the handles
|
|
6001
|
+
* const results = await client.deleteVariableHandleMulti([handle1, handle2]);
|
|
6002
|
+
*
|
|
6003
|
+
* if(results[0].success) {
|
|
6004
|
+
* console.log(`First deleted`);
|
|
6005
|
+
* } else {
|
|
6006
|
+
* console.log(`Deleting first handle failed: ${results[0].errorStr}`);
|
|
6007
|
+
* }
|
|
5645
6008
|
*
|
|
5646
|
-
*
|
|
6009
|
+
* } catch (err) {
|
|
6010
|
+
* console.log("Error:", err);
|
|
6011
|
+
* }
|
|
6012
|
+
* ```
|
|
5647
6013
|
*
|
|
5648
6014
|
* @param handles Array of variable handles to delete
|
|
5649
6015
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
6016
|
+
*
|
|
6017
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5650
6018
|
*/
|
|
5651
6019
|
async deleteVariableHandleMulti(handles, targetOpts = {}) {
|
|
5652
6020
|
if (!this.connection.connected) {
|
|
@@ -5718,13 +6086,24 @@ class Client extends events_1.default {
|
|
|
5718
6086
|
}
|
|
5719
6087
|
}
|
|
5720
6088
|
/**
|
|
5721
|
-
*
|
|
6089
|
+
* Reads raw data from the target system by a previously created variable handle (acquired using {@link createVariableHandle}())
|
|
5722
6090
|
*
|
|
5723
|
-
*
|
|
6091
|
+
* @example
|
|
6092
|
+
* ```js
|
|
6093
|
+
* try {
|
|
6094
|
+
* const handle = await client.createVariableHandle('GVL_Read.StandardTypes.INT_'');
|
|
6095
|
+
* const value = await client.readRawByHandle(handle);
|
|
6096
|
+
* await client.deleteVariableHandle(handle);
|
|
5724
6097
|
*
|
|
6098
|
+
* } catch (err) {
|
|
6099
|
+
* console.log("Error:", err);
|
|
6100
|
+
* }
|
|
6101
|
+
* ```
|
|
5725
6102
|
* @param handle Variable handle
|
|
5726
6103
|
* @param size Optional data length to read (bytes) - as default, size in handle is used if available. Uses 0xFFFFFFFF as fallback.
|
|
5727
|
-
* @param targetOpts Optional target settings that override values in `settings`
|
|
6104
|
+
* @param targetOpts Optional target settings that override values in `settings`
|
|
6105
|
+
*
|
|
6106
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5728
6107
|
*/
|
|
5729
6108
|
async readRawByHandle(handle, size, targetOpts = {}) {
|
|
5730
6109
|
if (!this.connection.connected) {
|
|
@@ -5748,13 +6127,28 @@ class Client extends events_1.default {
|
|
|
5748
6127
|
}
|
|
5749
6128
|
}
|
|
5750
6129
|
/**
|
|
5751
|
-
*
|
|
6130
|
+
* Writes raw data to the target system by a previously created variable handle (acquired using {@link createVariableHandle}())
|
|
5752
6131
|
*
|
|
5753
|
-
*
|
|
6132
|
+
* @example
|
|
6133
|
+
* ```js
|
|
6134
|
+
* try {
|
|
6135
|
+
* const value = await client.convertToRaw(32767, 'INT');
|
|
6136
|
+
* console.log(value); //<Buffer ff 7f>
|
|
6137
|
+
*
|
|
6138
|
+
* const handle = await client.createVariableHandle('GVL_Read.StandardTypes.INT_');
|
|
6139
|
+
* await client.writeRawByHandle(handle, value);
|
|
6140
|
+
* await client.deleteVariableHandle(handle);
|
|
6141
|
+
*
|
|
6142
|
+
* } catch (err) {
|
|
6143
|
+
* console.log("Error:", err);
|
|
6144
|
+
* }
|
|
6145
|
+
* ```
|
|
5754
6146
|
*
|
|
5755
6147
|
* @param handle Variable handle
|
|
5756
6148
|
* @param value Data to write
|
|
5757
|
-
* @param targetOpts Optional target settings that override values in `settings`
|
|
6149
|
+
* @param targetOpts Optional target settings that override values in `settings`
|
|
6150
|
+
*
|
|
6151
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5758
6152
|
*/
|
|
5759
6153
|
async writeRawByHandle(handle, value, targetOpts = {}) {
|
|
5760
6154
|
if (!this.connection.connected) {
|
|
@@ -5772,12 +6166,23 @@ class Client extends events_1.default {
|
|
|
5772
6166
|
}
|
|
5773
6167
|
}
|
|
5774
6168
|
/**
|
|
5775
|
-
*
|
|
6169
|
+
* Reads raw data from the target system by a symbol object (acquired using `getSymbol()`)
|
|
5776
6170
|
*
|
|
5777
|
-
*
|
|
6171
|
+
* @example
|
|
6172
|
+
* ```js
|
|
6173
|
+
* try {
|
|
6174
|
+
* const symbol = await client.getSymbol('GVL_Read.StandardTypes.INT_');
|
|
6175
|
+
* const value = await client.readRawBySymbol(symbol);
|
|
5778
6176
|
*
|
|
5779
|
-
*
|
|
5780
|
-
*
|
|
6177
|
+
* } catch (err) {
|
|
6178
|
+
* console.log("Error:", err);
|
|
6179
|
+
* }
|
|
6180
|
+
* ```
|
|
6181
|
+
*
|
|
6182
|
+
* @param symbol Symbol
|
|
6183
|
+
* @param targetOpts Optional target settings that override values in `settings`
|
|
6184
|
+
*
|
|
6185
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5781
6186
|
*/
|
|
5782
6187
|
async readRawBySymbol(symbol, targetOpts = {}) {
|
|
5783
6188
|
if (!this.connection.connected) {
|
|
@@ -5795,13 +6200,27 @@ class Client extends events_1.default {
|
|
|
5795
6200
|
}
|
|
5796
6201
|
}
|
|
5797
6202
|
/**
|
|
5798
|
-
*
|
|
6203
|
+
* Writes raw data to the target system by a symbol object (acquired using `getSymbol()`)
|
|
6204
|
+
*
|
|
6205
|
+
* @example
|
|
6206
|
+
* ```js
|
|
6207
|
+
* try {
|
|
6208
|
+
* const value = await client.convertToRaw(32767, 'INT');
|
|
6209
|
+
* console.log(value); //<Buffer ff 7f>
|
|
5799
6210
|
*
|
|
5800
|
-
*
|
|
6211
|
+
* const symbol = await client.getSymbol('GVL_Read.StandardTypes.INT_');
|
|
6212
|
+
* await client.writeRawBySymbol(symbol, value);
|
|
5801
6213
|
*
|
|
5802
|
-
*
|
|
6214
|
+
* } catch (err) {
|
|
6215
|
+
* console.log("Error:", err);
|
|
6216
|
+
* }
|
|
6217
|
+
* ```
|
|
6218
|
+
*
|
|
6219
|
+
* @param symbol Symbol
|
|
5803
6220
|
* @param value Data to write
|
|
5804
6221
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
6222
|
+
*
|
|
6223
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5805
6224
|
*/
|
|
5806
6225
|
async writeRawBySymbol(symbol, value, targetOpts = {}) {
|
|
5807
6226
|
if (!this.connection.connected) {
|
|
@@ -5818,21 +6237,40 @@ class Client extends events_1.default {
|
|
|
5818
6237
|
}
|
|
5819
6238
|
}
|
|
5820
6239
|
/**
|
|
5821
|
-
*
|
|
6240
|
+
* Invokes a function block RPC method on the target system.
|
|
5822
6241
|
*
|
|
5823
|
-
*
|
|
6242
|
+
* Returns the return value of the method and outputs (if any).
|
|
5824
6243
|
*
|
|
5825
|
-
*
|
|
6244
|
+
* NOTE: In the PLC, `{attribute 'TcRpcEnable'}` is required above the `METHOD` definition.
|
|
5826
6245
|
*
|
|
5827
|
-
*
|
|
6246
|
+
* @example
|
|
6247
|
+
* ```js
|
|
6248
|
+
* try {
|
|
6249
|
+
* const res = await client.invokeRpcMethod('GVL_RPC.RpcBlock', 'Calculator', {
|
|
6250
|
+
* Value1: 1,
|
|
6251
|
+
* Value2: 123
|
|
6252
|
+
* });
|
|
6253
|
+
*
|
|
6254
|
+
* console.log(res);
|
|
6255
|
+
* //{
|
|
6256
|
+
* // returnValue: true,
|
|
6257
|
+
* // outputs: { Sum: 124, Product: 123, Division: 0.008130080997943878 }
|
|
6258
|
+
* //}
|
|
5828
6259
|
*
|
|
5829
|
-
*
|
|
5830
|
-
*
|
|
5831
|
-
*
|
|
6260
|
+
* } catch (err) {
|
|
6261
|
+
* console.log("Error:", err);
|
|
6262
|
+
* }
|
|
6263
|
+
* ```
|
|
6264
|
+
*
|
|
6265
|
+
* @param path Variable path in the PLC of the function block instance (such as `GVL_Test.ExampleBlock`)
|
|
6266
|
+
* @param method Function block method name
|
|
6267
|
+
* @param parameters Method parameters (inputs) (if any)
|
|
5832
6268
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5833
6269
|
*
|
|
5834
6270
|
* @template T Typescript data type of the method return value, for example `invokeRpcMethod<number>(...)` or `invokeRpcMethod<ST_TypedStruct>(...)`
|
|
5835
6271
|
* @template U Typescript data type of the method outputs object, for example `invokeRpcMethod<number, ST_TypedStruct>(...)`
|
|
6272
|
+
*
|
|
6273
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5836
6274
|
*/
|
|
5837
6275
|
async invokeRpcMethod(path, method, parameters = {}, targetOpts = {}) {
|
|
5838
6276
|
if (!this.connection.connected) {
|
|
@@ -5867,8 +6305,8 @@ class Client extends events_1.default {
|
|
|
5867
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(', ')}`);
|
|
5868
6306
|
}
|
|
5869
6307
|
//Method inputs and outputs
|
|
5870
|
-
const inputs = rpcMethod.parameters.filter(p => p.flags === ADS.ADS_RCP_METHOD_PARAM_FLAGS.In);
|
|
5871
|
-
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);
|
|
5872
6310
|
//Creating data buffer for inputs
|
|
5873
6311
|
let inputsData = Buffer.alloc(0);
|
|
5874
6312
|
for (const input of inputs) {
|