Step 3: Organize Data
Prerequisite: You should have data flowing from Step 2. If not, go back and complete that first!
Understanding Messages
When data flows through bridges, each message contains:
payload: The actual data value
meta: Information about the message (source, destination, tag names, etc.)
In the processing code, you'll work with these as msg.payload
and msg.meta
.
The Challenge
Right now you have one measurement going to one place:
enterprise.siteA._raw.my_data
But real PLCs have:
Hundreds of data points with various address formats
Different data types (numbers, strings, booleans)
Multiple machines on the same line
Do you need to map each tag manually? No! Let's connect a real PLC and see how automatic mapping works.
Part 1: Connect a Real PLC (Or Simulate One)
Create a New Bridge for a Real PLC
We'll use Siemens S7 as an example, but this works identically with OPC UA, Modbus, or any other protocol.
Go to Data Flows → Add Bridge
Name:
s7-plc
Protocol: Select Siemens S7 (or your protocol)
Connection:
IP: Use
{{ .IP }}
(we'll set this in variables)Rack: 0
Slot: 1

Configure What to Read
In the Input section, you'll see protocol-specific settings:
s7comm:
addresses:
- DB1.DW20 # Example address
- DB1.S30.10 # Another address
- DB3.I270 # Third address
tcpDevice: '{{ .IP }}'
rack: 0
slot: 1

Automatic Tag Mapping
In the Processing section, look at the Always code:
// Set location from bridge config
msg.meta.location_path = "{{ .location_path }}";
// Use _raw (no validation - data passes through as-is)
msg.meta.data_contract = "_raw";
// Key concept: Protocol address becomes the tag name automatically
msg.meta.tag_name = msg.meta.s7_address; // For OPC UA: msg.meta.opcua_tag_name
// Pass the value through
msg.payload = msg.payload;
return msg;
Key line: msg.meta.tag_name = msg.meta.s7_address
Each protocol provides its address in metadata:
Siemens S7:
msg.meta.s7_address
OPC UA:
msg.meta.opcua_tag_name
Modbus:
msg.meta.modbus_address
The addresses automatically become tags - no manual mapping needed!
Template Variables
The {{ .IP }}
and {{ .PORT }}
in your configuration are template variables. They come from the Connection settings you entered earlier:
{{ .IP }}
- The IP address from Connection settings{{ .PORT }}
- The port from Connection settings{{ .location_path }}
- The location from Bridge configuration
This makes configurations reusable across different sites - just change the Connection settings.
Learn more: Template Variables Reference
Click Save & Deploy.
See Your PLC Data
In Topic Browser, you now see ALL your PLC data automatically organized:
enterprise.sksk._raw.DB1.DW20 [12345]
enterprise.sksk._raw.DB1.S30.10 ["Product ABC"]
enterprise.sksk._raw.DB3.I270 [789]
(These are S7 addresses, but OPC UA NodeIDs or Modbus registers would appear the same way)
Result: One bridge reads your entire PLC. Every address becomes a tag automatically.

Part 2: Special Handling with Conditions
Some PLC tags need special treatment - scaling, unit conversion, or validation. Let's use Conditions to handle specific addresses differently.
Add Your First Condition
Below the Always section, click Add Condition
If Condition:
msg.meta.s7_address == "DB1.DW20"
(use the appropriate metadata field for your protocol)Then Action:
// Scale the value and add engineering unit
msg.payload = parseFloat(msg.payload) * 1.0;
msg.meta.unit = "bar"; // This is pressure in bar
return msg;
Add Another Condition for String Data
Click Add Condition again
If Condition:
msg.meta.s7_address == "DB1.S30.10"
Then Action:
// Clean up string data
msg.payload = msg.payload.trim();
msg.meta.unit = "Text"; // Mark as text data
return msg;

What Conditions Do
Always section: Runs for EVERY tag (sets basics)
Conditions: Run ONLY for specific tags (special handling)
You can handle hundreds of tags with just a few conditions for the special cases.
View the Enhanced Data
In Topic Browser, click on DB1.DW20
. Notice:
The value might be scaled
Metadata now shows
unit: "bar"
Special handling applied ONLY to this tag

Part 3: Organize with Virtual Folders
Right now all tags are at the root level. Let's organize them into logical folders using virtual paths.
Add Organization with Conditions
Add Condition for production data
If Condition:
msg.meta.s7_address.startsWith("DB1")
Then Action:
// Group all DB1 data under "production" folder
msg.meta.virtual_path = "production"; // Creates organizational folder
return msg;
Add Condition for quality data
If Condition:
msg.meta.s7_address.startsWith("DB3")
Then Action:
// Group all DB3 data under "quality" folder
msg.meta.virtual_path = "quality"; // Creates organizational folder
return msg;
The Result: Organized Folders
In Topic Browser, your data is now organized:
enterprise.sksk._raw.production.DB1.DW20 [12345]
enterprise.sksk._raw.production.DB1.S30.10 ["Product ABC"]
enterprise.sksk._raw.quality.DB3.I270 [789]
All DB1 tags → production folder
All DB3 tags → quality folder
Automatically organized by data block
You organize entire data blocks with one condition, not individual tags.
Part 4: Route to Different Machines
What if different data blocks belong to different machines? Let's route them!
Dynamic Location Routing
Add conditions to route data blocks to different machines:
Add Condition for Machine 1
If Condition:
msg.meta.s7_address.startsWith("DB1")
Then Action:
// DB1 belongs to machine-1
msg.meta.location_path = "{{ .location_path }}.machine-1";
msg.meta.virtual_path = "sensors";
return msg;
Add Condition for Machine 2
If Condition:
msg.meta.s7_address.startsWith("DB3")
Then Action:
// DB3 belongs to machine-2
msg.meta.location_path = "{{ .location_path }}.machine-2";
msg.meta.virtual_path = "quality";
return msg;
Result
ONE bridge now routes to MULTIPLE machines:
enterprise.sksk.machine-1._raw.sensors.DB1.DW20 [12345]
enterprise.sksk.machine-1._raw.sensors.DB1.S30.10 ["Product ABC"]
enterprise.sksk.machine-2._raw.quality.DB3.I270 [789]
Understanding the Complete Picture
You now control every part of the topic:
umh.v1.enterprise.sksk.machine-1._raw.sensors.DB1.DW20
└─ fixed ─┘└─ location_path ─┘ └virtual┘└tag┘
path name
location_path: WHERE the device is (can be dynamic)
virtual_path: HOW you organize (folders for grouping)
tag_name: WHAT you're reading (automatic from S7 address!)
What You've Learned
✅ Protocol addresses become tag names - No manual mapping needed! ✅ Conditions handle special cases - Not every tag, just the exceptions ✅ Folders organize automatically - Group entire data blocks at once ✅ Dynamic routing is simple - Send different PLCs to different locations ✅ Variables make it reusable - {{ .IP }}
works for any PLC
Concepts Learned
Building on previous guides, you now understand:
Template variables - Dynamic configuration using
{{ .IP }}
,{{ .PORT }}
,{{ .location_path }}
Message structure - msg.meta (metadata) and msg.payload (actual value)
Conditions - If-then rules in Tag Processor for special handling
virtual_path - Additional folder organization within topics
Dynamic routing - One bridge serving multiple locations
Automatic tag mapping - Protocol addresses become tag names automatically
Metadata - Additional context in msg.meta (units, sources, custom fields)
What's Next?
You can now:
Connect entire PLCs with one bridge
Organize hundreds of tags with a few conditions
Route to different locations dynamically
But there's still a risk: What if DB1.DW20 suddenly sends a string instead of a number? Or DB3.I270 goes out of range?
Ready to add data validation? → Step 4: Validate Your Data
Last updated