File Coverage

blib/lib/Device/Serial/MSLuRM.pm
Criterion Covered Total %
statement 47 58 81.0
branch 1 2 50.0
condition n/a
subroutine 13 15 86.6
pod 4 4 100.0
total 65 79 82.2


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2023-2025 -- leonerd@leonerd.org.uk
5              
6 3     3   624888 use v5.26;
  3         13  
7 3     3   18 use warnings;
  3         9  
  3         228  
8 3     3   1375 use Object::Pad 0.807 ':experimental(inherit_field)';
  3         11783  
  3         226  
9              
10             package Device::Serial::MSLuRM 0.10;
11             class Device::Serial::MSLuRM;
12              
13             inherit Device::Serial::SLuRM
14 2     2   2320 qw( $_protocol );
  2         7  
  2         233  
15              
16 3     3   623 use Carp;
  3         5  
  3         247  
17              
18 3     3   19 use Future::AsyncAwait;
  3         5  
  3         30  
19 3     3   2063 use Future::Mutex;
  3         2488  
  3         221  
20              
21             =encoding UTF-8
22              
23             =head1 NAME
24              
25             C - communicate Multi-drop SLµRM over a serial port
26              
27             =head1 SYNOPSIS
28              
29             =for highlighter language=perl
30              
31             use v5.36;
32             use Device::Serial::MSLuRM;
33              
34             my $slurm = Device::Serial::MSLuRM->new(
35             dev => "/dev/ttyUSB0",
36             baud => 19200,
37             );
38              
39             $slurm->run(
40             on_notify => sub ($node_id, $payload) {
41             printf "NOTIFY(%d): %v02X\n", $node_id, $payload;
42             }
43             )->await;
44              
45             =head1 DESCRIPTION
46              
47             This variant of L allows communication with a
48             collection of nodes using Multi-drop SLµRM over a serial port, such as over
49             an RS-485 bus.
50              
51             The endpoint running with this module takes the role of the bus controller.
52             Currently this module does not support being a non-controller node.
53              
54             =cut
55              
56 3     3   20 use constant is_multidrop => 1;
  3         5  
  3         4326  
57              
58             =head1 METHODS
59              
60             =head2 run
61              
62             $run_f = $slurm->run( %args );
63              
64             Starts the receiver run-loop, which can be used to wait for incoming NOTIFY
65             packets. This method returns a future, but the returned future will not
66             complete in normal circumstances. It will remain pending while the run-loop is
67             running. If an unrecoverable error happens (such as an IO error on the
68             underlying serial port device) then this future will fail.
69              
70             Takes the following named arguments:
71              
72             =over 4
73              
74             =item on_notify => CODE
75              
76             $on_notify->( $node_id, $payload )
77              
78             Optional. Invoked on receipt of a NOTIFY packet.
79              
80             =back
81              
82             Note that unlike in the single-peer case, a multi-drop controller cannot
83             C all the nodes before starting this, as it does not know the full set
84             of nodes that need resetting.
85              
86             =cut
87              
88 1     1   3 async method _autoreset () {}
  1         3  
  1         5  
  1         18  
89              
90 2     2   21249 async method _reset ( $node_id )
  2         11  
  2         13  
  2         6  
91 2         7 {
92             # Node ID zero is the special broadcast address; do not attempt to reset it
93 2 50       8 return if $node_id == 0;
94              
95 2         58 await $self->SUPER::_reset( $node_id );
96             }
97              
98             =head2 recv_packet
99              
100             ( $pktctrl, $addr, $payload ) = await $slurm->recv_packet;
101              
102             Waits for and returns the next packet to be received from the serial port.
103             Note that in a multi-drop scenario this packet may well be a reflection of one
104             sent by the controller, depending on how the serial port adapter works.
105              
106             =cut
107              
108 0     0 1 0 method recv_packet () { $_protocol->recv; }
  0         0  
  0         0  
  0         0  
109              
110             =head2 send_packet
111              
112             await $slurm->send_packet( $pktctrl, $addr, $payload );
113              
114             Sends a packet to the serial port.
115              
116             =cut
117              
118 0     0 1 0 method send_packet ( $pktctrl, $addr, $payload ) { $self->_send( $pktctrl, $addr, $payload ); }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
119              
120             =head2 send_notify
121              
122             await $slurm->send_notify( $node_id, $payload );
123              
124             Sends a NOTIFY packet.
125              
126             Will automatically L first if required.
127              
128             =cut
129              
130 2     2 1 13271 method send_notify ( $node_id, $payload ) { $self->_send_notify( $node_id, $payload ); }
  2         13  
  2         7  
  2         5  
  2         4  
  2         17  
131              
132             =head2 request
133              
134             $data_in = await $slurm->request( $node_id, $data_out );
135              
136             Sends a REQUEST packet to the node, and waits for a response to it.
137              
138             If the peer responds with an ERR packet, the returned future will fail with
139             an error message, the category of C, and the payload body of the ERR
140             packet in the message details:
141              
142             $f->fail( $message, slurm => $payload );
143              
144             If the peer does not respond at all and all retransmit attempts end in a
145             timeout, the returned future will fail the same way but with C as the
146             message details:
147              
148             $f->fail( $message, slurm => undef );
149              
150             Will automatically L first if required.
151              
152             =cut
153              
154             field $bus_lock = Future::Mutex->new;
155              
156 3     3 1 7618 method request ( $node_id, $payload )
  3         16  
  3         4  
  3         5  
  3         5  
157             {
158             # On a multidrop bus we don't want to allow more than one outstanding
159             # REQUEST, or line collisions will likely result between nodes' RESPONSE
160             # frames, and possibly other outbound REQUESTs.
161             return $bus_lock->enter(
162 3     3   494 sub { $self->_request( $node_id, $payload ); }
163 3         24 );
164             }
165              
166             =head1 AUTHOR
167              
168             Paul Evans
169              
170             =cut
171              
172             0x55AA;