How Manifest Settings Drive Busroot¶
The Schema Reference says what each field is. This page says what it does: how each part of the configuration shapes what Busroot records, calculates and shows. It is worth reading before changing configuration on a live account, because several settings have effects that are not obvious from their names.
Time and calendar¶
A plant's timezone, dayStartHour and weekStartDay anchor every time-based
calculation for its stations:
- Every date range resolves in the plant's timezone. "Today", a named day, a week, a month, a quarter, a year: all are interpreted where the plant is, not where the server or the viewer is.
dayStartHourmoves the day boundary. A factory whose day starts at 06:00 means "Tuesday" runs from 06:00 Tuesday to 06:00 Wednesday, in every report and dashboard. "Today" and "Yesterday" respect the same offset.weekStartDaymoves the week boundary, and with it the boundaries of quarters and years, which are built from weeks. Weekly and longer views for a plant that starts its week on Sunday genuinely start on Sunday.
Change these on an established plant with care: the boundaries of historical reporting periods move with them.
Shift patterns and OEE¶
Shifts decide when time counts. Production counts only need a running schedule (below), but utilisation, OEE and the station's live running/stopped state also need the time to fall inside a shift (see In-shift time):
- Outside shift hours a station reads out of shift and accrues no productive time, however much it is producing. Counts still record; the percentages don't move.
- A station resolves its shift pattern from its own
shiftPatternCode, or its group's if it doesn't set one. A station with no resolvable pattern is never in shift. It will show production counts and permanently zero utilisation. This is the most common surprise when commissioning a new station: if the numbers are all zero except the count, check the shift pattern first. - This is why a 24/7 shift pattern is the practical choice while commissioning: a test at any hour still produces figures. Realistic patterns can come later.
Station codes are permanent¶
A station's code is the key that ties everything recorded (production, downtime,
utilisation, energy) to the station. Two consequences:
- Renaming a code orphans the station's history. The data recorded under the old code
is not deleted, but nothing will ever read it back. Treat a code change as
decommissioning one station and creating another; change
namefor display instead. - Incoming device messages must use the station code exactly as it is spelled in the
manifest. Signals are stored under the code as published on the MQTT topic, so a
device publishing to
station1does not land data on a station whose code isSTATION1.
Archived stations keep their history, and creating a new station with an archived station's code (case-insensitively) is rejected, so the historical data can never be silently claimed by a new station.
Station behaviour settings¶
These settings decide how raw device signals become "running", "down" and performance figures:
- Running is decided by
productiveStatusMode. With the defaultproductive_signal, the station is productive while the device sends productive signals, each one holding the state forproductiveHoldOnTime(so a machine that signals once per cycle stays "running" between cycles. Set the hold-on to comfortably exceed the gap between signals). Withelectrical_usageorline_speedthe station is running while the measured value is above the corresponding stopped-threshold. Production counts alone never make a station productive. A device that publishes only counts shows output and 0% utilisation until it also publishes a productive signal, or the mode is switched. - Down is decided by
downtimeDetectionMode. In the commonutilisationmode, a stop becomes a recorded downtime once it lasts longer thanutilisationDowntimeThreshold(default 1 hour, usually worth lowering). Inproductionmode the threshold derives from the SKU's cycle time anddowntimeDurationThresholdMultiplier.offrecords no downtimes at all. - Speed signals pass through
speedScaleandspeedOffsetbefore display, so a sensor whose raw value doesn't match the real speed can be calibrated in configuration rather than in the device.speedToProductionRatioadditionally converts speed into production counts for continuous processes that have no discrete unit signal. - Performance targets come from the running SKU's rate (
cycleTime,unitsPerMinuteorunitsPerHour) when it has one, falling back to the station's own rate fields.lineSpeedTargetswitches the performance comparison to speed-vs-target. - Costs use the SKU's
valueper unit where set, with the station'sidleCostPerMinutecovering losses that can't be attributed to a SKU. See Downtime cost and Lost production cost.electricalPowerNominalKwis used for Power vs nominal.
Configuration vs runtime state¶
A handful of station fields (statusCode, statusCodeUpdatedAt,
currentOperatorInitials, alertMessage, alertExpiresAt) are state Busroot
writes, not configuration you author. They appear in the schema so the document is
complete, but the live values are maintained by the platform as devices report and
operators sign in and out. Setting them in a manifest achieves nothing lasting.
SKUs¶
- A schedule may only name a SKU that already exists. Creating a schedule with an
unknown SKU code is rejected. Nothing creates the SKU for you. Add the SKU first,
either in the admin UI or in a version-controlled manifest; keeping a permanent test SKU
(e.g.
CALIBRATION) in the list is normal practice. valuedrives money. It is the unit value used by Lost production cost, Quality cost and Downtime cost.- Rates drive targets. The SKU's
cycleTime/unitsPerMinute/unitsPerHourset the Target count and the Performance comparison while a schedule for the SKU runs, and project schedule durations on the Gantt. stationCodes/stationGroupCodesrestrict scheduling. When set, only the named stations (or stations in the named groups) can be scheduled with the SKU.allowUseInQuickSchedulecontrols whether operators can pick the SKU when starting an ad-hoc schedule from the Operator Interface.
Reasons and the include/exclude cascade¶
A reason's type list decides where it is offered: downtime reasons when categorising
stoppages, non-production reasons when scheduling planned non-production time, quality
reasons when recording rejected units.
Which reasons a particular station offers is decided by the
includeReasonCodes/excludeReasonCodes lists, evaluated level by level: account, then
plant, then station group, then station:
- No include list anywhere → every reason is available.
- Any include list → the available set starts from the include lists: each level's includes are added (a lower level can re-add something a parent excluded).
- Each level's excludes are then subtracted at that level, after its includes.
The same cascade controls which Task lists apply to a station, via
includeTaskListCodes/excludeTaskListCodes, with one deliberate difference in the first rule:
- No include list anywhere → no Task list is available. A list has to be included explicitly at some level before its Tasks reach any station, so creating a Task can never put it on every Operator Interface in the company by accident.
The legacy includeTaskCodes/excludeTaskCodes are no longer read at any level.
requireNotes on a reason works differently: it inherits down the reason tree, not
the station hierarchy: a child reason that doesn't set it takes the nearest ancestor
reason's setting, and false explicitly opts out. When it resolves on, operators must
enter a note before the reason can be confirmed against a downtime.
Tasks¶
- Trigger is a property of the Task list, so every Task in a list shares it. A Task's own
triggeris legacy and no longer read. - A list
triggerofschedulemeans its Tasks must be completed once per production schedule. They become pending shortly after a schedule starts and are missed if the schedule ends without them. - A list
triggerofcronmeans its Tasks are pending at every cron fire, evaluated in the plant's timezone; the fire is the deadline.availableBeforeDeadlineMinutescontrols how long before the deadline the Tasks become pending and appear in the Operator Interface, so a weekly check due Friday 17:00 can be made available only from Friday morning; a Task not completed by the fire is missed. - A list
triggerofon_demand(On Demand) means its Tasks have no deadline: the operator starts the list in the Operator Interface (POST /api/task-list/start?code=<list code>with thestationCode), which makes every Task in it pending, completes them one by one, and can start it again straight away. They are never missed for time (only when the Task, list or station assignment is removed while a run is open), and count as pending from when the list is started until each is completed. - Any list can be started from the Operator Interface, whatever its trigger. The run is an On Demand instance: no deadline, recorded as On Demand. The scheduled instance still becomes pending on its own; if that happens while the manual run is open, the run becomes the scheduled instance and is missed like any other if it is not completed in time.
- A single Task can be started through the API (
POST /api/task/start?code=<task code>with thestationCodeandtaskListCode), which is the same as starting its list for that Task alone: an On Demand instance in that list only. The list is required because a Task in two lists is a separate instance in each. The Operator Interface only starts whole lists. - Older manifests may still carry
frequency/always; they are read astrigger/on_demandand rewritten on the next save. requireNotesstays on the Task, not the list: it is about what the operator records, not about timing. The note is kept with the completion and shown in Task History.- Which stations a Task applies to follows the Task list include/exclude cascade above. A SKU's
taskCodesstill name Tasks; SKU-level Task scoping has been removed from the schema entirely. - Completion is per (Task, Task list, station). A Task in two lists gets its own rows and its own completions in each, so completing it in one list leaves the other outstanding, and it counts twice towards a station's pending-task count while both are open.
- Each Task carries an
orderwithin each of its lists, which is the order it appears in in the Operator Interface and in the Task admin table. - The Task instance is a row, completed by id. A row is created when the Task becomes pending
(cron: when its availability window opens; per schedule: shortly after the schedule starts;
any list: when it is started from the Operator Interface) and appears on the station's
requiredTasksastaskCompletionId;POST /api/task-completion/{id}/completecompletes it. Every Task in every list assigned to the station is onrequiredTasksat all times;taskCompletionIdis null while it is not pending. Task History records every instance as completed, missed or pending, so the audit trail exists whether or not operators tick things off. Pending has one meaning everywhere: a row that is neither completed nor missed. It is what every pending-task indicator counts.
Energy totals¶
By default a plant's or group's energy (kWh, cost, CO2) is the sum over its member
stations. totalEnergyStationCodes replaces that: when set, the level's energy is the
designated stations' energy instead: the physical meter's reading rather than the sum
of sub-meters.
The designated stations are usually meter-only stations
(enableMeterOnly), which exist to carry a physical meter's electrical signals and are
hidden from production views. Group designations apply first, then the plant's own, so a
plant total can legitimately differ from the sum of the group rows on screen, because it
is a different physical measurement. Energy CSV exports include the meter stations as rows
so the totals can always be reconciled.
Maximum production count per schedule¶
maxProductionCountPerSchedule (Account Settings → Formatting & Units) is a safety net against a
grossly wrong count, not a target. It exists because a single mistyped parts-per-cycle value can
turn a normal shift into six figures of production: set it a little above the largest schedule you
would ever expect, and a schedule stops accumulating there instead.
- The limit is on units (after parts-per-cycle is applied, good plus bad), which is the number shown in the Operator Interface and in the Cockpit. It is per schedule, and a schedule belongs to one station.
- It applies to every way production is recorded: cycles from the machine simply stop being recorded, and a manual entry or a correction that would take the schedule past the limit is rejected. A negative correction is always allowed, so a schedule can be brought back down.
- Once a schedule has reached the limit, its count is displayed with
(limit)after it, for example2,000 (limit) / 500. There is no other warning; the count itself says recording stopped. - Setting the limit applies it immediately to every schedule, including ones already running. Nothing already recorded is trimmed, so a schedule that is already above the limit keeps its count and simply records nothing more.
-
It also bounds the parts-per-cycle a schedule will run at. A PPC above the limit could never be produced (the schedule would be capped on its very first cycle), so it is refused at entry rather than accepted and then silently capped. The bound applies to the final PPC whatever its source, and the message names that source, because the field to go and change differs:
Source Message The schedule's own partsPerCycle(schedule form, API, Upload Schedule) and the Parts Per Cycle field on Ad Hoc Production"PPC override exceeds the limit of N units per schedule" The PPC the schedule inherits from its SKU when it sets none of its own "This SKU's PPC exceeds the limit of N units per schedule" partsPerCycleOverride: the Operator Interface's 'Set Parts per Cycle' screen and the API"PPC exceeds the limit of N units per schedule" - The SKU's own Parts per Cycle field is not validated against the limit, so existing SKUs never have to be edited when a limit is set. The check happens where the SKU is applied to a schedule instead: scheduling a SKU whose PPC is above the limit is refused, and the planner has to enter a lower Parts per Cycle on the schedule or pick a different SKU. The Gantt's Add Schedule dialog has no Parts per Cycle field of its own, so there the message is shown on the SKU field (the only thing on that form that can be changed to clear it) as soon as the SKU is picked. - Only the schedule being saved is checked. Schedules created before the limit was set are left alone, and editing one (moving its start time, say) does not start failing because of a PPC it already carries. - Because production is processed in ~30 second cycles, a schedule can settle a few units above the limit. Leave the setting empty for no limit. That is the default, and how every account behaves until it is set. - Continuous schedules (no planned quantity) are capped too, and some run for months. Either set the limit high enough for them or leave it empty. - A capped schedule does not raise a stop. On stations using Production Cycle Monitoring a stop is inferred from a gap between recorded units, and a schedule that has reached the limit records nothing at all. So the period from the limit being reached to the end of the schedule is excluded from stop detection, the same way a non-production period is. Detection resumes when the schedule ends. Stations using Utilisation Monitoring are unaffected either way: they judge productivity from the machine's own signal, not from recorded production.
Feature flags in practice¶
The flag table lists what each flag does; the cascade is what makes them practical at scale. Two worked examples:
- Enable manual downtime for one plant only. Leave
enableManualDowntimeunset (off) at the account, set"on"on the plant. Every group and station in that plant inherits on; the rest of the account stays off. - Turn a flag on everywhere except one station. Set
"on"at the account, and"off"on the one station. The station's own value is the nearest defined one, so it wins over the account's.
Remember the default: a flag nothing sets is off. When something seems missing on a new account, check whether its flag was ever turned on at any level.