Implemented history writer
This commit is contained in:
@@ -2,19 +2,18 @@
|
||||
* streamhub_demo.cfg — MARTe2 app for the StreamHub demo.
|
||||
*
|
||||
* Three real-time threads, each feeding a UDPStreamer instance.
|
||||
* No DebugService — monitoring is done via StreamHub + WebSocket clients.
|
||||
*
|
||||
* Thread1 1 kHz — scalar signals → UDPStreamer port 44500
|
||||
* Counter, Time, Sine1 (1 Hz, 5 V), Sine2 (0.3 Hz, 3 V)
|
||||
* Accumulate mode, multicast group 239.0.0.1 data port 44503
|
||||
* Thread1 1 kHz — 2 scalar sines @ 1 ksps (Accumulate mode)
|
||||
* Sine1 (1 Hz, 5 V, phase 0)
|
||||
* Sine2 (0.3 Hz, 3 V, phase 60 deg)
|
||||
*
|
||||
* Thread2 5 kHz — packed arrays → UDPStreamer port 44501
|
||||
* Ch1 float32[1000] @ 1 kHz (TimeMode = FirstSample)
|
||||
* Ch2 float32[1000] @ 10 kHz (TimeMode = LastSample)
|
||||
* Thread2 1 kHz — 2 array sines @ 1 Msps (1000 elem × 1 kHz)
|
||||
* Ch1 (1 kHz, 1 V, phase 0)
|
||||
* Ch2 (5 kHz, 0.5 V, phase 90 deg)
|
||||
*
|
||||
* Thread3 5 kHz — FullArray + per-sample timestamps → UDPStreamer port 44502
|
||||
* Ch3 float32[1000] @ 3 kHz (TimeMode = FullArray)
|
||||
* Ch4 float32[1000] @ 500 Hz (TimeMode = FullArray)
|
||||
* Thread3 5 kHz — 2 array sines @ 5 Msps (1000 elem × 5 kHz)
|
||||
* Ch3 (10 kHz, 2 V, phase 0)
|
||||
* Ch4 (50 kHz, 1.5 V, phase 45 deg)
|
||||
*/
|
||||
|
||||
$App = {
|
||||
@@ -23,7 +22,7 @@ $App = {
|
||||
+Functions = {
|
||||
Class = ReferenceContainer
|
||||
|
||||
// ── Thread1: 1 kHz scalar signals ────────────────────────────────────
|
||||
// ── Thread1: 1 kHz scalar sines (1 ksps) ────────────────────────────
|
||||
|
||||
+TimerGAM = {
|
||||
Class = IOGAM
|
||||
@@ -84,15 +83,15 @@ $App = {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Thread2: 5 kHz packed arrays (FirstSample / LastSample) ──────────
|
||||
// ── Thread2: 1 kHz, 1000-element arrays → 1 Msps ───────────────────
|
||||
|
||||
+FastTimerGAM = {
|
||||
+MedTimerGAM = {
|
||||
Class = IOGAM
|
||||
InputSignals = {
|
||||
Time = {
|
||||
DataSource = FastTimer
|
||||
DataSource = MedTimer
|
||||
Type = uint32
|
||||
Frequency = 5000
|
||||
Frequency = 1000
|
||||
}
|
||||
}
|
||||
OutputSignals = {
|
||||
@@ -106,7 +105,7 @@ $App = {
|
||||
Amplitude = 1.0
|
||||
Phase = 0.0
|
||||
Offset = 0.0
|
||||
SamplingRate = 5000000.0
|
||||
SamplingRate = 1000000.0
|
||||
OutputSignals = {
|
||||
Ch1 = {
|
||||
DataSource = DDB2
|
||||
@@ -119,11 +118,11 @@ $App = {
|
||||
|
||||
+SineGAM4 = {
|
||||
Class = SineArrayGAM
|
||||
Frequency = 10000.0
|
||||
Frequency = 5000.0
|
||||
Amplitude = 0.5
|
||||
Phase = 1.5708
|
||||
Offset = 0.0
|
||||
SamplingRate = 5000000.0
|
||||
SamplingRate = 1000000.0
|
||||
OutputSignals = {
|
||||
Ch2 = {
|
||||
DataSource = DDB2
|
||||
@@ -134,27 +133,44 @@ $App = {
|
||||
}
|
||||
}
|
||||
|
||||
+StreamerGAM2 = {
|
||||
Class = IOGAM
|
||||
+TimeArrayGAM1 = {
|
||||
Class = TimeArrayGAM
|
||||
SamplingRate = 1000000.0
|
||||
Anchor = FirstSample
|
||||
InputSignals = {
|
||||
Time = { DataSource = DDB2 Type = uint32 }
|
||||
Ch1 = { DataSource = DDB2 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch2 = { DataSource = DDB2 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Time = { DataSource = DDB2 Type = uint32 }
|
||||
}
|
||||
OutputSignals = {
|
||||
Time = { DataSource = Streamer2 Type = uint32 }
|
||||
Ch1 = { DataSource = Streamer2 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch2 = { DataSource = Streamer2 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
TimeArray1 = {
|
||||
DataSource = DDB2
|
||||
Type = uint64
|
||||
NumberOfDimensions = 1
|
||||
NumberOfElements = 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Thread3: 5 kHz FullArray with per-sample timestamps ──────────────
|
||||
+StreamerGAM2 = {
|
||||
Class = IOGAM
|
||||
InputSignals = {
|
||||
TimeArray1 = { DataSource = DDB2 Type = uint64 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch1 = { DataSource = DDB2 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch2 = { DataSource = DDB2 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
}
|
||||
OutputSignals = {
|
||||
TimeArray1 = { DataSource = Streamer2 Type = uint64 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch1 = { DataSource = Streamer2 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch2 = { DataSource = Streamer2 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
}
|
||||
}
|
||||
|
||||
+FullArrTimerGAM = {
|
||||
// ── Thread3: 5 kHz, 1000-element arrays → 5 Msps ───────────────────
|
||||
|
||||
+FastTimerGAM = {
|
||||
Class = IOGAM
|
||||
InputSignals = {
|
||||
Time = {
|
||||
DataSource = FullArrTimer
|
||||
DataSource = FastTimer
|
||||
Type = uint32
|
||||
Frequency = 5000
|
||||
}
|
||||
@@ -166,7 +182,7 @@ $App = {
|
||||
|
||||
+SineGAM5 = {
|
||||
Class = SineArrayGAM
|
||||
Frequency = 3000.0
|
||||
Frequency = 10000.0
|
||||
Amplitude = 2.0
|
||||
Phase = 0.0
|
||||
Offset = 0.0
|
||||
@@ -183,8 +199,8 @@ $App = {
|
||||
|
||||
+SineGAM6 = {
|
||||
Class = SineArrayGAM
|
||||
Frequency = 500.0
|
||||
Amplitude = 3.0
|
||||
Frequency = 50000.0
|
||||
Amplitude = 1.5
|
||||
Phase = 0.7854
|
||||
Offset = 0.0
|
||||
SamplingRate = 5000000.0
|
||||
@@ -198,7 +214,7 @@ $App = {
|
||||
}
|
||||
}
|
||||
|
||||
+TimeArrayGAM1 = {
|
||||
+TimeArrayGAM2 = {
|
||||
Class = TimeArrayGAM
|
||||
SamplingRate = 5000000.0
|
||||
Anchor = FirstSample
|
||||
@@ -206,7 +222,7 @@ $App = {
|
||||
Time = { DataSource = DDB3 Type = uint32 }
|
||||
}
|
||||
OutputSignals = {
|
||||
TimeArray = {
|
||||
TimeArray2 = {
|
||||
DataSource = DDB3
|
||||
Type = uint64
|
||||
NumberOfDimensions = 1
|
||||
@@ -218,14 +234,14 @@ $App = {
|
||||
+StreamerGAM3 = {
|
||||
Class = IOGAM
|
||||
InputSignals = {
|
||||
TimeArray = { DataSource = DDB3 Type = uint64 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch3 = { DataSource = DDB3 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch4 = { DataSource = DDB3 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
TimeArray2 = { DataSource = DDB3 Type = uint64 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch3 = { DataSource = DDB3 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch4 = { DataSource = DDB3 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
}
|
||||
OutputSignals = {
|
||||
TimeArray = { DataSource = Streamer3 Type = uint64 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch3 = { DataSource = Streamer3 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch4 = { DataSource = Streamer3 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
TimeArray2 = { DataSource = Streamer3 Type = uint64 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch3 = { DataSource = Streamer3 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
Ch4 = { DataSource = Streamer3 Type = float32 NumberOfDimensions = 1 NumberOfElements = 1000 }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,7 +263,7 @@ $App = {
|
||||
}
|
||||
}
|
||||
|
||||
+FastTimer = {
|
||||
+MedTimer = {
|
||||
Class = LinuxTimer
|
||||
SleepNature = "Default"
|
||||
Signals = {
|
||||
@@ -256,7 +272,7 @@ $App = {
|
||||
}
|
||||
}
|
||||
|
||||
+FullArrTimer = {
|
||||
+FastTimer = {
|
||||
Class = LinuxTimer
|
||||
SleepNature = "Default"
|
||||
Signals = {
|
||||
@@ -286,18 +302,21 @@ $App = {
|
||||
Port = 44501
|
||||
MaxPayloadSize = 1400
|
||||
PublishingMode = "Decimate"
|
||||
Ratio = 50
|
||||
Ratio = 10
|
||||
Signals = {
|
||||
Time = { Type = uint32 Unit = "us" }
|
||||
Ch1 = {
|
||||
Type = float32 Unit = "V"
|
||||
TimeArray1 = {
|
||||
Type = uint64 Unit = "ns"
|
||||
NumberOfDimensions = 1 NumberOfElements = 1000
|
||||
TimeMode = FirstSample TimeSignal = Time SamplingRate = 5000000.0
|
||||
}
|
||||
Ch2 = {
|
||||
Ch1 = {
|
||||
Type = float32 Unit = "V"
|
||||
NumberOfDimensions = 1 NumberOfElements = 1000
|
||||
TimeMode = LastSample TimeSignal = Time SamplingRate = 5000000.0
|
||||
TimeMode = FullArray TimeSignal = TimeArray1
|
||||
}
|
||||
Ch2 = {
|
||||
Type = float32 Unit = "V"
|
||||
NumberOfDimensions = 1 NumberOfElements = 1000
|
||||
TimeMode = FullArray TimeSignal = TimeArray1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -309,19 +328,19 @@ $App = {
|
||||
PublishingMode = "Decimate"
|
||||
Ratio = 50
|
||||
Signals = {
|
||||
TimeArray = {
|
||||
TimeArray2 = {
|
||||
Type = uint64 Unit = "ns"
|
||||
NumberOfDimensions = 1 NumberOfElements = 1000
|
||||
}
|
||||
Ch3 = {
|
||||
Type = float32 Unit = "V"
|
||||
NumberOfDimensions = 1 NumberOfElements = 1000
|
||||
TimeMode = FullArray TimeSignal = TimeArray
|
||||
TimeMode = FullArray TimeSignal = TimeArray2
|
||||
}
|
||||
Ch4 = {
|
||||
Type = float32 Unit = "V"
|
||||
NumberOfDimensions = 1 NumberOfElements = 1000
|
||||
TimeMode = FullArray TimeSignal = TimeArray
|
||||
TimeMode = FullArray TimeSignal = TimeArray2
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,13 +364,13 @@ $App = {
|
||||
+Thread2 = {
|
||||
Class = RealTimeThread
|
||||
CPUs = 0x2
|
||||
Functions = {FastTimerGAM SineGAM3 SineGAM4 StreamerGAM2}
|
||||
Functions = {MedTimerGAM SineGAM3 SineGAM4 TimeArrayGAM1 StreamerGAM2}
|
||||
}
|
||||
|
||||
+Thread3 = {
|
||||
Class = RealTimeThread
|
||||
CPUs = 0x4
|
||||
Functions = {FullArrTimerGAM SineGAM5 SineGAM6 TimeArrayGAM1 StreamerGAM3}
|
||||
Functions = {FastTimerGAM SineGAM5 SineGAM6 TimeArrayGAM2 StreamerGAM3}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,18 @@ type zoomPoints struct {
|
||||
V []float64 `json:"v"`
|
||||
}
|
||||
|
||||
type historyInfoMsg struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
DurationHours float64 `json:"durationHours"`
|
||||
Decimation uint32 `json:"decimation"`
|
||||
Signals map[string]struct {
|
||||
T0 float64 `json:"t0"`
|
||||
T1 float64 `json:"t1"`
|
||||
Count uint32 `json:"count"`
|
||||
Capacity uint32 `json:"capacity"`
|
||||
} `json:"signals"`
|
||||
}
|
||||
|
||||
type event struct {
|
||||
Type string `json:"type"`
|
||||
Sources json.RawMessage `json:"sources"`
|
||||
@@ -187,9 +199,11 @@ type client struct {
|
||||
configs map[string][]signalInfo // sourceId → signals
|
||||
pushes []*pushFrame
|
||||
stats map[string]statInfo
|
||||
zooms map[uint32]map[string]zoomPoints
|
||||
trigSt []string // observed triggerState sequence
|
||||
captures []*captureFrame
|
||||
zooms map[uint32]map[string]zoomPoints
|
||||
histZooms map[uint32]map[string]zoomPoints
|
||||
historyInfo *historyInfoMsg
|
||||
trigSt []string // observed triggerState sequence
|
||||
captures []*captureFrame
|
||||
}
|
||||
|
||||
func (c *client) send(v interface{}) {
|
||||
@@ -263,6 +277,18 @@ func (c *client) pump() {
|
||||
if err := json.Unmarshal(data, &body); err == nil {
|
||||
c.zooms[ev.ReqID] = body.Signals
|
||||
}
|
||||
case "historyZoom":
|
||||
var body struct {
|
||||
Signals map[string]zoomPoints `json:"signals"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &body); err == nil {
|
||||
c.histZooms[ev.ReqID] = body.Signals
|
||||
}
|
||||
case "historyInfo":
|
||||
var hi historyInfoMsg
|
||||
if err := json.Unmarshal(data, &hi); err == nil {
|
||||
c.historyInfo = &hi
|
||||
}
|
||||
case "triggerState":
|
||||
c.trigSt = append(c.trigSt, ev.State)
|
||||
}
|
||||
@@ -302,10 +328,11 @@ func main() {
|
||||
defer ws.Close()
|
||||
|
||||
c := &client{
|
||||
ws: ws,
|
||||
deadline: time.Now().Add(*timeout),
|
||||
configs: map[string][]signalInfo{},
|
||||
zooms: map[uint32]map[string]zoomPoints{},
|
||||
ws: ws,
|
||||
deadline: time.Now().Add(*timeout),
|
||||
configs: map[string][]signalInfo{},
|
||||
zooms: map[uint32]map[string]zoomPoints{},
|
||||
histZooms: map[uint32]map[string]zoomPoints{},
|
||||
}
|
||||
|
||||
// ── 1. sources ────────────────────────────────────────────────────────
|
||||
@@ -409,6 +436,41 @@ func main() {
|
||||
return true
|
||||
})
|
||||
|
||||
// ── 5b. historyInfo — check the hub broadcast it on connect ─────────
|
||||
if c.historyInfo != nil && c.historyInfo.Enabled {
|
||||
log.Printf("OK historyInfo: enabled, %.1fh, decimation=%d, %d signals",
|
||||
c.historyInfo.DurationHours, c.historyInfo.Decimation,
|
||||
len(c.historyInfo.Signals))
|
||||
|
||||
// ── 5c. historyZoom round-trip ──────────────────────────────────
|
||||
const hReqID = 4243
|
||||
c.send(map[string]interface{}{
|
||||
"type": "historyZoom", "reqId": hReqID,
|
||||
"t0": t0, "t1": t1, "n": 200,
|
||||
"signals": zoomKey,
|
||||
})
|
||||
c.waitFor(fmt.Sprintf("historyZoom reply (reqId=%d, %s)", hReqID, zoomKey),
|
||||
10*time.Second, func() bool {
|
||||
sigs, ok := c.histZooms[hReqID]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
pts, ok := sigs[zoomKey]
|
||||
if !ok || len(pts.T) < 1 {
|
||||
// History data may still be sparse right after startup
|
||||
return true
|
||||
}
|
||||
for _, ht := range pts.T {
|
||||
if ht < t0-1e-6 || ht > t1+1e-6 {
|
||||
fail("historyZoom point outside range: t=%.9f not in [%.9f,%.9f]", ht, t0, t1)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
log.Println(" (history not enabled — skipping historyZoom test)")
|
||||
}
|
||||
|
||||
// ── 6. trigger: arm → capture ────────────────────────────────────────
|
||||
// Trigger on an *oscillating* signal at its mean observed value: a
|
||||
// monotonic ramp (counter, time array) crosses its past mean only once,
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user