/**
* Lego Ladder — Core Type Definitions
*
* Ladder logic is a graphical programming language used in PLCs.
* Programs are organized into RUNGS evaluated left-to-right, top-to-bottom.
* Each rung is a boolean expression that determines whether output COILS activate.
*/
// ─── Element Types ────────────────────────────────────────────────
/** A contact — the basic boolean element. Reads an input condition. */
export type ContactType = 'NO' | 'NC';
export type EdgeType = 'RISING' | 'FALLING' | 'BOTH';
export interface Contact {
type: 'contact';
/** Normally Open (NO) = passes when true. Normally Closed (NC) = passes when false. */
contactType: ContactType;
/** Address of the input this contact reads (e.g., 'I:0.0', 'SENSOR:dist') */
address: string;
/** Optional comparison: value must satisfy this to be true. */
condition?: ComparisonCondition;
/** When set, contact only passes for one scan on the edge. */
edgeType?: EdgeType;
}
export type ComparisonOp = '==' | '!=' | '>' | '<' | '>=' | '<=';
export interface ComparisonCondition {
op: ComparisonOp;
/** Constant value to compare against (from sensor, hardcoded threshold, etc.) */
value: number;
}
/** A coil — an output that gets energized when the rung logic is true. */
export type CoilType = 'OTE' | 'OTL' | 'OTU' | 'OUTPUT' | 'SET' | 'RESET' | 'TOGGLE' | 'LATCH' | 'UNLATCH';
// OTE = Output Energize (standard coil)
// OTL = Output Latch (SET alias)
// OTU = Output Unlatch (RESET alias)
// LATCH = once energized, stays on until UNLATCH (separate from SET which is momentary)
// UNLATCH = clears a LATCH
export interface Coil {
type: 'coil';
coilType: CoilType;
/** Address of the output (e.g., 'Q:0.0', 'MOTOR:B') */
address: string;
/** Optional: target value for the output (motor speed, brightness, etc.) */
value?: number;
}
/** A timer element — delays output activation or deactivation. */
export type TimerType = 'TON' | 'TOF' | 'TP' | 'RTO';
// TON = On-Delay (waits preset time before energizing)
// TOF = Off-Delay (stays energized for preset time after input goes false)
// TP = Pulse (pulses output for preset time when input goes true)
// RTO = Retain On-Delay (like TON but accumulated time persists when input goes false)
export interface Timer {
type: 'timer';
timerType: TimerType;
/** Unique timer instance ID */
instanceId: string;
/** Preset time in milliseconds */
preset: number;
/** Internal accumulated time */
accumulated: number;
/** Optional: reset address — when true, resets RTO accumulated time to 0 */
resetAddress?: string;
}
/** A counter element — counts transitions. */
export type CounterType = 'CTU' | 'CTD' | 'CTUD';
// CTU = Count Up
// CTD = Count Down
// CTUD = Count Up/Down
export interface Counter {
type: 'counter';
counterType: CounterType;
/** Unique counter instance ID */
instanceId: string;
/** Preset count value */
preset: number;
/** Current count value */
current: number;
/** Optional: reset address — when true, resets counter to 0 */
resetAddress?: string;
/** Overflow bit — set when ACC > 9999 */
ov?: boolean;
/** Underflow bit — set when ACC < 0 */
und?: boolean;
}
/** A logic gate element. */
export type GateType = 'AND' | 'OR' | 'XOR' | 'NOT' | 'NAND' | 'NOR';
export interface LogicGate {
type: 'gate';
gateType: GateType;
/** Number of inputs for multi-input gates */
inputs: string[];
/** Instance output address */
outputAddress: string;
}
/** A logic branch — allows for nested AND/OR logic within a rung. */
export interface Branch {
type: 'branch';
/** Unique ID for the branch element */
id: string;
/** The logic used to combine paths within this branch */
logic: 'AND' | 'OR';
/** The paths within this branch. Each path is a series of elements. */
paths: RungElement[][];
}
/** A math element — performs arithmetic on two inputs. */
export type MathOperator = 'ADD' | 'SUB' | 'MUL' | 'DIV';
export interface MathElement {
type: 'math';
operator: MathOperator;
inputA: string;
inputB: string;
outputAddress: string;
}
/** One-Shot contact — fires true for exactly one scan when input goes from false to true. */
export interface OneShot {
type: 'oneshot';
address: string;
edgeType: 'RISING' | 'FALLING'; // which edge triggers
}
/** MOV (Move) instruction — copies value from source to destination. */
export interface MoveElement {
type: 'move';
source: string; // source address
destination: string; // destination address
}
/** Scale (SLC) instruction — scales input from one range to another. */
export interface ScaleElement {
type: 'scale';
inputAddress: string; // raw input
destination: string; // scaled output
inMin: number; // input min
inMax: number; // input max
outMin: number; // output min
outMax: number; // output max
}
/** NOP (No Operation) — placeholder/separator. */
export interface NoOp {
type: 'noop';
comment?: string; // optional label
}
/** Standalone Compare instruction — compare two values, output boolean. */
export interface CompareElement {
type: 'compare';
op: ComparisonOp;
inputA: string;
inputB: string;
outputAddress: string; // boolean result address
}
/** Any element that can appear in a rung. */
export type RungElement = Contact | Coil | Timer | Counter | LogicGate | Branch | MathElement
| OneShot | MoveElement | ScaleElement | NoOp | CompareElement;
// ─── Rung & Program ──────────────────────────────────────────────
/** A single rung of ladder logic. */
export interface Rung {
id: string;
/** Display label for this rung */
label?: string;
/** Elements in series (AND logic) — left to right. Last element is the output coil.
* Can include Branch elements for nested logic. */
series: RungElement[];
/** Whether this rung is enabled */
enabled: boolean;
}
/** The complete ladder program. */
export interface Program {
name: string;
rungs: Rung[];
/** Cycle time in ms — how often rungs are re-evaluated */
cycleTime: number;
/** When true, math operations truncate to integer (32-bit DINT mode) */
integerMode?: boolean;
}
// ─── I/O Model ────────────────────────────────────────────────────
/** An input — a value read from sensors, buttons, or internal state. */
export interface Input {
address: string;
value: boolean | number;
/** Raw value before any threshold comparison */
rawValue?: number;
}
/** An output — a value written to motors, LEDs, or internal state. */
export interface Output {
address: string;
value: boolean | number;
/** When was this output last updated (cycle number) */
lastUpdated: number;
}
/** Internal memory bit — used for intermediate logic state. */
export interface MemoryBit {
address: string;
value: boolean | number;
}
// ─── Engine State ────────────────────────────────────────────────
/** Current engine state. */
export interface EngineState {
/** Current cycle number */
cycle: number;
inputs: Map<string, Input>;
outputs: Map<string, Output>;
memory: Map<string, MemoryBit>;
timers: Map<string, Timer>;
counters: Map<string, Counter>;
/** Previous scan cycle input values — used for edge detection. */
previousInputs: Map<string, boolean | number>;
running: boolean;
}