Advertisement
Guest User

rest of the code

a guest
Dec 4th, 2018
185
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 6 11.08 KB | None | 0 0
  1. use v6;
  2. use Data::Dump;
  3. use experimental :pack;
  4. use JSON::Tiny;
  5. use Redis::Async;
  6.  
  7. constant $SOCKET_PORT = 7000;
  8. constant $SOCKET_ADDR = '0.0.0.0';
  9. constant $REDIS_PORT = 6379;
  10. constant $REDIS_ADDR = '127.0.0.1';
  11. constant $REDIS_AUTH = 'xxxxxxxxx';
  12. constant $REDIS_PATH = '/usr/bin/redis-cli';
  13.  
  14. constant $IDLING_PERIOD_MIN = 178;
  15. constant $CACHE_EXPIRE_IN = 86400;
  16.  
  17. # create socket
  18. my $socket = IO::Socket::Async.listen($SOCKET_ADDR, $SOCKET_PORT);
  19.  
  20. # connnect to Redis ...
  21. my $redis;
  22. try {
  23.     my $error-code = "110";
  24.     $redis = Redis::Async.new("127.0.0.1:6379");
  25.     $redis.auth($REDIS_AUTH);
  26.  
  27.     CATCH {
  28.         default {
  29.             say "Error $error-code ", .^name, ': Failed to initiate connection to Redis';
  30.             exit;
  31.         }
  32.     }
  33. }
  34.  
  35. # react whenever there is connection
  36. react {
  37.     whenever $socket -> $conn {
  38.  
  39.         # do something when the connection wants to talk
  40.         whenever $conn.Supply(:bin) {
  41.             if $_.decode('utf-8').chars == 108 or $_.decode('utf-8').chars == 116 {
  42.                 say "Received --> "~$_.decode('utf-8');
  43.                 my $ack = generateAck($_.decode('utf-8'));
  44.                 if $ack {
  45.                     $conn.print: $ack;
  46.                 }else{
  47.                     say "No ack. Closing connection";
  48.                     $conn.close;
  49.                 }
  50.  
  51.             }
  52.         }
  53.     }
  54.     CATCH {
  55.         default {
  56.             say .^name, ': ', .Str;
  57.             say "handled in $?LINE";
  58.         }
  59.     }
  60. }
  61.  
  62. sub generateAck($raw) {
  63.     my $ack = 0;
  64.     $raw ~~ s/^\s+|\s+$//; # remove heading/trailing whitespace
  65.  
  66.     # split device and vehicle data
  67.     my ($device_data, $vehicle_data) = split('|', $raw);
  68.     my ($header, $length, $datatype, $imei) = @($device_data ~~ /(..)(....)(..)(.+)/);
  69.  
  70.     unless $length eq '0116' or $length eq '0108' { return 0};
  71.     my $veh_serial = substr($vehicle_data, 86, 4) || substr($vehicle_data, 78, 4);
  72.  
  73.     # at the same time, translate the data in parallel
  74.     my $translated = start { # this is expensive as we will save the translated data to Redis as well
  75.         my %vehicle_data_hash;
  76.         try {
  77.             my $error-code = "120";
  78.             %vehicle_data_hash = translate($imei.Str, $vehicle_data);
  79.  
  80.             CATCH {
  81.                 default {
  82.                     say "Error $error-code ", .^name, ': Failed to translate data';
  83.                     return 0;
  84.                 }
  85.             }
  86.         }
  87.  
  88.         print "Publishing data..";
  89.         try {
  90.             my $error-code = "112";
  91.             $redis.publish('data_json', to-json %vehicle_data_hash);
  92.  
  93.             CATCH {
  94.                 default {
  95.                     say "Error $error-code ", .^name, ': Failed to publish data to Redis';
  96.                     exit;
  97.                 }
  98.             }
  99.         }
  100.         say "OK!";
  101.         print "Storing data..";
  102.         try {
  103.             my $error-code = "113";
  104.             $redis.set($imei~'.'~time, to-json %vehicle_data_hash);
  105.  
  106.             CATCH {
  107.                 default {
  108.                     say "Error $error-code ", .^name, ': Failed to set data to Redis';
  109.                     exit;
  110.                 }
  111.             }
  112.         }
  113.         say "OK!";
  114.  
  115.         #say Dump %vehicle_data_hash;
  116.          1;
  117.      };
  118.  
  119.     # check data checksum
  120.     my $our_checksum = generateChecksum(substr($raw, 0, *-2));
  121.     my $data_checksum = substr($raw, *-2);
  122.     if $datatype ~~ /E\d/ or $datatype eq 'BB' {
  123.             return 0;
  124.     } else {
  125.         $datatype = 'AA';
  126.     }
  127.     # if checksum ok, generate ACK
  128.     if $our_checksum eq $data_checksum {
  129.         $ack = '$$0014'~$datatype~$veh_serial~generateChecksum('$$0014'~$datatype~$veh_serial);
  130.         say "Replied --> "~$ack;
  131.     }
  132.  
  133.     await Promise.anyof($translated, Promise.in(1)); #in 1 second, if the data is not translated yet, kill it
  134.    
  135.     if $translated {
  136.          return $ack;
  137.     }
  138.     else {
  139.         return 0;
  140.     }
  141.  
  142.  
  143. }
  144.  
  145. sub translate($imei, $vehicle_data) {
  146.     my %vehicle_data_hash;
  147.     $redis.auth($REDIS_AUTH);
  148.     if $vehicle_data.chars == 92 {
  149.         %vehicle_data_hash =
  150.             "veh_status" => substr($vehicle_data, 0, 8),
  151.             "veh_datetime" => substr($vehicle_data, 8, 12),
  152.             "veh_batt_voltage" => substr($vehicle_data, 20, 2),
  153.             "veh_supp_voltage" => substr($vehicle_data, 22, 2),
  154.             "veh_ADC1" => substr($vehicle_data, 24, 4),
  155.             "veh_temp1" => substr($vehicle_data, 28, 4),
  156.             "veh_temp2" => substr($vehicle_data, 32, 4),
  157.             "veh_loc" => substr($vehicle_data, 36, 4),
  158.             "veh_cellID" => substr($vehicle_data, 40, 4),
  159.             "veh_sat" => substr($vehicle_data, 44, 2),
  160.             "veh_signal" => substr($vehicle_data, 46, 2),
  161.             "veh_angle" => substr($vehicle_data, 48, 3),
  162.             "veh_speed" => substr($vehicle_data, 51, 3),
  163.             "veh_hdop" => substr($vehicle_data, 54, 4),
  164.             "veh_mileage" => substr($vehicle_data, 58, 7),
  165.             "veh_lat" => substr($vehicle_data, 65, 9),
  166.             "veh_ns" => substr($vehicle_data, 74, 1),
  167.             "veh_long" => substr($vehicle_data, 75, 10),
  168.             "veh_ew" => substr($vehicle_data, 85, 1),
  169.             "dev_IMEI" => $imei,
  170.         ;
  171.  
  172.     } elsif $vehicle_data.chars == 84 {
  173.         %vehicle_data_hash =
  174.             "veh_status" => substr($vehicle_data, 0, 8),
  175.             "veh_datetime" => substr($vehicle_data, 8, 12),
  176.             "veh_batt_voltage" => substr($vehicle_data, 20, 2),
  177.             "veh_supp_voltage" => substr($vehicle_data, 22, 2),
  178.             "veh_ADC1" => substr($vehicle_data, 24, 4),
  179.             "veh_loc" => substr($vehicle_data, 28, 4),
  180.             "veh_cellID" => substr($vehicle_data, 32, 4),
  181.             "veh_sat" => substr($vehicle_data, 36, 2),
  182.             "veh_signal" => substr($vehicle_data, 38, 2),
  183.             "veh_angle" => substr($vehicle_data, 40, 3),
  184.             "veh_speed" => substr($vehicle_data, 43, 3),
  185.             "veh_hdop" => substr($vehicle_data, 46, 4),
  186.             "veh_mileage" => substr($vehicle_data, 50, 7),
  187.             "veh_lat" => substr($vehicle_data, 57, 9),
  188.             "veh_ns" => substr($vehicle_data, 66, 1),
  189.             "veh_long" => substr($vehicle_data, 67, 10),
  190.             "veh_ew" => substr($vehicle_data, 78, 1),
  191.             "dev_IMEI" => $imei,
  192.         ;
  193.  
  194.     }
  195.  
  196.     my $veh_lat_degree = substr %vehicle_data_hash{"veh_lat"}, 0, 2;
  197.     my $veh_lat_minute = substr %vehicle_data_hash{"veh_lat"}, 2;
  198.     my $veh_long_degree = substr %vehicle_data_hash{"veh_long"}, 0, 3;
  199.     my $veh_long_minute = substr %vehicle_data_hash{"veh_long"}, 3;
  200.  
  201.     my @bin = split('',sprintf("%b",:16(%vehicle_data_hash{"veh_status"}).base(10))) :skip-empty; #convert hex to 32bit binary and each bit to array
  202.  
  203.     unshift @bin, 0 while @bin.elems != 32; #make sure we have 32 elems!
  204.  
  205.     my $isIdle = @bin[14].Int; # motherfuc##r!
  206.     my $isEngineStarted = @bin[1].Int;
  207.     my $isOverSpeed = @bin[2].Int;
  208.     my $isAntennaDrop = @bin[6].Int;
  209.  
  210.     if $isIdle {
  211.         #vehicle is in idle state
  212.         %vehicle_data_hash{"veh_status"} = "IDLE";
  213.  
  214.     } elsif $isOverSpeed {
  215.         #vehicle is in overspeed state
  216.         %vehicle_data_hash{"veh_status"} = "OSPD";
  217.  
  218.     } elsif $isAntennaDrop {
  219.         #gps device not running
  220.         %vehicle_data_hash{"veh_status"} = "NSIG";
  221.     } else {
  222.         #if vehicle is not in idle state, not in overspeed, gps is working fine and engine is running
  223.         if $isEngineStarted {
  224.             #vehicle is moving
  225.             %vehicle_data_hash{"veh_status"} = "MOVE";
  226.         } else {
  227.             %vehicle_data_hash{"veh_status"} = "STOP";
  228.         }
  229.  
  230.     }
  231.  
  232.     %vehicle_data_hash{"veh_lat_coordinate"} = sprintf("%.7f", $veh_lat_degree + sprintf("%.7f", $veh_lat_minute/60));
  233.     %vehicle_data_hash{"veh_long_coordinate"} = sprintf("%.7f", $veh_long_degree + sprintf("%.7f", $veh_long_minute/60));
  234.  
  235.     my ($orig_year,$orig_mon,$orig_day,$orig_hour,$orig_min,$orig_sec) = %vehicle_data_hash{"veh_datetime"}.comb(2);
  236.  
  237.     my $format = sub ($self) { sprintf "%04d-%02d-%02d %02d:%02d:%02d" , .year, .month, .day, .hour, .minute, .second given $self; };
  238.     my $dt = DateTime.new(
  239.         year       => $orig_year + 2000,
  240.         month      => $orig_mon,
  241.         day        => $orig_day,
  242.         hour       => $orig_hour,
  243.         minute     => $orig_min,
  244.         second     => $orig_sec,
  245.         formatter => $format,
  246.     ).utc;
  247.  
  248.     %vehicle_data_hash{"veh_datetime_parsed"} = $dt.local.Str;
  249.     %vehicle_data_hash{"veh_datetime_epoch"} = $dt.posix.Str;;
  250.  
  251.     %vehicle_data_hash{"veh_breached"} = 0;
  252.  
  253.     ## Logic for IDLING starts here
  254.  
  255.     state $accumulatedStopPeriod = 0;
  256.     my $stopPeriod = 0;
  257.     my $cached_speed = $redis.get('cache.'~%vehicle_data_hash{'dev_IMEI'}~'.speed');
  258.     my $cached_datetime = $redis.get('cache.'~%vehicle_data_hash{'dev_IMEI'}~'.datetime');
  259.     my $cached_status = $redis.get('cache.'~%vehicle_data_hash{'dev_IMEI'}~'.status');
  260.  
  261.     if $cached_speed and $cached_datetime and $cached_status {
  262.         #if was and is MOVE but speed was and is 0
  263.  
  264.         if (($cached_status eq 'MOVE' or $cached_status eq 'IDLE') and $cached_speed == '0' and %vehicle_data_hash{'veh_status'} eq 'MOVE' and %vehicle_data_hash{'veh_speed'} == '0') {
  265.             $stopPeriod = %vehicle_data_hash{'veh_datetime_epoch'} - $cached_datetime;
  266.  
  267.             $accumulatedStopPeriod += $stopPeriod;
  268.             say("total: $accumulatedStopPeriod");
  269.             say("current stop period: $stopPeriod");
  270.  
  271.             if ($accumulatedStopPeriod > $IDLING_PERIOD_MIN) {
  272.                 %vehicle_data_hash{'veh_status'} = 'IDLE';
  273.             }
  274.         }
  275.  
  276.     }
  277.     # $log->debug("S: $stopPeriod");
  278.     if ($cached_status) {
  279.         if ($cached_status eq 'IDLE' and (%vehicle_data_hash{'veh_status'} eq 'STOP' or %vehicle_data_hash{'veh_speed'} > '0' ) ) {
  280.             $redis.del('cache.'~%vehicle_data_hash{'dev_IMEI'}~'.datetime') if $cached_datetime;
  281.             $redis.del('cache.'~%vehicle_data_hash{'dev_IMEI'}~'.status') if $cached_status;
  282.             $redis.del('cache.'~%vehicle_data_hash{'dev_IMEI'}~'.speed') if $cached_speed;
  283.             $accumulatedStopPeriod = 0;
  284.         }
  285.     }
  286.  
  287.     $redis.set('cache.'~%vehicle_data_hash{'dev_IMEI'}~'.datetime',%vehicle_data_hash{'veh_datetime_epoch'});
  288.     $redis.set('cache.'~%vehicle_data_hash{'dev_IMEI'}~'.status',%vehicle_data_hash{'veh_status'});
  289.     $redis.set('cache.'~%vehicle_data_hash{'dev_IMEI'}~'.speed',%vehicle_data_hash{'veh_speed'});
  290.  
  291.  
  292.     ## Logic for IDLING ends here
  293.  
  294.     return %vehicle_data_hash;
  295.  
  296. }
  297.  
  298. sub generateChecksum($minus_checksum) {
  299.  
  300.    # turn string into Blob
  301.     my Blob $blob = $minus_checksum.encode('utf-8');
  302.     # unpack Blob into ascii list
  303.     my @array = $blob.unpack("C*");
  304.     # perform bitwise operation for each ascii in the list
  305.     my $dec +^= $_ for $blob.unpack("C*");
  306.     # only take 2 digits
  307.     $dec = sprintf("%02d", $dec) if $dec ~~ /^\d$/;
  308.     $dec = '0'.$dec if $dec ~~ /^[a..fA..F]$/;
  309.     $dec = uc $dec;
  310.     # convert it to hex
  311.     my $hex = sprintf '%02x', $dec;
  312.     return uc $hex;
  313.  
  314. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement