Edge Gateway

Logic and routing nodes

Condition, Join, Inject, Catch, Expression, Cast, Debounce, Batch, JSON template and Throttle: how each is drawn, and what the runtime does with it on the device.

Product page →

How the runtime executes them

Every node on the wire between a source and an output that is not folded into the tag (Scale, Unit conversion, Rename) or the route (Deadband, Rate limit) becomes a step on that route, in wire order. The runtime runs them in two halves around its publish decision.

A ROUTE, INSIDE THE RUNTIMETag updatefrom the tag engineValue stepsCondition · Expression · Castmay drop the valuemay change itPublish decisiondeadband · heartbeatMessage stepsDebounce · Batch · Template …may hold the messagemay reshape itOutputthe destination
What the value steps produce is what the route's individual, grouped, joined and periodic messages carry, so a Condition before an OPC UA server decides which values the server holds.

Condition / Route

Edge LogicPump house✓ Saved · v3DeployFilter nodes…PLC & INDUSTRIALModbusSiemens S7Allen-Bradley (CIP)…TRANSFORMSScaleDeadbandExpression…LOGIC & ROUTINGCondition / RouteJoinInject / TriggerOUTPUTMQTT outHTTP outSQL out…GATEWAYModbus converterMain meterModbus · 192.168.1.20every 1 s · 6 tagsVoltage high?Condition · value > 250AlarmsMQTT outalarms/{tag}Plant brokerMQTT outplant/{device}/{tag}Voltage high?TESTMessage valueOUTPUT 1> 250 · HighOTHERWISE OUTPUTonMATCH ALL RULESoff
One output per rule, plus Otherwise. The wire from output 1 goes to the alarm topic; the Otherwise wire carries everything else.

The node tests the value, the quality, the tag name, the device or the topic against rules evaluated top-down: ==, !=, >, >=, <, <=, between, contains, matches regex, is true, is false, is null / BAD and changed. Each rule is an output; a final Otherwise output catches the rest. First match wins unless Match all is on, in which case every matching output fires.

In the runtime each output is its own route, carrying the rules and which output it left through. A value that does not pass is counted as suppressed for that route and is not part of its grouped, joined or periodic messages. is null / BAD matches a reading whose quality is not usable, so a bad reading can be sent somewhere specific. changed remembers the last value it saw per tag.

Join

Edge LogicPump house✓ Saved · v3DeployFilter nodes…PLC & INDUSTRIALModbusSiemens S7Allen-Bradley (CIP)…TRANSFORMSScaleDeadbandExpression…LOGIC & ROUTINGCondition / RouteJoinInject / TriggerOUTPUTMQTT outHTTP outSQL out…GATEWAYModbus converterMain meterModbus · 192.168.1.20PumpModbus · 192.168.1.21Joinmerge tagsPlant brokerMQTT outplant/pump-houseJoinSTRATEGYMerge tags
Two devices, one message: values holds every tag of both.
StrategyThe runtime
Merge tagsCoalesces the updates of a poll cycle and sends one message with the current value of every joined tag, keyed by tag name. Two devices with a tag of the same name: the newer reading wins.
Zip by tsSends only once every joined device has reported since the last message, so a message always carries a fresh value from each.
Latest winsSends the full snapshot on every update, so a consumer always has everything at the cost of more messages.
A merged message
{"devices":["Main meter","Pump"],"timestamp":"2026-09-29T07:25:03.629Z","values":{"Voltage_L1":230.5,"Speed":1480,"Running":true}}

The topic's {device} becomes the joined device names (Main_meter_Pump); name the topic yourself when that is not what you want.

Inject / Trigger

Edge LogicPump house✓ Saved · v3DeployFilter nodes…PLC & INDUSTRIALModbusSiemens S7Allen-Bradley (CIP)…TRANSFORMSScaleDeadbandExpression…LOGIC & ROUTINGCondition / RouteJoinInject / TriggerOUTPUTMQTT outHTTP outSQL out…GATEWAYModbus converterMain meterModbus · 192.168.1.20Every minuteInject · 1mJoinmerge tagsHistorianSQL outEvery minuteINTERVAL1mPAYLOAD(empty)
Through a Join, the Inject decides when: one row set per minute, whatever the poll rate.
  • Through a Join with a device: the joined values are published on the Inject's interval, not on change. The runtime makes the route periodic at that interval.
  • Straight into an output: a message on the interval. The Payload is sent as-is when it is JSON, as text otherwise; empty, the message is {"event":"trigger","route":…,"timestamp":…}. A heartbeat for a broker, or a keep-alive for an API.
  • The interval is 500ms, 5s, 1m and so on; below 100 ms is refused at validation.

Catch / Status

Edge LogicPump house✓ Saved · v3DeployFilter nodes…PLC & INDUSTRIALModbusSiemens S7Allen-Bradley (CIP)…TRANSFORMSScaleDeadbandExpression…LOGIC & ROUTINGCondition / RouteJoinInject / TriggerOUTPUTMQTT outHTTP outSQL out…GATEWAYModbus converterMain meterModbus · 192.168.1.20every 1 s · 6 tagsStatusCatch · whole flowOps channelMQTT outops/statusStatusSCOPEWhole flow
A Catch route carries no values: it reports what went wrong, and when it recovered.

The route becomes a status channel. A tag's first good reading is not news; its first bad one, and every change of quality after that, is sent with the reason: a timeout, a refused address, a device that stopped answering. Wired nodes only watches the device it is wired to; Whole flow watches every tag, and also reports a failed delivery to any other output (a broker that refused the connection, a database that is down).

