• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

node-opcua / node-opcua / 22636309940

03 Mar 2026 06:03PM UTC coverage: 92.756% (+1.9%) from 90.81%
22636309940

push

github

erossignon
test: disable ENOENT on overlapping trustCertificate invocations in test environments with concurrency tests

18684 of 22141 branches covered (84.39%)

23 of 37 new or added lines in 1 file covered. (62.16%)

5775 existing lines in 228 files now uncovered.

160668 of 173216 relevant lines covered (92.76%)

875869.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.7
/packages/node-opcua-client-proxy/source/proxy_manager.ts
1
/**
2✔
2
 * @module node-opcua-client-proxy
2✔
3
 */
2✔
4
// tslint:disable:no-shadowed-variable
2✔
5
import { EventEmitter } from "stream";
2✔
6
import { assert } from "node-opcua-assert";
2✔
7

2✔
8
import { AttributeIds, NodeClass, coerceAccessLevelFlag } from "node-opcua-data-model";
2✔
9
import { NodeId, coerceNodeId } from "node-opcua-nodeid";
2✔
10
import { DataValue, TimestampsToReturn } from "node-opcua-data-value";
2✔
11
import { NodeIdLike } from "node-opcua-nodeid";
2✔
12
import { CreateSubscriptionRequestOptions, MonitoringParametersOptions } from "node-opcua-service-subscription";
2✔
13
import { StatusCodes } from "node-opcua-status-code";
2✔
14
import { IBasicSessionAsync, IBasicSessionGetArgumentDefinitionAsync } from "node-opcua-pseudo-session";
2✔
15
import { ReadValueIdOptions } from "node-opcua-service-read";
2✔
16
import { Variant } from "node-opcua-variant";
2✔
17
import { make_debugLog } from "node-opcua-debug";
2✔
18

2✔
19
import { readUAStructure } from "./object_explorer";
2✔
20
import { makeRefId } from "./proxy";
2✔
21
import { ProxyObject } from "./proxy_object";
2✔
22
import { ProxyStateMachineType } from "./state_machine_proxy";
2✔
23
import { ProxyNode } from "./proxy_transition";
2✔
24

2✔
25
const debugLog = make_debugLog(__filename);
2✔
26

2✔
27
export interface IProxy1 {
2✔
28
    nodeId: NodeId;
2✔
29
    executableFlag?: boolean;
2✔
30
    __monitoredItem_execution_flag?: EventEmitter;
2✔
31
    __monitoredItem?: EventEmitter;
2✔
32
}
2✔
33
export interface IProxy extends EventEmitter, IProxy1 {
2✔
34
    dataValue: DataValue;
2✔
35
}
2✔
36

2✔
37
async function internalGetObject(
866✔
38
    proxyManager: UAProxyManager, 
866✔
39
    nodeId: NodeIdLike | NodeId, options: any
866✔
40
): Promise<ProxyNode> {
866✔
41
   
866✔
42
    const session = proxyManager.session;
866✔
43

866✔
44
    nodeId = coerceNodeId(nodeId) as NodeId;
866✔
45

866✔
46
    if (nodeId.isEmpty()) {
866!
47
        throw new Error(" Invalid empty node in getObject");
×
UNCOV
48
    }
×
49

866✔
50
    const nodesToRead = [
866✔
51
        {
866✔
52
            attributeId: AttributeIds.BrowseName,
866✔
53
            nodeId
866✔
54
        },
866✔
55
        {
866✔
56
            attributeId: AttributeIds.Description,
866✔
57
            nodeId
866✔
58
        },
866✔
59
        {
866✔
60
            attributeId: AttributeIds.NodeClass,
866✔
61
            nodeId
866✔
62
        }
866✔
63
    ];
866✔
64

866✔
65
    async function read_accessLevels(clientObject: any) {
866✔
66
        const nodesToRead = [
514✔
67
            {
514✔
68
                attributeId: AttributeIds.Value,
514✔
69
                nodeId
514✔
70
            },
514✔
71
            {
514✔
72
                attributeId: AttributeIds.UserAccessLevel,
514✔
73
                nodeId
514✔
74
            },
514✔
75
            {
514✔
76
                attributeId: AttributeIds.AccessLevel,
514✔
77
                nodeId
514✔
78
            }
514✔
79
        ];
514✔
80

514✔
81
        const dataValues = await session.read(nodesToRead);
514✔
82

514✔
83
        if (dataValues[0].statusCode.isGood()) {
514✔
84
            clientObject.dataValue = dataValues[0].value;
498✔
85
        }
498✔
86
        if (dataValues[1].statusCode.isGood()) {
514✔
87
            clientObject.userAccessLevel = coerceAccessLevelFlag(dataValues[1].value.value);
514✔
88
        }
514✔
89
        if (dataValues[2].statusCode.isGood()) {
514✔
90
            clientObject.accessLevel = coerceAccessLevelFlag(dataValues[2].value.value);
514✔
91
        }
514✔
92
    }
514✔
93

866✔
94
    let clientObject: any;
866✔
95

866✔
96
    const dataValues = await session.read(nodesToRead);
866✔
97

866✔
98
    if (dataValues[0].statusCode.equals(StatusCodes.BadNodeIdUnknown)) {
866!
99
        throw new Error("Invalid Node " + nodeId.toString());
×
UNCOV
100
    }
×
101

866✔
102
    clientObject = new ProxyObject(proxyManager, nodeId as NodeId);
866✔
103

866✔
104
    clientObject.browseName = dataValues[0].value.value;
866✔
105
    clientObject.description = dataValues[1].value ? dataValues[1].value.value : "";
866!
106
    clientObject.nodeClass = dataValues[2].value.value;
866✔
107

866✔
108
    if (clientObject.nodeClass === NodeClass.Variable) {
866✔
109
        await read_accessLevels(clientObject);
514✔
110
    }
514✔
111
    // install monitored item
866✔
112
    if (clientObject.nodeClass === NodeClass.Variable) {
866✔
113
        await proxyManager._monitor_value(clientObject);
514✔
114
    }
514✔
115

866✔
116
    return await readUAStructure(proxyManager, clientObject);
866✔
117
}
866✔
118

