55 * SPDX-License-Identifier: Apache-2.0
66 */
77
8+ #include < chrono>
89#include < cstdint>
910#include < cstring>
10- #include < limits>
11- #include < memory>
12- #include < optional>
11+ #include < variant>
1312
1413#include < jansson.h>
1514
15+ #include < villas/config_helper.hpp>
1616#include < villas/exceptions.hpp>
1717#include < villas/hook.hpp>
1818#include < villas/sample.hpp>
1919
20+ #include " villas/timing.hpp"
21+
2022namespace villas {
2123namespace node {
2224
23- enum class Trigger {
24- TIMESTAMP,
25- SEQUENCE,
26- };
27-
28- enum class Unit {
29- MILLISECONDS,
30- SECONDS,
31- MINUTES,
32- HOURS,
33- };
34-
35- uint64_t unit_wrap (std::optional<Unit> unit) {
36- if (unit) {
37- switch (*unit) {
38- case Unit::HOURS:
39- return 24 ;
40- case Unit::MINUTES:
41- return 60 ;
42- case Unit::SECONDS:
43- return 60 ;
44- case Unit::MILLISECONDS:
45- return 1000 ;
46- }
47- }
48-
49- return std::numeric_limits<uint64_t >::max ();
50- }
51-
5225class FrameHook : public Hook {
53- using sample_ptr = std::unique_ptr<Sample, decltype (&sample_decref)>;
26+ using TimeInterval = std::chrono::milliseconds;
27+ using SequenceInterval = uint64_t ;
5428
5529private:
56- Trigger trigger;
57- uint64_t interval;
58- uint64_t offset;
59- std::optional<Unit> unit;
60- sample_ptr last_smp;
30+ std::variant<TimeInterval, SequenceInterval> interval;
31+ Sample::PtrUnique last_smp;
6132
6233 bool updateInterval (Sample const *next_smp) {
6334 bool changed = false ;
6435
6536 if (!last_smp.get () ||
6637 (next_smp->flags & (int )SampleFlags::NEW_SIMULATION)) {
6738 changed = true ;
68- } else if (trigger == Trigger::SEQUENCE ) {
39+ } else if (auto *i = std::get_if<SequenceInterval>(&interval) ) {
6940 if (!(next_smp->flags & (int )SampleFlags::HAS_SEQUENCE))
70- throw RuntimeError{" Missing sequence number." };
41+ throw RuntimeError (" Missing sequence number" );
42+
43+ auto last_interval = (last_smp->sequence + *i) / *i;
44+ auto next_interval = (next_smp->sequence + *i) / *i;
7145
72- auto last_interval = (last_smp->sequence + interval - offset) / interval;
73- auto next_interval = (next_smp->sequence + interval - offset) / interval;
7446 changed = last_interval != next_interval;
75- } else {
47+ } else if ( auto *i = std::get_if<TimeInterval>(&interval)) {
7648 if (!(next_smp->flags & (int )SampleFlags::HAS_TS_ORIGIN))
77- throw RuntimeError{" Missing origin timestamp." };
78-
79- switch (unit.value ()) {
80- case Unit::HOURS: {
81- auto last_hour = last_smp->ts .origin .tv_sec / 3'600 ;
82- auto next_hour = next_smp->ts .origin .tv_sec / 3'600 ;
83-
84- auto last_day = last_hour / 24 ;
85- auto next_day = next_hour / 24 ;
86- if (last_day != next_day) {
87- changed = true ;
88- break ;
89- }
90-
91- auto last_hour_of_day = last_hour - 24 * last_day;
92- auto next_hour_of_day = next_hour - 24 * next_day;
93- auto last_interval_of_day =
94- (last_hour_of_day + interval - offset) / interval;
95- auto next_interval_of_day =
96- (next_hour_of_day + interval - offset) / interval;
97- changed = last_interval_of_day != next_interval_of_day;
98- break ;
99- }
49+ throw RuntimeError (" Missing origin timestamp" );
10050
101- case Unit::MINUTES: {
102- auto last_minute = last_smp->ts .origin .tv_sec / 60 ;
103- auto next_minute = next_smp->ts .origin .tv_sec / 60 ;
51+ auto last_ts = time_to_timepoint<TimeInterval>(&last_smp->ts .origin );
52+ auto next_ts = time_to_timepoint<TimeInterval>(&next_smp->ts .origin );
10453
105- auto last_hour = last_minute / 60 ;
106- auto next_hour = next_minute / 60 ;
107- if (last_hour != next_hour) {
108- changed = true ;
109- break ;
110- }
54+ auto last_interval = (last_ts.time_since_epoch () + *i) / *i;
55+ auto next_interval = (next_ts.time_since_epoch () + *i) / *i;
11156
112- auto last_minute_of_hour = last_minute - 60 * last_hour;
113- auto next_minute_of_hour = next_minute - 60 * next_hour;
114- auto last_interval_of_hour =
115- (last_minute_of_hour + interval - offset) / interval;
116- auto next_interval_of_hour =
117- (next_minute_of_hour + interval - offset) / interval;
118- changed = last_interval_of_hour != next_interval_of_hour;
119- break ;
120- }
121-
122- case Unit::SECONDS: {
123- auto last_second = last_smp->ts .origin .tv_sec ;
124- auto next_second = next_smp->ts .origin .tv_sec ;
125-
126- auto last_minute = last_second / 60 ;
127- auto next_minute = next_second / 60 ;
128- if (last_minute != next_minute) {
129- changed = true ;
130- break ;
131- }
132-
133- auto last_second_of_minute = last_second - 60 * last_minute;
134- auto next_second_of_minute = next_second - 60 * next_minute;
135- auto last_interval_of_minute =
136- (last_second_of_minute + interval - offset) / interval;
137- auto next_interval_of_minute =
138- (next_second_of_minute + interval - offset) / interval;
139- changed = last_interval_of_minute != next_interval_of_minute;
140- break ;
141- }
142-
143- case Unit::MILLISECONDS: {
144- auto last_second = last_smp->ts .origin .tv_sec ;
145- auto next_second = next_smp->ts .origin .tv_sec ;
146- if (last_second != next_second) {
147- changed = true ;
148- break ;
149- }
150-
151- auto last_millisecond_of_second =
152- last_smp->ts .origin .tv_nsec / 1'000'000 ;
153- auto next_millisecond_of_second =
154- next_smp->ts .origin .tv_nsec / 1'000'000 ;
155- auto last_interval_of_second =
156- (last_millisecond_of_second + interval - offset) / interval;
157- auto next_interval_of_second =
158- (next_millisecond_of_second + interval - offset) / interval;
159- changed = last_interval_of_second != next_interval_of_second;
160- break ;
161- }
162- }
57+ changed = last_interval != next_interval;
16358 }
16459
165- if (changed)
166- logger->debug (" new frame" );
167-
16860 return changed;
16961 }
17062
17163public:
17264 FrameHook (Path *p, Node *n, int fl, int prio, bool en = true )
173- : Hook(p, n, fl, prio, en), trigger(Trigger::SEQUENCE), interval(1 ),
174- offset ( 0 ), unit{std:: nullopt }, last_smp{nullptr , &sample_decref} {}
65+ : Hook(p, n, fl, prio, en), interval(TimeInterval( 0 ) ),
66+ last_smp{nullptr , &sample_decref} {}
17567
17668 virtual ~FrameHook () { (void )last_smp.release (); }
17769
17870 void parse (json_t *json) override {
17971 Hook::parse (json);
18072
18173 char *trigger_str = nullptr ;
182- char *unit_str = nullptr ;
183- int interval_int = -1 ;
184- int offset_int = -1 ;
74+ json_t *json_interval = nullptr ;
18575
18676 json_error_t err;
187- auto ret = json_unpack_ex (json, &err, 0 , " { s?: s, s?: s, s?: i, s?: i }" ,
188- " trigger" , &trigger_str, " unit" , &unit_str,
189- " interval" , &interval_int, " offset" , &offset_int);
77+ auto ret = json_unpack_ex (json, &err, 0 , " { s?: s, s: o }" , " trigger" ,
78+ &trigger_str, " interval" , &json_interval);
19079 if (ret)
19180 throw ConfigError (json, err, " node-config-hook-frame" );
19281
193- if (trigger_str) {
194- if (!strcmp (trigger_str, " sequence" ))
195- trigger = Trigger::SEQUENCE;
196- else if (!strcmp (trigger_str, " timestamp" ))
197- trigger = Trigger::TIMESTAMP;
198- else
199- throw ConfigError (json, " node-config-hook-frame-unit" );
200- }
201-
202- if (trigger == Trigger::TIMESTAMP) {
203- if (!strcmp (unit_str, " milliseconds" ))
204- unit = Unit::MILLISECONDS;
205- else if (!strcmp (unit_str, " seconds" ))
206- unit = Unit::SECONDS;
207- else if (!strcmp (unit_str, " minutes" ))
208- unit = Unit::MINUTES;
209- else if (!strcmp (unit_str, " hours" ))
210- unit = Unit::HOURS;
211- else
212- throw ConfigError (json, " node-config-hook-frame-unit" );
213- }
214-
215- if (interval_int != -1 ) {
216- if (interval_int <= 0 || (uint64_t )interval_int > unit_wrap (unit))
217- throw ConfigError (json, " node-config-hook-frame-interval" );
218-
219- interval = interval_int;
220- }
82+ if (trigger_str == nullptr || !strcmp (trigger_str, " timestamp" )) {
83+ interval = parse_duration<TimeInterval>(json_interval);
22184
222- if (offset_int != -1 ) {
223- if (offset_int < 0 || (uint64_t )offset_int >= unit_wrap (unit))
224- throw ConfigError (json, " node-config-hook-frame-offset" );
85+ if (std::get<TimeInterval>(interval) == TimeInterval::zero ())
86+ throw ConfigError (json, " node-config-hook-frame-interval" ,
87+ " Interval must be greater than zero" );
88+ } else if (!strcmp (trigger_str, " sequence" )) {
89+ if (!json_is_integer (json))
90+ throw ConfigError (json, " node-config-hook-frame-interval" ,
91+ " Interval must be an integer" );
22592
226- offset = offset_int ;
93+ interval = SequenceInterval ( json_integer_value (json_interval)) ;
22794 }
22895 }
22996
@@ -236,7 +103,7 @@ class FrameHook : public Hook {
236103 smp->flags &= ~(int )SampleFlags::NEW_FRAME;
237104
238105 sample_incref (smp);
239- last_smp = sample_ptr {smp, &sample_decref};
106+ last_smp = Sample::PtrUnique {smp, &sample_decref};
240107
241108 return Reason::OK;
242109 }
0 commit comments