#!/usr/bin/perl -w package SCX::Reader; use Time::HiRes qw(gettimeofday tv_interval); use FileHandle; use SCX::CRC; our $PACKET_SIZE = 9; # 9 bytes + 0x05 our $LOG_ROTATE = 600; sub new { my ($class, $args) = @_; my $callback = $args->{callback} or die "callback arg not defined"; my $portname = $args->{portname} or die "portname not specified"; system 'stty', '-F', $portname, '115200', 'raw'; if ($?) { die "stty died with code $? (no permissions?)"; } open my $tty, '<:raw', $portname or die "Can't open $portname: $!"; my $logfile = $args->{logfile}; my $log_gen = 0; open my $logfh, '>', "$logfile.$log_gen" or die "Can't open $logfile.$log_gen: $!"; my $now = gettimeofday; my $self = { portname => $portname, fh => $tty, logfile => $logfile, logfh => $logfh, log_gen => $log_gen, log_start => $now, starttime => $now, callback => $callback, bytes => [], }; bless $self, $class; return $self; } sub fh { return shift->{fh}; } sub read { my ($self) = @_; my $data; my $bytes_read = sysread $self->fh, $data, $PACKET_SIZE; die "Read error on $self->{portname}: $!" if !$bytes_read; my @bytes = unpack("C*", $data); push @{ $self->{bytes} }, @bytes; @bytes = @{ $self->{bytes} }; my @bad_bytes; while (@bytes > $PACKET_SIZE) { if ($bytes[0] != 0x55) { push @bad_bytes, shift @bytes; next; } my $cmd = $bytes[1]; if ($bytes[$PACKET_SIZE] != 0x05 || SCX::CRC::digest(@bytes[0..$PACKET_SIZE-2]) != $bytes[$PACKET_SIZE-1]) { push @bad_bytes, shift @bytes; next; } if (@bad_bytes) { # Report previous bad bytes first $self->log_bytes(\@bad_bytes, "Cannot parse packet"); @bad_bytes = (); } my @packet = splice @bytes, 0, $PACKET_SIZE+1; my $rv = &{ $self->{callback} }(@packet[1..$PACKET_SIZE]); $self->log_bytes(@packet, $rv); } $self->log_bad_bytes(\@bad_bytes, "Cannot parse packet"); @{ $self->{bytes} } = @bytes; } sub log_bytes { my ($self, $bytes, $msg) = @_; return if !@$bytes; $msg = defined $msg ? ' # ' . $msg : ''; my $now = gettimeofday; if ($now - $self->{log_start} >= $LOG_ROTATE) { close $self->{logfh}; $self->{log_gen} = $self->{log_gen} ? 0 : 1; open my $fh, '>', $logfile . '.' . $self->{log_gen} or die "Can't open $logfile.$self->{log_gen}: $!"; $self->{logfh} = $fh; $self->{log_start} = $now; } $self->{logfh}->print(sprintf('% 10.3f', $now - $self->{starttime}), (map { sprintf(" %02x", $_) } @bytes), $msg, "\n"); } 1;