File Coverage

blib/lib/Device/Quasar3108.pm
Criterion Covered Total %
statement 20 158 12.6
branch 2 82 2.4
condition 1 6 16.6
subroutine 6 21 28.5
pod 10 13 76.9
total 39 280 13.9


line stmt bran cond sub pod time code
1             package Device::Quasar3108;
2              
3             ################
4             #
5             # Device::Quasar3108 - Control Quasar Electronics Kit Number 3108
6             #
7             # Nicholas J Humfrey
8             # njh@ecs.soton.ac.uk
9             #
10             # See the bottom of this file for the POD documentation.
11             #
12              
13              
14 1     1   6435 use strict;
  1         3  
  1         44  
15 1     1   6 use vars qw/$VERSION $DEFAULT_TIMEOUT $DEFAULT_PERIOD/;
  1         1  
  1         77  
16              
17 1     1   1273 use Device::SerialPort;
  1         35303  
  1         56  
18 1     1   966 use Time::HiRes qw( time sleep alarm );
  1         7939  
  1         7  
19 1     1   1915 use Carp;
  1         2  
  1         1908  
20              
21             $VERSION="0.04";
22             $DEFAULT_TIMEOUT=5; # Default timeout is 5 seconds
23             $DEFAULT_PERIOD=0.25; # Default flash period
24              
25              
26              
27             sub new {
28 1     1 1 94 my $class = shift;
29 1         4 my ($portname, $timeout) = @_;
30            
31             # Defaults
32 1 50       4 $portname = '/dev/ttyS0' unless (defined $portname);
33 1 50       3 $timeout = $DEFAULT_TIMEOUT unless (defined $timeout);
34              
35              
36             # Create serial port object
37 1   33     10 my $port = new Device::SerialPort( $portname )
38             || croak "Can't open serial port ($portname): $!\n";
39              
40              
41             # Check serial port features
42 0 0         croak "ioctl isn't available for serial port: $portname"
43             unless ($port->can_ioctl());
44 0 0         croak "status isn't available for serial port: $portname"
45             unless ($port->can_status());
46 0 0         croak "write_done isn't available for serial port: $portname"
47             unless ($port->can_write_done());
48              
49              
50             # Configure the serial port
51 0 0         $port->baudrate(9600) || croak ("Failed to set baud rate");
52 0 0         $port->parity("none") || croak ("Failed to set parity");
53 0 0         $port->databits(8) || croak ("Failed to set data bits");
54 0 0         $port->stopbits(1) || croak ("Failed to set stop bits");
55 0 0         $port->handshake("none") || croak ("Failed to set hardware handshaking");
56             # $port->stty_echo(0) || croak ("Failed to turn off echo");
57 0 0         $port->write_settings() || croak ("Failed to write settings");
58              
59 0           $port->read_char_time(0); # don't wait for each character
60 0           $port->read_const_time(500); # 1/2 second per unfulfilled "read" call
61              
62              
63              
64              
65             # Bless me
66 0           my $self = {
67             port => $port,
68             timeout => $timeout,
69             debug => 0,
70             };
71 0           bless $self, $class;
72              
73              
74 0           return $self;
75             }
76              
77              
78             ## Version of the hardware firmware
79             sub firmware_version {
80 0     0 1   my $self=shift;
81            
82 0           $self->serial_write( '?' );
83            
84 0           return $self->serial_read();
85             }
86              
87              
88              
89              
90             ## Version of perl module
91             sub version {
92 0     0 1   return $VERSION;
93             }
94              
95              
96             ## Check module is still there
97             sub ping {
98 0     0 1   my $self=shift;
99            
100 0           $self->serial_write( '' );
101 0           my $ok = $self->serial_read( 1 );
102 0 0         if ($ok eq '#') { return 1; } # Success
  0            
103 0           else { return 0; } # Failed
104             }
105              
106              
107             ## Turn specified relay on
108             sub relay_on {
109 0     0 1   my $self=shift;
110 0           my ($num) = @_;
111 0 0         croak('Usage: relay_on( $num );') unless (defined $num);
112            
113 0           $self->serial_write( 'N'.int($num) );
114 0           my $ok = $self->serial_read( 1 );
115 0 0         if ($ok eq '#') { return 1; } # Success
  0            
116 0           else { return 0; } # Failed
117             }
118              
119              
120             ## Turn specified relay off
121             sub relay_off {
122 0     0 0   my $self=shift;
123 0           my ($num) = @_;
124 0 0         croak('Usage: relay_off( $num );') unless (defined $num);
125              
126 0           $self->serial_write( 'F'.int($num) );
127 0           my $ok = $self->serial_read( 1 );
128 0 0         if ($ok eq '#') { return 1; } # Success
  0            
129 0           else { return 0; } # Failed
130             }
131              
132             ## Toggle specified relay
133             sub relay_toggle {
134 0     0 1   my $self=shift;
135 0           my ($num) = @_;
136 0 0         croak('Usage: relay_toggle( $num );') unless (defined $num);
137              
138 0           $self->serial_write( 'T'.int($num) );
139 0           my $ok = $self->serial_read( 1 );
140 0 0         if ($ok eq '#') { return 1; } # Success
  0            
141 0           else { return 0; } # Failed
142             }
143              
144              
145             ## Toggle relay on and then off again
146             sub relay_flash {
147 0     0 1   my $self=shift;
148 0           my ($num,$period) = @_;
149 0 0         croak('Usage: relay_flash( $num, [$period] );') unless (defined $num);
150              
151             # Use default period if none given
152 0 0         $period = $DEFAULT_PERIOD unless (defined $period);
153            
154             # Turn relay on, sleep for period, turn relay off again
155 0 0         $self->relay_on( $num ) || return 0;
156 0           sleep( $period );
157 0 0         $self->relay_off( $num ) || return 0;
158              
159             # Success
160 0           return 1;
161             }
162              
163              
164             ## Set all relays to specified value
165             sub relay_set {
166 0     0 1   my $self=shift;
167 0           my ($value) = @_;
168 0 0         croak('Usage: relay_set( $value );') unless (defined $value);
169              
170 0           $self->serial_write( 'R'.sprintf("%2.2x",$value) );
171 0           my $ok = $self->serial_read( 1 );
172 0 0         if ($ok eq '#') { return 1; } # Success
  0            
173 0           else { return 0; } # Failed
174             }
175              
176              
177             ## Get state of specified relay
178             sub relay_status {
179 0     0 1   my $self=shift;
180 0           my ($num) = @_;
181 0 0         $num = 0 unless defined ($num);
182            
183 0           $self->serial_write( 'S'.$num );
184            
185            
186             # Return the result
187 0           my $status;
188 0 0         if ($num==0) { $status = $self->serial_read( 4 ); }
  0            
189 0           else { $status = $self->serial_read( 3 ); }
190            
191             # Look for a '#' prompt on the end
192 0           my $ok = $self->serial_read( 1 );
193 0 0         if ($ok ne '#') { warn "relay_status() failed :-("; }
  0            
194            
195 0           return $status;
196             }
197              
198              
199             ## Get state of specified input
200             sub input_status {
201 0     0 1   my $self=shift;
202 0           my ($num) = @_;
203 0 0         $num = 0 unless defined ($num);
204            
205 0           $self->serial_write( 'I'.$num );
206            
207            
208             # Return the result
209 0           my $status;
210 0 0         if ($num==0) { $status = $self->serial_read( 4 ); }
  0            
211 0           else { $status = $self->serial_read( 3 ); }
212            
213             # Look for a '#' prompt on the end
214 0           my $ok = $self->serial_read( 1 );
215 0 0         if ($ok ne '#') { warn "input_status() failed :-("; }
  0            
216            
217 0           return $status;
218             }
219              
220              
221              
222              
223             ### Internal Methods ###
224              
225             sub serial_write {
226 0     0 0   my $self=shift;
227 0           my ($string) = @_;
228 0           my $bytes = 0;
229              
230             # if it doesn't end with a '\r' then append one
231 0 0         $string .= "\r\n" if ($string !~ /\r\n?$/);
232              
233            
234 0           eval {
235 0     0     local $SIG{ALRM} = sub { die "Timed out."; };
  0            
236 0           alarm($self->{timeout});
237            
238             # Send it
239 0           $bytes = $self->{port}->write( $string );
240            
241             # Block until it is sent
242 0           while(($self->{port}->write_done(0))[0] == 0) {}
243            
244 0           alarm 0;
245             };
246            
247 0 0         if ($@) {
248 0 0         die unless $@ =~ /Timed out./; # propagate unexpected errors
249             # timed out
250 0           carp "Timed out while writing to serial port.\n";
251 0           return undef;
252             }
253            
254            
255             # Debugging: display what was read in
256 0 0         if ($self->{debug}) {
257 0           my $serial_debug = $string;
258 0           $serial_debug=~s/([^\040-\176])/sprintf("{0x%02X}",ord($1))/ge;
  0            
259 0           print "written ->$serial_debug<- ($bytes)\n";
260             }
261              
262             # Read in the echoed back characters
263 0           my $echo = $self->serial_read( length($string) );
264             ### FIXME: Could do error checking here ###
265             }
266              
267              
268             sub serial_read
269             {
270 0     0 0   my $self=shift;
271 0           my ($bytes_wanted) = @_;
272 0           my ($string, $bytes) = ('', 0);
273            
274             # Default bytes wanted is 255
275 0 0         $bytes_wanted=255 unless (defined $bytes_wanted);
276            
277              
278 0           eval {
279 0     0     local $SIG{ALRM} = sub { die "Timed out."; };
  0            
280 0           alarm($self->{timeout});
281            
282 0           while (1) {
283 0           my ($count,$got)=$self->{port}->read($bytes_wanted);
284 0           $string.=$got;
285 0           $bytes+=$count;
286            
287             ## All commands end in the command prompt '#'
288 0 0 0       last if ($string =~ /#$/ or $bytes>=$bytes_wanted);
289             }
290            
291 0           alarm 0;
292             };
293            
294 0 0         if ($@) {
295 0 0         die unless $@ =~ /Timed out./; # propagate unexpected errors
296             # timed out
297 0           carp "Timed out while reading from serial port.\n";
298 0           return undef;
299             }
300            
301             # Debugging: display what was read in
302 0 0         if ($self->{debug}) {
303 0           my $debug_str = $string;
304 0           $debug_str=~s/([^\040-\176])/sprintf("{0x%02X}",ord($1))/ge;
  0            
305 0           print "saw ->$debug_str<- ($bytes) (wanted=$bytes_wanted)\n";
306             }
307            
308            
309             # Clean up response
310 0 0         if ($bytes_wanted == 1) {
311 0           return $string;
312             } else {
313             # Remove whitespace from start and end
314 0           ($string) = ($string =~ /^\s*(.*?)\s*\#?$/);
315 0           return $string;
316             }
317             }
318              
319              
320             sub DESTROY {
321 0     0     my $self=shift;
322            
323 0 0         $self->{port}->close || carp "close serial port failed";
324             }
325              
326              
327              
328              
329             1;
330              
331             __END__