[Perl] DAQ system for the FAPG.
1
#!/usr/bin/env perl
2
3
use v5.32.1;
4
use strict;
5
use warnings;
6
7
use FindBin qw($Bin);
8
use lib "$Bin/../../../lib";
9
use lib "$Bin/../lib";
10
11
use FAPG::DAQ::EZO::USB qw(
12
discover_ezo_usb_device
13
normalize_probe_type
14
open_ezo_usb
15
read_probe_info
16
read_once
17
);
18
19
use FAPG::DAQ::MQTT qw(publish_mqtt publish_status_mqtt);
20
use Sys::Hostname qw(hostname);
21
22
=head1 NAME
23
24
fapg-daq - read sensor data and send over MQTT
25
26
=cut
27
28
my $read_interval = positive_number_from_env( READ_INTERVAL => 5 );
29
my $retry_delay = positive_number_from_env( EZO_RECONNECT_INTERVAL => 5 );
30
my $expected_probe = expected_probe();
31
32
my ( $serial, $info, $device, $probe );
33
34
while (1) {
35
if ( !defined $serial || !defined $info ) {
36
( $serial, $info ) = eval {
37
$device = serial_device();
38
connect_probe($device);
39
};
40
41
if ($@) {
42
my $error = $@;
43
warn "DAQ probe connection error: $error";
44
publish_probe_status(
45
probe => $probe // $expected_probe,
46
status => 'probe_error',
47
error => $error,
48
device => $device,
49
message => 'Unable to connect to or interrogate EZO probe',
50
);
51
close_serial($serial);
52
( $serial, $info, $device, $probe ) = ();
53
sleep $retry_delay;
54
next;
55
}
56
57
$probe = $info->{probe};
58
}
59
60
my $reading = eval { read_once( $serial, probe => $info->{probe} ) };
61
62
if ($@) {
63
my $error = $@;
64
warn "DAQ probe read error, reconnecting to $device: $error";
65
publish_probe_status(
66
probe => $probe // $expected_probe,
67
status => 'probe_error',
68
error => $error,
69
device => $device,
70
message => 'EZO probe did not return a valid reading',
71
);
72
close_serial($serial);
73
( $serial, $info, $device, $probe ) = ();
74
sleep $retry_delay;
75
next;
76
}
77
78
say "$reading->{probe}: $reading->{value} $reading->{unit}";
79
80
eval { publish_mqtt($reading) } or warn "MQTT publish error: $@";
81
publish_probe_status(
82
probe => $reading->{probe},
83
status => 'ok',
84
device => $device,
85
message => 'Probe reading valid',
86
);
87
88
sleep $read_interval;
89
}
90
91
sub connect_probe {
92
my ($device) = @_;
93
94
my $serial = open_ezo_usb( device => $device );
95
my $info = eval { read_probe_info($serial) };
96
97
if ($@) {
98
my $error = $@;
99
close_serial($serial);
100
die $error;
101
}
102
103
print <<"EOF";
104
Device $device
105
Probe $info->{probe}
106
Firmware $info->{firmware}
107
Unit $info->{unit}
108
EOF
109
110
return ( $serial, $info );
111
}
112
113
sub serial_device {
114
return $ENV{EZO_SERIAL_DEVICE}
115
if defined $ENV{EZO_SERIAL_DEVICE} && $ENV{EZO_SERIAL_DEVICE} ne q{};
116
117
if ( defined $ENV{SERIAL_DEVICE} && $ENV{SERIAL_DEVICE} ne q{} ) {
118
return $ENV{SERIAL_DEVICE} if -e $ENV{SERIAL_DEVICE};
119
warn
120
"Configured SERIAL_DEVICE=$ENV{SERIAL_DEVICE} does not exist; attempting USB serial discovery\n";
121
}
122
123
return discover_ezo_usb_device();
124
}
125
126
sub expected_probe {
127
for my $name (qw(FAPG_DAQ_PROBE EZO_PROBE PROBE_TYPE)) {
128
next if !defined $ENV{$name} || $ENV{$name} eq q{};
129
return normalize_probe_type( $ENV{$name} );
130
}
131
132
my $hostname = hostname();
133
return $1 if $hostname =~ /(?:\A|-)(ph|do|ec|orp)(?:-|\z)/;
134
135
return undef;
136
}
137
138
sub publish_probe_status {
139
my (%status) = @_;
140
141
if ( !defined $status{probe} || $status{probe} eq q{} ) {
142
warn "DAQ status not published because probe type is unknown\n";
143
return;
144
}
145
146
eval { publish_status_mqtt( \%status ); 1 }
147
or warn "MQTT status publish error: $@";
148
}
149
150
sub close_serial {
151
my ($serial) = @_;
152
153
return if !defined $serial || !$serial->can('close');
154
155
eval { $serial->close; 1 }
156
or warn "DAQ serial close error: $@";
157
}
158
159
sub positive_number_from_env {
160
my ( $name, $default ) = @_;
161
my $value = $ENV{$name};
162
163
return $default
164
if !defined $value
165
|| $value !~ /\A(?:\d+(?:\.\d*)?|\.\d+)\z/
166
|| $value <= 0;
167
168
return 0 + $value;
169
}
170