had claude look at the bin player to determine how to take into account the component history for the top nodes
This commit is contained in:
parent
a4ed52db1e
commit
939a53d072
4 changed files with 135 additions and 6 deletions
|
|
@ -90,6 +90,58 @@ function upsertReading(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A trimmed record of a single component-settings history entry: just the timestamp and the
|
||||
* top-node value (`grain_filled_to`) that was in effect from that point forward.
|
||||
*/
|
||||
type TopNodeHistoryEntry = {
|
||||
timestamp: Moment;
|
||||
topNode: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts raw `ComponentHistory` entries for one cable into a sorted (ascending) array of
|
||||
* `TopNodeHistoryEntry` records, keeping only entries where `grain_filled_to` is defined and
|
||||
* non-zero so that we never overwrite a known top node with a "not set" value.
|
||||
*/
|
||||
function buildTopNodeHistory(
|
||||
history: pond.ComponentHistory[]
|
||||
): TopNodeHistoryEntry[] {
|
||||
return history
|
||||
.filter(h => h.component && h.component.grainFilledTo > 0 && h.timestamp)
|
||||
.map(h => ({
|
||||
timestamp: moment(h.timestamp),
|
||||
topNode: h.component ? h.component.grainFilledTo : 0,
|
||||
}))
|
||||
.sort((a, b) => a.timestamp.valueOf() - b.timestamp.valueOf());
|
||||
}
|
||||
|
||||
/**
|
||||
* Binary-searches a sorted `TopNodeHistoryEntry[]` for the most recent entry whose timestamp
|
||||
* is at or before `checkpointTime`. Returns `undefined` when no such entry exists (i.e. the
|
||||
* checkpoint is before the first recorded top-node change).
|
||||
*/
|
||||
function topNodeAtTime(
|
||||
history: TopNodeHistoryEntry[],
|
||||
checkpointTime: Moment
|
||||
): number | undefined {
|
||||
if (history.length === 0) return undefined;
|
||||
const t = checkpointTime.valueOf();
|
||||
let lo = 0;
|
||||
let hi = history.length - 1;
|
||||
let result: number | undefined = undefined;
|
||||
while (lo <= hi) {
|
||||
const mid = (lo + hi) >>> 1;
|
||||
if (history[mid].timestamp.valueOf() <= t) {
|
||||
result = history[mid].topNode;
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid - 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces `count` wall-clock times evenly spaced from `start` through `end` (inclusive).
|
||||
* These are the playback frames — not derived from measurement data.
|
||||
|
|
@ -360,6 +412,8 @@ function buildStatusCheckpoints(
|
|||
cableMeasurementResponses: pond.SampleUnitMeasurementsResponse[],
|
||||
fanMeasurementResponses: pond.SampleUnitMeasurementsResponse[],
|
||||
heaterMeasurementResponses: pond.SampleUnitMeasurementsResponse[],
|
||||
/** Sorted top-node history per cable key. Pass an empty Map when not needed. */
|
||||
topNodeHistories: Map<string, TopNodeHistoryEntry[]>,
|
||||
start: Moment,
|
||||
end: Moment,
|
||||
count: number = CHECKPOINT_COUNT
|
||||
|
|
@ -416,7 +470,19 @@ function buildStatusCheckpoints(
|
|||
const bucketReadings = cableBucket.get(key) ?? {};
|
||||
const merged = mergeCarriedReadings(carriedByCable.get(key)!, bucketReadings);
|
||||
carriedByCable.set(key, merged);
|
||||
return buildGrainCableFromReadings(template, merged, time);
|
||||
const cable = buildGrainCableFromReadings(template, merged, time);
|
||||
|
||||
// Apply the most recent top-node setting that was in effect at this checkpoint time.
|
||||
// Falls back to the template's current top_node when no history entry precedes this time.
|
||||
const history = topNodeHistories.get(key);
|
||||
if (history) {
|
||||
const tn = topNodeAtTime(history, time);
|
||||
if (tn !== undefined) {
|
||||
cable.topNode = tn;
|
||||
}
|
||||
}
|
||||
|
||||
return cable;
|
||||
});
|
||||
|
||||
// Build fans
|
||||
|
|
@ -529,6 +595,18 @@ export default function BinPlayer(props: Props) {
|
|||
return componentAPI.sampleUnitMeasurements(deviceID, cable.key, startDate, endDate, 100, undefined, undefined, undefined, undefined, true);
|
||||
});
|
||||
|
||||
// Cable top-node history requests — one per cable, covering the full playback range.
|
||||
const cableHistoryRequests = bin.status.grainCables.map(cable => {
|
||||
const deviceID = componentDevices.get(cable.key) ?? 0;
|
||||
console.log(deviceID)
|
||||
return componentAPI.listHistoryBetween(
|
||||
deviceID,
|
||||
cable.key,
|
||||
startDate.toISOString(),
|
||||
endDate.toISOString()
|
||||
);
|
||||
});
|
||||
|
||||
// Fan requests
|
||||
const fanSampleRequests = bin.status.fans.map(fan => {
|
||||
const deviceID = componentDevices.get(fan.key) ?? 0;
|
||||
|
|
@ -541,12 +619,23 @@ export default function BinPlayer(props: Props) {
|
|||
return componentAPI.sampleUnitMeasurements(deviceID, heater.key, startDate, endDate, 100, undefined, undefined, undefined, undefined, true);
|
||||
});
|
||||
|
||||
const [cableResponses, fanResponses, heaterResponses] = await Promise.all([
|
||||
const [cableResponses, fanResponses, heaterResponses, cableHistoryResponses] = await Promise.all([
|
||||
Promise.all(cableSampleRequests),
|
||||
Promise.all(fanSampleRequests),
|
||||
Promise.all(heaterSampleRequests),
|
||||
Promise.all(cableHistoryRequests),
|
||||
]);
|
||||
|
||||
console.log(cableHistoryResponses)
|
||||
|
||||
// Build a per-cable sorted top-node history map for use during checkpoint construction.
|
||||
const topNodeHistories = new Map<string, TopNodeHistoryEntry[]>();
|
||||
bin.status.grainCables.forEach((cable, i) => {
|
||||
const historyResp = cableHistoryResponses[i];
|
||||
const entries = buildTopNodeHistory(historyResp?.data?.history ?? []);
|
||||
topNodeHistories.set(cable.key, entries);
|
||||
});
|
||||
|
||||
const checkpoints = buildStatusCheckpoints(
|
||||
bin.status.grainCables,
|
||||
bin.status.fans,
|
||||
|
|
@ -554,6 +643,7 @@ export default function BinPlayer(props: Props) {
|
|||
cableResponses.map(r => r.data),
|
||||
fanResponses.map(r => r.data),
|
||||
heaterResponses.map(r => r.data),
|
||||
topNodeHistories,
|
||||
startDate,
|
||||
endDate,
|
||||
checkpointCountFromRange(startDate, endDate)
|
||||
|
|
@ -715,4 +805,4 @@ export default function BinPlayer(props: Props) {
|
|||
</Box>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue