node-red-contrib-uos-nats 0.2.46 → 0.2.48

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/lib/payloads.js CHANGED
@@ -115,14 +115,43 @@ export function encodeWriteVariablesCommand(variables) {
115
115
 
116
116
  // Encode value based on type
117
117
  // Explicitly handle null/undefined by defaulting to safe empty values based on type
118
- // This prevents "Field 6 must be set" error
119
118
  const val = v.value;
120
- if (typeof val === 'boolean') {
119
+ let targetType = v.dataType; // Optional known type from definition
120
+
121
+ // If targetType is provided, force specific flatbuffer encoding
122
+ if (targetType) {
123
+ targetType = String(targetType).toUpperCase();
124
+ if (targetType === 'BOOLEAN') {
125
+ const boolVal = new VariableValueBooleanT();
126
+ boolVal.value = Boolean(val);
127
+ varT.value = boolVal;
128
+ varT.valueType = VariableValue.Boolean;
129
+ } else if (targetType === 'INT64') {
130
+ const intVal = new VariableValueInt64T();
131
+ intVal.value = BigInt(parseInt(val) || 0); // Ensure BigInt
132
+ varT.value = intVal;
133
+ varT.valueType = VariableValue.Int64;
134
+ } else if (targetType === 'FLOAT64') {
135
+ const floatVal = new VariableValueFloat64T();
136
+ floatVal.value = Number(val);
137
+ varT.value = floatVal;
138
+ varT.valueType = VariableValue.Float64;
139
+ } else {
140
+ // STRING or default
141
+ const strVal = new VariableValueStringT();
142
+ strVal.value = (typeof val === 'object') ? JSON.stringify(val) : String(val);
143
+ varT.value = strVal;
144
+ varT.valueType = VariableValue.String;
145
+ }
146
+ }
147
+ // Fallback: Infer from value (Risk: Int sent as Float or vice versa)
148
+ else if (typeof val === 'boolean') {
121
149
  const boolVal = new VariableValueBooleanT();
122
150
  boolVal.value = val;
123
151
  varT.value = boolVal;
124
152
  varT.valueType = VariableValue.Boolean;
125
153
  } else if (Number.isInteger(val)) {
154
+
126
155
  const intVal = new VariableValueInt64T();
127
156
  intVal.value = BigInt(val);
128
157
  varT.value = intVal;
@@ -65,11 +65,10 @@ module.exports = function (RED) {
65
65
  }
66
66
  // Retrieve configuration node
67
67
  this.providerId = (config.providerId || 'nodered').trim();
68
- const uosConfig = RED.nodes.getNode(config.uosConfig);
69
- this.definitions = config.definitions || [];
68
+ // this.definitions = config.definitions || [];
70
69
 
71
70
  const defMap = new Map();
72
- // const definitions = []; // This line is effectively replaced by this.definitions
71
+ const definitions = [];
73
72
  const stateMap = new Map();
74
73
  let nextId = 100;
75
74
  let fingerprint = 0;
@@ -92,7 +91,7 @@ module.exports = function (RED) {
92
91
  stateMap.set(def.id, {
93
92
  id: def.id,
94
93
  value: defaultValue(dataType),
95
- timestampNs: Date.now() * 1_000_000,
94
+ timestamp: BigInt(Date.now()) * 1_000_000n,
96
95
  quality: 'GOOD',
97
96
  });
98
97
  return { def, created: true };
@@ -139,7 +138,7 @@ module.exports = function (RED) {
139
138
  const stateObj = {};
140
139
  const nowNs = Date.now() * 1_000_000;
141
140
  for (const s of stateMap.values()) {
142
- s.timestampNs = nowNs; // Force refresh timestamp
141
+ s.timestamp = BigInt(Date.now()) * 1_000_000n; // Force refresh timestamp
143
142
  stateObj[s.id] = s;
144
143
  }
145
144
  try {
@@ -153,6 +152,7 @@ module.exports = function (RED) {
153
152
  console.log(`[DataHub Output] Packet HEX (${payload.length} bytes): ${hex}`);
154
153
 
155
154
  await nc.publish(subject, payload);
155
+ await nc.flush(); // Ensure NATS accepts the packet (catches Permission Errors)
156
156
  console.log(`[DataHub Output] Heartbeat sent. State count: ${Object.keys(stateObj).length}`);
157
157
  } catch (err) {
158
158
  this.warn(`Heartbeat error: ${err.message}`);
@@ -263,7 +263,7 @@ module.exports = function (RED) {
263
263
  const state = {
264
264
  id: def.id,
265
265
  value,
266
- timestampNs: Date.now() * 1_000_000,
266
+ timestamp: BigInt(Date.now()) * 1_000_000n,
267
267
  quality: 'GOOD',
268
268
  };
269
269
  // states.push(state); // No longer pushing to a temporary 'states' array
@@ -17,20 +17,36 @@ module.exports = function (RED) {
17
17
  const variable = cached.definition.variables.find(v => v.key === key);
18
18
  if (variable) {
19
19
  node.debug && node.debug(`Key '${key}' resolved to ID ${variable.id} (cached)`);
20
- return variable.id;
20
+ // Return full object including type
21
+ return { id: variable.id, dataType: variable.dataType };
21
22
  }
22
23
  }
23
24
 
24
25
  // Query provider definition
26
+ let definition = null;
25
27
  try {
26
28
  const query = payloads.buildReadProviderDefinitionQuery();
27
- const subject = `v1.loc.registry.providers.${providerId}.def.qry.read`;
28
29
 
29
- const response = await nc.request(subject, query, { timeout: 3000 });
30
- const definition = payloads.decodeProviderDefinition(response.data);
30
+ // Strategy 1: Direct Query (v1.loc.<provider>.def.qry.read)
31
+ // This is required for providers like Node-RED itself or simple Python scripts
32
+ const directSubject = `v1.loc.${providerId}.def.qry.read`;
33
+ try {
34
+ const response = await nc.request(directSubject, query, { timeout: 1000 });
35
+ definition = payloads.decodeProviderDefinition(response.data);
36
+ } catch (err) {
37
+ node.debug && node.debug(`Direct Query failed: ${err.message}`);
38
+ }
39
+
40
+ // Strategy 2: Registry Query (v1.loc.registry.providers.<provider>.def.qry.read)
41
+ // Fallback for managed providers
42
+ if (!definition) {
43
+ const registrySubject = `v1.loc.registry.providers.${providerId}.def.qry.read`;
44
+ const response = await nc.request(registrySubject, query, { timeout: 2000 });
45
+ definition = payloads.decodeProviderDefinition(response.data);
46
+ }
31
47
 
32
48
  if (!definition) {
33
- throw new Error(`Provider ${providerId} not found or no definition returned`);
49
+ throw new Error(`Provider ${providerId} not found (tried Direct & Registry)`);
34
50
  }
35
51
 
36
52
  // Cache the definition
@@ -45,8 +61,8 @@ module.exports = function (RED) {
45
61
  throw new Error(`Variable key '${key}' not found in provider ${providerId}`);
46
62
  }
47
63
 
48
- node.debug && node.debug(`Key '${key}' resolved to ID ${variable.id}`);
49
- return variable.id;
64
+ node.debug && node.debug(`Key '${key}' resolved to ID ${variable.id} (Type: ${variable.dataType})`);
65
+ return { id: variable.id, dataType: variable.dataType };
50
66
 
51
67
  } catch (err) {
52
68
  throw new Error(`Failed to resolve key '${key}': ${err.message}`);
@@ -71,6 +87,7 @@ module.exports = function (RED) {
71
87
  this.variableId = config.variableId ? parseInt(config.variableId, 10) : null;
72
88
  this.variableKey = config.variableKey?.trim();
73
89
  this.resolvedId = null; // Cached resolved ID
90
+ this.resolvedDataType = null; // Cached Data Type for strict writing
74
91
  this.payloads = null; // Will be loaded dynamically
75
92
 
76
93
  if (!this.providerId) {
@@ -131,10 +148,16 @@ module.exports = function (RED) {
131
148
 
132
149
  // Resolve variable ID if needed
133
150
  let varId = node.resolvedId;
151
+ let varType = node.resolvedDataType;
152
+
134
153
  if (!varId && node.variableKey) {
135
154
  node.status({ fill: 'yellow', shape: 'dot', text: 'resolving key...' });
136
- varId = await resolveVariableKey(nc, node.providerId, node.variableKey, node, node.payloads);
137
- node.resolvedId = varId; // Cache for future messages
155
+ const resolved = await resolveVariableKey(nc, node.providerId, node.variableKey, node, node.payloads);
156
+ varId = resolved.id;
157
+ varType = resolved.dataType; // Store resolved type
158
+
159
+ node.resolvedId = varId;
160
+ node.resolvedDataType = varType;
138
161
  node.status({ fill: 'green', shape: 'ring', text: 'ready' });
139
162
  }
140
163
 
@@ -142,7 +165,8 @@ module.exports = function (RED) {
142
165
  const writeCommand = node.payloads.encodeWriteVariablesCommand([
143
166
  {
144
167
  id: varId,
145
- value: value
168
+ value: value,
169
+ dataType: varType // Pass dataType for strict encoding
146
170
  }
147
171
  ]);
148
172
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-uos-nats",
3
- "version": "0.2.46",
3
+ "version": "0.2.48",
4
4
  "description": "Node-RED nodes for Weidmüller u-OS Data Hub. Read, write, and provide variables via NATS protocol with OAuth2 authentication. Features: Variable Key resolution, custom icons, example flows, and provider definition caching.",
5
5
  "author": {
6
6
  "name": "IoTUeli",