Skip to content

Live data in dashboards

A dashboard shows plant data by reading parameter values and changes equipment by writing them. It never sends Modbus addresses: it names parameters by ID, and the parameter’s source and value type decide which register is read and how its bytes become a value. Moving a value to another register changes the parameter, not every dashboard that shows it.

@overvis/hmi-workspace-ui provides the live-value hooks on top of the host’s API client, and the same hooks serve Setup’s live values. Dashboards, Setup, the assistant, and integrations all read and write through the same procedures, described in Live value procedures. For the steps, see Show live values in a dashboard and Write parameter values from a dashboard. The components for a dashboard made for one device model are in Device dashboard components, and the device API hooks call an MC252 or MC261L’s own API without parameters.

Every loaded dashboard is wrapped in one ParameterValuesProvider. It makes no request until a useParameterValues call registers demand.

APIResponsibility
useParameterValuesRegister demand, cadence, aggregate status, and Retry
useParameterValueSubscribe to one value state; it creates no network demand
useParameterValueActionsBatch writes and perform explicit refreshes
useParameterValueHistoryKeep recent numeric samples for this mount; it never queries stored readings
useParameterValueHistoriesThe same for a list of parameters, in list order, for a chart of several lines
useStoredParameterHistoryLoad stored readings over a window
useParameterMetadataNames, units, and other metadata of parameters
ParameterDiagnosticsShow grouped problems with the corrective action, Setup links, and Retry
ParameterValueDisplayShow one value with its loading, unavailable, and stale presentation
ParameterValuesProviderIsolate the scheduling of a complex part of the dashboard

useParameterValues takes parameterIds and intervalMs; an empty list registers no demand. The parameters of one call form a cohort. A parameter requested by several cohorts is read once, at the fastest cadence, and results arrive progressively as the device answers. A nested ParameterValuesProvider has its own schedule and demand.

For plant HMIs, an interval of 1 to 5 seconds suits most processes, depending on how fast the process moves and how loaded the endpoint is. Put slow-changing identity and settings values in a slower cohort than the fast readings. The provider deduplicates IDs, batches what is due, pauses hidden tabs, and backs off per parameter, so a dashboard declares what it needs instead of running a timer per tile.

useParameterValue(id) returns one of:

  • idle: no demand, or the demand is disabled.
  • loading: requested, with no value or failure yet.
  • value: snapshot, freshness (fresh or stale), refreshing, saving, and an optional current diagnosis.
  • error: a failure before any value arrived.

A timeout or a lost connection says nothing about the value itself, so a failed refresh keeps the last value, marks it stale, and adds the diagnosis. Only a Reading unavailable answer, where the device reports that it has no value, replaces the last value.

snapshot.receivedAt is when the device was read, in milliseconds. A reading shared with another request keeps its original time, so a repeated delivery never looks newly read. formatAppDateTime formats it as YYYY-MM-DD HH:mm:ss in the browser time zone.

useParameterValues also returns an aggregate state: idle, loading, partial, ready, degraded, error, or paused. It counts states; it is not a device diagnosis. All requested parameters failing does not prove that the device is offline.

  • parameterValueNumber(state) and parameterValueBoolean(state) return the typed value, or null for an absent, pending, failed, or differently typed state. They never parse the localized display string.
  • parameterValueFlag(state) reads a device flag that may arrive as a boolean or a numeric register: zero is false, nonzero is true.
  • parameterValueComparable(state) is the value a numeric condition compares: a number, or a boolean as 0 or 1.
  • parameterValueTime(state) reads a date-time value, such as a maintenance date, as epoch milliseconds.

isCurrentParameterValue(state) holds when a value is fresh and nothing has failed since it was read. Every reader above takes { current: true } to return null for a kept value, as in parameterValueNumber(state, { current: true }). isParameterValuePending(state) holds until the first value or failure arrives. parameterStatesStaleSince(states) is when the oldest kept value was read, for a “stale since” note, or null.

useParameterMetadata(parameterIds, locale) returns a lookup by ID: loading, missing (deleted), error, or ready with parameter (name and unit in locale, branchId, valueType, writable, and recorded). Names and units edited in Setup then apply without editing the dashboard. parameterMetadata(parameter, locale) builds the same object from a parameter.get result. isParameterRecorded(parameter) is true when the parameter’s readings are stored: it has a background reading period, or an MC252 program records it on the device.

View-only members can read live values. Writing requires the Operator role or higher. A permission problem is an access failure, not a device error, and an API request deadline is not a device timeout. Diagnoses come from the structured error code and reason; the runtime error codes list them all.

DiagnosisExamples
EndpointMissing or disabled endpoint, connection refused or lost
Device timeoutThe device did not answer in time; gateway exception 10 or 11
ConfigurationUnknown or disabled parameter, invalid source or value type
ProtocolOther Modbus exceptions, malformed or mismatched response
CapacityThe service is busy or not ready
PermissionThe workspace role cannot read or write live values
UnavailableThe device reports no value or timestamp for the reading
InternalUnexpected request failure that has no safer diagnosis

demand.diagnostics has one entry per problem on a device, not per register: when one device fails several registers the same way, such as a unit behind a gateway that stopped answering, the entry lists every affected parameter in parameterIds, and requests holds each failed request. Different endpoints, units, or Modbus exception codes stay separate entries. combineParameterValuesDemands groups the diagnostics of several cohorts the same way.

ParameterDiagnostics shows each entry as one NoticeCard. Its title names the device when it is known, such as Unit 7 on Campus North is not answering, and below it says what to check: for a device timeout, the device’s power, wiring, Modbus unit ID, and the serial settings behind a gateway. Technical details list the error, the number of affected parameters, and the addresses, such as hold 100-102, 140. With workspaceId, it shows endpoint names. Endpoint and device-timeout notices are warnings; configuration, protocol, and internal faults are errors; an unavailable value, a busy service, and missing read permission are information. With onRetry, Retry is the main button, unless a Setup change fixes the problem: editors then get the endpoint, parameter, or dashboard settings link first.

The provider reads values in repeated requests; there is no server push of values.

  • Hidden tabs pause reading and refresh immediately when shown again.
  • enabled: false pauses a demand; values already read stay available.
  • Endpoint failures, timeouts, and temporary capacity failures back off automatically per parameter. Configuration failures are retried slowly, so a Setup correction is eventually picked up. Permission failures stop until access changes.
  • retry() clears the backoff of that demand and reads at once.

One failing parameter does not slow its healthy neighbors. Requests for the same parameter share one device read, and a reading taken a moment earlier can answer another request, so ten screens watching one temperature cost one device read. Writes and relevant configuration changes discard such shared readings. Each device read is stored once in the parameter’s history, also when it was requested by a live view; a shared reading adds nothing.

writeValues from useParameterValueActions() sends several writes in one request:

const { writeValues } = useParameterValueActions();
const output = await writeValues({
items: [
{ paramId: setpointId, value: "21.5" },
{ paramId: hysteresisId, value: "1.0" },
],
});

isParameterValueWritable(parameter) checks parameter metadata: it rejects disabled parameters, calculated sources, constants without write access, and read-only Modbus sources. Workspace permissions and server checks apply in addition.

A write acknowledges, it does not observe. The result says which bytes were sent, not what the device now holds: a device can clamp a value, a command register can clear itself after it runs, and some devices report a command’s effect in another register. So a later read can differ without the write having failed, and an acknowledgement does not prove a physical result; show the equipment’s own feedback parameter where it has one. While a write is in flight, the written parameters are marked saving. After it settles, the provider reads them again, and until that read completes, the last observed value stays on screen as stale. A write timeout is ambiguous, because the device may have applied it, so that read matters most.

Writes are not a transaction. output.results[paramId] holds each write’s own result, and a failed write does not undo the others, because rolling back would undo real device writes. Apply the successful values, show the failed ones with their errors, and check per-item results rather than only whether the call threw. Writes come from the open dashboard: after it closes, a request already sent completes, and nothing more is sent.

  • writeValues(input, { writeOnly: [passwordId] }) writes a parameter that is never read back, such as a device’s unlock password.
  • runSequence(sequence) runs an ordered sequence of writes after any other sequence on the page has finished, so the steps of two sequences never interleave.

useStoredParameterHistory({ workspaceId, parameterIds, window, cadence, refetchMs, enabled }) loads stored readings over the window milliseconds that end at each load, as closed buckets of cadence (1m, 15m, 1h, …): averages for num parameters, latest values for others. Only numbers are returned: text, date-time, and geo parameters have an empty series. It loads again every refetchMs, and not at all while enabled is false.

series(parameterId) returns status (loading, ready, empty, or error), points ({ at, value } at each bucket’s start), and recorded (whether the parameter’s readings are stored at all). The open bucket at the end of the window is not included. withLiveTail(points, live, cadenceMs) appends the live values from useParameterValueHistory received after the last bucket, and liveTailPoints(window, fastestRefreshMs) is the maxPoints that holds a whole open bucket. ParameterTrendChart and Sparkline draw these points directly.

These points describe a trend, not the last reading: a num point is its bucket’s average, which may never have been read. For actual stored readings with the time each was read, query readings.segments through trpc.

storedHistoryWindows has the usual 1h, 24h, and 7d windows with their cadence and reload interval. useStoredWindowExtremes({ workspaceId, parameterId, sourceValueType, window, live }) returns the minimum and maximum over a window that ends now. useDistinctParameterNames(parameters, locale) names parameters shown together, adding the branch where two names would read the same.

trpc from the package root is the host’s typed React Query client. Do not create a second one.

import { trpc } from "@overvis/hmi-workspace-ui";

Typical direct calls are parameter.listInventory for a picker, report procedures for embedded reports, or dashboard-specific queries. Entity procedures, such as parameter reads and writes, take entity IDs; workspace procedures, such as parameter.listInventory, take workspaceId from the dashboard props. The dashboard’s configuration is already in its props, and the workspace is available through useWorkspace(). Queries are considered fresh for 30 seconds and are not retried automatically. When the workspace’s setup changes, the host refreshes the queries made through trpc, including the dashboard’s own configuration.

endpoint.watchStatus reports endpoint connection state, separately from parameter values: a connected endpoint can still have parameters that fail.

trpc.endpoint.watchStatus.useSubscription(
{ workspaceId: props.workspaceId },
{ onData: (event) => applyEndpointStatus(event) },
);

The first event is a snapshot of every endpoint, followed by one change event (endpointId, status) per change. status is null until the endpoint has reported. The event types (EndpointStatusEvent, EndpointStatus) come from @overvis/hmi-shared/endpoint.

ParameterMetric shows a value state with a caller-supplied label, optional icon, engineering unit, and locale; compact is the dense variant for summaries. ParameterReading shows just the value and unit for tables, settings rows, and map popups: as the parameter formats it, or at exactly fractionDigits places, with an optional unavailable text such as ”—” and a skeletonClassName sized to the place it fills. Both keep the loading, unavailable, and stale presentation of ParameterValueDisplay.

Setup’s live values and dashboards read through the same provider and procedures. When a value works in Setup but not on a dashboard, the difference is in the dashboard: other parameter IDs, another principal (a workspace API key of another workspace gets param_not_found), or a dashboard that hides per-parameter errors. See Troubleshooting.