node-opcua-client 2.173.0 → 2.174.0
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/README.md +67 -33
- package/dist/client_session.d.ts +75 -3
- package/dist/opcua_client.d.ts +139 -5
- package/dist/opcua_client.js +40 -0
- package/dist/opcua_client.js.map +1 -1
- package/dist/private/client_session_impl.d.ts +1 -0
- package/dist/private/client_session_impl.js +1 -0
- package/dist/private/client_session_impl.js.map +1 -1
- package/dist/private/opcua_client_impl.js +6 -2
- package/dist/private/opcua_client_impl.js.map +1 -1
- package/dist/private/reconnection/client_publish_engine_reconnection.js +41 -7
- package/dist/private/reconnection/client_publish_engine_reconnection.js.map +1 -1
- package/dist/private/reconnection/reconnection.js +12 -0
- package/dist/private/reconnection/reconnection.js.map +1 -1
- package/package.json +38 -38
- package/source/client_session.ts +75 -3
- package/source/opcua_client.ts +139 -6
- package/source/private/client_session_impl.ts +15 -15
- package/source/private/opcua_client_impl.ts +19 -18
- package/source/private/reconnection/client_publish_engine_reconnection.ts +43 -8
- package/source/private/reconnection/reconnection.ts +13 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import assert from "node-opcua-assert";
|
|
3
3
|
import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
|
|
4
|
-
import { StatusCodes } from "node-opcua-status-code";
|
|
4
|
+
import { type StatusCode, StatusCodes } from "node-opcua-status-code";
|
|
5
5
|
import { RepublishRequest, type RepublishResponse } from "node-opcua-types";
|
|
6
6
|
import type { ClientSidePublishEngine } from "../client_publish_engine";
|
|
7
7
|
import type { ClientSessionImpl } from "../client_session_impl";
|
|
@@ -12,6 +12,33 @@ import { _shouldNotContinue2 } from "./reconnection";
|
|
|
12
12
|
const debugLog = make_debugLog("RECONNECTION");
|
|
13
13
|
const doDebug = checkDebugFlag("RECONNECTION");
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Recover the StatusCode carried by a Republish reply, whether it came back as a regular
|
|
17
|
+
* RepublishResponse (operation-level serviceResult) or as a ServiceFault.
|
|
18
|
+
*
|
|
19
|
+
* Precedence:
|
|
20
|
+
* 1. the explicit `response` argument, when the server answered with a regular RepublishResponse
|
|
21
|
+
* (the operation-level serviceResult, e.g. CoDeSys);
|
|
22
|
+
* 2. otherwise `err.response` — the secure channel layer turns a ServiceFault into an Error and
|
|
23
|
+
* attaches the original response there;
|
|
24
|
+
* 3. otherwise fall back to parsing the error message.
|
|
25
|
+
*/
|
|
26
|
+
function extractStatusCode(err: Error | null | undefined, response?: RepublishResponse): StatusCode {
|
|
27
|
+
if (response) {
|
|
28
|
+
return response.responseHeader.serviceResult;
|
|
29
|
+
}
|
|
30
|
+
if (err) {
|
|
31
|
+
const faultResponse = (err as { response?: { responseHeader?: { serviceResult?: StatusCode } } }).response;
|
|
32
|
+
if (faultResponse?.responseHeader?.serviceResult) {
|
|
33
|
+
return faultResponse.responseHeader.serviceResult;
|
|
34
|
+
}
|
|
35
|
+
if (/BadMessageNotAvailable/.test(err.message)) {
|
|
36
|
+
return StatusCodes.BadMessageNotAvailable;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return StatusCodes.Bad;
|
|
40
|
+
}
|
|
41
|
+
|
|
15
42
|
function _sendRepublish(
|
|
16
43
|
session: ClientSessionImpl,
|
|
17
44
|
subscription: ClientSubscriptionImpl
|
|
@@ -41,16 +68,22 @@ function _sendRepublish(
|
|
|
41
68
|
}
|
|
42
69
|
|
|
43
70
|
session.republish(request, (err: Error | null, response?: RepublishResponse) => {
|
|
71
|
+
// BadMessageNotAvailable signals the end of the republish loop: there are no more
|
|
72
|
+
// queued NotificationMessages to replay and the client must now resume normal Publish
|
|
73
|
+
// handling (OPC UA Part 4 §6.5/§6.7). A server may report it either as a ServiceFault
|
|
74
|
+
// (err set, no response) or as the serviceResult of a regular RepublishResponse
|
|
75
|
+
// (operation-level result, e.g. CoDeSys). Both cases must terminate the loop.
|
|
76
|
+
const statusCode = extractStatusCode(err, response);
|
|
77
|
+
if (statusCode.equals(StatusCodes.BadMessageNotAvailable)) {
|
|
78
|
+
return resolve({ isDone: true });
|
|
79
|
+
}
|
|
44
80
|
if (!response) {
|
|
45
81
|
reject(err);
|
|
46
82
|
return;
|
|
47
83
|
}
|
|
48
|
-
|
|
49
|
-
if (!err && (statusCode.equals(StatusCodes.Good) || statusCode.equals(StatusCodes.BadMessageNotAvailable))) {
|
|
84
|
+
if (!err && statusCode.equals(StatusCodes.Good)) {
|
|
50
85
|
// reprocess notification message and keep going
|
|
51
|
-
|
|
52
|
-
subscription.onNotificationMessage(response.notificationMessage);
|
|
53
|
-
}
|
|
86
|
+
subscription.onNotificationMessage(response.notificationMessage);
|
|
54
87
|
return resolve({ isDone: false });
|
|
55
88
|
}
|
|
56
89
|
if (!err) {
|
|
@@ -120,8 +153,10 @@ async function __askSubscriptionRepublish(
|
|
|
120
153
|
await recreateSubscriptionAndMonitoredItem(subscription);
|
|
121
154
|
return;
|
|
122
155
|
}
|
|
123
|
-
if (error.message.match(
|
|
124
|
-
//
|
|
156
|
+
if (error.message.match(/BadMessageNotAvailable/)) {
|
|
157
|
+
// End of the republish loop: no more queued messages to replay.
|
|
158
|
+
// Nothing else to do here, the caller will resume normal Publish handling.
|
|
159
|
+
return;
|
|
125
160
|
}
|
|
126
161
|
}
|
|
127
162
|
// check after successful republish too
|
|
@@ -162,6 +162,19 @@ function _ask_for_subscription_republish(session: ClientSessionImpl, callback: (
|
|
|
162
162
|
return repair_client_session_by_recreating_a_new_session(session._client!, session, callback);
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
// Republish has completed: resume normal Publish handling. During the connection break the
|
|
166
|
+
// in-flight PublishRequests have failed and the publish-request queue has drained, so we must
|
|
167
|
+
// replenish it now, otherwise the client would stop sending PublishRequests and never receive
|
|
168
|
+
// live notifications again. (https://github.com/node-opcua/node-opcua/issues/1524)
|
|
169
|
+
// Note: on the new-session repair path the engine is still suspended here, so this is a no-op
|
|
170
|
+
// there and the queue is replenished later by suspend(false).
|
|
171
|
+
// Guard on subscriptionCount: with no subscription, send_publish_request() would still emit
|
|
172
|
+
// spurious PublishRequests (it does not check the subscription count itself), adding useless
|
|
173
|
+
// round-trips on every reconnection of a subscription-less session.
|
|
174
|
+
if (engine.subscriptionCount > 0) {
|
|
175
|
+
engine.replenish_publish_request_queue();
|
|
176
|
+
}
|
|
177
|
+
|
|
165
178
|
callback(err);
|
|
166
179
|
});
|
|
167
180
|
}
|