node-opcua-address-space 2.115.0 → 2.116.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/dist/source/helpers/call_helpers.d.ts +1 -2
- package/dist/source/helpers/call_helpers.js +15 -0
- package/dist/source/helpers/call_helpers.js.map +1 -1
- package/dist/source/index.d.ts +1 -0
- package/dist/source/index.js +3 -1
- package/dist/source/index.js.map +1 -1
- package/dist/source/loader/generateAddressSpaceRaw.d.ts +19 -1
- package/dist/source/loader/generateAddressSpaceRaw.js +153 -4
- package/dist/source/loader/generateAddressSpaceRaw.js.map +1 -1
- package/dist/source/loader/load_nodeset2.js +1 -8
- package/dist/source/loader/load_nodeset2.js.map +1 -1
- package/dist/src/index_current.d.ts +1 -0
- package/dist/src/index_current.js +1 -0
- package/dist/src/index_current.js.map +1 -1
- package/dist/src/ua_variable_impl.js +2 -74
- package/dist/src/ua_variable_impl.js.map +1 -1
- package/dist/src/validate_data_type_correctness.d.ts +6 -0
- package/dist/src/validate_data_type_correctness.js +98 -0
- package/dist/src/validate_data_type_correctness.js.map +1 -0
- package/dist/tsconfig_common.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/source/helpers/call_helpers.ts +1 -1
- package/source/index.ts +1 -1
- package/source/loader/generateAddressSpaceRaw.ts +174 -5
- package/source/loader/load_nodeset2.ts +1 -7
- package/src/index_current.ts +1 -1
- package/src/ua_variable_impl.ts +2 -89
- package/src/validate_data_type_correctness.ts +115 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-opcua-address-space",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.116.0",
|
|
4
4
|
"description": "pure nodejs OPCUA SDK - module address-space",
|
|
5
5
|
"main": "./dist/src/index_current.js",
|
|
6
6
|
"types": "./dist/source/index.d.ts",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"node-opcua-assert": "2.105.0",
|
|
27
27
|
"node-opcua-basic-types": "2.114.0",
|
|
28
28
|
"node-opcua-binary-stream": "2.114.0",
|
|
29
|
-
"node-opcua-client-dynamic-extension-object": "2.
|
|
29
|
+
"node-opcua-client-dynamic-extension-object": "2.116.0",
|
|
30
30
|
"node-opcua-constants": "2.114.0",
|
|
31
31
|
"node-opcua-crypto": "4.5.0",
|
|
32
32
|
"node-opcua-data-access": "2.114.0",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"node-opcua-nodeset-ua": "2.114.0",
|
|
42
42
|
"node-opcua-numeric-range": "2.114.0",
|
|
43
43
|
"node-opcua-object-registry": "2.114.0",
|
|
44
|
-
"node-opcua-pseudo-session": "2.
|
|
44
|
+
"node-opcua-pseudo-session": "2.116.0",
|
|
45
45
|
"node-opcua-service-browse": "2.114.0",
|
|
46
46
|
"node-opcua-service-call": "2.114.0",
|
|
47
47
|
"node-opcua-service-history": "2.114.0",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"internet of things"
|
|
85
85
|
],
|
|
86
86
|
"homepage": "http://node-opcua.github.io/",
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "713ad387571a323c8d886ad1c907f50ad96a5e76",
|
|
88
88
|
"files": [
|
|
89
89
|
"dist",
|
|
90
90
|
"distHelpers",
|
|
@@ -8,6 +8,7 @@ import { CallMethodRequest } from "node-opcua-service-call";
|
|
|
8
8
|
import { StatusCode, StatusCodes } from "node-opcua-status-code";
|
|
9
9
|
import { CallMethodResultOptions } from "node-opcua-types";
|
|
10
10
|
import { Variant } from "node-opcua-variant";
|
|
11
|
+
import { ResponseCallback } from "node-opcua-pseudo-session";
|
|
11
12
|
import { ISessionContext, IAddressSpace, UAMethod, UAObject } from "node-opcua-address-space-base";
|
|
12
13
|
|
|
13
14
|
import { getMethodDeclaration_ArgumentList, verifyArguments_ArgumentList } from "./argument_list";
|
|
@@ -28,7 +29,6 @@ import { resolveOpaqueOnAddressSpace } from "./resolve_opaque_on_address_space";
|
|
|
28
29
|
// A ByteString is structurally the same as a one dimensional array of Byte.
|
|
29
30
|
// A server shall accept a ByteString if an array of Byte is expected.
|
|
30
31
|
// BadNoCommunication
|
|
31
|
-
type ResponseCallback<T> = (err: Error | null, result?: T) => void;
|
|
32
32
|
|
|
33
33
|
export function callMethodHelper(
|
|
34
34
|
context: ISessionContext,
|
package/source/index.ts
CHANGED
|
@@ -41,7 +41,7 @@ export { promoteToMultiStateDiscrete } from "../src/data_access/ua_multistate_di
|
|
|
41
41
|
export { promoteToMultiStateValueDiscrete } from "../src/data_access/ua_multistate_value_discrete_impl";
|
|
42
42
|
export { promoteToTwoStateDiscrete } from "../src/data_access/ua_two_state_discrete_impl";
|
|
43
43
|
export { validateDataType } from "../src/data_access/ua_multistate_value_discrete_impl";
|
|
44
|
-
|
|
44
|
+
export { validateDataTypeCorrectness } from "../src/validate_data_type_correctness";
|
|
45
45
|
export * from "./ua_root_folder";
|
|
46
46
|
export * from "./session_context";
|
|
47
47
|
export * from "./pseudo_session";
|
|
@@ -1,15 +1,173 @@
|
|
|
1
1
|
import { checkDebugFlag, make_debugLog, make_errorLog } from "node-opcua-debug";
|
|
2
2
|
import { CallbackT } from "node-opcua-status-code";
|
|
3
|
-
import { IAddressSpace } from "node-opcua-address-space-base";
|
|
4
|
-
|
|
3
|
+
import { IAddressSpace, RequiredModel } from "node-opcua-address-space-base";
|
|
4
|
+
import { ReaderStateParserLike, Xml2Json } from "node-opcua-xml2json";
|
|
5
|
+
import { minDate } from "node-opcua-date-time";
|
|
5
6
|
import { adjustNamespaceArray } from "../../src/nodeset_tools/adjust_namespace_array";
|
|
6
7
|
import { NodeSetLoaderOptions } from "../interfaces/nodeset_loader_options";
|
|
8
|
+
import { NamespacePrivate } from "../../src/namespace_private";
|
|
7
9
|
import { NodeSetLoader } from "./load_nodeset2";
|
|
8
10
|
|
|
9
11
|
const doDebug = checkDebugFlag(__filename);
|
|
10
12
|
const debugLog = make_debugLog(__filename);
|
|
11
13
|
const errorLog = make_errorLog(__filename);
|
|
12
14
|
|
|
15
|
+
interface Model extends RequiredModel {
|
|
16
|
+
requiredModel: RequiredModel[];
|
|
17
|
+
}
|
|
18
|
+
interface NodesetInfo {
|
|
19
|
+
namespaceUris: string[];
|
|
20
|
+
models: Model[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function parseDependencies(xmlData: string): Promise<NodesetInfo> {
|
|
24
|
+
const namespaceUris: string[] = [];
|
|
25
|
+
|
|
26
|
+
const models: Model[] = [];
|
|
27
|
+
let currentModel: Model | undefined = undefined;
|
|
28
|
+
const state0: ReaderStateParserLike = {
|
|
29
|
+
parser: {
|
|
30
|
+
UANodeSet: {
|
|
31
|
+
parser: {
|
|
32
|
+
NamespaceUris: {
|
|
33
|
+
parser: {
|
|
34
|
+
Uri: {
|
|
35
|
+
finish() {
|
|
36
|
+
namespaceUris.push(this.text);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
Models: {
|
|
42
|
+
parser: {
|
|
43
|
+
Model: {
|
|
44
|
+
init(elementName: string, attrs: any) {
|
|
45
|
+
const modelUri = attrs.ModelUri;
|
|
46
|
+
const version = attrs.Version;
|
|
47
|
+
const publicationDate = new Date(Date.parse(attrs.PublicationDate));
|
|
48
|
+
currentModel = {
|
|
49
|
+
modelUri,
|
|
50
|
+
version,
|
|
51
|
+
publicationDate,
|
|
52
|
+
requiredModel: []
|
|
53
|
+
};
|
|
54
|
+
doDebug && console.log(`currentModel = ${JSON.stringify(currentModel)}`);
|
|
55
|
+
models.push(currentModel);
|
|
56
|
+
},
|
|
57
|
+
parser: {
|
|
58
|
+
RequiredModel: {
|
|
59
|
+
init(elementName: string, attrs: any) {
|
|
60
|
+
const modelUri = attrs.ModelUri;
|
|
61
|
+
const version = attrs.Version;
|
|
62
|
+
const publicationDate = new Date(Date.parse(attrs.PublicationDate));
|
|
63
|
+
|
|
64
|
+
if (!currentModel) {
|
|
65
|
+
throw new Error("Internal Error");
|
|
66
|
+
}
|
|
67
|
+
currentModel.requiredModel.push({
|
|
68
|
+
modelUri,
|
|
69
|
+
version,
|
|
70
|
+
publicationDate
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const parser = new Xml2Json(state0);
|
|
83
|
+
parser.parseStringSync(xmlData);
|
|
84
|
+
if (models.length === 0 && namespaceUris.length >= 1) {
|
|
85
|
+
models.push({
|
|
86
|
+
modelUri: namespaceUris[0],
|
|
87
|
+
version: "1",
|
|
88
|
+
publicationDate: minDate,
|
|
89
|
+
requiredModel: []
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return { models, namespaceUris: namespaceUris };
|
|
93
|
+
}
|
|
94
|
+
interface NodesetDesc {
|
|
95
|
+
index: number;
|
|
96
|
+
xmlData: string;
|
|
97
|
+
namespaceModel: NodesetInfo;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Detect order of namespace loading
|
|
101
|
+
*/
|
|
102
|
+
export async function preLoad(xmlFiles: string[], xmlLoader: (nodeset2xmlUri: string) => Promise<string>): Promise<NodesetDesc[]> {
|
|
103
|
+
// a nodeset2 file may define multiple namespaces
|
|
104
|
+
const namespaceDesc: NodesetDesc[] = [];
|
|
105
|
+
for (let index = 0; index < xmlFiles.length; index++) {
|
|
106
|
+
doDebug && console.log("---------------------------------------------", xmlFiles[index]);
|
|
107
|
+
const xmlData = await xmlLoader(xmlFiles[index]);
|
|
108
|
+
|
|
109
|
+
const indexStart = xmlData.match(/<UANodeSet/m)?.index;
|
|
110
|
+
const i1 = (xmlData.match(/<\/Models>/m)?.index || 0) + "</Models>".length;
|
|
111
|
+
const i2 = (xmlData.match(/<\/NamespaceUris>/m)?.index || 0) + "</NamespaceUris>".length;
|
|
112
|
+
|
|
113
|
+
const indexEnd = Math.max(i1, i2);
|
|
114
|
+
if (indexStart === undefined || indexEnd === undefined) {
|
|
115
|
+
throw new Error("Internal Error");
|
|
116
|
+
}
|
|
117
|
+
const xmlData2 = xmlData.substring(indexStart, indexEnd);
|
|
118
|
+
doDebug &&
|
|
119
|
+
console.log(
|
|
120
|
+
xmlData2
|
|
121
|
+
.split("\n")
|
|
122
|
+
.splice(0, 46)
|
|
123
|
+
.map((x, i) => `${i + 0} ${x}`)
|
|
124
|
+
.join("\n")
|
|
125
|
+
);
|
|
126
|
+
const namespaceModel = await parseDependencies(xmlData2);
|
|
127
|
+
namespaceDesc.push({ xmlData, namespaceModel, index });
|
|
128
|
+
}
|
|
129
|
+
return namespaceDesc;
|
|
130
|
+
}
|
|
131
|
+
export function findOrder(nodesetDescs: NodesetDesc[]): number[] {
|
|
132
|
+
// compute the order of loading of the namespaces
|
|
133
|
+
const order: number[] = [];
|
|
134
|
+
const visited: Set<string> = new Set<string>();
|
|
135
|
+
|
|
136
|
+
const findNodesetIndex = (namespaceUri: string) => {
|
|
137
|
+
const index = nodesetDescs.findIndex((x) => x.namespaceModel.models.findIndex((e) => e.modelUri === namespaceUri) !== -1);
|
|
138
|
+
return index;
|
|
139
|
+
};
|
|
140
|
+
const visit = (model: Model) => {
|
|
141
|
+
const key = model.modelUri;
|
|
142
|
+
if (visited.has(key)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
visited.add(key);
|
|
146
|
+
for (const requiredModel of model.requiredModel) {
|
|
147
|
+
const requiredModelIndex = findNodesetIndex(requiredModel.modelUri);
|
|
148
|
+
if (requiredModelIndex === -1) {
|
|
149
|
+
throw new Error("Cannot find namespace for " + requiredModel.modelUri);
|
|
150
|
+
}
|
|
151
|
+
const nd = nodesetDescs[requiredModelIndex];
|
|
152
|
+
for (const n of nd.namespaceModel.models) {
|
|
153
|
+
visit(n);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const nodesetIndex = findNodesetIndex(model.modelUri);
|
|
157
|
+
const alreadyIn = order.findIndex((x) => x === nodesetIndex) !== -1;
|
|
158
|
+
if (!alreadyIn) order.push(nodesetIndex);
|
|
159
|
+
};
|
|
160
|
+
const visit2 = (nodesetDesc: NodesetDesc) => {
|
|
161
|
+
for (const model of nodesetDesc.namespaceModel.models.values()) {
|
|
162
|
+
visit(model);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
for (let index = 0; index < nodesetDescs.length; index++) {
|
|
166
|
+
const nodesetDesc = nodesetDescs[index];
|
|
167
|
+
visit2(nodesetDesc);
|
|
168
|
+
}
|
|
169
|
+
return order;
|
|
170
|
+
}
|
|
13
171
|
/**
|
|
14
172
|
* @param addressSpace the addressSpace to populate
|
|
15
173
|
* @xmlFiles: a lis of xml files
|
|
@@ -26,15 +184,26 @@ export async function generateAddressSpaceRaw(
|
|
|
26
184
|
if (!Array.isArray(xmlFiles)) {
|
|
27
185
|
xmlFiles = [xmlFiles];
|
|
28
186
|
}
|
|
29
|
-
|
|
30
|
-
|
|
187
|
+
|
|
188
|
+
const nodesetDesc = await preLoad(xmlFiles, xmlLoader);
|
|
189
|
+
const order = findOrder(nodesetDesc);
|
|
190
|
+
for (let index = 0; index < order.length; index++) {
|
|
191
|
+
const nodesetIndex = order[index];
|
|
192
|
+
const nodeset = nodesetDesc[nodesetIndex];
|
|
193
|
+
debugLog(" loading ", nodesetIndex, nodeset.xmlData.length);
|
|
194
|
+
for (const model of nodeset.namespaceModel.models) {
|
|
195
|
+
const ns = addressSpace.registerNamespace(model.modelUri) as NamespacePrivate;
|
|
196
|
+
ns.setRequiredModels(model.requiredModel);
|
|
197
|
+
}
|
|
198
|
+
|
|
31
199
|
try {
|
|
32
|
-
await nodesetLoader.addNodeSetAsync(xmlData);
|
|
200
|
+
await nodesetLoader.addNodeSetAsync(nodeset.xmlData);
|
|
33
201
|
} catch (err) {
|
|
34
202
|
errorLog("generateAddressSpace: Loading xml file ", xmlFiles[index], " failed with error ", (err as Error).message);
|
|
35
203
|
throw err;
|
|
36
204
|
}
|
|
37
205
|
}
|
|
206
|
+
|
|
38
207
|
await nodesetLoader.terminateAsync();
|
|
39
208
|
adjustNamespaceArray(addressSpace);
|
|
40
209
|
// however process them in series
|
|
@@ -306,13 +306,7 @@ function makeNodeSetParserEngine(addressSpace: IAddressSpace, options: NodeSetLo
|
|
|
306
306
|
// Model must not be already registered
|
|
307
307
|
const existingNamespace = addressSpace1.getNamespace(model.modelUri);
|
|
308
308
|
if (existingNamespace) {
|
|
309
|
-
|
|
310
|
-
// istanbul ignore else
|
|
311
|
-
if (model.modelUri === "http://opcfoundation.org/UA/") {
|
|
312
|
-
namespace = existingNamespace;
|
|
313
|
-
} else {
|
|
314
|
-
throw new Error(" namespace already registered " + model.modelUri);
|
|
315
|
-
}
|
|
309
|
+
namespace = existingNamespace;
|
|
316
310
|
} else {
|
|
317
311
|
namespace = addressSpace1.registerNamespace(model.modelUri);
|
|
318
312
|
namespace.setRequiredModels(model.requiredModels);
|
package/src/index_current.ts
CHANGED
|
@@ -12,8 +12,8 @@ export * from "../source/helpers/call_helpers";
|
|
|
12
12
|
export * from "../source/helpers/ensure_secure_access";
|
|
13
13
|
export * from "../source/helpers/resolve_opaque_on_address_space";
|
|
14
14
|
export * from "../source/interfaces/alarms_and_conditions/condition_info_i";
|
|
15
|
-
|
|
16
15
|
export * from "../src/nodeset_tools/construct_namespace_dependency";
|
|
16
|
+
export * from "../src/validate_data_type_correctness";
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
export * from "../source/set_namespace_meta_data";
|
package/src/ua_variable_impl.ts
CHANGED
|
@@ -94,6 +94,7 @@ import {
|
|
|
94
94
|
} from "./ua_variable_impl_ext_obj";
|
|
95
95
|
import { adjustDataValueStatusCode } from "./data_access/adjust_datavalue_status_code";
|
|
96
96
|
import { _getBasicDataType } from "./get_basic_datatype";
|
|
97
|
+
import { validateDataTypeCorrectness} from "./validate_data_type_correctness";
|
|
97
98
|
|
|
98
99
|
const debugLog = make_debugLog(__filename);
|
|
99
100
|
const warningLog = make_warningLog(__filename);
|
|
@@ -149,94 +150,6 @@ function is_Variant_or_StatusCode(v: any): boolean {
|
|
|
149
150
|
return is_Variant(v) || is_StatusCode(v);
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
function _dataType_toUADataType(addressSpace: IAddressSpace, dataType: DataType): UADataType {
|
|
153
|
-
assert(addressSpace);
|
|
154
|
-
assert(dataType !== DataType.Null);
|
|
155
|
-
|
|
156
|
-
const dataTypeNode = addressSpace.findDataType(DataType[dataType]);
|
|
157
|
-
/* istanbul ignore next */
|
|
158
|
-
if (!dataTypeNode) {
|
|
159
|
-
throw new Error(" Cannot find DataType " + DataType[dataType] + " in address Space");
|
|
160
|
-
}
|
|
161
|
-
return dataTypeNode as UADataType;
|
|
162
|
-
}
|
|
163
|
-
/*=
|
|
164
|
-
*
|
|
165
|
-
* @param addressSpace
|
|
166
|
-
* @param dataTypeNodeId : the nodeId matching the dataType of the destination variable.
|
|
167
|
-
* @param variantDataType: the dataType of the variant to write to the destination variable
|
|
168
|
-
* @param nodeId
|
|
169
|
-
* @return {boolean} true if the variant dataType is compatible with the Variable DataType
|
|
170
|
-
*/
|
|
171
|
-
function validateDataType(
|
|
172
|
-
addressSpace: IAddressSpace,
|
|
173
|
-
dataTypeNodeId: NodeId,
|
|
174
|
-
variantDataType: DataType,
|
|
175
|
-
nodeId: NodeId,
|
|
176
|
-
allowNulls: boolean
|
|
177
|
-
): boolean {
|
|
178
|
-
if (variantDataType === DataType.ExtensionObject) {
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
if (variantDataType === DataType.Null && allowNulls) {
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
if (variantDataType === DataType.Null && !allowNulls) {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
let builtInType: DataType;
|
|
188
|
-
let builtInUADataType: UADataType;
|
|
189
|
-
|
|
190
|
-
const destUADataType = addressSpace.findDataType(dataTypeNodeId)!;
|
|
191
|
-
assert(destUADataType instanceof UADataTypeImpl);
|
|
192
|
-
|
|
193
|
-
if (destUADataType.isAbstract || destUADataType.nodeId.namespace !== 0) {
|
|
194
|
-
builtInUADataType = destUADataType;
|
|
195
|
-
} else {
|
|
196
|
-
builtInType = addressSpace.findCorrespondingBasicDataType(destUADataType);
|
|
197
|
-
builtInUADataType = addressSpace.findDataType(builtInType)!;
|
|
198
|
-
}
|
|
199
|
-
assert(builtInUADataType instanceof UADataTypeImpl);
|
|
200
|
-
|
|
201
|
-
const enumerationUADataType = addressSpace.findDataType("Enumeration");
|
|
202
|
-
if (!enumerationUADataType) {
|
|
203
|
-
throw new Error("cannot find Enumeration DataType node in standard address space");
|
|
204
|
-
}
|
|
205
|
-
if (destUADataType.isSubtypeOf(enumerationUADataType)) {
|
|
206
|
-
// istanbul ignore next
|
|
207
|
-
if (doDebug) {
|
|
208
|
-
debugLog("destUADataType.", destUADataType.browseName.toString(), destUADataType.nodeId.toString());
|
|
209
|
-
debugLog(
|
|
210
|
-
"enumerationUADataType.",
|
|
211
|
-
enumerationUADataType.browseName.toString(),
|
|
212
|
-
enumerationUADataType.nodeId.toString()
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
return true;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// The value supplied for the attribute is not of the same type as the value.
|
|
219
|
-
const variantUADataType = _dataType_toUADataType(addressSpace, variantDataType);
|
|
220
|
-
assert(variantUADataType instanceof UADataTypeImpl);
|
|
221
|
-
|
|
222
|
-
const dest_isSubTypeOf_variant = variantUADataType.isSubtypeOf(builtInUADataType);
|
|
223
|
-
|
|
224
|
-
/* istanbul ignore next */
|
|
225
|
-
if (doDebug) {
|
|
226
|
-
if (dest_isSubTypeOf_variant) {
|
|
227
|
-
/* istanbul ignore next*/
|
|
228
|
-
debugLog(chalk.green(" ---------- Type match !!! "), " on ", nodeId.toString());
|
|
229
|
-
} else {
|
|
230
|
-
/* istanbul ignore next*/
|
|
231
|
-
debugLog(chalk.red(" ---------- Type mismatch "), " on ", nodeId.toString());
|
|
232
|
-
}
|
|
233
|
-
debugLog(chalk.cyan(" Variable data Type is = "), destUADataType.browseName.toString());
|
|
234
|
-
debugLog(chalk.cyan(" which matches basic Type = "), builtInUADataType.browseName.toString());
|
|
235
|
-
debugLog(chalk.yellow(" Actual dataType = "), variantUADataType.browseName.toString());
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return dest_isSubTypeOf_variant;
|
|
239
|
-
}
|
|
240
153
|
|
|
241
154
|
function default_func(this: UAVariable, dataValue1: DataValue, callback1: CallbackT<StatusCode>) {
|
|
242
155
|
return _default_writable_timestamped_set_func.call(this, dataValue1, callback1);
|
|
@@ -1707,7 +1620,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
|
|
|
1707
1620
|
}
|
|
1708
1621
|
|
|
1709
1622
|
public _validate_DataType(variantDataType: DataType): boolean {
|
|
1710
|
-
return
|
|
1623
|
+
return validateDataTypeCorrectness(this.addressSpace, this.dataType, variantDataType, /* allow Nulls */ false, this.nodeId);
|
|
1711
1624
|
}
|
|
1712
1625
|
|
|
1713
1626
|
public _internal_set_value(value: Variant): void {
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { assert } from "node-opcua-assert";
|
|
3
|
+
import { IAddressSpace, UADataType } from "node-opcua-address-space-base";
|
|
4
|
+
import { DataType } from "node-opcua-basic-types";
|
|
5
|
+
import { make_debugLog, make_warningLog, checkDebugFlag, make_errorLog } from "node-opcua-debug";
|
|
6
|
+
import { NodeId } from "node-opcua-nodeid";
|
|
7
|
+
|
|
8
|
+
const debugLog = make_debugLog(__filename);
|
|
9
|
+
const doDebug = checkDebugFlag(__filename);
|
|
10
|
+
|
|
11
|
+
function _dataType_toUADataType(addressSpace: IAddressSpace, dataType: DataType): UADataType {
|
|
12
|
+
assert(addressSpace);
|
|
13
|
+
assert(dataType !== DataType.Null);
|
|
14
|
+
|
|
15
|
+
const dataTypeNode = addressSpace.findDataType(DataType[dataType]);
|
|
16
|
+
/* istanbul ignore next */
|
|
17
|
+
if (!dataTypeNode) {
|
|
18
|
+
throw new Error(" Cannot find DataType " + DataType[dataType] + " in address Space");
|
|
19
|
+
}
|
|
20
|
+
return dataTypeNode as UADataType;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const validDataTypeForEnumValue = [DataType.Int32];
|
|
24
|
+
// , DataType.UInt32, DataType.Int64, DataType.UInt64];
|
|
25
|
+
|
|
26
|
+
/*=
|
|
27
|
+
*
|
|
28
|
+
* @param addressSpace
|
|
29
|
+
* @param dataTypeNodeId : the nodeId matching the dataType of the destination variable.
|
|
30
|
+
* @param variantDataType: the dataType of the variant to write to the destination variable
|
|
31
|
+
* @param nodeId
|
|
32
|
+
* @return {boolean} true if the variant dataType is compatible with the Variable DataType
|
|
33
|
+
*/
|
|
34
|
+
export function validateDataTypeCorrectness(
|
|
35
|
+
addressSpace: IAddressSpace,
|
|
36
|
+
dataTypeNodeId: NodeId,
|
|
37
|
+
variantDataType: DataType,
|
|
38
|
+
allowNulls: boolean,
|
|
39
|
+
context?: { toString(): string }
|
|
40
|
+
): boolean {
|
|
41
|
+
if (variantDataType === DataType.Null && allowNulls) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
if (variantDataType === DataType.Null && !allowNulls) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
let builtInType: DataType;
|
|
48
|
+
let builtInUADataType: UADataType;
|
|
49
|
+
|
|
50
|
+
const destUADataType = addressSpace.findDataType(dataTypeNodeId)!;
|
|
51
|
+
|
|
52
|
+
// istanbul ignore next
|
|
53
|
+
if (!destUADataType) {
|
|
54
|
+
throw new Error("Cannot find UADataType " + dataTypeNodeId.toString() + " in address Space");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (variantDataType === DataType.ExtensionObject) {
|
|
58
|
+
const structure = addressSpace.findDataType("Structure")!;
|
|
59
|
+
if (destUADataType.isSubtypeOf(structure)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (destUADataType.isAbstract) {
|
|
66
|
+
builtInUADataType = destUADataType;
|
|
67
|
+
} else {
|
|
68
|
+
builtInType = addressSpace.findCorrespondingBasicDataType(destUADataType);
|
|
69
|
+
if (builtInType === DataType.ExtensionObject) {
|
|
70
|
+
// it should have been trapped earlier
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
builtInUADataType = addressSpace.findDataType(builtInType)!;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const enumerationUADataType = addressSpace.findDataType("Enumeration");
|
|
77
|
+
// istanbul ignore next
|
|
78
|
+
if (!enumerationUADataType) {
|
|
79
|
+
throw new Error("cannot find Enumeration DataType node in standard address space");
|
|
80
|
+
}
|
|
81
|
+
if (destUADataType.isSubtypeOf(enumerationUADataType)) {
|
|
82
|
+
// istanbul ignore next
|
|
83
|
+
if (doDebug) {
|
|
84
|
+
debugLog("destUADataType.", destUADataType.browseName.toString(), destUADataType.nodeId.toString());
|
|
85
|
+
debugLog(
|
|
86
|
+
"enumerationUADataType.",
|
|
87
|
+
enumerationUADataType.browseName.toString(),
|
|
88
|
+
enumerationUADataType.nodeId.toString()
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return validDataTypeForEnumValue.indexOf(variantDataType) >= 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// The value supplied for the attribute is not of the same type as the value.
|
|
96
|
+
const variantUADataType = _dataType_toUADataType(addressSpace, variantDataType);
|
|
97
|
+
|
|
98
|
+
const dest_isSubTypeOf_variant = variantUADataType.isSubtypeOf(builtInUADataType);
|
|
99
|
+
|
|
100
|
+
// istanbul ignore next
|
|
101
|
+
if (doDebug) {
|
|
102
|
+
if (dest_isSubTypeOf_variant) {
|
|
103
|
+
/* istanbul ignore next*/
|
|
104
|
+
debugLog(chalk.green(" ---------- Type match !!! "), " on ", context?.toString());
|
|
105
|
+
} else {
|
|
106
|
+
/* istanbul ignore next*/
|
|
107
|
+
debugLog(chalk.red(" ---------- Type mismatch "), " on ", context?.toString());
|
|
108
|
+
}
|
|
109
|
+
debugLog(chalk.cyan(" Variable data Type is = "), destUADataType.browseName.toString());
|
|
110
|
+
debugLog(chalk.cyan(" which matches basic Type = "), builtInUADataType.browseName.toString());
|
|
111
|
+
debugLog(chalk.yellow(" Actual dataType = "), variantUADataType.browseName.toString());
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return dest_isSubTypeOf_variant;
|
|
115
|
+
}
|