[Perl] DAQ system for the FAPG.
Consider DAQ node is unreachable 60 seconds after the last status.
Changed files
roles/dashboard/lib/dashboard/Controller/Reading.pm
@@ -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
@@ -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
@@ -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:
}