Skip to main content

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

Step-by-step

step 1: create the flow

  1. add nodes to your workspace:

    [Inject] → [Function] → [OPC UA Write] → [Debug]
  2. configure the Function node to prepare multiple write data

  3. configure the write node with endpoint connection

  4. 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

  1. Limit Batch Size: Keep batches under 50-100 items for optimal performance
  2. Error Handling: Always check individual write results
  3. Data Validation: Validate values before writing
  4. Retry Logic: Implement intelligent retry for failed writes
  5. Performance Monitoring: Track write success rates and timing
  6. Logging: Maintain detailed logs for troubleshooting

Troubleshooting

Common Issues

  1. Partial Failures: Some writes succeed, others fail

    • Check individual statusCode values in results
    • Verify permissions and data types for failed writes
  2. Performance Issues: Slow write operations

    • Reduce batch size
    • Check server performance and network latency
  3. Memory Issues: Large batches consuming too much memory

    • Split large operations into smaller batches
    • Process results incrementally