Implemented history writer
This commit is contained in:
@@ -130,6 +130,13 @@ bool StreamHub::Initialise(StructuredDataI &cfg) {
|
||||
StreamString sf;
|
||||
if (cfg.Read("SourcesFile", sf)) { sourcesFile_ = sf; }
|
||||
|
||||
/* Parse +History block (optional).
|
||||
* StandardParser stores the '+' prefix in the node name, so we try both. */
|
||||
if (cfg.MoveRelative("+History") || cfg.MoveRelative("History")) {
|
||||
history_.Initialise(cfg);
|
||||
cfg.MoveToAncestor(1u);
|
||||
}
|
||||
|
||||
/* Allocate scratch buffers */
|
||||
pushBuf_ = new uint8[kPushBufSize];
|
||||
lttbT_ = new float64[maxPushPoints_];
|
||||
@@ -248,6 +255,16 @@ bool StreamHub::Run() {
|
||||
PushStats();
|
||||
}
|
||||
|
||||
/* History: flush headers periodically */
|
||||
if (history_.IsEnabled()) {
|
||||
uint32 histFlushDiv = history_.Decimation() > 0u
|
||||
? pushRateHz_ * 5u : pushRateHz_ * 5u; /* every 5s */
|
||||
if (histFlushDiv == 0u) { histFlushDiv = 1u; }
|
||||
if ((tickCount_ % histFlushDiv) == 0u) {
|
||||
history_.FlushHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
tickCount_++;
|
||||
|
||||
/* Sleep for remainder of period */
|
||||
@@ -305,6 +322,12 @@ void StreamHub::PushData() {
|
||||
}
|
||||
BroadcastSources();
|
||||
BroadcastConfig(i);
|
||||
|
||||
/* Open history files for this source */
|
||||
if (history_.IsEnabled()) {
|
||||
StreamString sid = sessions_[i].GetId();
|
||||
history_.OnSourceConfigured(i, sid.Buffer(), sessions_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Always serialize (advancing the per-signal cursors), even with no
|
||||
@@ -313,6 +336,11 @@ void StreamHub::PushData() {
|
||||
if (frameLen > 0u) {
|
||||
wsServer_.BroadcastBinary(pushBuf_, frameLen);
|
||||
}
|
||||
|
||||
/* Write to disk history (separate read cursors, own decimation) */
|
||||
if (history_.IsEnabled()) {
|
||||
history_.WriteTick(i, sessions_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,6 +635,19 @@ void StreamHub::OnWSClientConnected() {
|
||||
|
||||
/* Let the new client render the current trigger badge/buttons. */
|
||||
BroadcastTriggerState();
|
||||
|
||||
/* Inform the new client about history availability. */
|
||||
if (history_.IsEnabled()) {
|
||||
/* Broadcast to all (simple; no unicast-on-connect path for broadcast). */
|
||||
uint32 cap2 = 8192u;
|
||||
char *hbuf = new char[cap2];
|
||||
uint32 hoff = 0u;
|
||||
JsonAppendf(hbuf, hoff, cap2, "{\"type\":\"historyInfo\",");
|
||||
history_.AppendInfoJSON(hbuf, hoff, cap2);
|
||||
JsonAppendf(hbuf, hoff, cap2, "}");
|
||||
wsServer_.BroadcastText(hbuf, hoff);
|
||||
delete[] hbuf;
|
||||
}
|
||||
}
|
||||
|
||||
void StreamHub::OnWSClientDisconnected() {
|
||||
@@ -631,6 +672,8 @@ void StreamHub::OnWSCommand(const char *json, uint32 /*len*/, uint32 slotIdx) {
|
||||
else if (strcmp(type, "trigStop") == 0) { HandleTrigStop(json); }
|
||||
else if (strcmp(type, "setTrigger") == 0) { HandleSetTrigger(json); }
|
||||
else if (strcmp(type, "zoom") == 0) { HandleZoom(json, slotIdx); }
|
||||
else if (strcmp(type, "historyZoom") == 0) { HandleHistoryZoom(json, slotIdx); }
|
||||
else if (strcmp(type, "historyInfo") == 0) { HandleHistoryInfo(slotIdx); }
|
||||
else if (strcmp(type, "setMaxPoints") == 0) { HandleSetMaxPoints(json); }
|
||||
else if (strcmp(type, "ping") == 0) { HandlePing(slotIdx); }
|
||||
}
|
||||
@@ -1225,6 +1268,113 @@ void StreamHub::HandleZoom(const char *json, uint32 slotIdx) {
|
||||
delete[] tDec; delete[] vDec;
|
||||
}
|
||||
|
||||
void StreamHub::HandleHistoryZoom(const char *json, uint32 slotIdx) {
|
||||
if (!history_.IsEnabled()) {
|
||||
const char *msg = "{\"type\":\"historyZoom\",\"error\":\"history not enabled\"}";
|
||||
wsServer_.SendText(slotIdx, msg, static_cast<uint32>(strlen(msg)));
|
||||
return;
|
||||
}
|
||||
|
||||
float64 t0 = 0.0, t1 = 0.0;
|
||||
float64 nF = 2400.0;
|
||||
float64 reqIdF = 0.0;
|
||||
|
||||
JsonGetFloat(json, "t0", t0);
|
||||
JsonGetFloat(json, "t1", t1);
|
||||
JsonGetFloat(json, "reqId", reqIdF);
|
||||
const bool haveN = JsonGetFloat(json, "n", nF);
|
||||
|
||||
uint32 maxOut = 2400u;
|
||||
if (haveN) {
|
||||
if (nF <= 0.0) { maxOut = 0u; }
|
||||
else if (nF < 10.0) { maxOut = 2400u; }
|
||||
else { maxOut = static_cast<uint32>(nF); }
|
||||
}
|
||||
|
||||
/* Parse comma-separated full keys "src:sig" */
|
||||
static const uint32 kKeysBuf = 8192u;
|
||||
char *keys = new char[kKeysBuf];
|
||||
keys[0] = '\0';
|
||||
JsonGetString(json, "signals", keys, kKeysBuf);
|
||||
|
||||
/* Allocate scratch for the largest possible read */
|
||||
const uint32 scratchCap = 2000000u; /* 2M pts should be enough */
|
||||
float64 *tRaw = new float64[scratchCap];
|
||||
float64 *vRaw = new float64[scratchCap];
|
||||
float64 *tDec = (maxOut > 0u) ? new float64[maxOut] : static_cast<float64 *>(0);
|
||||
float64 *vDec = (maxOut > 0u) ? new float64[maxOut] : static_cast<float64 *>(0);
|
||||
|
||||
uint32 cap = 65536u;
|
||||
char *buf = new char[cap];
|
||||
uint32 off = 0u;
|
||||
|
||||
JsonAppendf(buf, off, cap, "{\"type\":\"historyZoom\",\"reqId\":%u,\"signals\":{",
|
||||
static_cast<uint32>(reqIdF));
|
||||
|
||||
bool first = true;
|
||||
char *tok = keys;
|
||||
while ((tok != static_cast<char *>(0)) && (*tok != '\0') && (t1 > t0)) {
|
||||
char *next = strchr(tok, ',');
|
||||
if (next != static_cast<char *>(0)) { *next = '\0'; next++; }
|
||||
while (*tok == ' ') { tok++; }
|
||||
|
||||
char *colon = strchr(tok, ':');
|
||||
if (colon == static_cast<char *>(0)) { tok = next; continue; }
|
||||
*colon = '\0';
|
||||
const char *srcId = tok;
|
||||
const char *sigName = colon + 1;
|
||||
|
||||
uint32 nRaw = history_.ReadRange(srcId, sigName, t0, t1,
|
||||
tRaw, vRaw, scratchCap);
|
||||
if (nRaw > 0u) {
|
||||
const float64 *tOut = tRaw;
|
||||
const float64 *vOut = vRaw;
|
||||
uint32 nOut = nRaw;
|
||||
if ((maxOut > 0u) && (nRaw > maxOut)) {
|
||||
nOut = LTTBDecimate(tRaw, vRaw, nRaw, tDec, vDec, maxOut);
|
||||
tOut = tDec;
|
||||
vOut = vDec;
|
||||
}
|
||||
|
||||
JsonAppendf(buf, off, cap, "%s\"%s:%s\":{\"t\":[",
|
||||
(first ? "" : ","), srcId, sigName);
|
||||
first = false;
|
||||
for (uint32 p = 0u; p < nOut; p++) {
|
||||
JsonAppendf(buf, off, cap, "%s%.17g",
|
||||
(p > 0u ? "," : ""), tOut[p]);
|
||||
}
|
||||
JsonAppendf(buf, off, cap, "],\"v\":[");
|
||||
for (uint32 p = 0u; p < nOut; p++) {
|
||||
JsonAppendf(buf, off, cap, "%s%.9g",
|
||||
(p > 0u ? "," : ""), vOut[p]);
|
||||
}
|
||||
JsonAppendf(buf, off, cap, "]}");
|
||||
}
|
||||
tok = next;
|
||||
}
|
||||
|
||||
JsonAppendf(buf, off, cap, "}}");
|
||||
wsServer_.SendText(slotIdx, buf, off);
|
||||
|
||||
delete[] buf;
|
||||
delete[] keys;
|
||||
delete[] tRaw; delete[] vRaw;
|
||||
delete[] tDec; delete[] vDec;
|
||||
}
|
||||
|
||||
void StreamHub::HandleHistoryInfo(uint32 slotIdx) {
|
||||
uint32 cap = 8192u;
|
||||
char *buf = new char[cap];
|
||||
uint32 off = 0u;
|
||||
|
||||
JsonAppendf(buf, off, cap, "{\"type\":\"historyInfo\",");
|
||||
history_.AppendInfoJSON(buf, off, cap);
|
||||
JsonAppendf(buf, off, cap, "}");
|
||||
|
||||
wsServer_.SendText(slotIdx, buf, off);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
void StreamHub::HandleSetMaxPoints(const char *json) {
|
||||
float64 mpF = static_cast<float64>(maxPoints_);
|
||||
JsonGetFloat(json, "maxPoints", mpF);
|
||||
|
||||
Reference in New Issue
Block a user