source: core/trunk/client/flarmclient.pl @ 292

Last change on this file since 292 was 291, checked in by dominic, 11 years ago

#158

  • Property svn:mime-type set to text/plain
File size: 9.2 KB
RevLine 
[130]1#!/usr/bin/perl
2#-------------------------------------------------------------------------------
3# This file is part of the FLARM¨-Radar Project.
4#   
5#   Copyright 2013 Netzschmiede GmbH (http://www.netzschmiede.ch)
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11#   http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19#   Project Website: www.flarmradar.ch
20#   Email: info@flarmradar.ch
21#-------------------------------------------------------------------------------
22
23use strict;
24use warnings;
25use Getopt::Std;
26use File::Basename;
[224]27use Time::HiRes qw(gettimeofday);
[133]28use POSIX qw(strftime);
29use POSIX qw(setsid);
[130]30use LWP::UserAgent;
[278]31use Device::SerialPort;
32use constant {
33        OPS => 0b001,
34        DEBUG => 0b010,
35        TRACE => 0b100
36};
[130]37my %config;
38my %options;
[278]39my $log;
40my $log_initialized = 0;
[288]41my $flarm;
[278]42
43# default values
44my $log_ops = 1;
[282]45my $log_debug = 0;
[278]46my $log_trace = 0;
[251]47my $interval = 3;
48my $skip = 1;
[130]49
[278]50# signal handlers
51# toggle $log_trace with 'kill -USR1 <pid>'
52$SIG{USR1} = sub {
53        if ($log_trace) {
54                $log_trace = 0;
55        } else {
56                $log_trace = 1;
57        }
58};
59$SIG{INT} = sub {
60        die("terminate program");
61};
[130]62
[278]63# display a help page
[130]64sub usage {
65  print <<EOF;
66NAME
[278]67  $0 -- stream flarm records to server
[130]68
69SYNOPSIS
[278]70  $0 [-c config_file] [-d] [-f data_file] [-i n] [-j m] [-r] [-t] [-h]
[130]71
72DESCRIPTION
73  The following options are available:
74 
75  -c    Use the specified configuration file. Use the default configuration
76                file as a starting point for customization.
77               
[284]78  -d    Write out debug information
79                WARNING: If your configuration is to write the out put to a file (see
80                option 'log') then this might fill up the file system.                 
[224]81               
[284]82  -f    Read the data from the specified data file instead of the serial
83                device. This is mainly used for testing and development.
[253]84
[251]85  -i    Bundle records, send a request to the server every i-th GPGGA record.
86                Used for bandwidth optimization. Defaults to 3.
[212]87               
[251]88  -j    Send every j-th record to the server. Used for bandwidth
89                optimization. Defaults to 1.
90               
[284]91  -t    Trace client operations, write out all data read from the flarm device
92                WARNING: If your configuration is to write the out put to a file (see
93                option 'log') then this might fill up the file system.
[224]94               
[130]95  -h    Print this help.
96
97EOF
98  exit 0;
99}
100
[278]101# a very basic logger
[204]102sub logit {
[278]103        my ($aspect, $msg) = @_;
104       
105        if (!$log_initialized) {
[279]106                if (exists($config{'log'})) {
[284]107                        open($log, ">> $config{'log'}") || die("Failed to open log file $config{'log'}: $!");
[278]108                } else {
109                        $log = *STDOUT;
110                }
111                $log_initialized = 1;
[204]112        }
[278]113        print $log "$msg\n" if (($aspect & OPS) && $log_ops);
114        print $log "$msg\n" if (($aspect & TRACE) && $log_trace);
115        print $log "$msg\n" if (($aspect & DEBUG) && $log_debug);
[204]116}
117
[278]118# read config file
[130]119sub readconfig {
[278]120        my ($config_file) = @_;
121        open(CONF, "< $config_file") || die("failed to open config file for reading: $!");
[130]122        while(my $line = <CONF>) {
123                chomp($line);
124                next if $line =~ /^\s*#/;
125                next if $line =~ /^\s*$/;
126                if ($line =~ /^\s*(\S*)\s*=\s*(\S*)\s*$/) {
127                        $config{$1} = $2;
128                }
129        }
130        close(CONF);
131}
132
[288]133# abstraction for reading data (from flarm device or from file)
134sub readdata {
135        unless (defined($flarm)) {
136                # initialize our flarm object
137                if (defined($options{'f'})) {
138                        logit(OPS, "read flarm data from file " . $options{'f'});
139                        open($flarm, "< $options{'f'}") || die("failed to open flarm data file for reading: $!");
140                } else {
141                        # check that we have the required configuration options
142                        die("missing option 'serial_device' in configuration file") unless (defined($config{'serial_device'}));
143                        die("device does not exist: " . $config{'serial_device'}) unless (-c $config{'serial_device'});
144                        die("missing option 'baud' in configuration file") unless (defined($config{'baud'}));
145                       
146                        # create serial device object
147                        logit(OPS, "read flarm data from " . $config{'serial_device'} . " with baud=" . $config{'baud'});
[289]148                        $flarm = new Device::SerialPort ($config{'serial_device'}) || die "Can't open $config{'serial_device'}!\n";
[288]149                        $flarm->baudrate($config{'baud'});
150                        $flarm->parity("none");
151                        $flarm->databits(8);
152                        $flarm->stopbits(1);
153                        $flarm->are_match("\r\n");
[289]154                       
155                        # === BEGIN Flarm Config ===
156                        my $cmd = undef;
157                        # ACFT = Type of aircraft: unknown
158                        # NMEAOUT = Send all data incl. Flarm propriatery data: 1
159                        # UI = Disable buzzer, enable led warnings: 3
160                        # PRIV = Disable stealth mode: 0
161                        # THRE = Threshold for aircraft on ground is 2 m/s: 2
[291]162                        # RANGE = Set detection range of aircraft to maximum: 25500
163                        # DEBUG = Removes height restrictions
[289]164                        my @item = ("\$PFLAC,S,ACFT,0\r\n","\$PFLAC,S,NMEAOUT,1\r\n","\$PFLAC,S,UI,3\r\n",
[291]165                                    "\$PFLAC,S,PRIV,0\r\n","\$PFLAC,S,THRE,2\r\n","\$PFLAC,S,RANGE,25500\r\n",
166                                    "\$PFLAC,S,DEBUG,9\r\n");
[289]167                       
168                        foreach $cmd (@item) {
169                                local $/ = "\r\n";
170                                # Read data back from Flarm
171                                my $continue = 1;
172                                my $record = undef;
173                                               
174                                while ($continue) {
175                                        $record = $flarm->lookfor();
176                                        if (length($record)) {
177                                                my ($fanswer,$checksum) = split('\*',$record);
178                                                undef $checksum;
[291]179                                                my $cmd2 = uc($cmd);
[289]180                                                $cmd2 =~ s/,S,/,A,/g;
181                                                chomp($cmd2);
182                                                # print $cmd2 . "\n";
183                                                if ($fanswer ~~ $cmd2) {
184                                                        # print "Setting accepted by Flarm: " . $cmd2 . "\n";
185                                                        logit(OPS, "Setting accepted by Flarm: " . $cmd2);
186                                                        $continue = 0;
187                                                } else {
188                                                        my $cmd3 = $cmd;
189                                                        chomp($cmd3);
190                                                        logit(OPS, "Send Flarm config command " . $cmd3);
191                                                        my $count_out = $flarm->write($cmd);
192                                                        warn "write failed\n" unless ($count_out);
193                                                        warn "write incomplete\n" if ( $count_out != length($cmd) );
194                                                } #else
195                                        } #endif
196                                } #while
197                        } #foreach
198                        undef $cmd;
199                        undef @item;
200                        # === END Flarm Config ===
[288]201                }
202        }
203       
204        my $record = undef;
205        if (defined($options{'f'})) {
206                $record = <$flarm>;
207        } else {
208                until($record = $flarm->lookfor()) {
209                        sleep(1);
210                }
211        }
212        chomp($record);
213        return $record;
214}
215
[278]216# as the name says
[224]217sub exact_time {
218        return strftime("%H:%M:%S", localtime()) . "." . (gettimeofday())[1];
219}
220
[130]221# send the records to the server. We don't make a request for each record for
222# performance reasons.
223sub flush {
[278]224        my ($records, $url, $ua) = @_;
225        logit(DEBUG, exact_time() . " start flushing data to server");
[224]226       
[217]227        my $date = `date -u +%Y/%m/%d`;
[224]228        chomp($date);
[130]229        my $resturl = $url . "/" . $date;
[278]230        logit(DEBUG, exact_time() . " request resource: " . $resturl);
[224]231       
232        # compose the request
[130]233        my $request = HTTP::Request->new('PUT');
234        $request->url($resturl);
235        $request->header('stationKey'=>$config{'key'});
[224]236        my $content = compress($records);
[278]237        logit(DEBUG, exact_time() . " put on wire: " . $content);
[224]238        $request->content($content);
239       
[130]240        # run the request
[278]241        logit(DEBUG, exact_time() . " start server push");
[130]242        my $response = $ua->request($request);
[278]243        logit(DEBUG, exact_time() . " end server push");
[224]244       
[130]245        # analyze the response
246        my $code = $response->code;
[278]247        $response->code == 200 || logit(DEBUG, "error processing records (" . $response->code . ") records=[" . $records . "]");
248        logit(DEBUG, exact_time() . " end flushing data");
[130]249}
250
[223]251# remove all unused records, debug information, etc.
252sub compress {
253        my ($records) = @_;
254        my $on_wire;
255        foreach my $record (split(';', $records)) {
256                if ($record =~ /^\$GPGGA,/ || $record =~ /^\$PFLAA,/) {
257                        $on_wire = (defined($on_wire)) ? $on_wire . ";" . $record : $record;
258                }               
259        }
260        return $on_wire;
261}
262
[278]263#
264# here starts our main program
265#
[261]266
[130]267# parse options
[251]268getopts('c:di:j:f:th', \%options);
[130]269
[278]270my $cfile;
[130]271if (defined($options{'c'})) {
272        $cfile = $options{'c'};
[278]273} elsif (-f "/etc/flarmclient.conf") {
274        $cfile = "/etc/flarmclient.conf";
275} else {
276        # last resort, search for a config file in the local directory
277        $cfile = "./flarmclient.conf";
[130]278}
[278]279
280# read config file
[283]281die ("no config file found") unless (-f "$cfile");
[278]282readconfig($cfile);
283
[224]284if (defined($options{'d'})) {
[278]285        $log_debug = 1;
[224]286}
[212]287if (defined($options{'i'})) {
288        $interval = $options{'i'};
289}
[251]290if (defined($options{'j'})) {
291        $skip = $options{'j'}
292}
[130]293if (defined($options{'h'})) {
294        usage();
295}
[224]296if (defined($options{'t'})) {
[278]297        $log_trace = 1;
[224]298}
[204]299
[130]300# validation: key must be present in config file
[251]301die("no key found in config file " . $cfile . " (option: key)") unless defined($config{'key'});
[130]302
303# force a flush right away and after every write or print
304local $| = 1;
305
[278]306# ok, everything is setup
307logit(OPS, "start client, connect to " . $config{'url'});
[257]308
[278]309# create UserAgent object
310my $ua = new LWP::UserAgent;
[130]311
312my $buf;
[212]313my $i = 0;
[289]314
[288]315while(my $record = readdata()) {
316       
317        # send only n-th sequence to the server (option -s). A sequence is terminated with a GPGGA-record
318        if ($i % $skip == 0) {
[278]319                       
[288]320                logit(TRACE, $record);
321                $buf = (defined($buf)) ? "$buf;$record" : $record;
322        }
[251]323               
[288]324        # a GPGGA record terminates the sequence
325        if ($record =~ /^\$GPGGA,/) {
326                if ($i % ($interval * $skip) == 0) {
327                        flush($buf, $config{'url'}, $ua) ;
328                        $buf = undef;
329                        sleep($interval * $skip) if (defined($options{'f'}));
[278]330                }
331               
[288]332                $i++;
[130]333        }
334}
335exit 0;
336
[278]337END {
338        logit(OPS, "client is shutting down");
339        if (exists($config{"log"})) {
340                close($log);
341        }
342        undef $ua;
343        undef $flarm;
344}
345
Note: See TracBrowser for help on using the repository browser.