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.
| What | Where | |
|---|---|---|
| Input | a way the outside acts on an asset of a type — its input port, with the shape of what it carries | the asset type's inputs (system-core) |
| Input event | one occurrence: an input, at an asset, at a time, with a payload | emitted by the runtime; recorded |
| Input stream | a source of input events: a list (a recorded trace is one), or a sampler drawing them at a rate | the system's, beside its behaviours (system-core holds them; system-sim plays them) |
| What an input does | its effect on the asset's signals | the behaviour's inputs (system-sim) |
| Intervention | forcing a signal for a while — a different thing: it overrides, an input arrives | the 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
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
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| Kind | Fields | Plays |
|---|---|---|
list | events: [{ 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) |
sampler | input, 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.streamsThe 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:
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 firstEach 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.
