File Coverage

blib/lib/Hardware/PortScanner.pm
Criterion Covered Total %
statement 129 422 30.5
branch 35 162 21.6
condition 3 20 15.0
subroutine 17 35 48.5
pod 7 9 77.7
total 191 648 29.4


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Hardware::PortScanner;
4              
5             our $debug = 0;
6              
7             our $IsWin = 1 if ( $^O eq 'MSWin32' );
8              
9             if ($IsWin) {
10             require Win32::SerialPort;
11             Win32::SerialPort::debug( "true", "True" ) if ($debug);
12             }
13             else {
14             require Device::SerialPort;
15             Device::SerialPort::debug( "true", "True" ) if ($debug);
16             }
17              
18 2     2   54167 use Data::Dumper; # Remove before production
  2         7480  
  2         167  
19              
20             # $Data::Dumper::Useqq = 1; # Remove before production
21              
22 2     2   17 use Carp;
  2         4  
  2         128  
23              
24             # $Carp::Verbose = 0;
25              
26 2     2   22 use strict;
  2         3  
  2         69  
27 2     2   11 use warnings;
  2         4  
  2         5554  
28              
29             our $VERSION;
30             $VERSION = '0.51';
31              
32             # PortScanner constructor
33              
34             sub new(;@) {
35 1     1 1 892 my $serial = bless( {} );
36 1 50       147 my $class = shift if ( $_[0] =~ /::/ );
37 1         2 my $parm;
38             my $value;
39 0         0 my $parm_found;
40              
41             # Default
42              
43 1         19 $serial->{MAX_PORT} = 20;
44              
45             # Parse Parameters
46              
47 1         116 while ( $parm = uc shift ) {
48 0         0 $parm_found = 0;
49 0         0 $value = shift;
50              
51 0 0       0 if ( $parm eq "MAX_PORT" ) {
52 0         0 $value =~ s/^COM//i;
53              
54 0 0       0 if ( $value !~ /^\d+$/ ) {
55 0         0 croak "ERROR: (new): MAX_PORT \$value\" must be a number\n";
56             }
57 0         0 $serial->{MAX_PORT} = $value;
58 0         0 $parm_found++;
59             }
60              
61 0 0       0 if ( !$parm_found ) {
62 0         0 croak "Parmeter passed to new() \"$parm\" is not valid\n";
63             }
64             }
65              
66             # These must be here - they are not for show
67              
68             # This one can be changed
69 1         4 $serial->{READ_CONST_TIME} = 100; # in msecs
70              
71             # This one can be changed but it will mess up our wait time calculations (since it is ignored)
72 1         4 $serial->{READ_CHAR_TIME} = 0; # in msecs
73              
74 1         5 return $serial;
75             }
76              
77             # Creates the port name from a com number portably
78              
79             sub _get_com_device_name($) {
80 20     20   28 my $serial = shift;
81 20         22 my $com_port = shift;
82              
83 20 50       57 if ($IsWin) {
84              
85             # Yea, this is some weird windows crap
86 0         0 return '\\\\.\\' . "COM$com_port";
87             }
88             else {
89 20         80 return sprintf( '/dev/ttyS%d', $com_port - 1 );
90             }
91              
92             }
93              
94             # get a connection to a port portably
95              
96             sub _get_com_connection($;$) {
97 20     20   25 my $serial = shift;
98 20         22 my $com_port = shift;
99 20   50     71 my $quite = shift || 1;
100 20         22 my $com_device_name;
101             my $PortObj;
102              
103 20         51 $com_device_name = $serial->_get_com_device_name($com_port);
104              
105             {
106 20         26 local $SIG{__WARN__} = sub {
107 0     0   0 my $err;
108              
109 0 0       0 if ( grep { $_ =~ /can.t getattr:/ } @_ ) {
  0         0  
110 0         0 $err = " Warning: (get_connection): " . join( " ", @_ );
111 0         0 $err =~ s/\015?\012$//;
112 0         0 $serial->_add_scan_log($err);
113             }
114             else {
115 0         0 print STDERR @_;
116             }
117 0         0 return 1;
118 20         132 };
119              
120 20 50       42 if ($IsWin) {
121 0         0 $PortObj = new Win32::SerialPort( "$com_device_name", $quite );
122              
123             }
124             else {
125 20         290 $PortObj = new Device::SerialPort( "$com_device_name", $quite );
126             }
127             }
128              
129 20         1391 return $PortObj;
130              
131             }
132              
133             # Internal method to collect messages for scan_log
134              
135             sub _add_scan_log($) {
136 3     3   6 my $serial = shift;
137 3         3 push( @{ $serial->{SCAN_LOG} }, @_ );
  3         9  
138              
139 3 50       12 print "DEBUG: $_[0] \n" if ($debug);
140             }
141              
142             sub scan_log($) {
143 0     0 1 0 my $serial = shift;
144 0         0 my @scan_log = ();
145              
146 0 0       0 @{ $serial->{SCAN_LOG} } = () if ( !defined( $serial->{SCAN_LOG} ) );
  0         0  
147              
148 0         0 foreach ( @{ $serial->{SCAN_LOG} } ) {
  0         0  
149 0         0 s/\015/\\r/g;
150 0         0 s/\012/\\n/g;
151 0         0 s/\011/\\t/g;
152 0         0 push( @scan_log, $_ );
153             }
154              
155 0         0 return @scan_log;
156             }
157              
158             sub scan_report() {
159 0     0 1 0 my $serial = shift;
160              
161 0         0 foreach ( $serial->scan_log ) {
162 0         0 print "$_\n";
163             }
164             }
165              
166             sub num_found_devices($) {
167 0     0 0 0 my $serial = shift;
168              
169 0 0       0 return defined( $serial->{FOUND_DEVICE} ) ? scalar( @{ $serial->{FOUND_DEVICE} } ) : 0;
  0         0  
170             }
171              
172             sub found_devices($) {
173 1     1 1 591 my $serial = shift;
174              
175 1 50       76 return defined( $serial->{FOUND_DEVICE} ) ? @{ $serial->{FOUND_DEVICE} } : ();
  0         0  
176             }
177              
178             sub connection($) {
179 0     0 1 0 my $serial = shift;
180              
181 0         0 return $serial->{CONNECTION};
182             }
183              
184             sub available_com_ports(;@) {
185 1     1 0 7 my $serial = shift;
186 1         3 my $com_port;
187             my $com_device_name;
188 0         0 my @ports;
189 0         0 my $PortObj;
190              
191 1         7 for ( $com_port = 1 ; $com_port <= $serial->{MAX_PORT} ; $com_port++ ) {
192              
193 20         62 $PortObj = $serial->_get_com_connection($com_port);
194              
195 20 50       45 if ($PortObj) {
196 0         0 push( @ports, $com_port );
197 0         0 $PortObj->close;
198 0         0 undef $PortObj;
199             }
200              
201 20         58 undef $PortObj; # Just extra safe measure
202             }
203              
204 1         5 return @ports;
205             }
206              
207             sub scan_ports(;@) {
208 1     1 1 1255 my $serial = shift;
209 1         2 my $com_port;
210             my $com_device_name;
211 0         0 my $PortObj;
212 0         0 my $feedback;
213 0         0 my $count_in;
214 0         0 my $send;
215 0         0 my $baud;
216 0         0 my $parm;
217 0         0 my $value;
218 0         0 my $key;
219 0         0 my ( $databits, $parity, $stopbits, $handshake );
220 0         0 my $setting;
221 0         0 my $device;
222 1         4 my $config = $serial->{SEARCH_PARM} = {};
223 1         2 my $parm_found;
224             my $read_iterations;
225 0         0 my $iterations;
226 1         2 my $chars = 0;
227 1         2 my ( $bytes_read, $data_read );
228 0         0 my $waited;
229              
230 1         139 $serial->_add_scan_log("Scan Ports Request");
231 1         3 $serial->_add_scan_log("==================");
232              
233             # Parse parameters
234              
235 1         5 while ( $parm = uc shift ) {
236 5         6 $value = shift;
237 5         7 $parm_found = 0;
238              
239 5         6 $key = "BAUD";
240 5 50       19 if ( $parm eq $key ) {
241 0 0       0 if ( ref $value eq "ARRAY" ) {
    0          
242 0         0 @{ $config->{$key} } = @{$value};
  0         0  
  0         0  
243             }
244             elsif ( ref $value eq "" ) {
245 0         0 @{ $config->{$key} } = ($value);
  0         0  
246             }
247             else {
248 0         0 croak "ERROR: (scan_ports): $key value must be either a SCALAR or ARRAY REF\n";
249             }
250              
251 0         0 foreach ( @{ $config->{$key} } ) {
  0         0  
252 0 0       0 if ( !/^\d+$/ ) {
253 0         0 croak "ERROR: (scan_ports): BAUD rate \$_\" not valid\n";
254             }
255             }
256 0         0 $parm_found++;
257             }
258              
259 5         7 $key = "COM";
260 5 100       10 if ( $parm eq $key ) {
261              
262 1 50       5 if ( ref $value eq "ARRAY" ) {
    0          
263 1         2 @{ $config->{$key} } = map { s/^COM(\d+)/$1/; $_; } @{$value};
  1         4  
  0         0  
  0         0  
  1         2  
264             }
265             elsif ( ref $value eq "" ) {
266 0         0 @{ $config->{$key} } = map { s/^COM(\d+)/$1/; $_; } ($value);
  0         0  
  0         0  
  0         0  
267             }
268             else {
269 0         0 croak "ERROR: (scan_ports): $key value must be either a SCALAR or ARRAY REF\n";
270             }
271              
272 1         3 foreach ( @{ $config->{$key} } ) {
  1         4  
273 0 0 0     0 if ( !( /^\d+$/ && $_ > 0 && $_ <= 50 ) ) {
      0        
274 0         0 croak "ERROR: (scan_ports): COM port \"$_\" not valid\n";
275             }
276             }
277 1         2 $parm_found++;
278             }
279              
280 5         8 $key = "SETTING";
281 5 100       11 if ( $parm eq $key ) {
282 1 50       4 foreach ( ref $value eq "ARRAY" ? @{$value} : ($value) ) {
  1         4  
283              
284 1         3 $value = uc $value;
285 1 50       7 if (/^([5678])([NEO])([12])([NRX])?$/) {
286 2     2   26 no warnings;
  2         4  
  2         4775  
287              
288 1         5 $databits = $1;
289 1 50       15 $parity = $2 eq "E" ? "even" : ( $2 eq "O" ? "odd" : "none" );
    50          
290 1         3 $stopbits = $3;
291 1 50       7 $handshake = $4 eq "R" ? "rts" : ( $4 eq "X" ? "xoff" : "none" );
    50          
292              
293 1         21 push(
294 1         1 @{ $config->{SETTING} },
295             {
296             DATABITS => $databits,
297             PARITY => $parity,
298             STOPBITS => $stopbits,
299             HANDSHAKE => $handshake,
300             SETTING => $_
301             }
302             );
303             }
304             else {
305 0         0 croak "ERROR: (scan_ports): SETTING value of \"$_\" is not valid\n";
306             }
307             }
308 1         3 $parm_found++;
309             }
310              
311 5         13 $key = "MAX_WAIT";
312 5 100       11 if ( $parm eq $key ) {
313 1 50 33     31 if ( $value !~ /^\d+$/ && $value !~ /^\d+[.]\d+$/ ) {
314 0         0 croak "ERROR: (scan_ports): $key value must be an integer or float in seconds\n";
315             }
316              
317 1 50       4 if ( ref $value eq "" ) {
318 1         3 $config->{$key} = $value;
319             }
320             else {
321 0         0 croak "ERROR: (scan_ports): $key value must be a SCALAR\n";
322             }
323 1         2 $parm_found++;
324             }
325              
326 5         6 $key = "TEST_STRING";
327 5 100       11 if ( $parm eq $key ) {
328 1 50       4 if ( ref $value eq "" ) {
329 1         3 $config->{$key} = $value;
330             }
331             else {
332 0         0 croak "ERROR: (scan_ports): $key value must be a SCALAR\n";
333             }
334 1         2 $parm_found++;
335             }
336              
337 5         7 $key = "VALID_REPLY_RE";
338 5 100       9 if ( $parm eq $key ) {
339 1 50       4 if ( ref $value eq "" ) {
340 1         31 $config->{$key} = qr/$value/;
341             }
342             else {
343 0         0 croak "ERROR: (scan_ports): $key value must be a SCALAR\n";
344             }
345 1         1 $parm_found++;
346             }
347              
348 5 50       117 if ( !$parm_found ) {
349 0         0 croak "ERROR: (scan_ports): Parameter \"$parm\" is not valid for scan port\n";
350             }
351              
352             }
353              
354 1 50 33     14 if ( !exists( $config->{TEST_STRING} ) || !exists( $config->{VALID_REPLY_RE} ) ) {
355 0         0 croak "ERROR: (scan_ports): TEST_STRING and VALID_REPLY must be provided to scan_ports\n";
356             }
357              
358             # Handle Default when certain parms were not provided
359              
360 1 50       4 if ( !exists( $config->{BAUD} ) ) {
361 1         3 @{ $config->{BAUD} } = (qw/1200 2400 4800 9600 19200 38400 57600 115200/);
  1         5  
362             }
363              
364 1 50       4 if ( !exists( $config->{COM} ) ) {
365 0         0 @{ $config->{COM} } = ( 1 .. $serial->{MAX_PORT} );
  0         0  
366             }
367              
368 1 50       4 if ( !exists( $config->{SETTING} ) ) {
369              
370             # Default for setting when not provided
371              
372 0         0 push(
373 0         0 @{ $config->{SETTING} },
374             {
375             DATABITS => 8,
376             PARITY => "none",
377             STOPBITS => 1,
378             HANDSHAKE => "none",
379             SETTING => "8N1"
380             }
381             );
382             }
383              
384             # Figure the number of read iterations is needed
385              
386 1 50       4 if ( exists( $config->{MAX_WAIT} ) ) {
387 1         9 $serial->_add_scan_log("(Max Wait set at $config->{MAX_WAIT})");
388 1         6 $read_iterations = int( $config->{MAX_WAIT} / ( $serial->{READ_CONST_TIME} / 1000 ) );
389 1 50       4 $read_iterations = 1 if ( $read_iterations < 1 );
390              
391             }
392             else {
393              
394             # Must always go though the loop once
395 0         0 $read_iterations = 1;
396             }
397              
398             # Begin Scan of Com Ports
399              
400 0         0 PORT:
401 1         10 foreach $com_port ( sort { $a <=> $b } @{ $config->{COM} } ) {
  1         7  
402              
403 0           $com_device_name = $serial->_get_com_device_name($com_port);
404 0           $serial->_add_scan_log("Scan Port COM${com_port} @ $com_device_name");
405              
406             # Baud rates are attempted from highest to lowest because some
407             # might be using a virtual COM via USB and it "looks" nicer to
408             # see the faster buad rate (virtual USB com ports dont care about baud rates or settings)
409              
410 0           BAUD:
411 0           foreach $baud ( sort { $b <=> $a } @{ $config->{BAUD} } ) {
  0            
412 0           $serial->_add_scan_log(" Checking with baudrate of $baud");
413              
414 0           SETTING:
415 0           foreach $setting ( sort { $b <=> $a } @{ $config->{SETTING} } ) {
  0            
416 0           $serial->_add_scan_log(" Checking with setting of $setting->{SETTING}");
417 0           $PortObj = $serial->_get_com_connection($com_port);
418              
419 0 0         if ($PortObj) {
420              
421             #$PortObj->user_msg("ON");
422 0 0         if ( !$PortObj->baudrate($baud) ) {
423              
424             # If *::SerialPort says this baudrate is invalid then go to the next one
425             # (eg. Dont keep scanning it at other settings)
426 0           $serial->_add_scan_log(" Warning: Baud rate of $baud is not valid for this com port - skipping to next one");
427 0           next BAUD;
428             }
429              
430 0           $PortObj->databits( $setting->{DATABITS} );
431 0           $PortObj->parity( $setting->{PARITY} );
432 0           $PortObj->stopbits( $setting->{STOPBITS} );
433 0           $PortObj->handshake( $setting->{HANDSHAKE} );
434              
435             # Just kept this based on *::SerialPort examples
436 0           $PortObj->buffers( 4096, 4096 );
437              
438 0 0         if ( $PortObj->write_settings ) {
439              
440             # Ok, port is available, now is it our device?
441              
442 0           $PortObj->write( $config->{TEST_STRING} );
443              
444 0           $serial->_add_scan_log(" Sending test string \"$config->{TEST_STRING}\"");
445              
446             # Due to a bug or something, this locks up
447             # on Windows sometimes
448              
449 0           $PortObj->read_char_time( $serial->{READ_CHAR_TIME} );
450 0           $PortObj->read_const_time( $serial->{READ_CONST_TIME} );
451              
452 0           $feedback = "";
453              
454             # Calculated outside loops for performance
455              
456 0           $waited = 0;
457              
458             # Wait a maximum amount of time to get expected output but move on
459             # if we dont get it in the alloted amount of time. This also protects
460             # us from a device just spewing data.
461              
462 0           for ( $iterations = 1 ; $iterations <= $read_iterations ; $iterations++ ) {
463              
464             # Read from the port
465 0           ( $bytes_read, $data_read ) = $PortObj->read(255); # docs say this must be 255 always
466 0           $waited += $serial->{READ_CONST_TIME};
467              
468 0 0         if ( $bytes_read > 0 ) {
469 0           $feedback .= $data_read;
470              
471             # This is what makes this loop faster
472 0 0         last if ( $feedback =~ /$config->{VALID_REPLY_RE}/ );
473             }
474             }
475              
476 0           $feedback =~ s/\015?\012$//;
477              
478 0           $serial->_add_scan_log( sprintf( " Received back from device \"%s\" (Waited %.2f secs)", $feedback, $waited / 1000 ) );
479              
480 0 0         if ( $feedback =~ /$config->{VALID_REPLY_RE}/ ) {
481              
482             # Get a new "device" and store all the properties in it
483              
484 0           $device = Hardware::PortScanner::Device->new_device($serial);
485              
486 0           $device->com_port("COM${com_port}");
487 0           $device->baudrate($baud);
488 0           $device->databits( $setting->{DATABITS} );
489 0           $device->parity( $setting->{PARITY} );
490 0           $device->stopbits( $setting->{STOPBITS} );
491 0           $device->handshake( $setting->{HANDSHAKE} );
492 0           $device->port_name($com_device_name);
493 0           $device->setting( $setting->{SETTING} );
494 0           $device->{VALID_REPLY} = $feedback;
495 0           $device->{TEST_STRING} = $config->{TEST_STRING};
496 0 0         $device->{MAX_WAIT} = $config->{MAX_WAIT} if ( exists( $config->{MAX_WAIT} ) );
497              
498 0           push( @{ $serial->{FOUND_DEVICE} }, $device );
  0            
499              
500             # Since this device was found, skip scanning on this port anymore
501              
502 0           $serial->_add_scan_log(" Matched valid reply RE so returning");
503 0           last BAUD;
504             }
505             }
506              
507 0           $PortObj->close;
508 0           undef $PortObj;
509              
510             } # PortObj
511             else {
512 0           $serial->_add_scan_log(" Com Port $com_port appears not to be available - skipping to next port");
513 0           next PORT;
514             }
515             } # Setting
516             } # Baud
517             } # Com Port
518              
519             }
520              
521             sub connect_to_device(@) {
522 0     0 1   my $serial = shift;
523 0           my $PortObj;
524             my $port;
525 0           my $baud;
526 0           my $config = {};
527 0           my $parm;
528             my $value;
529 0           my $key;
530 0           my ( $databits, $parity, $stopbits, $handshake );
531 0           my $setting;
532 0           my $device;
533 0           my $parm_found;
534              
535             # If there is a connection then remove it so this method is
536             # reentrant
537              
538 0 0         if ( defined( $serial->{CONNECTION} ) ) {
539 0           $serial->{CONNECTION}->close;
540 0           undef $serial->{CONNECTION};
541             }
542              
543             # Handle no parameters by assuming the user wants to connect to the only
544             # device found by faking the passing of "DEVICE => $device" if one
545             # and only one was found
546              
547 0 0         if ( scalar(@_) == 0 ) {
548 0 0         if ( $serial->num_found_devices == 1 ) {
    0          
549 0           @_ = ( "DEVICE", ( $serial->found_devices )[0] );
550             }
551             elsif ( $serial->num_found_devices == 0 ) {
552 0           croak "ERROR: (connect_to_device): No devices were found or not scanned - cannot auto-connect\n";
553             }
554             else {
555 0           croak "ERROR: (connect_to_device): More than one device was found - cannot auto-connect\n";
556             }
557             }
558              
559             # Handle the normal parameters
560              
561 0           while ( $parm = uc shift ) {
562 0           $value = shift;
563 0           $parm_found = 0;
564              
565 0           $key = "BAUD";
566 0 0         if ( $parm eq $key ) {
567 0 0         if ( ref $value eq "" ) {
568 0           $config->{$key} = $value;
569             }
570             else {
571 0           croak "ERROR: (connect_to_device): $key value must be a SCALAR\n";
572             }
573              
574             # Must be all digits. We don't check the actual value because we will
575             # let the *::SerialPort determine validity
576              
577 0 0         if ( $config->{$key} !~ /^\d+$/ ) {
578 0           croak "ERROR: (connect_to_device): BAUD rate \"$value\" not valid\n";
579             }
580 0           $parm_found++;
581             }
582              
583 0           $key = "COM";
584 0 0         if ( $parm eq $key ) {
585              
586 0 0         if ( ref $value eq "" ) {
587              
588             # Handle the format of "COM5" or just "5"
589 0           $value =~ s/^COM(\d+)/$1/i;
590 0           $config->{$key} = $value;
591             }
592             else {
593 0           croak "ERROR: (connect_to_device): $key value must be a SCALAR\n";
594             }
595              
596 0 0 0       if ( !( $value =~ /^\d+$/ && $value > 0 && $value <= 50 ) ) {
      0        
597 0           croak "ERROR: (connect_to_device): COM port\"$value\" not valid\n";
598             }
599              
600 0           $config->{PORTNAME} = $serial->_get_com_device_name($value);
601              
602 0           $parm_found++;
603             }
604              
605 0           $key = "SETTING";
606 0 0         if ( $parm eq $key ) {
607 0           $value = uc $value;
608 0 0         if ( $value =~ /^([5678])([NEO])([12])([NRX])?$/ ) {
609 2     2   21 no warnings;
  2         4  
  2         1853  
610              
611             # Convert our setting format to domain expected by *::SerialPort
612              
613 0           $databits = $1;
614 0 0         $parity = $2 eq "E" ? "even" : ( $2 eq "O" ? "odd" : "none" );
    0          
615 0           $stopbits = $3;
616 0 0         $handshake = $4 eq "R" ? "rts" : ( $4 eq "X" ? "xoff" : "none" );
    0          
617              
618 0           $config->{SETTING} = {
619             DATABITS => $databits,
620             PARITY => $parity,
621             STOPBITS => $stopbits,
622             HANDSHAKE => $handshake,
623             SETTING => $value
624             };
625             }
626             else {
627 0           croak "ERROR: (connect_to_device): SETTING value of \"$value\" is not valid \n";
628             }
629 0           $parm_found++;
630             }
631              
632 0           $key = "DEVICE";
633 0 0         if ( $parm eq $key ) {
634 0           $device = $value;
635              
636 0 0         if ( !defined($device) ) {
637 0           croak "ERROR: (connect_to_device): device specified is undefined\n";
638             }
639              
640             # Get the port configuration info from the device
641              
642 0           $config->{BAUD} = $device->baudrate;
643 0           $config->{COM} = $device->com_port;
644 0           $config->{COM} =~ s/^COM(\d+)/$1/i;
645 0           $config->{PORTNAME} = $device->port_name;
646 0           $config->{SETTING}->{DATABITS} = $device->databits;
647 0           $config->{SETTING}->{PARITY} = $device->parity;
648 0           $config->{SETTING}->{STOPBITS} = $device->stopbits;
649 0           $config->{SETTING}->{HANDSHAKE} = $device->handshake;
650 0           $config->{SETTING}->{SETTING} = $device->setting;
651              
652 0           $parm_found++;
653             }
654              
655             # Check for unsupported options
656              
657 0 0         if ( !$parm_found ) {
658 0           croak "ERROR: (connect_to_device): Parameter \"$parm\" is not valid for scan_port\n";
659             }
660              
661             }
662              
663             # Baud is required to have been determined
664              
665 0 0         if ( !exists( $config->{BAUD} ) ) {
666 0           croak "ERROR: (connect_to_device): Baud rate not specified or identifiable\n";
667             }
668              
669             # Com Port is required to have been determined
670              
671 0 0         if ( !exists( $config->{COM} ) ) {
672 0           croak "ERROR: (connect_to_device): Com port not specified or identifiable\n";
673             }
674              
675             # Settings can be defaulted to most common if not specified
676              
677 0 0         if ( !exists( $config->{SETTING} ) ) {
678 0           $config->{SETTING} = {
679             DATABITS => 8,
680             PARITY => "none",
681             STOPBITS => 1,
682             HANDSHAKE => "none",
683             SETTING => "8N1"
684             };
685             }
686              
687             # Attempt to get connection to serial port
688              
689 0           $PortObj = $serial->_get_com_connection( $config->{COM}, 1 );
690              
691 0 0         if ($PortObj) {
692              
693 0           $PortObj->user_msg("ON"); # Should this be on?
694              
695 0           $PortObj->databits( $config->{SETTING}->{DATABITS} );
696 0           $PortObj->baudrate( $config->{BAUD} );
697 0           $PortObj->parity( $config->{SETTING}->{PARITY} );
698 0           $PortObj->stopbits( $config->{SETTING}->{STOPBITS} );
699 0           $PortObj->handshake( $config->{SETTING}->{HANDSHAKE} );
700 0           $PortObj->buffers( 4096, 4096 ); # Better default?
701              
702 0 0         if ( !$PortObj->write_settings ) {
703              
704             # Getting this error is a little strange IF the device parameter was used
705              
706 0           croak "ERROR: (connect_to_device): Failed to write_settings to com port ($config->{COM})!\n";
707 0           $PortObj->close;
708 0           undef $PortObj;
709             }
710              
711             # Store connection in meta-data
712 0           $serial->{CONNECTION} = $PortObj;
713             }
714             else {
715 0           croak "ERROR: (connect_to_device): Failed to connect to known com port ($config->{COM})!\n";
716             }
717              
718 0           return 1;
719             }
720              
721             # Circular references are used to handle them during garbage collection
722              
723             sub DESTROY() {
724              
725             # Remove the child (device) reference so we will
726             # not have circular references
727              
728 0     0     $_[0]->{FOUND_DEVICE} = undef;
729 0           $_[0]->{CONNECTION} = undef;
730              
731             }
732              
733             # Devices have there own package but I didnot want to have an additional
734             # module file so ... two in one
735              
736             package Hardware::PortScanner::Device;
737              
738 2     2   16 use Data::Dumper; # Remove before production
  2         3  
  2         714  
