ReferenceControlArcConceptsChannels and Series

Channels and Series

How Arc connects to Synnax telemetry through channels and handles array data with series

Channels are Arc’s connection to Synnax telemetry data. They let you read sensor values, write to actuators, and respond to operator inputs. Series are arrays of values, useful for batch processing and aggregation.

Channels

A channel in Arc references a channel that exists in your Synnax cluster. You don’t declare channels. Just use them by name:

// Read from a pressure transducer and scale it into another channel
tank_pressure -> scale{factor=2.0} -> pressure_scaled
// Write a fixed value to a valve command
1 -> valve_cmd

Channel names follow the same conventions as Synnax channels: start with a letter or underscore, then letters, digits, or underscores. Examples: tank_pressure, inlet_temp, start_btn.

Channel Discovery

The Arc text editor provides autocomplete for channels in your cluster. As you type, matching channel names appear with their data types. You can also browse available channels in the Console’s resources panel before writing code.

Reading Channels

Channels behave differently depending on whether you’re in a reactive context (flow statements) or an imperative context (inside a function body).

You can also bind a channel, or an expression over channels, to a variable that updates as new data arrives.

AspectReactive (->)Imperative (function body)
TriggerNew data arrivalFunction invocation
Data formTime-series streamSingle scalar value
Empty channelSkips executionReturns zero
Use caseMain data pipelinesSide-channel reads, setpoints

Channels as Inputs

Channels can be passed to functions as input parameters:

func monitor{sensor chan f64, limit f64} () u8 {
    return sensor > limit
}
// Wire it up on a 50ms cadence
time.interval{period=50ms} -> monitor{sensor=tank_pressure, limit=500.0} -> pressure_high

This lets you write reusable functions that work with different channels.

Writing to Channels

Write to a channel by making it the final target of a flow statement, or with assignment syntax inside a function.

Writes are queued until the end of the execution cycle. Multiple writes to the same channel in a single cycle result in the last value being sent.

Series

Series are homogeneous arrays of values. They’re useful for batch operations and working with buffered data.

Creating Series

// Literal
data := [1.0, 2.0, 3.0]
// Empty (requires type annotation)
buffer series f64 := []

Indexing and Slicing

data := [10.0, 20.0, 30.0, 40.0]

first := data[0] // 10.0
last := data[3] // 40.0
middle := data[1: 3] // [20.0, 30.0]

Out-of-bounds access causes a runtime error.

Series Length

data := [1.0, 2.0, 3.0]
count := len(data) // 3 (i64)

Element-wise Operations

Arithmetic and comparison operators work element-wise on series:

data := [1.0, 2.0, 3.0]
// Scalar operations (broadcast)
scaled := data * 2.0 // [2.0, 4.0, 6.0]
offset := data + 10.0 // [11.0, 12.0, 13.0]
// Series operations (must be equal length)
other := [4.0, 5.0, 6.0]
sum := data + other // [5.0, 7.0, 9.0]
// Comparisons return series u8
mask := data > 2.0 // [0, 0, 1]

Binary operations between two series require them to have equal length. Mismatched lengths cause a runtime error.

Channel Types

When passing channels as input parameters, you declare their type:

func process{input chan f64, output chan f64} () {
// ...
}

The type system ensures you don’t accidentally connect incompatible channels. If you try to read an f64 channel and use it where an i32 is expected, the compiler will catch it.