What the ops channel receives
{"event":"quality","device":"Main meter","tag":"Voltage_L1","quality":"TIMEOUT","previous":"GOOD","error":"no response","timestamp":"2026-09-29T07:25:03.629Z"}
{"event":"destination","destination":"Historian","error":"dial tcp 10.0.0.5:5432: connection refused","timestamp":"2026-09-29T07:25:04.101Z"}

The default topic is modbuslogic/status/{device}/{tag}; set the node's output topic to put everything on one.

Expression, Cast, Debounce, Batch, JSON template, Throttle

NodeFieldsThe runtime
Expressionvalue * 1.8 + 32, value * Running, round(value, 1)Computes over value and other tags by name, in the same closed grammar as a calculated tag: arithmetic, comparisons, abs, sqrt, floor, ceil, round, min, max, avg, sum, and, or. A tag that is missing or bad makes the result unavailable; the value is dropped and the log says why, once. A bad reading is passed through as bad, not computed on.
Castbool, int64, uint64, float64, stringConverts the value before it is published: int64 truncates, bool treats a non-zero number or true/1/on as true, string gives the text.
DebounceQuiet period (ms)Holds the message until the tag has been quiet for that long, then sends the last value. A flapping input gives one message with the value it settled on.
Batch / AggregateMax count, Max window (ms)Collects messages into one JSON array and sends it when the count is reached or the window closes, addressed to the device.
JSON template{ "t": "{{ts}}", "v": {{value}} }Fills the template and sends it as JSON when it parses, as text otherwise. Placeholders: {{value}} (JSON form: a string is quoted), {{text}} (plain), {{tag}}, {{device}}, {{unit}}, {{quality}}, {{ts}}, {{topic}}, {{project}}, {{route}} and {{payload}} (the whole standard envelope).
ThrottleQueue depthDrops new messages while more than that many wait in memory for the destination, so a slow output cannot back up the gateway. Dropped messages are counted as suppressed on the route.
On the way to a server-style output (OPC UA, BACnet/IP, WebSocket, Modbus Server) the message-shaping nodes have nothing to act on: the compiler leaves them off and validation says so. Condition, Expression and Cast do apply there.

Test it with the Modbus Simulator

You do not need the PLC to try this flow. The Modbus Simulator answers as a Modbus slave on your own PC, with registers that move, so the whole path from polling to the output can be watched before any hardware is involved.

  1. Start the Simulator and add a slave: Modbus TCP, bind host set to this PC's LAN address (the prefilled one), port 1502, unit ID 1. Add a few holding registers, say 40001–40010 as FLOAT32 or INT16 in random mode so the values change, and press Start.
  2. In the editor, set the source node's host to that address, port 1502, unit ID 1, and the same addresses and data types. Poll interval 1s is fine.
  3. Deploy the design to a device on the same network, or to this very PC: the Edge Gateway Console installs on a laptop as readily as on an edge computer, and the gateway polls the Simulator over the LAN.
  4. The Simulator's request log shows the gateway's reads arriving: function code 03, the start address and quantity, once per poll interval. Neighbouring registers arrive as one read.
  5. Put a register in fixed mode and set it by hand across a Condition's threshold: the message appears on the one output and stops on the other. Random mode on a register behind a Debounce shows the difference between the poll rate and the messages that leave.
THE TEST BENCHModbus Simulatora slave on your PC · port 1502registers in random moderequest log shows each readEdge Gatewaythe runtime on your devicepolls every secondthe live view shows valuesThe outputon the same PC or elsewhere

Registers in fixed mode make a deadband or a condition easy to exercise: change a value by hand and watch what is, and is not, published. A register set to a value the Simulator refuses (uncheck Fill unmapped and read an address that is not there) shows what a BAD quality looks like at the output. The Simulator's own page has the rest.

From design to device

THE SAME FOUR STEPS FOR EVERY FLOWValidateon the design pageInstallthe console on the deviceDevice codeentered on the design pageBundlegiven to the console
  1. Validate. On the design page, Run validation. The editor's rules, the tag document and the runtime binary itself check the design; every issue names its node. Details: Validating a design.
  2. Install. From the Devices panel download the installer for the device's platform (Windows x64, Linux x64 or Linux ARM64). Windows: extract and run Edge-Gateway-Console.exe from an administrator terminal. Linux (Debian, Ubuntu, Raspberry Pi OS): sudo dpkg -i the .deb, then sudo edge-gateway-console. It shows the device code. Details: Installing on the device.
  3. Device code. Enter it in the Devices panel, choose a trial or one of your purchased licences, and press Download activation bundle: one small zip with the design and a licence bound to that machine, sealed so only that machine can open it. Details: Downloading.
  4. Bundle. Give the zip to the console (drag it onto the program, pass its path, or put it beside the program and run it again; a USB stick is fine for a device without internet). It opens the bundle with its own code, installs the runtime as a service and starts it. The device never contacts the site. Details: Activation and the licence.

The console then opens the gateway's web view in a browser (on a device without a desktop it prints the address instead) and stays attached with the live view: devices up or down, values as they change, what was published, and the log.

  • The live view counts, per route, what was published and what a Condition, Debounce or Throttle held back (suppressed).
  • A Catch route's messages arrive on its output when you stop the Simulator, and again when you start it.

Ctrl-C leaves the gateway running as a service. Run edge-gateway-console again at any time: it shows the service, the licence, every device and destination with what to check, the web view, and the live log. Running covers the data directory, secrets and the diagnostics.

Logic and routing nodes · Edge Gateway documentation | Modbus Logic