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

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

#156

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