write multiple values
It is possitble to write multiple values to different OPC UA variables in a single operation using the OPC UA Write Node
.
Writing multiple values simultaneously is more efficient than individual write operations and ensures better performance when updating several variables.
The Write Node supports batch operations through a specialized message format that allows you to specify multiple target variables and their corresponding values.
Prerequisites
- read the writing a single value page
Step-by-step
step 1: create the flow
-
add nodes to your workspace:
[Inject] → [Function] → [OPC UA Write] → [Debug]
-
configure the Function node to prepare multiple write data
-
configure the write node with endpoint connection
-
add Debug node to observe results
step 2: configure multiple write data
basic multiple Write format
In the Function node, create a message with a payload array:
msg.payload = [
{
nodeId: "ns=2;s=TemperatureSetpoint",
value: 25.5,
},
{
nodeId: "ns=2;s=PressureSetpoint",
value: 1013,
},
{
nodeId: "ns=2;s=EnableFlag",
value: true,
}
];
return msg;
advanced multiple Write with options
msg.payload = [
{
nodeId: "ns=2;s=Setpoint1",
value: 45.2,
dataType: "Float",
attributeId: "Value"
},
{
nodeId: "ns=2;s=Setpoint2",
value: 67.8,
dataType: "Float",
attributeId: "Value"
},
{
nodeId: "ns=2;s=StatusText",
value: "System Ready",
dataType: "String",
attributeId: "Value"
}
];
// Set default values for all writes
msg.attributeId = "Value"; // Default attribute
return msg;
Output Message Format
Successful Multiple Write Response
{
// Original input properties
payload: [...], // Original write requests
// Array of write results
results: [
{
statusCode: "Good",
dataType: "Double",
nodeId: "ns=2;s=Temperature",
attributeId: "Value",
message: undefined
},
{
statusCode: "Good",
dataType: "Int32",
nodeId: "ns=2;s=Pressure",
attributeId: "Value",
message: undefined
},
{
statusCode: "BadNodeIdUnknown",
dataType: "Null",
nodeId: "ns=2;s=InvalidVariable",
attributeId: "Value",
message: "Node not found"
}
]
}
Processing Results
Use a Function node after the Write node to process results:
// Check all write results
const results = msg.results || [];
let successCount = 0;
let errorCount = 0;
const errors = [];
results.forEach((result, index) => {
if (result.statusCode === "Good") {
successCount++;
} else {
errorCount++;
errors.push({
index: index,
nodeId: result.nodeId,
error: result.statusCode,
message: result.message
});
}
});
// Create summary message
msg.summary = {
total: results.length,
success: successCount,
errors: errorCount,
errorDetails: errors
};
// Log results
node.log(`Write Summary: ${successCount}/${results.length} successful`);
if (errors.length > 0) {
node.warn(`Failed writes: ${JSON.stringify(errors)}`);
}
return msg;
Common Use Cases
1. Process Control Updates
// Update multiple process parameters
msg.payload = [
{
nodeId: "ns=2;s=ReactorTemperature",
value: flow.get("targetTemp") || 150.0,
dataType: "Double"
},
{
nodeId: "ns=2;s=ReactorPressure",
value: flow.get("targetPressure") || 2.5,
dataType: "Double"
},
{
nodeId: "ns=2;s=FlowRate",
value: flow.get("targetFlow") || 100.0,
dataType: "Double"
},
{
nodeId: "ns=2;s=ProcessMode",
value: flow.get("processMode") || "AUTO",
dataType: "String"
}
];
return msg;
2. Recipe Management
// Load production recipe
const recipe = flow.get("currentRecipe") || {};
msg.payload = [
{
nodeId: "ns=2;s=Recipe.Temperature",
value: recipe.temperature || 25.0,
dataType: "Double"
},
{
nodeId: "ns=2;s=Recipe.Duration",
value: recipe.duration || 3600,
dataType: "Int32"
},
{
nodeId: "ns=2;s=Recipe.Material",
value: recipe.material || "DefaultMaterial",
dataType: "String"
},
{
nodeId: "ns=2;s=Recipe.Quantities",
value: recipe.quantities || [100, 200, 50],
dataType: "Double"
}
];
return msg;
3. System Configuration
// Update system configuration
const config = msg.payload || {};
msg.payload = [
{
nodeId: "ns=2;s=Config.SamplingRate",
value: config.samplingRate || 1000,
dataType: "Int32"
},
{
nodeId: "ns=2;s=Config.LoggingEnabled",
value: config.logging !== false,
dataType: "Boolean"
},
{
nodeId: "ns=2;s=Config.AlarmThresholds",
value: config.thresholds || [10, 50, 90],
dataType: "Double"
},
{
nodeId: "ns=2;s=Config.OperatorName",
value: config.operator || "System",
dataType: "String"
}
];
return msg;
Performance Optimization
1. Batch Size Considerations
// Limit batch size for better performance
const MAX_BATCH_SIZE = 50;
const allWrites = [...]; // Your complete write list
// Split into smaller batches
const batches = [];
for (let i = 0; i < allWrites.length; i += MAX_BATCH_SIZE) {
batches.push(allWrites.slice(i, i + MAX_BATCH_SIZE));
}
// Process first batch
msg.payload = batches[0];
msg._batches = batches;
msg._currentBatch = 0;
return msg;
2. Conditional Writes
// Only write changed values
const currentValues = flow.get("lastWrittenValues") || {};
const newValues = msg.payload || {};
const writes = [];
Object.keys(newValues).forEach(nodeId => {
if (currentValues[nodeId] !== newValues[nodeId]) {
writes.push({
nodeId: nodeId,
value: newValues[nodeId],
dataType: "Variant"
});
}
});
if (writes.length > 0) {
msg.payload = writes;
// Store new values for next comparison
flow.set("lastWrittenValues", newValues);
return msg;
} else {
// No changes, skip write
return null;
}
Error Handling Strategies
1. Retry Failed Writes
// Process write results and retry failures
const results = msg.results || [];
const retryWrites = [];
results.forEach((result, index) => {
if (result.statusCode !== "Good") {
// Get original write request
const originalWrite = msg.originalPayload[index];
retryWrites.push(originalWrite);
}
});
if (retryWrites.length > 0) {
const retryCount = (msg.retryCount || 0) + 1;
if (retryCount <= 3) { // Max 3 retries
msg.payload = retryWrites;
msg.retryCount = retryCount;
node.warn(`Retrying ${retryWrites.length} failed writes (attempt ${retryCount})`);
return msg;
} else {
node.error(`${retryWrites.length} writes failed after 3 retries`);
}
}
return null;
2. Partial Success Handling
// Handle partial success scenarios
const results = msg.results || [];
const successful = results.filter(r => r.statusCode === "Good");
const failed = results.filter(r => r.statusCode !== "Good");
if (failed.length > 0) {
// Log detailed error information
failed.forEach(result => {
node.error(`Write failed for ${result.nodeId}: ${result.statusCode}`);
});
// Send notification about failures
const errorMsg = {
topic: "write-errors",
payload: {
timestamp: new Date().toISOString(),
failedCount: failed.length,
successCount: successful.length,
errors: failed
}
};
node.send([msg, errorMsg]);
} else {
node.send([msg, null]);
}
Best Practices
- Limit Batch Size: Keep batches under 50-100 items for optimal performance
- Error Handling: Always check individual write results
- Data Validation: Validate values before writing
- Retry Logic: Implement intelligent retry for failed writes
- Performance Monitoring: Track write success rates and timing
- Logging: Maintain detailed logs for troubleshooting
Troubleshooting
Common Issues
-
Partial Failures: Some writes succeed, others fail
- Check individual
statusCode
values in results - Verify permissions and data types for failed writes
- Check individual
-
Performance Issues: Slow write operations
- Reduce batch size
- Check server performance and network latency
-
Memory Issues: Large batches consuming too much memory
- Split large operations into smaller batches
- Process results incrementally