2✔
119
export interface IClientSubscription {
2✔
120
    monitor(
2✔
121
        itemToMonitor: ReadValueIdOptions,
2✔
122
        monitoringParameters: MonitoringParametersOptions,
2✔
123
        timestampToReturn: TimestampsToReturn
2✔
124
    ): Promise<IClientMonitoredItemBase>;
2✔
125

2✔
126
    terminate(): Promise<void>;
2✔
127
    on(eventName: "terminated", eventHandler: () => void): void;
2✔
128
}
2✔
129

2✔
130
export interface IClientMonitoredItemBase {
2✔
131
    on(eventName: "changed", eventHandler: (data: DataValue | Variant[]) => void): void;
2✔
132
}
2✔
133
export interface IBasicSessionWithSubscriptionAsync extends IBasicSessionAsync, IBasicSessionGetArgumentDefinitionAsync {
2✔
134
    createSubscription2(options: CreateSubscriptionRequestOptions): Promise<IClientSubscription>;
2✔
135
}
2✔
136

2✔
137
// tslint:disable-next-line: max-classes-per-file
2✔
138
export class UAProxyManager {
16✔
139
    public readonly session: IBasicSessionWithSubscriptionAsync;
16✔
140
    public subscription?: IClientSubscription;
16✔
141
    #_map: any;
16✔
142

16✔
143
    constructor(session: IBasicSessionWithSubscriptionAsync) {
16✔
144
        this.session = session;
16✔
145
        this.#_map = {};
16✔
146
    }
16✔
147

16✔
148
    public async start(): Promise<void> {
16✔
149

16✔
150
        const createSubscriptionRequest: CreateSubscriptionRequestOptions = {
16✔
151
            maxNotificationsPerPublish: 1000,
16✔
152
            priority: 10,
16✔
153
            publishingEnabled: true,
16✔
154
            requestedLifetimeCount: 6000,
16✔
155
            requestedMaxKeepAliveCount: 100,
16✔
156
            requestedPublishingInterval: 100
16✔
157
        };
16✔
158

16✔
159
        const subscription = await this.session.createSubscription2(createSubscriptionRequest);
16✔
160
        this.subscription = subscription!;
16✔
161
        this.subscription!.on("terminated", () => {
16✔
162
            this.subscription = undefined;
16✔
163
        });
16✔
164
    }
16✔
165

16✔
166
    public async stop(): Promise<void> {
16✔
167
        if (this.subscription) {
16✔
168
            await this.subscription.terminate();
16✔
169
            this.subscription = undefined;
16✔
170
        } else {
16!
UNCOV
171
            // throw new Error("UAProxyManager already stopped ?");
×
UNCOV
172
        }
×
173
    }
16✔
174

16✔
175
    // todo: rename getObject as getNode
16✔
176
    public async getObject(nodeId: NodeIdLike): Promise<ProxyNode> {
16✔
177
        let options: any = {};
1,600✔
178
        options = options || {};
1,600!
179
        options.depth = options.depth || 1;
1,600✔
180

1,600✔
181
        const key = nodeId.toString();
1,600✔
182
        // the object already exist in the map ?
1,600✔
183
        if (Object.prototype.hasOwnProperty.call(this.#_map, key)) {
1,600✔
184
            return this.#_map[key];
734✔
185
        }
734✔
186

866✔
187
        const obj = await internalGetObject(this, nodeId, options);
866✔
188
        this.#_map[key] = obj;
866✔
189
        return obj;
866✔
190
    }
866✔
191

16✔
192
    public async _monitor_value(proxyObject: IProxy): Promise<void> {
16✔
193
        if (!this.subscription) {
514!
UNCOV
194
            // debugLog("cannot monitor _monitor_value: no subscription");
×
UNCOV
195
            // some server do not provide subscription support, do not treat this as an error.
×
196
            return; // new Error("No subscription"));
×
UNCOV
197
        }
×
198

514✔
199
        const itemToMonitor: ReadValueIdOptions = {
514✔
200
            // ReadValueId
514✔
201
            attributeId: AttributeIds.Value,
514✔
202
            nodeId: proxyObject.nodeId
514✔
203
        };
514✔
204
        const monitoringParameters: MonitoringParametersOptions = {
514✔
205
            // MonitoringParameters
514✔
206
            discardOldest: true,
514✔
207
            queueSize: 10,
514✔
208
            samplingInterval: 0 /* event-based */
514✔
209
        };
514✔
210
        const requestedParameters = TimestampsToReturn.Both;
514✔
211

514✔
212
        const monitoredItem = await this.subscription.monitor(itemToMonitor, monitoringParameters, requestedParameters);
514✔
213

514✔
214
        Object.defineProperty(proxyObject, "__monitoredItem", { value: monitoredItem, enumerable: false });
514✔
215
        proxyObject.__monitoredItem!.on("changed", (dataValue: DataValue) => {
514✔
216
            proxyObject.dataValue = dataValue;
598✔
217
            proxyObject.emit("value_changed", dataValue);
598✔
218
        });
514✔
219
        proxyObject.__monitoredItem!.on("err", (err: Error) => {
514✔
220
            debugLog("Proxy: cannot monitor variable ", itemToMonitor.nodeId?.toString(), err.message);
×
221
        });
514✔
222
    }
514✔
223

16✔
224
    public async _monitor_execution_flag(proxyObject: IProxy1): Promise<void> {
16✔
225
        // note : proxyObject must wrap a method
184✔
226
        assert(proxyObject.nodeId instanceof NodeId);
184✔
227

184✔
228
        if (!this.subscription) {
184!
UNCOV
229
            // some server do not provide subscription support, do not treat this as an error.
×
230
            return; // new Error("No subscription"));
×
UNCOV
231
        }
×
232

184✔
233
        const itemToMonitor = {
184✔
234
            // ReadValueId
184✔
235
            attributeId: AttributeIds.Executable,
184✔
236
            nodeId: proxyObject.nodeId
184✔
237
        };
184✔
238

184✔
239
        const monitoringParameters = {
184✔
240
            // MonitoringParameters
184✔
241
            discardOldest: true,
184✔
242
            queueSize: 10,
184✔
243
            samplingInterval: 0 /* event-based */
184✔
244
        };
184✔
245
        const requestedParameters = TimestampsToReturn.Neither;
184✔
246

184✔
247
        const monitoredItem = await this.subscription.monitor(itemToMonitor, monitoringParameters, requestedParameters);
184✔
248
        Object.defineProperty(proxyObject, "__monitoredItem_execution_flag", {
184✔
249
            value: monitoredItem,
184✔
250

184✔
251
            enumerable: false
184✔
252
        });
184✔
253
        proxyObject.__monitoredItem_execution_flag!.on("changed", (dataValue: DataValue) => {
184✔
254
            proxyObject.executableFlag = dataValue.value.value;
196✔
255
        });
184✔
256
    }
184✔
257
    public async getStateMachineType(nodeId: NodeIdLike): Promise<ProxyStateMachineType> {
16✔
258
        if (typeof nodeId === "string") {
8✔
259
            nodeId = makeRefId(nodeId);
6✔
260
        }
6✔
261
        const obj = await this.getObject(nodeId);
8✔
262
        return new ProxyStateMachineType(obj);
8✔
263
    }
8✔
264
}
16✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc