3 The series model§
PROVISIONAL
3.1 What a series is§
A bus:Series declares one exported time series: the history of one point, in one unit, under one sampling discipline, in one sample file. It is the receiving system’s reading order — everything it needs to know before opening the file, and everything it needs afterward to say what the file was.
A point MAY be declared by several series: successive coverage intervals of a long history split for size, or raw records alongside the rollups computed from them. Two series for the same point with the same sampling and aggregation SHOULD NOT overlap in coverage, because overlapping duplicates of the same records are a merge problem this part declines to create.
NOTE A series names its point through pointRef, intrinsically, and BUS-1 5.3.1 lists the constructs permitted to do that. A series joins them for the same reason an event does: it exists in order to describe its point’s past, and would be meaningless without the reference. No bus:hasSeries relationship is created.
3.2 What a series declares§
| Field | Requirement |
|---|---|
pointRef | REQUIRED. The bus:Point whose history this is. MUST resolve within the package (test TS-2). |
format | REQUIRED. The sample format of the named file. This version defines bus-csv-1 (clause 4); further formats are a MINOR change. |
resourceRef | REQUIRED. Package-relative path of the sample file, exactly as listed in manifest.resources (5.2). resources/series/ is the RECOMMENDED home. |
coverage | REQUIRED. The closed interval the export covers, both ends stated, both UTC. 3.3. |
sampling | REQUIRED. A kind (3.5) and, where the kind is sampled, the recording interval. |
aggregation | Present where the rows are rollups rather than source records: the function and the period. Absence asserts the rows are what the source system recorded. 3.6. |
unitCode | REQUIRED where the point carries one. The unit of every value in the file. 3.4. |
sourceTimeZone | RECOMMENDED where the source system stored local time. The IANA zone converted from. 6.5. |
rowCount | REQUIRED. The number of data rows in the file, excluding the header. 3.3. |
provenance | RECOMMENDED, via the entity envelope. Which historian, read when, by what — the same questions BUS-1 14 asks of every assertion. |
A series is an entity, so it also carries an identifier, a name, external identifiers — the historian’s own series or trend-log id belongs in externalIds, and is frequently the only bridge back to an archive tier this package does not carry — classifications, and everything else in BUS-1 8.1. The schema requires pointRef, format, resourceRef, coverage, sampling, rowCount.
3.3 Coverage and row count are declared twice on purpose§
Both coverage and rowCount restate something the file itself shows, and the redundancy is the mechanism. A declared value that must match the content is how truncation, padding, and tampering become detectable rather than plausible — the same pattern as the actuation declaration (BUS-1 16.7) and the reference dimension on a drawing (BUS-1 13.3). Tests TS-3 and TS-4 do the comparing.
Coverage MAY be wider than the data: an export of 2020 through 2025 for a point that was offline through 2021 declares the interval it exported, and the empty year is visible as the absence of rows. Coverage MUST NOT be narrower than the data, because rows outside the declared interval are rows a reader was told not to expect.
3.4 Units§
unitCode states the unit of every value in the file, in the vocabulary of BUS-1 Annex C. It is stated on the series, once, rather than on each row — a row-level unit column would repeat one fact hundreds of millions of times, which is 5.3.1’s argument at data scale.
The rule when they disagree was decided deliberately. A historian that kept recording in degF after the point was re-configured to degC in 2019 is a real system, and its history is really in degF. Forcing agreement would force conversion, and BUS-1 7.4-1 forbids conversion precisely because it is a silent, compounding loss that destroys the ability to tell a measured value from a computed one. So exact agreement is reported when absent, never manufactured; only a cross-quantity mismatch — pressure samples on a temperature point — is a package error, because that is not a fact about a historian, it is a mis-wired export.
3.5 Sampling kinds§
sampling.kind states how rows relate to time: sampled, cov, totalized, event. The vocabulary follows the one piece of real prior art, Project Haystack’s hisMode — sampled, cov, and consumption, here renamed totalized — with event added for observations that are neither periodic nor change-driven.
| Kind | What a row is |
|---|---|
sampled | A reading taken at the regular interval the series declares. interval is REQUIRED, and a gap longer than it is visible as missing rows. |
cov | A reading taken at the instant the value changed. The value holds until the next row. No interval exists to declare. |
totalized | An accumulated quantity for the period ending at the row’s timestamp — meter consumption. The unit is the accumulated quantity’s, kWh not kW. |
event | An irregular observation: a manually logged reading, a spot measurement. The timestamps are the only statement about timing. |
3.6 Rollups§
An aggregation block states that the rows are computed rollups: a function — mean, min, max, sum, first, last, count, integral, other — and the period each row summarizes. integral is the honest name for most "average demand" rollups, which are time-weighted integrals rather than arithmetic means, and other requires the method be named in the series’ description.
Absence of the block is itself an assertion: these rows are what the source system recorded. An exporter holding only rollups exports them as rollups, honestly declared, and clause 6 says what its completeness declaration owes. It does not pretend uniformity that the historian never had.
3.7 Point.history, and what this part changes about it§
BUS-1 8.8’s point-level declaration is unchanged: Point.history still states that durable history exists, over what extent, and at what nominal interval. It is the summary a receiver reads while deciding what it is missing, and it remains meaningful in packages that carry no series at all.
One field of it is superseded. history.resourceRef pointed at a resource with no statement of format, coverage, unit, or count — a locator without a declaration. It is deprecated in this version in favor of bus:Series.resourceRef, and per BUS-1 15.5 it MUST still be accepted; where both are present, the series entity is the statement and the point field is a label. The declaration (available, from, to, interval) is not deprecated, and test TS-7 holds it and the carried series to the same story.