Skip to content

Inputs: what comes in from outside ​

A system's behaviour says how it changes by itself. Inputs say what comes into it from outside: requests arriving at a front desk, alarms at a cell, orders at a warehouse. The two are kept apart, as every modelling tradition keeps them apart — the model (x′ = f(x, u)) and its inputs u; DEVS's external transitions and input ports, fed by a generator outside the model; the arrivals of a queue. Recorded apart, what came in can never be mistaken for what the system did by itself — which is what identifying a system's dynamics from data (SINDy with control) depends on.

WhatWhere
Inputa way the outside acts on an asset of a type — its input port, with the shape of what it carriesthe asset type's inputs (system-core)
Input eventone occurrence: an input, at an asset, at a time, with a payloademitted by the runtime; recorded
Input streama source of input events: a list (a recorded trace is one), or a sampler drawing them at a ratethe system's, beside its behaviours (system-core holds them; system-sim plays them)
What an input doesits effect on the asset's signalsthe behaviour's inputs (system-sim)
Interventionforcing a signal for a while — a different thing: it overrides, an input arrivesthe runtime (Interventions)

A source has no state of its own beyond where it is in time. Something outside that reacts — users who retry when served slowly — is a system of its own, whose outputs would be this one's inputs (coupling: not built yet).

Declaring inputs ​

js
sys.registerAssetType('desk', {
    features: { waiting: { frequency: 1000, range: [0, 10] } },
    inputs:   { request: { label: 'A request', payload: { text: 'text', size: 'number' } } },
});
sys.getInputs('desk');      // { request: { label, payload } }

A payload's fields are text, number or boolean (PAYLOAD_KINDS). An input event may leave a declared field out; it may not carry one the input does not declare. inputsProblems(inputs) → string[] is the check registerAssetType applies (it throws on a problem). A type without inputs stores exactly as before.

Input streams ​

js
sys.setInputStream({ id: 'calls', kind: 'list', every: 60000, events: [
    { at: 1500, input: 'request', asset: 'front', payload: { text: 'a logo for NORTHBOUND' } },
] });
sys.setInputStream({ id: 'traffic', kind: 'sampler', input: 'request', assets: ['front', 'back'], every: 2000, seed: 7,
    draw: [{ payload: { text: 'a logo for my bakery' }, weight: 3 }, { payload: { text: 'a 5 s clip of waves' } }] });
sys.setRun({ streams: ['traffic'] });     // run with these; null (the default): every one; []: none
KindFieldsPlays
listevents: [{ at, input, asset, payload? }], every?these input events at these times (ms of simulated time); every: the list again every so often (longer than its last at)
samplerinput, assets, every, arrivals?, draw?, start?, until?, seed?one input event every every ms on average — 'poisson' (default: independent arrivals) or 'regular' — at each of assets in turn, its payload drawn from draw by weight; from start until until; seed: the same draws every run
getInputStreams() → stream[]                 // in order (copies)
getInputStream(id) → stream | null
setInputStream(stream) → this                // add, or replace the one with its id in place
removeInputStream(id) → boolean              // also dropped from run.streams

The system holds a stream as data and saves it with itself (inputStreams in its snapshot; a system without any saves exactly as before). Whether one is valid is system-sim's, as for behaviours: validateInputStream(stream, system) → string[] checks its kind and fields, that each asset accepts the input and that each payload fits it. The kinds are a registry, sourceArchetypes (list(), register(kind, { params, compile })) — a kind compiles a stream to (from, to) → input events, and the runtime asks it about each stretch of time once.

What an input does ​

In the behaviour, by type:input — changes to the asset's signals, each a number or a number its payload carries:

js
const day = {
    generators: { 'desk:waiting': { kind: 'constant', value: 0 } },          // the queue empties by itself
    events:     { busy: { kind: 'threshold', feature: 'waiting', op: '>=', value: 2 } },
    inputs:     { 'desk:request': [{ feature: 'waiting', add: 1 }] },     // or add: { payload: 'size' }
};

An input event arrives on the tick its time falls in, after the generators and before influence — so what it does is carried along the connections, recorded, and checked for events on that tick. It changes the signal's natural value: an intervention on the signal still acts over it. An input with nothing in inputs still arrives — it is emitted and recorded, and can be decided on. validateDynamics checks inputs against the declarations (with ctx.inputOf), and that no event takes an input's name (with ctx.inputNames): both are heard by name.

Running with inputs ​

buildRuntime(system, spec, opts?) plays the streams the system is run with — or opts.streams (ids; []: none). simulate() likewise, and reports what came in (inputs: per input, its count, first and last time and where) apart from the events the system raised.

runtime.playInputStream(stream, sources?) → this            // throws only on a kind it does not know
runtime.sendInput({ input, asset, payload? })                // one, now: it arrives on the next tick (checked)
runtime.defineInputEffect(typeName, input, effects) → this   // what compileDynamics does with `inputs`
runtime.getInputEvents({ from?, to? }) → [{ input, uid, simTime, payload?, stream? }]   // what came in, oldest first

Each input event is emitted on 'event' — { id: <input>, uid, simTime, state, source: 'input', input, payload?, stream?, neighbours? } — before the events the tick raised: a decision binding hears it by its input's name, as it hears the system's events, and reads its payload in the trigger's context ({payload.text}). inputEventProblems(system, event) and payloadProblems(payload, declared) are the checks sendInput applies, and the runtime applies again as each input event arrives; streamsToRun(system, ids?) the streams buildRuntime plays. The runtime keeps the last opts.inputLogDepth (1000) input events.

Playing is lenient, as compiling a behaviour is: a stream the system has changed under since it was saved (an asset removed, an input no longer declared) still plays, and only its input events that no longer fit do not arrive; a stream that cannot be timed (no every, no assets) plays nothing. validateInputStream is the strict check, for authoring — as validateDynamics is for a behaviour.