opcjs-client 0.1.18-alpha → 0.1.26-alpha

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/dist/index.cjs CHANGED
@@ -3,11 +3,38 @@
3
3
  var opcjsBase = require('opcjs-base');
4
4
 
5
5
  // src/client.ts
6
+ var SessionInvalidError = class extends Error {
7
+ statusCode;
8
+ constructor(statusCode) {
9
+ super(`Session is no longer valid: ${opcjsBase.StatusCodeToString(statusCode)} (0x${statusCode.toString(16).toUpperCase()})`);
10
+ this.name = "SessionInvalidError";
11
+ this.statusCode = statusCode;
12
+ }
13
+ };
14
+
15
+ // src/services/serviceBase.ts
6
16
  var ServiceBase = class {
7
17
  constructor(authToken, secureChannel) {
8
18
  this.authToken = authToken;
9
19
  this.secureChannel = secureChannel;
10
20
  }
21
+ /**
22
+ * Validates the `serviceResult` value from a response header.
23
+ *
24
+ * Throws `SessionInvalidError` for session-related status codes so callers
25
+ * can detect a dropped session and act accordingly (e.g. reconnect).
26
+ * Throws a generic `Error` for all other non-Good codes.
27
+ *
28
+ * @param result - The `serviceResult` value from the response header.
29
+ * @param context - Short description used in the error message (e.g. "ReadRequest").
30
+ */
31
+ checkServiceResult(result, context) {
32
+ if (result === void 0 || result === opcjsBase.StatusCode.Good) return;
33
+ if (result === opcjsBase.StatusCode.BadSessionIdInvalid || result === opcjsBase.StatusCode.BadSessionClosed) {
34
+ throw new SessionInvalidError(result);
35
+ }
36
+ throw new Error(`${context} failed: ${opcjsBase.StatusCodeToString(result)} (${opcjsBase.StatusCodeToStringNumber(result)})`);
37
+ }
11
38
  createRequestHeader() {
12
39
  const requestHeader = new opcjsBase.RequestHeader();
13
40
  requestHeader.authenticationToken = this.authToken;
@@ -28,6 +55,10 @@ var SessionService = class _SessionService extends ServiceBase {
28
55
  this.configuration = configuration;
29
56
  }
30
57
  logger = opcjsBase.getLogger("services.SessionService");
58
+ /**
59
+ * Creates a new session on the server (OPC UA Part 4, Section 5.7.2).
60
+ * @returns The session ID, authentication token, and selected server endpoint.
61
+ */
31
62
  async createSession() {
32
63
  this.logger.debug("Creating session...");
33
64
  const clientDescription = new opcjsBase.ApplicationDescription();
@@ -57,6 +88,10 @@ var SessionService = class _SessionService extends ServiceBase {
57
88
  if (!castedResponse || !castedResponse.sessionId || !castedResponse.authenticationToken) {
58
89
  throw new Error("CreateSessionResponse missing SessionId or AuthenticationToken");
59
90
  }
91
+ const serviceResult = castedResponse.responseHeader?.serviceResult;
92
+ if (serviceResult !== void 0 && serviceResult !== opcjsBase.StatusCode.Good) {
93
+ throw new Error(`CreateSessionRequest failed: ${opcjsBase.StatusCodeToString(serviceResult)}`);
94
+ }
60
95
  const endpoint = "opc." + this.secureChannel.getEndpointUrl();
61
96
  const endpointUrl = new URL(endpoint);
62
97
  const securityMode = this.secureChannel.getSecurityMode();
@@ -75,6 +110,10 @@ var SessionService = class _SessionService extends ServiceBase {
75
110
  endpoint: serverEndpoint
76
111
  };
77
112
  }
113
+ /**
114
+ * Activates an existing session using the supplied identity token (OPC UA Part 4, Section 5.7.3).
115
+ * @param identityToken - User identity token (anonymous, username/password, certificate, or issued token).
116
+ */
78
117
  async activateSession(identityToken) {
79
118
  const signatureData = new opcjsBase.SignatureData();
80
119
  signatureData.algorithm = this.secureChannel.getSecurityPolicy();
@@ -87,7 +126,11 @@ var SessionService = class _SessionService extends ServiceBase {
87
126
  request.userIdentityToken = opcjsBase.ExtensionObject.newBinary(identityToken);
88
127
  request.userTokenSignature = signatureData;
89
128
  this.logger.debug("Sending ActivateSessionRequest...");
90
- await this.secureChannel.issueServiceRequest(request);
129
+ const activateResponse = await this.secureChannel.issueServiceRequest(request);
130
+ const activateResult = activateResponse?.responseHeader?.serviceResult;
131
+ if (activateResult !== void 0 && activateResult !== opcjsBase.StatusCode.Good) {
132
+ throw new Error(`ActivateSessionRequest failed: ${opcjsBase.StatusCodeToString(activateResult)}`);
133
+ }
91
134
  this.logger.debug("Session activated.");
92
135
  }
93
136
  recreate(authToken) {
@@ -171,268 +214,17 @@ var SessionHandler = class {
171
214
  // src/services/attributeServiceAttributes.ts
172
215
  var AttrIdValue = 13;
173
216
 
174
- // ../base/src/types/statusCode.ts
175
- function StatusCodeToString(statusCode) {
176
- if (statusCode === void 0) {
177
- return "Unknown";
178
- }
179
- const name = Object.entries(StatusCode).find(([, v]) => v === statusCode)?.[0];
180
- return name ?? `0x${statusCode.toString(16).toUpperCase().padStart(8, "0")}`;
181
- }
182
- var StatusCode = /* @__PURE__ */ ((StatusCode2) => {
183
- StatusCode2[StatusCode2["Good"] = 0] = "Good";
184
- StatusCode2[StatusCode2["Uncertain"] = 1073741824] = "Uncertain";
185
- StatusCode2[StatusCode2["Bad"] = 2147483648] = "Bad";
186
- StatusCode2[StatusCode2["BadUnexpectedError"] = 2147549184] = "BadUnexpectedError";
187
- StatusCode2[StatusCode2["BadInternalError"] = 2147614720] = "BadInternalError";
188
- StatusCode2[StatusCode2["BadOutOfMemory"] = 2147680256] = "BadOutOfMemory";
189
- StatusCode2[StatusCode2["BadResourceUnavailable"] = 2147745792] = "BadResourceUnavailable";
190
- StatusCode2[StatusCode2["BadCommunicationError"] = 2147811328] = "BadCommunicationError";
191
- StatusCode2[StatusCode2["BadEncodingError"] = 2147876864] = "BadEncodingError";
192
- StatusCode2[StatusCode2["BadDecodingError"] = 2147942400] = "BadDecodingError";
193
- StatusCode2[StatusCode2["BadEncodingLimitsExceeded"] = 2148007936] = "BadEncodingLimitsExceeded";
194
- StatusCode2[StatusCode2["BadRequestTooLarge"] = 2159542272] = "BadRequestTooLarge";
195
- StatusCode2[StatusCode2["BadResponseTooLarge"] = 2159607808] = "BadResponseTooLarge";
196
- StatusCode2[StatusCode2["BadUnknownResponse"] = 2148073472] = "BadUnknownResponse";
197
- StatusCode2[StatusCode2["BadTimeout"] = 2148139008] = "BadTimeout";
198
- StatusCode2[StatusCode2["BadServiceUnsupported"] = 2148204544] = "BadServiceUnsupported";
199
- StatusCode2[StatusCode2["BadShutdown"] = 2148270080] = "BadShutdown";
200
- StatusCode2[StatusCode2["BadServerNotConnected"] = 2148335616] = "BadServerNotConnected";
201
- StatusCode2[StatusCode2["BadServerHalted"] = 2148401152] = "BadServerHalted";
202
- StatusCode2[StatusCode2["BadNothingToDo"] = 2148466688] = "BadNothingToDo";
203
- StatusCode2[StatusCode2["BadTooManyOperations"] = 2148532224] = "BadTooManyOperations";
204
- StatusCode2[StatusCode2["BadTooManyMonitoredItems"] = 2161836032] = "BadTooManyMonitoredItems";
205
- StatusCode2[StatusCode2["BadDataTypeIdUnknown"] = 2148597760] = "BadDataTypeIdUnknown";
206
- StatusCode2[StatusCode2["BadCertificateInvalid"] = 2148663296] = "BadCertificateInvalid";
207
- StatusCode2[StatusCode2["BadSecurityChecksFailed"] = 2148728832] = "BadSecurityChecksFailed";
208
- StatusCode2[StatusCode2["BadCertificatePolicyCheckFailed"] = 2165571584] = "BadCertificatePolicyCheckFailed";
209
- StatusCode2[StatusCode2["BadCertificateTimeInvalid"] = 2148794368] = "BadCertificateTimeInvalid";
210
- StatusCode2[StatusCode2["BadCertificateIssuerTimeInvalid"] = 2148859904] = "BadCertificateIssuerTimeInvalid";
211
- StatusCode2[StatusCode2["BadCertificateHostNameInvalid"] = 2148925440] = "BadCertificateHostNameInvalid";
212
- StatusCode2[StatusCode2["BadCertificateUriInvalid"] = 2148990976] = "BadCertificateUriInvalid";
213
- StatusCode2[StatusCode2["BadCertificateUseNotAllowed"] = 2149056512] = "BadCertificateUseNotAllowed";
214
- StatusCode2[StatusCode2["BadCertificateIssuerUseNotAllowed"] = 2149122048] = "BadCertificateIssuerUseNotAllowed";
215
- StatusCode2[StatusCode2["BadCertificateUntrusted"] = 2149187584] = "BadCertificateUntrusted";
216
- StatusCode2[StatusCode2["BadCertificateRevocationUnknown"] = 2149253120] = "BadCertificateRevocationUnknown";
217
- StatusCode2[StatusCode2["BadCertificateIssuerRevocationUnknown"] = 2149318656] = "BadCertificateIssuerRevocationUnknown";
218
- StatusCode2[StatusCode2["BadCertificateRevoked"] = 2149384192] = "BadCertificateRevoked";
219
- StatusCode2[StatusCode2["BadCertificateIssuerRevoked"] = 2149449728] = "BadCertificateIssuerRevoked";
220
- StatusCode2[StatusCode2["BadCertificateChainIncomplete"] = 2165112832] = "BadCertificateChainIncomplete";
221
- StatusCode2[StatusCode2["BadUserAccessDenied"] = 2149515264] = "BadUserAccessDenied";
222
- StatusCode2[StatusCode2["BadIdentityTokenInvalid"] = 2149580800] = "BadIdentityTokenInvalid";
223
- StatusCode2[StatusCode2["BadIdentityTokenRejected"] = 2149646336] = "BadIdentityTokenRejected";
224
- StatusCode2[StatusCode2["BadSecureChannelIdInvalid"] = 2149711872] = "BadSecureChannelIdInvalid";
225
- StatusCode2[StatusCode2["BadInvalidTimestamp"] = 2149777408] = "BadInvalidTimestamp";
226
- StatusCode2[StatusCode2["BadNonceInvalid"] = 2149842944] = "BadNonceInvalid";
227
- StatusCode2[StatusCode2["BadSessionIdInvalid"] = 2149908480] = "BadSessionIdInvalid";
228
- StatusCode2[StatusCode2["BadSessionClosed"] = 2149974016] = "BadSessionClosed";
229
- StatusCode2[StatusCode2["BadSessionNotActivated"] = 2150039552] = "BadSessionNotActivated";
230
- StatusCode2[StatusCode2["BadSubscriptionIdInvalid"] = 2150105088] = "BadSubscriptionIdInvalid";
231
- StatusCode2[StatusCode2["BadRequestHeaderInvalid"] = 2150236160] = "BadRequestHeaderInvalid";
232
- StatusCode2[StatusCode2["BadTimestampsToReturnInvalid"] = 2150301696] = "BadTimestampsToReturnInvalid";
233
- StatusCode2[StatusCode2["BadRequestCancelledByClient"] = 2150367232] = "BadRequestCancelledByClient";
234
- StatusCode2[StatusCode2["BadTooManyArguments"] = 2162491392] = "BadTooManyArguments";
235
- StatusCode2[StatusCode2["BadLicenseExpired"] = 2165178368] = "BadLicenseExpired";
236
- StatusCode2[StatusCode2["BadLicenseLimitsExceeded"] = 2165243904] = "BadLicenseLimitsExceeded";
237
- StatusCode2[StatusCode2["BadLicenseNotAvailable"] = 2165309440] = "BadLicenseNotAvailable";
238
- StatusCode2[StatusCode2["GoodSubscriptionTransferred"] = 2949120] = "GoodSubscriptionTransferred";
239
- StatusCode2[StatusCode2["GoodCompletesAsynchronously"] = 3014656] = "GoodCompletesAsynchronously";
240
- StatusCode2[StatusCode2["GoodOverload"] = 3080192] = "GoodOverload";
241
- StatusCode2[StatusCode2["GoodClamped"] = 3145728] = "GoodClamped";
242
- StatusCode2[StatusCode2["BadNoCommunication"] = 2150694912] = "BadNoCommunication";
243
- StatusCode2[StatusCode2["BadWaitingForInitialData"] = 2150760448] = "BadWaitingForInitialData";
244
- StatusCode2[StatusCode2["BadNodeIdInvalid"] = 2150825984] = "BadNodeIdInvalid";
245
- StatusCode2[StatusCode2["BadNodeIdUnknown"] = 2150891520] = "BadNodeIdUnknown";
246
- StatusCode2[StatusCode2["BadAttributeIdInvalid"] = 2150957056] = "BadAttributeIdInvalid";
247
- StatusCode2[StatusCode2["BadIndexRangeInvalid"] = 2151022592] = "BadIndexRangeInvalid";
248
- StatusCode2[StatusCode2["BadIndexRangeNoData"] = 2151088128] = "BadIndexRangeNoData";
249
- StatusCode2[StatusCode2["BadDataEncodingInvalid"] = 2151153664] = "BadDataEncodingInvalid";
250
- StatusCode2[StatusCode2["BadDataEncodingUnsupported"] = 2151219200] = "BadDataEncodingUnsupported";
251
- StatusCode2[StatusCode2["BadNotReadable"] = 2151284736] = "BadNotReadable";
252
- StatusCode2[StatusCode2["BadNotWritable"] = 2151350272] = "BadNotWritable";
253
- StatusCode2[StatusCode2["BadOutOfRange"] = 2151415808] = "BadOutOfRange";
254
- StatusCode2[StatusCode2["BadNotSupported"] = 2151481344] = "BadNotSupported";
255
- StatusCode2[StatusCode2["BadNotFound"] = 2151546880] = "BadNotFound";
256
- StatusCode2[StatusCode2["BadObjectDeleted"] = 2151612416] = "BadObjectDeleted";
257
- StatusCode2[StatusCode2["BadNotImplemented"] = 2151677952] = "BadNotImplemented";
258
- StatusCode2[StatusCode2["BadMonitoringModeInvalid"] = 2151743488] = "BadMonitoringModeInvalid";
259
- StatusCode2[StatusCode2["BadMonitoredItemIdInvalid"] = 2151809024] = "BadMonitoredItemIdInvalid";
260
- StatusCode2[StatusCode2["BadMonitoredItemFilterInvalid"] = 2151874560] = "BadMonitoredItemFilterInvalid";
261
- StatusCode2[StatusCode2["BadMonitoredItemFilterUnsupported"] = 2151940096] = "BadMonitoredItemFilterUnsupported";
262
- StatusCode2[StatusCode2["BadFilterNotAllowed"] = 2152005632] = "BadFilterNotAllowed";
263
- StatusCode2[StatusCode2["BadStructureMissing"] = 2152071168] = "BadStructureMissing";
264
- StatusCode2[StatusCode2["BadEventFilterInvalid"] = 2152136704] = "BadEventFilterInvalid";
265
- StatusCode2[StatusCode2["BadContentFilterInvalid"] = 2152202240] = "BadContentFilterInvalid";
266
- StatusCode2[StatusCode2["BadFilterOperatorInvalid"] = 2160132096] = "BadFilterOperatorInvalid";
267
- StatusCode2[StatusCode2["BadFilterOperatorUnsupported"] = 2160197632] = "BadFilterOperatorUnsupported";
268
- StatusCode2[StatusCode2["BadFilterOperandCountMismatch"] = 2160263168] = "BadFilterOperandCountMismatch";
269
- StatusCode2[StatusCode2["BadFilterOperandInvalid"] = 2152267776] = "BadFilterOperandInvalid";
270
- StatusCode2[StatusCode2["BadFilterElementInvalid"] = 2160328704] = "BadFilterElementInvalid";
271
- StatusCode2[StatusCode2["BadFilterLiteralInvalid"] = 2160394240] = "BadFilterLiteralInvalid";
272
- StatusCode2[StatusCode2["BadContinuationPointInvalid"] = 2152333312] = "BadContinuationPointInvalid";
273
- StatusCode2[StatusCode2["BadNoContinuationPoints"] = 2152398848] = "BadNoContinuationPoints";
274
- StatusCode2[StatusCode2["BadReferenceTypeIdInvalid"] = 2152464384] = "BadReferenceTypeIdInvalid";
275
- StatusCode2[StatusCode2["BadBrowseDirectionInvalid"] = 2152529920] = "BadBrowseDirectionInvalid";
276
- StatusCode2[StatusCode2["BadNodeNotInView"] = 2152595456] = "BadNodeNotInView";
277
- StatusCode2[StatusCode2["BadNumericOverflow"] = 2165440512] = "BadNumericOverflow";
278
- StatusCode2[StatusCode2["BadServerUriInvalid"] = 2152660992] = "BadServerUriInvalid";
279
- StatusCode2[StatusCode2["BadServerNameMissing"] = 2152726528] = "BadServerNameMissing";
280
- StatusCode2[StatusCode2["BadDiscoveryUrlMissing"] = 2152792064] = "BadDiscoveryUrlMissing";
281
- StatusCode2[StatusCode2["BadSempahoreFileMissing"] = 2152857600] = "BadSempahoreFileMissing";
282
- StatusCode2[StatusCode2["BadRequestTypeInvalid"] = 2152923136] = "BadRequestTypeInvalid";
283
- StatusCode2[StatusCode2["BadSecurityModeRejected"] = 2152988672] = "BadSecurityModeRejected";
284
- StatusCode2[StatusCode2["BadSecurityPolicyRejected"] = 2153054208] = "BadSecurityPolicyRejected";
285
- StatusCode2[StatusCode2["BadTooManySessions"] = 2153119744] = "BadTooManySessions";
286
- StatusCode2[StatusCode2["BadUserSignatureInvalid"] = 2153185280] = "BadUserSignatureInvalid";
287
- StatusCode2[StatusCode2["BadApplicationSignatureInvalid"] = 2153250816] = "BadApplicationSignatureInvalid";
288
- StatusCode2[StatusCode2["BadNoValidCertificates"] = 2153316352] = "BadNoValidCertificates";
289
- StatusCode2[StatusCode2["BadIdentityChangeNotSupported"] = 2160459776] = "BadIdentityChangeNotSupported";
290
- StatusCode2[StatusCode2["BadRequestCancelledByRequest"] = 2153381888] = "BadRequestCancelledByRequest";
291
- StatusCode2[StatusCode2["BadParentNodeIdInvalid"] = 2153447424] = "BadParentNodeIdInvalid";
292
- StatusCode2[StatusCode2["BadReferenceNotAllowed"] = 2153512960] = "BadReferenceNotAllowed";
293
- StatusCode2[StatusCode2["BadNodeIdRejected"] = 2153578496] = "BadNodeIdRejected";
294
- StatusCode2[StatusCode2["BadNodeIdExists"] = 2153644032] = "BadNodeIdExists";
295
- StatusCode2[StatusCode2["BadNodeClassInvalid"] = 2153709568] = "BadNodeClassInvalid";
296
- StatusCode2[StatusCode2["BadBrowseNameInvalid"] = 2153775104] = "BadBrowseNameInvalid";
297
- StatusCode2[StatusCode2["BadBrowseNameDuplicated"] = 2153840640] = "BadBrowseNameDuplicated";
298
- StatusCode2[StatusCode2["BadNodeAttributesInvalid"] = 2153906176] = "BadNodeAttributesInvalid";
299
- StatusCode2[StatusCode2["BadTypeDefinitionInvalid"] = 2153971712] = "BadTypeDefinitionInvalid";
300
- StatusCode2[StatusCode2["BadSourceNodeIdInvalid"] = 2154037248] = "BadSourceNodeIdInvalid";
301
- StatusCode2[StatusCode2["BadTargetNodeIdInvalid"] = 2154102784] = "BadTargetNodeIdInvalid";
302
- StatusCode2[StatusCode2["BadDuplicateReferenceNotAllowed"] = 2154168320] = "BadDuplicateReferenceNotAllowed";
303
- StatusCode2[StatusCode2["BadInvalidSelfReference"] = 2154233856] = "BadInvalidSelfReference";
304
- StatusCode2[StatusCode2["BadReferenceLocalOnly"] = 2154299392] = "BadReferenceLocalOnly";
305
- StatusCode2[StatusCode2["BadNoDeleteRights"] = 2154364928] = "BadNoDeleteRights";
306
- StatusCode2[StatusCode2["UncertainReferenceNotDeleted"] = 1086062592] = "UncertainReferenceNotDeleted";
307
- StatusCode2[StatusCode2["BadServerIndexInvalid"] = 2154430464] = "BadServerIndexInvalid";
308
- StatusCode2[StatusCode2["BadViewIdUnknown"] = 2154496e3] = "BadViewIdUnknown";
309
- StatusCode2[StatusCode2["BadViewTimestampInvalid"] = 2160656384] = "BadViewTimestampInvalid";
310
- StatusCode2[StatusCode2["BadViewParameterMismatch"] = 2160721920] = "BadViewParameterMismatch";
311
- StatusCode2[StatusCode2["BadViewVersionInvalid"] = 2160787456] = "BadViewVersionInvalid";
312
- StatusCode2[StatusCode2["UncertainNotAllNodesAvailable"] = 1086324736] = "UncertainNotAllNodesAvailable";
313
- StatusCode2[StatusCode2["GoodResultsMayBeIncomplete"] = 12189696] = "GoodResultsMayBeIncomplete";
314
- StatusCode2[StatusCode2["BadNotTypeDefinition"] = 2160590848] = "BadNotTypeDefinition";
315
- StatusCode2[StatusCode2["UncertainReferenceOutOfServer"] = 1080819712] = "UncertainReferenceOutOfServer";
316
- StatusCode2[StatusCode2["BadTooManyMatches"] = 2154627072] = "BadTooManyMatches";
317
- StatusCode2[StatusCode2["BadQueryTooComplex"] = 2154692608] = "BadQueryTooComplex";
318
- StatusCode2[StatusCode2["BadNoMatch"] = 2154758144] = "BadNoMatch";
319
- StatusCode2[StatusCode2["BadMaxAgeInvalid"] = 2154823680] = "BadMaxAgeInvalid";
320
- StatusCode2[StatusCode2["BadSecurityModeInsufficient"] = 2162556928] = "BadSecurityModeInsufficient";
321
- StatusCode2[StatusCode2["BadHistoryOperationInvalid"] = 2154889216] = "BadHistoryOperationInvalid";
322
- StatusCode2[StatusCode2["BadHistoryOperationUnsupported"] = 2154954752] = "BadHistoryOperationUnsupported";
323
- StatusCode2[StatusCode2["BadInvalidTimestampArgument"] = 2159869952] = "BadInvalidTimestampArgument";
324
- StatusCode2[StatusCode2["BadWriteNotSupported"] = 2155020288] = "BadWriteNotSupported";
325
- StatusCode2[StatusCode2["BadTypeMismatch"] = 2155085824] = "BadTypeMismatch";
326
- StatusCode2[StatusCode2["BadMethodInvalid"] = 2155151360] = "BadMethodInvalid";
327
- StatusCode2[StatusCode2["BadArgumentsMissing"] = 2155216896] = "BadArgumentsMissing";
328
- StatusCode2[StatusCode2["BadNotExecutable"] = 2165374976] = "BadNotExecutable";
329
- StatusCode2[StatusCode2["BadTooManySubscriptions"] = 2155282432] = "BadTooManySubscriptions";
330
- StatusCode2[StatusCode2["BadTooManyPublishRequests"] = 2155347968] = "BadTooManyPublishRequests";
331
- StatusCode2[StatusCode2["BadNoSubscription"] = 2155413504] = "BadNoSubscription";
332
- StatusCode2[StatusCode2["BadSequenceNumberUnknown"] = 2155479040] = "BadSequenceNumberUnknown";
333
- StatusCode2[StatusCode2["BadMessageNotAvailable"] = 2155544576] = "BadMessageNotAvailable";
334
- StatusCode2[StatusCode2["BadInsufficientClientProfile"] = 2155610112] = "BadInsufficientClientProfile";
335
- StatusCode2[StatusCode2["BadStateNotActive"] = 2160001024] = "BadStateNotActive";
336
- StatusCode2[StatusCode2["BadAlreadyExists"] = 2165637120] = "BadAlreadyExists";
337
- StatusCode2[StatusCode2["BadTcpServerTooBusy"] = 2155675648] = "BadTcpServerTooBusy";
338
- StatusCode2[StatusCode2["BadTcpMessageTypeInvalid"] = 2155741184] = "BadTcpMessageTypeInvalid";
339
- StatusCode2[StatusCode2["BadTcpSecureChannelUnknown"] = 2155806720] = "BadTcpSecureChannelUnknown";
340
- StatusCode2[StatusCode2["BadTcpMessageTooLarge"] = 2155872256] = "BadTcpMessageTooLarge";
341
- StatusCode2[StatusCode2["BadTcpNotEnoughResources"] = 2155937792] = "BadTcpNotEnoughResources";
342
- StatusCode2[StatusCode2["BadTcpInternalError"] = 2156003328] = "BadTcpInternalError";
343
- StatusCode2[StatusCode2["BadTcpEndpointUrlInvalid"] = 2156068864] = "BadTcpEndpointUrlInvalid";
344
- StatusCode2[StatusCode2["BadRequestInterrupted"] = 2156134400] = "BadRequestInterrupted";
345
- StatusCode2[StatusCode2["BadRequestTimeout"] = 2156199936] = "BadRequestTimeout";
346
- StatusCode2[StatusCode2["BadSecureChannelClosed"] = 2156265472] = "BadSecureChannelClosed";
347
- StatusCode2[StatusCode2["BadSecureChannelTokenUnknown"] = 2156331008] = "BadSecureChannelTokenUnknown";
348
- StatusCode2[StatusCode2["BadSequenceNumberInvalid"] = 2156396544] = "BadSequenceNumberInvalid";
349
- StatusCode2[StatusCode2["BadProtocolVersionUnsupported"] = 2159935488] = "BadProtocolVersionUnsupported";
350
- StatusCode2[StatusCode2["BadConfigurationError"] = 2156462080] = "BadConfigurationError";
351
- StatusCode2[StatusCode2["BadNotConnected"] = 2156527616] = "BadNotConnected";
352
- StatusCode2[StatusCode2["BadDeviceFailure"] = 2156593152] = "BadDeviceFailure";
353
- StatusCode2[StatusCode2["BadSensorFailure"] = 2156658688] = "BadSensorFailure";
354
- StatusCode2[StatusCode2["BadOutOfService"] = 2156724224] = "BadOutOfService";
355
- StatusCode2[StatusCode2["BadDeadbandFilterInvalid"] = 2156789760] = "BadDeadbandFilterInvalid";
356
- StatusCode2[StatusCode2["UncertainNoCommunicationLastUsableValue"] = 1083113472] = "UncertainNoCommunicationLastUsableValue";
357
- StatusCode2[StatusCode2["UncertainLastUsableValue"] = 1083179008] = "UncertainLastUsableValue";
358
- StatusCode2[StatusCode2["UncertainSubstituteValue"] = 1083244544] = "UncertainSubstituteValue";
359
- StatusCode2[StatusCode2["UncertainInitialValue"] = 1083310080] = "UncertainInitialValue";
360
- StatusCode2[StatusCode2["UncertainSensorNotAccurate"] = 1083375616] = "UncertainSensorNotAccurate";
361
- StatusCode2[StatusCode2["UncertainEngineeringUnitsExceeded"] = 1083441152] = "UncertainEngineeringUnitsExceeded";
362
- StatusCode2[StatusCode2["UncertainSubNormal"] = 1083506688] = "UncertainSubNormal";
363
- StatusCode2[StatusCode2["GoodLocalOverride"] = 9830400] = "GoodLocalOverride";
364
- StatusCode2[StatusCode2["BadRefreshInProgress"] = 2157379584] = "BadRefreshInProgress";
365
- StatusCode2[StatusCode2["BadConditionAlreadyDisabled"] = 2157445120] = "BadConditionAlreadyDisabled";
366
- StatusCode2[StatusCode2["BadConditionAlreadyEnabled"] = 2160852992] = "BadConditionAlreadyEnabled";
367
- StatusCode2[StatusCode2["BadConditionDisabled"] = 2157510656] = "BadConditionDisabled";
368
- StatusCode2[StatusCode2["BadEventIdUnknown"] = 2157576192] = "BadEventIdUnknown";
369
- StatusCode2[StatusCode2["BadEventNotAcknowledgeable"] = 2159738880] = "BadEventNotAcknowledgeable";
370
- StatusCode2[StatusCode2["BadDialogNotActive"] = 2160918528] = "BadDialogNotActive";
371
- StatusCode2[StatusCode2["BadDialogResponseInvalid"] = 2160984064] = "BadDialogResponseInvalid";
372
- StatusCode2[StatusCode2["BadConditionBranchAlreadyAcked"] = 2161049600] = "BadConditionBranchAlreadyAcked";
373
- StatusCode2[StatusCode2["BadConditionBranchAlreadyConfirmed"] = 2161115136] = "BadConditionBranchAlreadyConfirmed";
374
- StatusCode2[StatusCode2["BadConditionAlreadyShelved"] = 2161180672] = "BadConditionAlreadyShelved";
375
- StatusCode2[StatusCode2["BadConditionNotShelved"] = 2161246208] = "BadConditionNotShelved";
376
- StatusCode2[StatusCode2["BadShelvingTimeOutOfRange"] = 2161311744] = "BadShelvingTimeOutOfRange";
377
- StatusCode2[StatusCode2["BadNoData"] = 2157641728] = "BadNoData";
378
- StatusCode2[StatusCode2["BadBoundNotFound"] = 2161573888] = "BadBoundNotFound";
379
- StatusCode2[StatusCode2["BadBoundNotSupported"] = 2161639424] = "BadBoundNotSupported";
380
- StatusCode2[StatusCode2["BadDataLost"] = 2157772800] = "BadDataLost";
381
- StatusCode2[StatusCode2["BadDataUnavailable"] = 2157838336] = "BadDataUnavailable";
382
- StatusCode2[StatusCode2["BadEntryExists"] = 2157903872] = "BadEntryExists";
383
- StatusCode2[StatusCode2["BadNoEntryExists"] = 2157969408] = "BadNoEntryExists";
384
- StatusCode2[StatusCode2["BadTimestampNotSupported"] = 2158034944] = "BadTimestampNotSupported";
385
- StatusCode2[StatusCode2["GoodEntryInserted"] = 10616832] = "GoodEntryInserted";
386
- StatusCode2[StatusCode2["GoodEntryReplaced"] = 10682368] = "GoodEntryReplaced";
387
- StatusCode2[StatusCode2["UncertainDataSubNormal"] = 1084489728] = "UncertainDataSubNormal";
388
- StatusCode2[StatusCode2["GoodNoData"] = 10813440] = "GoodNoData";
389
- StatusCode2[StatusCode2["GoodMoreData"] = 10878976] = "GoodMoreData";
390
- StatusCode2[StatusCode2["BadAggregateListMismatch"] = 2161377280] = "BadAggregateListMismatch";
391
- StatusCode2[StatusCode2["BadAggregateNotSupported"] = 2161442816] = "BadAggregateNotSupported";
392
- StatusCode2[StatusCode2["BadAggregateInvalidInputs"] = 2161508352] = "BadAggregateInvalidInputs";
393
- StatusCode2[StatusCode2["BadAggregateConfigurationRejected"] = 2161770496] = "BadAggregateConfigurationRejected";
394
- StatusCode2[StatusCode2["GoodDataIgnored"] = 14221312] = "GoodDataIgnored";
395
- StatusCode2[StatusCode2["BadRequestNotAllowed"] = 2162425856] = "BadRequestNotAllowed";
396
- StatusCode2[StatusCode2["BadRequestNotComplete"] = 2165506048] = "BadRequestNotComplete";
397
- StatusCode2[StatusCode2["GoodEdited"] = 14417920] = "GoodEdited";
398
- StatusCode2[StatusCode2["GoodPostActionFailed"] = 14483456] = "GoodPostActionFailed";
399
- StatusCode2[StatusCode2["UncertainDominantValueChanged"] = 1088290816] = "UncertainDominantValueChanged";
400
- StatusCode2[StatusCode2["GoodDependentValueChanged"] = 14680064] = "GoodDependentValueChanged";
401
- StatusCode2[StatusCode2["BadDominantValueChanged"] = 2162229248] = "BadDominantValueChanged";
402
- StatusCode2[StatusCode2["UncertainDependentValueChanged"] = 1088552960] = "UncertainDependentValueChanged";
403
- StatusCode2[StatusCode2["BadDependentValueChanged"] = 2162360320] = "BadDependentValueChanged";
404
- StatusCode2[StatusCode2["GoodEditedDependentValueChanged"] = 18219008] = "GoodEditedDependentValueChanged";
405
- StatusCode2[StatusCode2["GoodEditedDominantValueChanged"] = 18284544] = "GoodEditedDominantValueChanged";
406
- StatusCode2[StatusCode2["GoodEditedDominantValueChangedDependentValueChanged"] = 18350080] = "GoodEditedDominantValueChangedDependentValueChanged";
407
- StatusCode2[StatusCode2["BadEditedOutOfRange"] = 2165899264] = "BadEditedOutOfRange";
408
- StatusCode2[StatusCode2["BadInitialValueOutOfRange"] = 2165964800] = "BadInitialValueOutOfRange";
409
- StatusCode2[StatusCode2["BadOutOfRangeDominantValueChanged"] = 2166030336] = "BadOutOfRangeDominantValueChanged";
410
- StatusCode2[StatusCode2["BadEditedOutOfRangeDominantValueChanged"] = 2166095872] = "BadEditedOutOfRangeDominantValueChanged";
411
- StatusCode2[StatusCode2["BadOutOfRangeDominantValueChangedDependentValueChanged"] = 2166161408] = "BadOutOfRangeDominantValueChangedDependentValueChanged";
412
- StatusCode2[StatusCode2["BadEditedOutOfRangeDominantValueChangedDependentValueChanged"] = 2166226944] = "BadEditedOutOfRangeDominantValueChangedDependentValueChanged";
413
- StatusCode2[StatusCode2["GoodCommunicationEvent"] = 10944512] = "GoodCommunicationEvent";
414
- StatusCode2[StatusCode2["GoodShutdownEvent"] = 11010048] = "GoodShutdownEvent";
415
- StatusCode2[StatusCode2["GoodCallAgain"] = 11075584] = "GoodCallAgain";
416
- StatusCode2[StatusCode2["GoodNonCriticalTimeout"] = 11141120] = "GoodNonCriticalTimeout";
417
- StatusCode2[StatusCode2["BadInvalidArgument"] = 2158690304] = "BadInvalidArgument";
418
- StatusCode2[StatusCode2["BadConnectionRejected"] = 2158755840] = "BadConnectionRejected";
419
- StatusCode2[StatusCode2["BadDisconnect"] = 2158821376] = "BadDisconnect";
420
- StatusCode2[StatusCode2["BadConnectionClosed"] = 2158886912] = "BadConnectionClosed";
421
- StatusCode2[StatusCode2["BadInvalidState"] = 2158952448] = "BadInvalidState";
422
- StatusCode2[StatusCode2["BadEndOfStream"] = 2159017984] = "BadEndOfStream";
423
- StatusCode2[StatusCode2["BadNoDataAvailable"] = 2159083520] = "BadNoDataAvailable";
424
- StatusCode2[StatusCode2["BadWaitingForResponse"] = 2159149056] = "BadWaitingForResponse";
425
- StatusCode2[StatusCode2["BadOperationAbandoned"] = 2159214592] = "BadOperationAbandoned";
426
- StatusCode2[StatusCode2["BadExpectedStreamToBlock"] = 2159280128] = "BadExpectedStreamToBlock";
427
- StatusCode2[StatusCode2["BadWouldBlock"] = 2159345664] = "BadWouldBlock";
428
- StatusCode2[StatusCode2["BadSyntaxError"] = 2159411200] = "BadSyntaxError";
429
- StatusCode2[StatusCode2["BadMaxConnectionsReached"] = 2159476736] = "BadMaxConnectionsReached";
430
- return StatusCode2;
431
- })(StatusCode || {});
432
-
433
217
  // src/services/attributeService.ts
434
218
  var AttributeService = class extends ServiceBase {
435
- async ReadValue(nodeIds) {
219
+ logger = opcjsBase.getLogger("services.AttributeService");
220
+ /**
221
+ * Reads the Value attribute of one or more Nodes (OPC UA Part 4, Section 5.10.2).
222
+ * @param nodeIds - NodeIds of the Nodes to read.
223
+ * @param maxAge - Maximum age of the cached value in milliseconds the server may return. 0 = always current value.
224
+ * @param timestampsToReturn - Which timestamps to include in results. Default: Source.
225
+ * @returns Array of results containing value and raw status code number, one per requested NodeId.
226
+ */
227
+ async ReadValue(nodeIds, maxAge = 0, timestampsToReturn = opcjsBase.TimestampsToReturnEnum.Source) {
436
228
  const readValueIds = nodeIds.map((ni) => {
437
229
  const readValueId = new opcjsBase.ReadValueId();
438
230
  readValueId.nodeId = ni;
@@ -443,18 +235,18 @@ var AttributeService = class extends ServiceBase {
443
235
  });
444
236
  const request = new opcjsBase.ReadRequest();
445
237
  request.requestHeader = this.createRequestHeader();
446
- request.maxAge = 6e4;
447
- request.timestampsToReturn = opcjsBase.TimestampsToReturnEnum.Source;
238
+ request.maxAge = maxAge;
239
+ request.timestampsToReturn = timestampsToReturn;
448
240
  request.nodesToRead = readValueIds;
449
- console.log("Sending ReadRequest...");
241
+ this.logger.debug("Sending ReadRequest...");
450
242
  const response = await this.secureChannel.issueServiceRequest(request);
243
+ this.checkServiceResult(response.responseHeader?.serviceResult, "ReadRequest");
451
244
  const results = new Array();
452
245
  for (const dataValue of response.results ?? []) {
453
- const result = {
454
- status: StatusCodeToString(dataValue.statusCode),
246
+ results.push({
247
+ statusCode: dataValue.statusCode ?? opcjsBase.StatusCode.Good,
455
248
  value: dataValue.value
456
- };
457
- results.push(result);
249
+ });
458
250
  }
459
251
  return results;
460
252
  }
@@ -465,9 +257,9 @@ var AttributeService = class extends ServiceBase {
465
257
 
466
258
  // src/readValueResult.ts
467
259
  var ReadValueResult = class {
468
- constructor(value, status) {
260
+ constructor(value, statusCode) {
469
261
  this.value = value;
470
- this.status = status;
262
+ this.statusCode = statusCode;
471
263
  }
472
264
  };
473
265
 
@@ -482,13 +274,17 @@ var SubscriptionHandlerEntry = class {
482
274
  };
483
275
 
484
276
  // src/subscriptionHandler.ts
277
+ var NODE_ID_DATA_CHANGE_NOTIFICATION = 811;
278
+ var NODE_ID_STATUS_CHANGE_NOTIFICATION = 818;
485
279
  var SubscriptionHandler = class {
486
280
  constructor(subscriptionService, monitoredItemService) {
487
281
  this.subscriptionService = subscriptionService;
488
282
  this.monitoredItemService = monitoredItemService;
489
283
  }
284
+ logger = opcjsBase.getLogger("SubscriptionHandler");
490
285
  entries = new Array();
491
286
  nextHandle = 0;
287
+ isRunning = false;
492
288
  async subscribe(ids, callback) {
493
289
  if (this.entries.length > 0) {
494
290
  throw new Error("Subscribing more than once is not implemented");
@@ -496,80 +292,111 @@ var SubscriptionHandler = class {
496
292
  const subscriptionId = await this.subscriptionService.createSubscription();
497
293
  const items = [];
498
294
  for (const id of ids) {
499
- const entry = new SubscriptionHandlerEntry(
500
- subscriptionId,
501
- this.nextHandle++,
502
- id,
503
- callback
504
- );
295
+ const entry = new SubscriptionHandlerEntry(subscriptionId, this.nextHandle++, id, callback);
505
296
  this.entries.push(entry);
506
- const item = {
507
- id,
508
- handle: entry.handle
509
- };
510
- items.push(item);
297
+ items.push({ id, handle: entry.handle });
511
298
  }
512
299
  await this.monitoredItemService.createMonitoredItems(subscriptionId, items);
513
- this.publish([]);
514
- }
515
- async publish(acknowledgeSequenceNumbers) {
516
- const acknowledgements = [];
517
- for (let i = 0; i < acknowledgeSequenceNumbers.length; i++) {
518
- const acknowledgement = new opcjsBase.SubscriptionAcknowledgement();
519
- acknowledgement.subscriptionId = this.entries[i].subscriptionId;
520
- acknowledgement.sequenceNumber = acknowledgeSequenceNumbers[i];
521
- acknowledgements.push(acknowledgement);
300
+ this.isRunning = true;
301
+ void this.publishLoop([]);
302
+ }
303
+ // https://reference.opcfoundation.org/Core/Part4/v105/docs/5.14.5
304
+ async publishLoop(pendingAcknowledgements) {
305
+ if (!this.isRunning) return;
306
+ let response;
307
+ try {
308
+ response = await this.subscriptionService.publish(pendingAcknowledgements);
309
+ } catch (err) {
310
+ this.logger.error(`Publish failed, stopping publish loop: ${err}`);
311
+ this.isRunning = false;
312
+ return;
313
+ }
314
+ const { subscriptionId, availableSequenceNumbers, moreNotifications, notificationMessage } = response;
315
+ const notificationDatas = notificationMessage?.notificationData ?? [];
316
+ const seqNumber = notificationMessage?.sequenceNumber;
317
+ const nextAcknowledgements = [];
318
+ const isKeepAlive = notificationDatas.length === 0;
319
+ if (!isKeepAlive && seqNumber !== void 0) {
320
+ const isAvailable = !availableSequenceNumbers || availableSequenceNumbers.includes(seqNumber);
321
+ if (isAvailable) {
322
+ const ack = new opcjsBase.SubscriptionAcknowledgement();
323
+ ack.subscriptionId = subscriptionId;
324
+ ack.sequenceNumber = seqNumber;
325
+ nextAcknowledgements.push(ack);
326
+ }
522
327
  }
523
- const response = await this.subscriptionService.publish(acknowledgements);
524
- const messagesToAcknowledge = response.notificationMessage.sequenceNumber;
525
- const notificationDatas = response.notificationMessage.notificationData;
526
328
  for (const notificationData of notificationDatas) {
527
329
  const decodedData = notificationData.data;
528
330
  const typeNodeId = notificationData.typeId;
529
- if (typeNodeId.namespace === 0 && typeNodeId.identifier === 811) {
331
+ if (typeNodeId.namespace === 0 && typeNodeId.identifier === NODE_ID_DATA_CHANGE_NOTIFICATION) {
530
332
  const dataChangeNotification = decodedData;
531
333
  for (const item of dataChangeNotification.monitoredItems) {
532
- const clientHandle = item.clientHandle;
533
- const value = item.value;
534
- const entry = this.entries.find((e) => e.handle == clientHandle);
535
- entry?.callback([{
536
- id: entry.id,
537
- value: value.value?.value
538
- }]);
334
+ const entry = this.entries.find((e) => e.handle === item.clientHandle);
335
+ entry?.callback([{ id: entry.id, value: item.value.value?.value }]);
539
336
  }
337
+ } else if (typeNodeId.namespace === 0 && typeNodeId.identifier === NODE_ID_STATUS_CHANGE_NOTIFICATION) {
338
+ const statusChange = decodedData;
339
+ this.logger.warn(
340
+ `Subscription ${subscriptionId} status changed: 0x${statusChange.status?.toString(16).toUpperCase()}`
341
+ );
342
+ this.isRunning = false;
343
+ return;
540
344
  } else {
541
- console.log(`The change notification data type ${typeNodeId.namespace}:${typeNodeId.identifier} is not supported.`);
345
+ this.logger.warn(
346
+ `Notification data type ${typeNodeId.namespace}:${typeNodeId.identifier} is not supported.`
347
+ );
542
348
  }
543
349
  }
544
- setTimeout(() => {
545
- this.publish([messagesToAcknowledge]);
546
- }, 500);
350
+ if (moreNotifications) {
351
+ void this.publishLoop(nextAcknowledgements);
352
+ } else {
353
+ setTimeout(() => void this.publishLoop(nextAcknowledgements), 0);
354
+ }
547
355
  }
548
356
  };
549
357
  var SubscriptionService = class extends ServiceBase {
358
+ logger = opcjsBase.getLogger("services.SubscriptionService");
359
+ /**
360
+ * Creates a new subscription on the server (OPC UA Part 4, Section 5.14.2).
361
+ * @param options - Optional subscription parameters; server revises all values.
362
+ * @returns The server-assigned subscription ID.
363
+ */
550
364
  // https://reference.opcfoundation.org/Core/Part4/v105/docs/5.14.2
551
- async createSubscription() {
365
+ async createSubscription(options) {
552
366
  const request = new opcjsBase.CreateSubscriptionRequest();
553
367
  request.requestHeader = this.createRequestHeader();
554
- request.requestedPublishingInterval = 2e3;
555
- request.requestedLifetimeCount = 36e4;
556
- request.requestedMaxKeepAliveCount = 6e4;
557
- request.maxNotificationsPerPublish = 200;
368
+ request.requestedPublishingInterval = options?.requestedPublishingInterval ?? 2e3;
369
+ request.requestedLifetimeCount = options?.requestedLifetimeCount ?? 36e4;
370
+ request.requestedMaxKeepAliveCount = options?.requestedMaxKeepAliveCount ?? 6e4;
371
+ request.maxNotificationsPerPublish = options?.maxNotificationsPerPublish ?? 200;
558
372
  request.publishingEnabled = true;
559
- request.priority = 1;
560
- console.log("Sending createSubscription...");
373
+ request.priority = options?.priority ?? 1;
374
+ this.logger.debug("Sending CreateSubscriptionRequest...");
561
375
  const response = await this.secureChannel.issueServiceRequest(request);
562
- console.log("Subscription created with id:", response.subscriptionId);
376
+ const serviceResult = response.responseHeader?.serviceResult;
377
+ if (serviceResult !== void 0 && serviceResult !== opcjsBase.StatusCode.Good) {
378
+ throw new Error(`CreateSubscriptionRequest failed: ${opcjsBase.StatusCodeToString(serviceResult)}`);
379
+ }
380
+ this.logger.debug(`Subscription created with id: ${response.subscriptionId}`);
563
381
  return response.subscriptionId;
564
382
  }
383
+ /**
384
+ * Sends a publish request to receive notifications from active subscriptions (OPC UA Part 4, Section 5.14.5).
385
+ * @param acknowledgements - Sequence numbers to acknowledge from previous publish responses.
386
+ * @returns The publish response containing notification data.
387
+ */
565
388
  // https://reference.opcfoundation.org/Core/Part4/v105/docs/5.14.5
566
389
  async publish(acknowledgements) {
567
390
  const request = new opcjsBase.PublishRequest();
568
391
  request.requestHeader = this.createRequestHeader();
569
392
  request.subscriptionAcknowledgements = acknowledgements;
570
- console.log("Sending publish...");
393
+ this.logger.debug("Sending PublishRequest...");
571
394
  const response = await this.secureChannel.issueServiceRequest(request);
572
- console.log("Received publish response.");
395
+ const serviceResult = response.responseHeader?.serviceResult;
396
+ if (serviceResult !== void 0 && serviceResult !== opcjsBase.StatusCode.Good) {
397
+ throw new Error(`PublishRequest failed: ${opcjsBase.StatusCodeToString(serviceResult)}`);
398
+ }
399
+ this.logger.debug("Received PublishResponse.");
573
400
  return response;
574
401
  }
575
402
  constructor(authToken, secureChannel) {
@@ -577,7 +404,15 @@ var SubscriptionService = class extends ServiceBase {
577
404
  }
578
405
  };
579
406
  var MonitoredItemService = class extends ServiceBase {
580
- async createMonitoredItems(subscriptionId, ids) {
407
+ logger = opcjsBase.getLogger("services.MonitoredItemService");
408
+ /**
409
+ * Creates monitored items within a subscription (OPC UA Part 4, Section 5.13.2).
410
+ * @param subscriptionId - ID of the subscription to add monitored items to.
411
+ * @param ids - Array of NodeIds and client handles identifying the items to monitor.
412
+ * @param samplingInterval - Requested sampling interval in milliseconds. -1 = use subscription publishing interval.
413
+ * @param queueSize - Requested queue size for each monitored item.
414
+ */
415
+ async createMonitoredItems(subscriptionId, ids, samplingInterval = 1e3, queueSize = 100) {
581
416
  const items = ids.map((ni) => {
582
417
  const readValueId = new opcjsBase.ReadValueId();
583
418
  readValueId.nodeId = ni.id;
@@ -586,9 +421,9 @@ var MonitoredItemService = class extends ServiceBase {
586
421
  readValueId.dataEncoding = new opcjsBase.QualifiedName(0, "");
587
422
  const monitoringParameters = new opcjsBase.MonitoringParameters();
588
423
  monitoringParameters.clientHandle = ni.handle;
589
- monitoringParameters.samplingInterval = 1e3;
424
+ monitoringParameters.samplingInterval = samplingInterval;
590
425
  monitoringParameters.filter = opcjsBase.ExtensionObject.newEmpty();
591
- monitoringParameters.queueSize = 100;
426
+ monitoringParameters.queueSize = queueSize;
592
427
  monitoringParameters.discardOldest = true;
593
428
  const monitoredItemCreateRequest = new opcjsBase.MonitoredItemCreateRequest();
594
429
  monitoredItemCreateRequest.itemToMonitor = readValueId;
@@ -601,26 +436,38 @@ var MonitoredItemService = class extends ServiceBase {
601
436
  request.subscriptionId = subscriptionId;
602
437
  request.timestampsToReturn = opcjsBase.TimestampsToReturnEnum.Source;
603
438
  request.itemsToCreate = items;
604
- console.log("Sending createMonitoredItems...");
605
- await this.secureChannel.issueServiceRequest(request);
439
+ this.logger.debug("Sending CreateMonitoredItemsRequest...");
440
+ const response = await this.secureChannel.issueServiceRequest(request);
441
+ const serviceResult = response.responseHeader?.serviceResult;
442
+ if (serviceResult !== void 0 && serviceResult !== opcjsBase.StatusCode.Good) {
443
+ throw new Error(`CreateMonitoredItemsRequest failed: ${opcjsBase.StatusCodeToString(serviceResult)}`);
444
+ }
445
+ for (const result of response.results ?? []) {
446
+ if (result.statusCode !== void 0 && result.statusCode !== opcjsBase.StatusCode.Good) {
447
+ this.logger.warn(`Failed to create monitored item: ${opcjsBase.StatusCodeToString(result.statusCode)}`);
448
+ }
449
+ }
606
450
  }
607
451
  constructor(authToken, secureChannel) {
608
452
  super(authToken, secureChannel);
609
453
  }
610
454
  };
611
455
  var MethodService = class extends ServiceBase {
456
+ logger = opcjsBase.getLogger("services.MethodService");
612
457
  /**
613
458
  * Calls one or more methods on the server (OPC UA Part 4, Section 5.11.2).
614
459
  * @param methodsToCall - Array of CallMethodRequest describing each method to invoke.
615
- * @returns Array of CallMethodResult, one per requested method.
460
+ * @returns Array of results containing output argument values and raw status code number, one per requested method.
616
461
  */
617
462
  async call(methodsToCall) {
618
463
  const request = new opcjsBase.CallRequest();
619
464
  request.requestHeader = this.createRequestHeader();
620
465
  request.methodsToCall = methodsToCall;
466
+ this.logger.debug("Sending CallRequest...");
621
467
  const response = await this.secureChannel.issueServiceRequest(request);
468
+ this.checkServiceResult(response.responseHeader?.serviceResult, "CallRequest");
622
469
  return response.results.map((result) => ({
623
- status: StatusCodeToString(result.statusCode),
470
+ statusCode: result.statusCode ?? opcjsBase.StatusCode.Good,
624
471
  value: result.outputArguments.map((arg) => arg.value)
625
472
  }));
626
473
  }
@@ -631,12 +478,18 @@ var MethodService = class extends ServiceBase {
631
478
 
632
479
  // src/callMethodResult.ts
633
480
  var CallMethodResult = class {
634
- constructor(values, status) {
481
+ constructor(values, statusCode) {
635
482
  this.values = values;
636
- this.status = status;
483
+ this.statusCode = statusCode;
637
484
  }
638
485
  };
639
486
  var BrowseService = class extends ServiceBase {
487
+ logger = opcjsBase.getLogger("services.BrowseService");
488
+ /**
489
+ * Browses one or more Nodes and returns their References (OPC UA Part 4, Section 5.9.2).
490
+ * @param nodesToBrowse - Array of BrowseDescriptions specifying nodes and filter criteria.
491
+ * @returns Array of BrowseResult, one per requested node.
492
+ */
640
493
  async browse(nodesToBrowse) {
641
494
  const view = new opcjsBase.ViewDescription();
642
495
  view.viewId = opcjsBase.NodeId.newNumeric(0, 0);
@@ -647,15 +500,25 @@ var BrowseService = class extends ServiceBase {
647
500
  request.view = view;
648
501
  request.requestedMaxReferencesPerNode = 0;
649
502
  request.nodesToBrowse = nodesToBrowse;
503
+ this.logger.debug("Sending BrowseRequest...");
650
504
  const response = await this.secureChannel.issueServiceRequest(request);
505
+ this.checkServiceResult(response.responseHeader?.serviceResult, "BrowseRequest");
651
506
  return response.results ?? [];
652
507
  }
508
+ /**
509
+ * Continues a Browse operation using continuation points (OPC UA Part 4, Section 5.9.3).
510
+ * @param continuationPoints - Continuation points returned by a prior Browse or BrowseNext call.
511
+ * @param releaseContinuationPoints - If true, releases the continuation points without returning results.
512
+ * @returns Array of BrowseResult, one per continuation point.
513
+ */
653
514
  async browseNext(continuationPoints, releaseContinuationPoints) {
654
515
  const request = new opcjsBase.BrowseNextRequest();
655
516
  request.requestHeader = this.createRequestHeader();
656
517
  request.releaseContinuationPoints = releaseContinuationPoints;
657
518
  request.continuationPoints = continuationPoints;
519
+ this.logger.debug("Sending BrowseNextRequest...");
658
520
  const response = await this.secureChannel.issueServiceRequest(request);
521
+ this.checkServiceResult(response.responseHeader?.serviceResult, "BrowseNextRequest");
659
522
  return response.results ?? [];
660
523
  }
661
524
  constructor(authToken, secureChannel) {
@@ -692,12 +555,50 @@ var Client = class {
692
555
  session;
693
556
  subscriptionHandler;
694
557
  logger;
558
+ // Stored after connect() so that refreshSession() can recreate services.
559
+ secureChannel;
560
+ sessionHandler;
695
561
  getSession() {
696
562
  if (!this.session) {
697
563
  throw new Error("No session available");
698
564
  }
699
565
  return this.session;
700
566
  }
567
+ /**
568
+ * (Re-)initialises all session-scoped services from the current `this.session`.
569
+ * Called both after the initial `connect()` and after a session refresh.
570
+ */
571
+ initServices() {
572
+ const authToken = this.session.getAuthToken();
573
+ const sc = this.secureChannel;
574
+ this.attributeService = new AttributeService(authToken, sc);
575
+ this.methodService = new MethodService(authToken, sc);
576
+ this.browseService = new BrowseService(authToken, sc);
577
+ this.subscriptionHandler = new SubscriptionHandler(
578
+ new SubscriptionService(authToken, sc),
579
+ new MonitoredItemService(authToken, sc)
580
+ );
581
+ }
582
+ /**
583
+ * Executes `fn` and, if it throws a `SessionInvalidError`, creates a fresh
584
+ * session and retries the operation exactly once.
585
+ *
586
+ * This covers the reactive case: a service call reveals that the server has
587
+ * already dropped the session (e.g. due to timeout). The new session is
588
+ * established transparently before re-running the original operation.
589
+ */
590
+ async withSessionRefresh(fn) {
591
+ try {
592
+ return await fn();
593
+ } catch (err) {
594
+ if (!(err instanceof SessionInvalidError)) throw err;
595
+ this.logger.info(`Session invalid (${err.statusCode.toString(16)}), refreshing session...`);
596
+ this.session = await this.sessionHandler.createNewSession(this.identity);
597
+ this.initServices();
598
+ this.logger.info("Session refreshed, retrying operation.");
599
+ return await fn();
600
+ }
601
+ }
701
602
  async connect() {
702
603
  const wsOptions = { endpoint: this.endpointUrl };
703
604
  const ws = new opcjsBase.WebSocketFascade(wsOptions);
@@ -742,24 +643,21 @@ var Client = class {
742
643
  await sc.openSecureChannel();
743
644
  this.logger.debug("Secure channel established.");
744
645
  this.logger.debug("Creating session...");
745
- const sessionHandler = new SessionHandler(sc, this.configuration);
746
- this.session = await sessionHandler.createNewSession(this.identity);
646
+ this.sessionHandler = new SessionHandler(sc, this.configuration);
647
+ this.secureChannel = sc;
648
+ this.session = await this.sessionHandler.createNewSession(this.identity);
747
649
  this.logger.debug("Session created.");
748
650
  this.logger.debug("Initializing services...");
749
- this.attributeService = new AttributeService(this.session.getAuthToken(), sc);
750
- this.methodService = new MethodService(this.session.getAuthToken(), sc);
751
- this.browseService = new BrowseService(this.session.getAuthToken(), sc);
752
- this.subscriptionHandler = new SubscriptionHandler(
753
- new SubscriptionService(this.session.getAuthToken(), sc),
754
- new MonitoredItemService(this.session.getAuthToken(), sc)
755
- );
651
+ this.initServices();
756
652
  }
757
653
  async disconnect() {
758
654
  this.logger.info("Disconnecting from OPC UA server...");
759
655
  }
760
656
  async read(ids) {
761
- const result = await this.attributeService?.ReadValue(ids);
762
- return result?.map((r) => new ReadValueResult(r.value, r.status)) || [];
657
+ return this.withSessionRefresh(async () => {
658
+ const result = await this.attributeService?.ReadValue(ids);
659
+ return result?.map((r) => new ReadValueResult(r.value, r.statusCode)) ?? [];
660
+ });
763
661
  }
764
662
  /**
765
663
  * Method for calling a single method on the server.
@@ -769,17 +667,21 @@ var Client = class {
769
667
  * @returns The CallMethodResult for the invoked method.
770
668
  */
771
669
  async callMethod(objectId, methodId, inputArguments = []) {
772
- const request = new opcjsBase.CallMethodRequest();
773
- request.objectId = objectId;
774
- request.methodId = methodId;
775
- request.inputArguments = inputArguments.map((arg) => opcjsBase.Variant.newFrom(arg));
776
- const responses = await this.methodService.call([request]);
777
- const response = responses[0];
778
- return new CallMethodResult(response.value, response.status);
670
+ return this.withSessionRefresh(async () => {
671
+ const request = new opcjsBase.CallMethodRequest();
672
+ request.objectId = objectId;
673
+ request.methodId = methodId;
674
+ request.inputArguments = inputArguments.map((arg) => opcjsBase.Variant.newFrom(arg));
675
+ const responses = await this.methodService.call([request]);
676
+ const response = responses[0];
677
+ return new CallMethodResult(response.value, response.statusCode);
678
+ });
779
679
  }
780
680
  async browse(nodeId, recursive = false) {
781
- const visited = /* @__PURE__ */ new Set();
782
- return this.browseRecursive(nodeId, recursive, visited);
681
+ return this.withSessionRefresh(() => {
682
+ const visited = /* @__PURE__ */ new Set();
683
+ return this.browseRecursive(nodeId, recursive, visited);
684
+ });
783
685
  }
784
686
  async browseRecursive(nodeId, recursive, visited) {
785
687
  const nodeKey = `${nodeId.namespace}:${nodeId.identifier}`;
@@ -928,6 +830,7 @@ var UserIdentity = class _UserIdentity {
928
830
  exports.BrowseNodeResult = BrowseNodeResult;
929
831
  exports.Client = Client;
930
832
  exports.ConfigurationClient = ConfigurationClient;
833
+ exports.SessionInvalidError = SessionInvalidError;
931
834
  exports.UserIdentity = UserIdentity;
932
835
  //# sourceMappingURL=index.cjs.map
933
836
  //# sourceMappingURL=index.cjs.map