Consider DAQ node is unreachable 60 seconds after the last status.

Commit
aacbbf9c2281c9fbb9c26e5c2187ead1477abdc2
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
roles/dashboard/lib/dashboard/Controller/Reading.pm
index ed8e87ae..4aed0566 100644..100644
@@ -3,6 +3,10 @@
3 3 package dashboard::Controller::Reading;
4 4 use Mojo::Base 'Mojolicious::Controller', -signatures;
5 5
6 Added: use POSIX qw(strftime);
7 Added:
8 Added: my $STATUS_REACHABLE_SECONDS = 60;
9 Added:
6 10 sub list ($self) {
7 11 my $probe = $self->param('probe') // '';
8 12
@@ -68,7 +72,8 @@
68 72 )->hash;
69 73 };
70 74
71 Removed: $row = undef if $@;
75 Added: $row = undef if $@;
76 Added: mark_unreachable_if_stale($row) if $row;
72 77
73 78 $self->render(
74 79 json => {
@@ -76,6 +81,18 @@
76 81 status => $row,
77 82 },
78 83 );
84 Added: }
85 Added:
86 Added: sub mark_unreachable_if_stale ($row) {
87 Added: my $received_at = $row->{received_at} // $row->{timestamp};
88 Added: return if !defined $received_at || $received_at ge reachable_since();
89 Added:
90 Added: $row->{status} = 'unreachable';
91 Added: $row->{message} = "No status received in the last $STATUS_REACHABLE_SECONDS seconds";
92 Added: }
93 Added:
94 Added: sub reachable_since {
95 Added: return strftime( '%Y-%m-%dT%H:%M:%SZ', gmtime( time - $STATUS_REACHABLE_SECONDS ) );
79 96 }
80 97
81 98 1;
roles/dashboard/public/js/dashboard.js
index a2ec901c..68395c44 100644..100644
@@ -1,5 +1,5 @@
1 1 const charts = new Map();
2 Removed: const OFFLINE_AFTER_MS = 15 * 60 * 1000;
2 Added: const OFFLINE_AFTER_MS = 60 * 1000;
3 3
4 4 function probeRows() {
5 5 return Array.from(document.querySelectorAll("[data-latest-probe]"));
roles/dashboard/t/basic.t
index 056ac253..76e35182 100644..100644
@@ -8,10 +8,13 @@
8 8 use FindBin;
9 9 use lib "${FindBin::Bin}/../lib/";
10 10 use dashboard;
11 Added: use POSIX qw(strftime);
11 12
12 13 $ENV{FAPG_DAQ_DB} = ':memory:';
13 14
14 Removed: my $t = Test::Mojo->new('dashboard');
15 Added: my $t = Test::Mojo->new('dashboard');
16 Added: my $fresh_status_at = utc_timestamp(time);
17 Added: my $stale_status_at = utc_timestamp( time - 61 );
15 18
16 19 $t->app->sqlite->db->query(
17 20 q{
@@ -61,10 +64,10 @@
61 64 received_at, topic, schema, timestamp, probe, node, status, message, payload
62 65 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
63 66 },
64 Removed: '2026-07-05T12:00:02Z',
67 Added: $fresh_status_at,
65 68 'fapg/daq/ph/fapg-daq-zero-ph-01/status',
66 69 'fapg.daq.status.v1',
67 Removed: '2026-07-05T12:00:02Z',
70 Added: $fresh_status_at,
68 71 'ph',
69 72 'fapg-daq-zero-ph-01',
70 73 'ok',
@@ -72,6 +75,23 @@
72 75 '{}',
73 76 );
74 77
78 Added: $t->app->sqlite->db->query(
79 Added: q{
80 Added: INSERT INTO node_status (
81 Added: received_at, topic, schema, timestamp, probe, node, status, message, payload
82 Added: ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
83 Added: },
84 Added: $stale_status_at,
85 Added: 'fapg/daq/do/fapg-daq-zero-do-01/status',
86 Added: 'fapg.daq.status.v1',
87 Added: $stale_status_at,
88 Added: 'do',
89 Added: 'fapg-daq-zero-do-01',
90 Added: 'ok',
91 Added: 'Probe reading valid',
92 Added: '{}',
93 Added: );
94 Added:
75 95 $t->get_ok('/')
76 96 ->status_is(200)
77 97 ->content_like(qr/FAPG DAQ Dashboard/)
@@ -90,7 +110,18 @@
90 110 ->json_is( '/probe' => 'ph' )
91 111 ->json_is( '/status/status' => 'ok' );
92 112
113 Added: $t->get_ok('/api/status/do')
114 Added: ->status_is(200)
115 Added: ->json_is( '/probe' => 'do' )
116 Added: ->json_is( '/status/status' => 'unreachable' )
117 Added: ->json_is( '/status/message' => 'No status received in the last 60 seconds' );
118 Added:
93 119 $t->get_ok('/api/readings/nope')->status_is(404);
94 120 $t->get_ok('/api/status/nope')->status_is(404);
95 121
96 122 done_testing;
123 Added:
124 Added: sub utc_timestamp {
125 Added: my ($epoch) = @_;
126 Added: return strftime( '%Y-%m-%dT%H:%M:%SZ', gmtime $epoch );
127 Added: }