]> www.fi.muni.cz Git - slotcarman.git/commitdiff
Factor out packet parsing into a subclassable module.
authorJan "Yenya" Kasprzak <kas@fi.muni.cz>
Fri, 11 Feb 2011 23:16:14 +0000 (00:16 +0100)
committerJan "Yenya" Kasprzak <kas@fi.muni.cz>
Fri, 11 Feb 2011 23:16:14 +0000 (00:16 +0100)
SCX/Parser.pm [new file with mode: 0644]

diff --git a/SCX/Parser.pm b/SCX/Parser.pm
new file mode 100644 (file)
index 0000000..b71f26b
--- /dev/null
@@ -0,0 +1,422 @@
+#!/usr/bin/perl -w
+
+package SCX::Parser;
+
+use strict;
+use SCX::CRC;
+
+our $PACKET_SIZE = 9; # 9 bytes + 0x05
+
+sub new {
+       my ($class, $args) = @_;
+
+       my $self = {
+               bytes     => [],
+               now       => 0,
+       };
+
+       bless $self, $class;
+
+       return $self;
+}
+
+sub now { return shift->{now} }
+
+sub add_data {
+       my ($self, $time, @bytes) = @_;
+
+       push @{ $self->{bytes} }, @bytes;
+       @bytes = @{ $self->{bytes} };
+
+       $self->{now} = $time;
+
+       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->bad_bytes(@bad_bytes);
+                       @bad_bytes = ();
+               }
+
+               my @packet = splice @bytes, 0, $PACKET_SIZE+1;
+               $self->log_packet(@packet);
+               $self->parse_packet(@packet);
+       }
+
+       if (@bad_bytes) {
+               while (@bytes && $bytes[0] != 0x55) {
+                       push @bad_bytes, shift @bytes;
+               }
+               $self->bad_bytes(@bad_bytes);
+       }
+
+       @{ $self->{bytes} } = @bytes;
+}
+
+# Subclass these:
+
+sub log_packet { }
+sub bad_bytes { }
+sub unknown_packet { }
+sub strange_packet { }
+
+sub bus_free_time { }
+sub car_programming { }
+sub reset { }
+sub standings { }
+sub car_lap_time { }
+sub race_setup { }
+sub fuel_level { }
+sub brake_set { }
+sub qualification { }
+sub end_of_race { }
+sub race_start { }
+sub display_change { }
+sub finish_line { }
+sub controller_status { }
+
+our %COMMANDS = (
+       0xAA => \&bus_free_time_packet,
+       0xCC => \&car_programming_packet,
+       0xD0 => \&reset_packet,
+       0xD3 => \&standings_packet,
+       0xD4 => \&car_lap_time_packet,
+       0xD5 => \&race_setup_packet,
+       0xD6 => \&fuel_level_packet,
+       0xD7 => \&brake_set_packet,
+       0xDB => \&qualification_packet,
+       0xDC => \&end_of_race_packet,
+       0xDD => \&race_start_packet,
+       0xDE => \&display_change_packet,
+       0xEE => \&finish_line_packet,
+       0xFF => \&controller_status_packet,
+);
+
+sub parse_packet {
+       my ($self, @data) = @_;
+
+       my $cmd = $data[1];
+       my @args = @data[2..7];
+
+       my $sub = $COMMANDS{$cmd};
+
+       if (!defined $sub) {
+               $self->unknown_packet($cmd, @args);
+               return;
+       }
+
+       return &$sub($self, @args);
+}
+
+sub bus_free_time_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('bus free time', @bytes)
+               if $bytes[2] != 0xF0
+               || $bytes[3] != 0xF0
+               || $bytes[4] != 0xF0
+               || $bytes[5] != 0xF0;
+
+       $self->bus_free_time($bytes[1], $bytes[0]);
+}
+
+sub car_programming_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('car programming', @bytes)
+               if ($bytes[0] & 0xF8) != 0 || ($bytes[0] & 0x07) > 5
+               || $bytes[1] != 0xFE
+               || $bytes[2] != 0xFF
+               || $bytes[3] != 0xFF
+               || $bytes[4] != 0xFF
+               || $bytes[5] != 0xFF;
+
+       $self->car_programming($bytes[0] & 0x07);
+}
+
+sub reset_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('reset', @bytes)
+               if $bytes[0] != 0xFF
+               || $bytes[3] != 0xAA
+               || $bytes[4] != 0xAA
+               || $bytes[5] != 0xAA;
+
+       $self->reset($bytes[1], $bytes[2]);
+}
+
+sub standings_packet {
+       my ($self, @bytes) = @_;
+
+       self->strange_packet('standings', @bytes)
+               if ($bytes[0] != 0xFF && ($bytes[0] & 0x07) > 5)
+               || ($bytes[1] != 0xFF && ($bytes[1] & 0x07) > 5)
+               || ($bytes[2] != 0xFF && ($bytes[2] & 0x07) > 5)
+               || ($bytes[3] != 0xFF && ($bytes[3] & 0x07) > 5)
+               || ($bytes[4] != 0xFF && ($bytes[4] & 0x07) > 5)
+               || ($bytes[5] != 0xFF && ($bytes[5] & 0x07) > 5);
+
+       $self->standings(map { $_ != 0xFF ? $_ & (0x07) : () } @bytes);
+}
+
+sub car_lap_time_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('car lap time', @bytes)
+               if $bytes[0] > 5
+               || $bytes[1] & 0x01
+               || $bytes[2] & 0x01
+               || ($bytes[3] & 0xF0) != 0
+               || $bytes[4] & 0x01
+               || $bytes[5] & 0x01;
+
+       $self->car_lap_time($bytes[0],
+               256*$bytes[1] + $bytes[2] + ($bytes[3] & 0x01 ? 1 : 0),
+               sprintf('%.3f', 0.01024 * (256*$bytes[4] + $bytes[5]
+                       + ($bytes[3] & 0x08 ? 1 : 0))),
+               sprintf('%04b', $bytes[3])
+       );
+}
+
+sub race_setup_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('race setup')
+               if ($bytes[0] != 0x00 && $bytes[0] != 0xFF)
+               || $bytes[1] & 0xF0
+               || $bytes[2] & 0xF0
+               || $bytes[3] & 0xF0
+               || $bytes[4] != 0xFF
+               || $bytes[5] != 0xFF;
+
+       my $rounds = $bytes[0] == 0x00
+               ? 0
+               : ($bytes[1] & 0x0F) * 256
+                       + ($bytes[2] & 0x0F) * 16
+                       + ($bytes[3] & 0x0F);
+
+       $self->race_setup($rounds);
+}
+
+sub fuel_level_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('fuel level')
+               if ($bytes[0] >> 4) > 8
+               || ($bytes[0] & 0x0F) > 8
+               || ($bytes[1] >> 4) > 8
+               || ($bytes[1] & 0x0F) > 8
+               || ($bytes[2] >> 4) > 8
+               || ($bytes[2] & 0x0F) > 8
+               || ($bytes[5] != 0xAA && $bytes[5] != 0xFF);
+
+       my @fuel = (
+               $bytes[0] >> 4, $bytes[0] & 0x0f,
+               $bytes[1] >> 4, $bytes[1] & 0x0f,
+               $bytes[2] >> 4, $bytes[2] & 0x0f,
+       );
+
+       $self->fuel_level(@fuel);
+}
+
+sub brake_set_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('brake set')
+               if ($bytes[0] > 5)
+               || ($bytes[1] != 0x00 && $bytes[1] != 0x02 && $bytes[1] != 0x04)
+               || $bytes[2] != 0x83
+               || $bytes[3] != 0x93
+               || $bytes[4] != 0xDB
+               || $bytes[5] != 0xFF;
+
+       $self->brake_set($bytes[0],
+               $bytes[1] == 0x00 ? 0
+               : $bytes[1] == 0x02 ? 50
+               : 100);
+}
+
+sub qualification_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('qualification')
+               if $bytes[0] & 0xF0
+               || $bytes[1] & 0xF0
+               || $bytes[2] & 0xF0
+               || $bytes[3] > 6
+               || $bytes[4] != 0xFF
+               || $bytes[5] != 0xFF;
+
+       my $rounds = ($bytes[0] & 0x0F) * 256
+                       + ($bytes[1] & 0x0F) * 16
+                       + ($bytes[2] & 0x0F);
+       my $cars = $bytes[3];
+
+       $self->qualification($rounds, $cars);
+}
+
+sub end_of_race_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('end of race')
+               if $bytes[0] != 0xFF
+               || $bytes[1] != 0xFF
+               || $bytes[2] != 0xFF
+               || $bytes[3] != 0xFF
+               || $bytes[4] != 0xFF
+               || $bytes[5] != 0xFF;
+
+       $self->end_of_race();
+}
+
+sub race_start_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('race start')
+               if $bytes[0] != 0x00
+               || $bytes[1] != 0xAA
+               || $bytes[2] != 0xAA
+               || $bytes[3] != 0xAA
+               || $bytes[4] != 0xAA
+               || $bytes[5] != 0xAA;
+
+       $self->race_start();
+}
+
+sub display_change_packet {
+       my ($self, @bytes) = @_;
+
+       $self->strange_packet('display change')
+               if $bytes[0] & 0xFE
+               || $bytes[1] != 0xFF
+               || $bytes[2] != 0xFF
+               || $bytes[3] != 0xFF
+               || $bytes[4] != 0xFF
+               || $bytes[5] != 0xFF;
+
+       $self->display_change();
+}
+
+=comment
+
+# FIXME: we still do not know the meaning of the bytes
+sub finish_line_packet {
+       my ($self, @bytes) = @_;
+
+       my $fail;
+       for my $byte (@bytes) {
+               $fail = 1
+                       if $byte != 0xAA
+                       && $byte != 0xE7
+                       && $byte != 0xF0
+                       && $byte != 0xFE
+       }
+
+       my $msg = 'Strange finish_line packet'
+               if $fail;
+
+       my $regular = 1;
+       my @cars_finished;
+       for my $i (0..5) {
+               my $byte = $bytes[$i];
+
+               $regular = 0
+                       if $byte != 0xAA && $byte != 0xE7 && $byte != 0xFE;
+
+               push @cars_finished, $i if $byte == 0xE7;
+       }
+
+       $self->log_cmd('finish_line', $regular, @cars_finished);
+       $self->track->finish_line(
+               $self->{last_read_time},
+               $regular,
+               @cars_finished
+       );
+
+       return $msg;
+}
+=cut
+
+sub finish_line_packet {
+       my ($self, @bytes) = @_;
+
+       my $fail;
+       for my $byte (@bytes) {
+               $fail = 1
+                       if $byte != 0xAA
+                       && $byte != 0xE7
+                       && $byte != 0xF0
+                       && $byte != 0xFE
+       }
+
+       $self->strange_packet('finish line')
+               if $fail;
+
+       my $regular = 1;
+       my @cars_finished;
+       for my $i (0..5) {
+               my $byte = $bytes[$i];
+
+               $regular = 0
+                       if $byte != 0xAA && $byte != 0xE7 && $byte != 0xFE;
+
+               push @cars_finished, $i if $byte == 0xE7;
+       }
+
+       $self->finish_line($regular, @cars_finished);
+}
+
+sub controller_status_packet {
+       my ($self, @bytes) = @_;
+
+       my $fail;
+       for my $byte (@bytes) {
+               next if $byte == 0xAA;
+               $fail = 1
+                       if ($byte & 0xC0) != 0xC0
+                       || ($byte & 0x0F) > 12
+       }
+
+       $self->strange_packet('controller status')
+               if $fail;
+
+       my @ctrl_data;
+
+       for my $car (0..5) {
+               my $byte = $bytes[$car];
+
+               if ($byte == 0xAA) {
+                       push @ctrl_data, undef;
+                       next;
+               }
+
+               my $light = ($byte & 0x20) ? 0 : 1;
+               my $backbutton = ($byte & 0x10) ? 0 : 1;
+               my $throttle = $byte & 0x0f;
+
+               push @ctrl_data, {
+                       throttle => $throttle,
+                       button   => $backbutton,
+                       light    => $light,
+               };
+       }
+
+       $self->controller_status(@ctrl_data);
+}
+
+1;
+