Lua Transforms

Lua nodes process batches of SerialFlow frames. The current Lua contract is frame-based: your script defines process_frames(frames) and returns either frame tables or raw strings.

Lua works on frames, not lines intrinsically. For newline-delimited ASCII protocols, one frame will often correspond to one line of text. For binary or other framed protocols, frame.data contains the full payload for that frame.

The Lua Contract

Every Lua script must define process_frames(frames):

function process_frames(frames)
  return frames
end

frames is a list of tables. Each frame includes:

Field Type Notes
id string Frame identifier
ts integer Timestamp
direction string "rx" or "tx"
data string Frame payload
meta table Frame metadata

Pass Through Unchanged

Return the incoming frames as-is:

function process_frames(frames)
  return frames
end

This is the default passthrough shape used by the built-in Lua examples and templates.

Update Data Or Metadata

You can mutate frame fields before returning them:

function process_frames(frames)
  for _, frame in ipairs(frames) do
    frame.data = string.upper(frame.data)

    if frame.meta == nil then
      frame.meta = {}
    end

    frame.meta["tag"] = "lua"
  end

  return frames
end

Filter Frames

To drop frames, return only the ones you want to keep:

function process_frames(frames)
  local out = {}

  for _, frame in ipairs(frames) do
    if frame.direction == "rx" then
      table.insert(out, frame)
    end
  end

  return out
end

Returning nil is not the documented way to drop frames. Use an empty list or a filtered list instead.

Hardware-Verified Text Filter

This filter has been verified against a real CLI-connected device in our HIL flow. It passes through only frames whose payload contains the literal text error:

function process_frames(frames)
  local result = {}

  for _, frame in ipairs(frames) do
    if string.find(frame.data, "error", 1, true) then
      result[#result + 1] = frame
    end
  end

  return result
end

On a newline-delimited text protocol, this behaves like filtering lines. For example:

  • status-ok is dropped
  • error-overheat passes through

Return Raw Strings

Lua can also return raw strings instead of frame tables:

function process_frames(frames)
  local out = {}

  for i, frame in ipairs(frames) do
    out[i] = string.upper(frame.data)
  end

  return out
end

When raw-string output has the same length as the input, SerialFlow preserves the original frame metadata and updates only data. When the output length changes, SerialFlow wraps the returned strings into new frames.

Error And Timeout Behavior

Lua nodes are fail-open. If a script errors, times out, or returns invalid output, SerialFlow passes the original frames through unchanged.

Editing In Studio

When you edit a Lua node in Studio and click Apply, SerialFlow validates and hot-reloads the script. If the new script is invalid, the previous script stays active.

Notes

  • Use process_frames(frames), not transform(data).
  • Lua receives frames. If your device speaks newline-delimited text, filtering frame.data usually feels like filtering lines.
  • For binary or framed protocols, frame.data is the full frame payload presented to Lua.
  • frame.direction is a string in Lua: "rx" or "tx".
  • frame.data is the payload string you usually transform.
  • frame.meta is where you can attach extra per-frame metadata.

Next Steps