]> www.fi.muni.cz Git - slotcarman.git/blob - SCX/Parser.pm
b71f26b75639c01c8477bd68cc0b867c643cfa54
[slotcarman.git] / SCX / Parser.pm
1 #!/usr/bin/perl -w
2
3 package SCX::Parser;
4
5 use strict;
6 use SCX::CRC;
7
8 our $PACKET_SIZE = 9; # 9 bytes + 0x05
9
10 sub new {
11         my ($class, $args) = @_;
12
13         my $self = {
14                 bytes     => [],
15                 now       => 0,
16         };
17
18         bless $self, $class;
19
20         return $self;
21 }
22
23 sub now { return shift->{now} }
24
25 sub add_data {
26         my ($self, $time, @bytes) = @_;
27
28         push @{ $self->{bytes} }, @bytes;
29         @bytes = @{ $self->{bytes} };
30
31         $self->{now} = $time;
32
33         my @bad_bytes;
34
35         while (@bytes > $PACKET_SIZE) {
36                 if ($bytes[0] != 0x55) {
37                         push @bad_bytes, shift @bytes;
38                         next;
39                 }
40                 my $cmd = $bytes[1];
41
42                 if ($bytes[$PACKET_SIZE] != 0x05
43                         || SCX::CRC::digest(@bytes[0..$PACKET_SIZE-2])
44                                 != $bytes[$PACKET_SIZE-1]) {
45                         push @bad_bytes, shift @bytes;
46                         next;
47                 }
48                 
49                 if (@bad_bytes) { # Report previous bad bytes first
50                         $self->bad_bytes(@bad_bytes);
51                         @bad_bytes = ();
52                 }
53
54                 my @packet = splice @bytes, 0, $PACKET_SIZE+1;
55                 $self->log_packet(@packet);
56                 $self->parse_packet(@packet);
57         }
58
59         if (@bad_bytes) {
60                 while (@bytes && $bytes[0] != 0x55) {
61                         push @bad_bytes, shift @bytes;
62                 }
63                 $self->bad_bytes(@bad_bytes);
64         }
65
66         @{ $self->{bytes} } = @bytes;
67 }
68
69 # Subclass these:
70
71 sub log_packet { }
72 sub bad_bytes { }
73 sub unknown_packet { }
74 sub strange_packet { }
75
76 sub bus_free_time { }
77 sub car_programming { }
78 sub reset { }
79 sub standings { }
80 sub car_lap_time { }
81 sub race_setup { }
82 sub fuel_level { }
83 sub brake_set { }
84 sub qualification { }
85 sub end_of_race { }
86 sub race_start { }
87 sub display_change { }
88 sub finish_line { }
89 sub controller_status { }
90
91 our %COMMANDS = (
92         0xAA => \&bus_free_time_packet,
93         0xCC => \&car_programming_packet,
94         0xD0 => \&reset_packet,
95         0xD3 => \&standings_packet,
96         0xD4 => \&car_lap_time_packet,
97         0xD5 => \&race_setup_packet,
98         0xD6 => \&fuel_level_packet,
99         0xD7 => \&brake_set_packet,
100         0xDB => \&qualification_packet,
101         0xDC => \&end_of_race_packet,
102         0xDD => \&race_start_packet,
103         0xDE => \&display_change_packet,
104         0xEE => \&finish_line_packet,
105         0xFF => \&controller_status_packet,
106 );
107
108 sub parse_packet {
109         my ($self, @data) = @_;
110
111         my $cmd = $data[1];
112         my @args = @data[2..7];
113
114         my $sub = $COMMANDS{$cmd};
115
116         if (!defined $sub) {
117                 $self->unknown_packet($cmd, @args);
118                 return;
119         }
120
121         return &$sub($self, @args);
122 }
123
124 sub bus_free_time_packet {
125         my ($self, @bytes) = @_;
126
127         $self->strange_packet('bus free time', @bytes)
128                 if $bytes[2] != 0xF0
129                 || $bytes[3] != 0xF0
130                 || $bytes[4] != 0xF0
131                 || $bytes[5] != 0xF0;
132
133         $self->bus_free_time($bytes[1], $bytes[0]);
134 }
135
136 sub car_programming_packet {
137         my ($self, @bytes) = @_;
138
139         $self->strange_packet('car programming', @bytes)
140                 if ($bytes[0] & 0xF8) != 0 || ($bytes[0] & 0x07) > 5
141                 || $bytes[1] != 0xFE
142                 || $bytes[2] != 0xFF
143                 || $bytes[3] != 0xFF
144                 || $bytes[4] != 0xFF
145                 || $bytes[5] != 0xFF;
146
147         $self->car_programming($bytes[0] & 0x07);
148 }
149
150 sub reset_packet {
151         my ($self, @bytes) = @_;
152
153         $self->strange_packet('reset', @bytes)
154                 if $bytes[0] != 0xFF
155                 || $bytes[3] != 0xAA
156                 || $bytes[4] != 0xAA
157                 || $bytes[5] != 0xAA;
158
159         $self->reset($bytes[1], $bytes[2]);
160 }
161
162 sub standings_packet {
163         my ($self, @bytes) = @_;
164
165         self->strange_packet('standings', @bytes)
166                 if ($bytes[0] != 0xFF && ($bytes[0] & 0x07) > 5)
167                 || ($bytes[1] != 0xFF && ($bytes[1] & 0x07) > 5)
168                 || ($bytes[2] != 0xFF && ($bytes[2] & 0x07) > 5)
169                 || ($bytes[3] != 0xFF && ($bytes[3] & 0x07) > 5)
170                 || ($bytes[4] != 0xFF && ($bytes[4] & 0x07) > 5)
171                 || ($bytes[5] != 0xFF && ($bytes[5] & 0x07) > 5);
172
173         $self->standings(map { $_ != 0xFF ? $_ & (0x07) : () } @bytes);
174 }
175
176 sub car_lap_time_packet {
177         my ($self, @bytes) = @_;
178
179         $self->strange_packet('car lap time', @bytes)
180                 if $bytes[0] > 5
181                 || $bytes[1] & 0x01
182                 || $bytes[2] & 0x01
183                 || ($bytes[3] & 0xF0) != 0
184                 || $bytes[4] & 0x01
185                 || $bytes[5] & 0x01;
186
187         $self->car_lap_time($bytes[0],
188                 256*$bytes[1] + $bytes[2] + ($bytes[3] & 0x01 ? 1 : 0),
189                 sprintf('%.3f', 0.01024 * (256*$bytes[4] + $bytes[5]
190                         + ($bytes[3] & 0x08 ? 1 : 0))),
191                 sprintf('%04b', $bytes[3])
192         );
193 }
194
195 sub race_setup_packet {
196         my ($self, @bytes) = @_;
197
198         $self->strange_packet('race setup')
199                 if ($bytes[0] != 0x00 && $bytes[0] != 0xFF)
200                 || $bytes[1] & 0xF0
201                 || $bytes[2] & 0xF0
202                 || $bytes[3] & 0xF0
203                 || $bytes[4] != 0xFF
204                 || $bytes[5] != 0xFF;
205
206         my $rounds = $bytes[0] == 0x00
207                 ? 0
208                 : ($bytes[1] & 0x0F) * 256
209                         + ($bytes[2] & 0x0F) * 16
210                         + ($bytes[3] & 0x0F);
211
212         $self->race_setup($rounds);
213 }
214
215 sub fuel_level_packet {
216         my ($self, @bytes) = @_;
217
218         $self->strange_packet('fuel level')
219                 if ($bytes[0] >> 4) > 8
220                 || ($bytes[0] & 0x0F) > 8
221                 || ($bytes[1] >> 4) > 8
222                 || ($bytes[1] & 0x0F) > 8
223                 || ($bytes[2] >> 4) > 8
224                 || ($bytes[2] & 0x0F) > 8
225                 || ($bytes[5] != 0xAA && $bytes[5] != 0xFF);
226
227         my @fuel = (
228                 $bytes[0] >> 4, $bytes[0] & 0x0f,
229                 $bytes[1] >> 4, $bytes[1] & 0x0f,
230                 $bytes[2] >> 4, $bytes[2] & 0x0f,
231         );
232
233         $self->fuel_level(@fuel);
234 }
235
236 sub brake_set_packet {
237         my ($self, @bytes) = @_;
238
239         $self->strange_packet('brake set')
240                 if ($bytes[0] > 5)
241                 || ($bytes[1] != 0x00 && $bytes[1] != 0x02 && $bytes[1] != 0x04)
242                 || $bytes[2] != 0x83
243                 || $bytes[3] != 0x93
244                 || $bytes[4] != 0xDB
245                 || $bytes[5] != 0xFF;
246
247         $self->brake_set($bytes[0],
248                 $bytes[1] == 0x00 ? 0
249                 : $bytes[1] == 0x02 ? 50
250                 : 100);
251 }
252
253 sub qualification_packet {
254         my ($self, @bytes) = @_;
255
256         $self->strange_packet('qualification')
257                 if $bytes[0] & 0xF0
258                 || $bytes[1] & 0xF0
259                 || $bytes[2] & 0xF0
260                 || $bytes[3] > 6
261                 || $bytes[4] != 0xFF
262                 || $bytes[5] != 0xFF;
263
264         my $rounds = ($bytes[0] & 0x0F) * 256
265                         + ($bytes[1] & 0x0F) * 16
266                         + ($bytes[2] & 0x0F);
267         my $cars = $bytes[3];
268
269         $self->qualification($rounds, $cars);
270 }
271
272 sub end_of_race_packet {
273         my ($self, @bytes) = @_;
274
275         $self->strange_packet('end of race')
276                 if $bytes[0] != 0xFF
277                 || $bytes[1] != 0xFF
278                 || $bytes[2] != 0xFF
279                 || $bytes[3] != 0xFF
280                 || $bytes[4] != 0xFF
281                 || $bytes[5] != 0xFF;
282
283         $self->end_of_race();
284 }
285
286 sub race_start_packet {
287         my ($self, @bytes) = @_;
288
289         $self->strange_packet('race start')
290                 if $bytes[0] != 0x00
291                 || $bytes[1] != 0xAA
292                 || $bytes[2] != 0xAA
293                 || $bytes[3] != 0xAA
294                 || $bytes[4] != 0xAA
295                 || $bytes[5] != 0xAA;
296
297         $self->race_start();
298 }
299
300 sub display_change_packet {
301         my ($self, @bytes) = @_;
302
303         $self->strange_packet('display change')
304                 if $bytes[0] & 0xFE
305                 || $bytes[1] != 0xFF
306                 || $bytes[2] != 0xFF
307                 || $bytes[3] != 0xFF
308                 || $bytes[4] != 0xFF
309                 || $bytes[5] != 0xFF;
310
311         $self->display_change();
312 }
313
314 =comment
315
316 # FIXME: we still do not know the meaning of the bytes
317 sub finish_line_packet {
318         my ($self, @bytes) = @_;
319
320         my $fail;
321         for my $byte (@bytes) {
322                 $fail = 1
323                         if $byte != 0xAA
324                         && $byte != 0xE7
325                         && $byte != 0xF0
326                         && $byte != 0xFE
327         }
328
329         my $msg = 'Strange finish_line packet'
330                 if $fail;
331
332         my $regular = 1;
333         my @cars_finished;
334         for my $i (0..5) {
335                 my $byte = $bytes[$i];
336
337                 $regular = 0
338                         if $byte != 0xAA && $byte != 0xE7 && $byte != 0xFE;
339
340                 push @cars_finished, $i if $byte == 0xE7;
341         }
342
343         $self->log_cmd('finish_line', $regular, @cars_finished);
344         $self->track->finish_line(
345                 $self->{last_read_time},
346                 $regular,
347                 @cars_finished
348         );
349
350         return $msg;
351 }
352 =cut
353
354 sub finish_line_packet {
355         my ($self, @bytes) = @_;
356
357         my $fail;
358         for my $byte (@bytes) {
359                 $fail = 1
360                         if $byte != 0xAA
361                         && $byte != 0xE7
362                         && $byte != 0xF0
363                         && $byte != 0xFE
364         }
365
366         $self->strange_packet('finish line')
367                 if $fail;
368
369         my $regular = 1;
370         my @cars_finished;
371         for my $i (0..5) {
372                 my $byte = $bytes[$i];
373
374                 $regular = 0
375                         if $byte != 0xAA && $byte != 0xE7 && $byte != 0xFE;
376
377                 push @cars_finished, $i if $byte == 0xE7;
378         }
379
380         $self->finish_line($regular, @cars_finished);
381 }
382
383 sub controller_status_packet {
384         my ($self, @bytes) = @_;
385
386         my $fail;
387         for my $byte (@bytes) {
388                 next if $byte == 0xAA;
389                 $fail = 1
390                         if ($byte & 0xC0) != 0xC0
391                         || ($byte & 0x0F) > 12
392         }
393
394         $self->strange_packet('controller status')
395                 if $fail;
396
397         my @ctrl_data;
398
399         for my $car (0..5) {
400                 my $byte = $bytes[$car];
401
402                 if ($byte == 0xAA) {
403                         push @ctrl_data, undef;
404                         next;
405                 }
406
407                 my $light = ($byte & 0x20) ? 0 : 1;
408                 my $backbutton = ($byte & 0x10) ? 0 : 1;
409                 my $throttle = $byte & 0x0f;
410
411                 push @ctrl_data, {
412                         throttle => $throttle,
413                         button   => $backbutton,
414                         light    => $light,
415                 };
416         }
417
418         $self->controller_status(@ctrl_data);
419 }
420
421 1;
422