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

Last change on this file since 284 was 284, checked in by smoser, 11 years ago

#156

  • Property svn:mime-type set to text/plain
File size: 6.7 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;
41
42# default values
43my $log_ops = 1;
[282]44my $log_debug = 0;
[278]45my $log_trace = 0;
[251]46my $interval = 3;
47my $skip = 1;
[130]48
[278]49# signal handlers
50# toggle $log_trace with 'kill -USR1 <pid>'
51$SIG{USR1} = sub {
52        if ($log_trace) {
53                $log_trace = 0;
54        } else {
55                $log_trace = 1;
56        }
57};
58$SIG{INT} = sub {
59        die("terminate program");
60};
[130]61
[278]62# display a help page
[130]63sub usage {
64  print <<EOF;
65NAME
[278]66  $0 -- stream flarm records to server
[130]67
68SYNOPSIS
[278]69  $0 [-c config_file] [-d] [-f data_file] [-i n] [-j m] [-r] [-t] [-h]
[130]70
71DESCRIPTION
72  The following options are available:
73 
74  -c    Use the specified configuration file. Use the default configuration
75                file as a starting point for customization.
76               
[284]77  -d    Write out debug information
78                WARNING: If your configuration is to write the out put to a file (see
79                option 'log') then this might fill up the file system.                 
[224]80               
[284]81  -f    Read the data from the specified data file instead of the serial
82                device. This is mainly used for testing and development.
[253]83
[251]84  -i    Bundle records, send a request to the server every i-th GPGGA record.
85                Used for bandwidth optimization. Defaults to 3.
[212]86               
[251]87  -j    Send every j-th record to the server. Used for bandwidth
88                optimization. Defaults to 1.
89               
[284]90  -t    Trace client operations, write out all data read from the flarm device
91                WARNING: If your configuration is to write the out put to a file (see
92                option 'log') then this might fill up the file system.
[224]93               
[130]94  -h    Print this help.
95
96EOF
97  exit 0;
98}
99
[278]100# a very basic logger
[204]101sub logit {
[278]102        my ($aspect, $msg) = @_;
103       
104        if (!$log_initialized) {
[279]105                if (exists($config{'log'})) {
[284]106                        open($log, ">> $config{'log'}") || die("Failed to open log file $config{'log'}: $!");
[278]107                } else {
108                        $log = *STDOUT;
109                }
110                $log_initialized = 1;
[204]111        }
[278]112        print $log "$msg\n" if (($aspect & OPS) && $log_ops);
113        print $log "$msg\n" if (($aspect & TRACE) && $log_trace);
114        print $log "$msg\n" if (($aspect & DEBUG) && $log_debug);
[204]115}
116
[278]117# read config file
[130]118sub readconfig {
[278]119        my ($config_file) = @_;
120        open(CONF, "< $config_file") || die("failed to open config file for reading: $!");
[130]121        while(my $line = <CONF>) {
122                chomp($line);
123                next if $line =~ /^\s*#/;
124                next if $line =~ /^\s*$/;
125                if ($line =~ /^\s*(\S*)\s*=\s*(\S*)\s*$/) {
126                        $config{$1} = $2;
127                }
128        }
129        close(CONF);
130}
131
[278]132# as the name says
[224]133sub exact_time {
134        return strftime("%H:%M:%S", localtime()) . "." . (gettimeofday())[1];
135}
136
[130]137# send the records to the server. We don't make a request for each record for
138# performance reasons.
139sub flush {
[278]140        my ($records, $url, $ua) = @_;
141        logit(DEBUG, exact_time() . " start flushing data to server");
[224]142       
[217]143        my $date = `date -u +%Y/%m/%d`;
[224]144        chomp($date);
[130]145        my $resturl = $url . "/" . $date;
[278]146        logit(DEBUG, exact_time() . " request resource: " . $resturl);
[224]147       
148        # compose the request
[130]149        my $request = HTTP::Request->new('PUT');
150        $request->url($resturl);
151        $request->header('stationKey'=>$config{'key'});
[224]152        my $content = compress($records);
[278]153        logit(DEBUG, exact_time() . " put on wire: " . $content);
[224]154        $request->content($content);
155       
[130]156        # run the request
[278]157        logit(DEBUG, exact_time() . " start server push");
[130]158        my $response = $ua->request($request);
[278]159        logit(DEBUG, exact_time() . " end server push");
[224]160       
[130]161        # analyze the response
162        my $code = $response->code;
[278]163        $response->code == 200 || logit(DEBUG, "error processing records (" . $response->code . ") records=[" . $records . "]");
164        logit(DEBUG, exact_time() . " end flushing data");
[130]165}
166
[223]167# remove all unused records, debug information, etc.
168sub compress {
169        my ($records) = @_;
170        my $on_wire;
171        foreach my $record (split(';', $records)) {
172                if ($record =~ /^\$GPGGA,/ || $record =~ /^\$PFLAA,/) {
173                        $on_wire = (defined($on_wire)) ? $on_wire . ";" . $record : $record;
174                }               
175        }
176        return $on_wire;
177}
178
[278]179#
180# here starts our main program
181#
[261]182
[130]183# parse options
[251]184getopts('c:di:j:f:th', \%options);
[130]185
[278]186my $cfile;
[130]187if (defined($options{'c'})) {
188        $cfile = $options{'c'};
[278]189} elsif (-f "/etc/flarmclient.conf") {
190        $cfile = "/etc/flarmclient.conf";
191} else {
192        # last resort, search for a config file in the local directory
193        $cfile = "./flarmclient.conf";
[130]194}
[278]195
196# read config file
[283]197die ("no config file found") unless (-f "$cfile");
[278]198readconfig($cfile);
199
[224]200if (defined($options{'d'})) {
[278]201        $log_debug = 1;
[224]202}
[212]203if (defined($options{'i'})) {
204        $interval = $options{'i'};
205}
[251]206if (defined($options{'j'})) {
207        $skip = $options{'j'}
208}
[130]209if (defined($options{'h'})) {
210        usage();
211}
[224]212if (defined($options{'t'})) {
[278]213        $log_trace = 1;
[224]214}
[204]215
[130]216# validation: key must be present in config file
[251]217die("no key found in config file " . $cfile . " (option: key)") unless defined($config{'key'});
[130]218
219# force a flush right away and after every write or print
220local $| = 1;
221
[278]222# ok, everything is setup
223logit(OPS, "start client, connect to " . $config{'url'});
[257]224
[278]225# create UserAgent object
226my $ua = new LWP::UserAgent;
[130]227
[278]228# create Flarm object
229my $flarm = new Device::SerialPort ($config{'serial_device'});
230$flarm->baudrate($config{'baud'});
231$flarm->parity("none");
232$flarm->databits(8);
233$flarm->stopbits(1);
234$flarm->are_match("\r\n");
235
[130]236my $buf;
[212]237my $i = 0;
[278]238while(1) {
239        my $record = $flarm->lookfor();
240        if ($record) {
[251]241                chomp($record);
[278]242
243                # send only n-th record to the server (option -s)
244                if ($i % $skip == 0) {
245                        chomp($record);
246                       
247                        logit(TRACE, $record);
248                        $buf = (defined($buf)) ? "$buf;$record" : $record;
[133]249                }
[251]250               
[278]251                # a GPGGA record terminates the sequence
252                if ($record =~ /^\$GPGGA,/) {
253                        if ($i % ($interval * $skip) == 0) {
254                                flush($buf, $config{'url'}, $ua) ;
255                                $buf = undef;
256                                sleep($interval * $skip) if (defined($options{'f'}));
257                        }
258               
259                        $i++;
260                }
261               
262        } else {
263                sleep(1);
[130]264        }
265}
266exit 0;
267
[278]268END {
269        logit(OPS, "client is shutting down");
270        if (exists($config{"log"})) {
271                close($log);
272        }
273        undef $ua;
274        undef $flarm;
275}
276
Note: See TracBrowser for help on using the repository browser.