739 2     2   13 use Carp;
  2         3  
  2         155  
740              
741             $Data::Dumper::Useqq = 1; # Remove before production
742              
743 2     2   13 use strict;
  2         4  
  2         221  
744 2     2   11 use warnings;
  2         3  
  2         1572  
745              
746             # Get connection to device. Used PortScanner method by same name
747              
748             sub connect_to_device($) {
749 0     0     my $device = shift;
750 0           my $serial = $device->{SERIAL};
751              
752 0           return $serial->connect_to_device( DEVICE => $device );
753              
754             }
755              
756             # Constructor for new device
757              
758             sub new_device($) {
759 0     0     my $device = bless( {}, __PACKAGE__ );
760 0           my $serial;
761             my $class;
762              
763 0 0         if ( ref $_[0] ) {
764 0           $serial = shift;
765             }
766             else {
767 0           $class = shift;
768 0           $serial = shift;
769             }
770              
771 0           $device->{SERIAL_OBJ} = $serial;
772              
773 0           return $device;
774             }
775              
776             # Return/set baudrate for device
777              
778             sub baudrate(;$) {
779 0     0     my $device = shift;
780 0           my $att_name = "BAUD";
781              
782 0 0         if ( $_[0] ) {
783 0           $device->{$att_name} = shift;
784             }
785              
786 0           return $device->{$att_name};
787             }
788              
789             # Return/set setting for device
790              
791             sub setting(;$) {
792 0     0     my $device = shift;
793 0           my $att_name = "SETTING";
794              
795 0 0         if ( $_[0] ) {
796 0           $device->{$att_name} = shift;
797             }
798              
799 0           return $device->{$att_name};
800             }
801              
802             # Return/set databits for device
803              
804             sub databits(;$) {
805 0     0     my $device = shift;
806 0           my $att_name = "DATABITS";
807              
808 0 0         if ( $_[0] ) {
809 0           $device->{$att_name} = shift;
810             }
811              
812 0           return $device->{$att_name};
813             }
814              
815             # Return/set stopbits for device
816              
817             sub stopbits(;$) {
818 0     0     my $device = shift;
819 0           my $att_name = "STOPBITS";
820              
821 0 0         if ( $_[0] ) {
822 0           $device->{$att_name} = shift;
823             }
824              
825 0           return $device->{$att_name};
826             }
827              
828             # Return/set parity for device
829              
830             sub parity(;$) {
831 0     0     my $device = shift;
832 0           my $att_name = "PARITY";
833              
834 0 0         if ( $_[0] ) {
835 0           $device->{$att_name} = shift;
836             }
837              
838 0           return $device->{$att_name};
839             }
840              
841             # Return/set handshake for device
842              
843             sub handshake(;$) {
844 0     0     my $device = shift;
845 0           my $att_name = "HANDSHAKE";
846              
847 0 0         if ( $_[0] ) {
848 0           $device->{$att_name} = shift;
849             }
850              
851 0           return $device->{$att_name};
852             }
853              
854             # Return/set com_port for device
855              
856             sub com_port(;$) {
857 0     0     my $device = shift;
858 0           my $att_name = "COM";
859              
860 0 0         if ( $_[0] ) {
861 0           $device->{$att_name} = shift;
862 0           $device->{$att_name} =~ s/^\d+$/COM&/;
863             }
864              
865 0           return $device->{$att_name};
866             }
867              
868             # Return/set port_name for device
869              
870             sub port_name(;$) {
871 0     0     my $device = shift;
872 0           my $att_name = "PORTNAME";
873              
874 0 0         if ( $_[0] ) {
875 0           $device->{$att_name} = shift;
876             }
877              
878 0           return $device->{$att_name};
879             }
880              
881             # Circular references are used to handle them during garbage collection
882              
883             sub DESTROY() {
884              
885             # Remove the parent (serial) reference so we will
886             # not have circular references
887              
888 0     0     $_[0]->{SERIAL} = undef;
889             }
890              
891             1;
892              
893             __END__