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

Last change on this file since 259 was 257, checked in by smoser, 12 years ago

#153

  • Property svn:mime-type set to text/plain
File size: 7.0 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;
31
32my %config;
33my %options;
34my $ua;
[224]35my $debug = 0;
[204]36my $trace = 0;
[251]37my $interval = 3;
38my $skip = 1;
[130]39
40# default values
[251]41my $cfile = "/etc/flarmclient.conf";
42my $log = "$ENV{'HOME'}/flarmclient.trace";
[249]43my $fifo = "$ENV{'HOME'}/fifo";
[130]44
45# functions
46sub usage {
47  print <<EOF;
48NAME
49  $0 -- stream flarm data to server
50
51SYNOPSIS
[253]52  $0 [-c config_file] [-d] [-f data_file] [-i n] [-j m] [-t] [-h]
[130]53
54DESCRIPTION
55  The following options are available:
56 
57  -c    Use the specified configuration file. Use the default configuration
58                file as a starting point for customization.
59               
[224]60  -d    Write debug information. The debug information is written to STDOUT
61                unless tracing (option -t) is turned on. With tracing switched on,
[253]62                the debug information is written to the trace file (see below).                 
[224]63               
[253]64  -f    Read the data from the specified data file. This is mainly used for
65                testing and development.
66
[251]67  -i    Bundle records, send a request to the server every i-th GPGGA record.
68                Used for bandwidth optimization. Defaults to 3.
[212]69               
[251]70  -j    Send every j-th record to the server. Used for bandwidth
71                optimization. Defaults to 1.
72               
[224]73  -t    Trace client operations. A trace file is created in
[251]74                \$HOME/flarmclient.trace
75                WARNING: Do not use that in Production, the trace file is not truncated
76                and might fill up the file system.
[224]77               
[130]78  -h    Print this help.
79
80EOF
81  exit 0;
82}
83
[204]84# print statistic information to logfile
85$SIG{USR1} = sub {
86        if ($trace) {
87                $trace = 0;
88        } else {
89                $trace = 1;
90        }
91};
92
[257]93
[224]94sub openlog {
95        if (tell(LOG) == -1) {
96                open(LOG, ">> $log") || print "Failed to open trace file $log: $!";
97        }
98        return 1;
99}
100
[204]101sub logit {
102        my ($level, $msg) = @_;
[224]103        if ($trace && $level eq "TRACE") {
104                openlog() && print LOG "$msg\n";
[204]105        }
[224]106        if ($debug && $level eq "DEBUG") {
[225]107                if ($trace) {
108                        openlog() && print LOG "$msg\n";
109                } else {
110                        print "$msg\n";
111                }
112               
[224]113        }
[204]114}
115
[130]116sub cleanup {
117        if (-e "$fifo") {
118                unlink($fifo) || die("unable to remove $fifo: $!");
119        }
[223]120        close(LOG);
[130]121}
122
123sub readconfig {
124        open(CONF, "< $cfile") || die("failed to open config file for reading: $!");
125        while(my $line = <CONF>) {
126                chomp($line);
127                next if $line =~ /^\s*#/;
128                next if $line =~ /^\s*$/;
129                if ($line =~ /^\s*(\S*)\s*=\s*(\S*)\s*$/) {
130                        $config{$1} = $2;
131                }
132        }
133        close(CONF);
134}
135
[224]136sub exact_time {
137        return strftime("%H:%M:%S", localtime()) . "." . (gettimeofday())[1];
138}
139
[130]140# send the records to the server. We don't make a request for each record for
141# performance reasons.
142sub flush {
143        my ($records, $url) = @_;
[224]144        logit("DEBUG", exact_time() . " Start flushing data to server");
145       
[217]146        my $date = `date -u +%Y/%m/%d`;
[224]147        chomp($date);
[130]148        my $resturl = $url . "/" . $date;
[224]149        logit("DEBUG", exact_time() . " Request resource: " . $resturl);
150       
151        # compose the request
[130]152        my $request = HTTP::Request->new('PUT');
153        $request->url($resturl);
154        $request->header('stationKey'=>$config{'key'});
[224]155        my $content = compress($records);
156        logit("DEBUG", exact_time() . " Put on wire: " . $content);
157        $request->content($content);
158       
[130]159        # run the request
[224]160        logit("DEBUG", exact_time() . " Start server push");
[130]161        my $response = $ua->request($request);
[224]162        logit("DEBUG", exact_time() . " End server push");
163       
[130]164        # analyze the response
165        my $code = $response->code;
[224]166        $response->code == 200 || logit("DEBUG", "Error processing records (" . $response->code . ") records=[" . $records . "]");
167        logit("DEBUG", exact_time() . " End flushing data");
[130]168}
169
[223]170# remove all unused records, debug information, etc.
171sub compress {
172        my ($records) = @_;
173        my $on_wire;
174        foreach my $record (split(';', $records)) {
175                if ($record =~ /^\$GPGGA,/ || $record =~ /^\$PFLAA,/) {
176                        $on_wire = (defined($on_wire)) ? $on_wire . ";" . $record : $record;
177                }               
178        }
179        return $on_wire;
180}
181
[130]182# parse options
[251]183getopts('c:di:j:f:th', \%options);
[130]184
185# read config file
186if (defined($options{'c'})) {
187        $cfile = $options{'c'};
188}
[224]189if (defined($options{'d'})) {
190        $debug = 1;
191}
[212]192if (defined($options{'i'})) {
193        $interval = $options{'i'};
194}
[251]195if (defined($options{'j'})) {
196        $skip = $options{'j'}
197}
[130]198if (defined($options{'h'})) {
199        usage();
200}
[224]201if (defined($options{'t'})) {
202        $trace = 1;
203}
[204]204
205# read config file
[130]206readconfig();
207
208# validation: key must be present in config file
[251]209die("no key found in config file " . $cfile . " (option: key)") unless defined($config{'key'});
[130]210
211# remove old leftovers
212cleanup();
213
214# create pipe
215die("no fifo found in config file (option: fifo)") unless defined($fifo);
216if (! -d dirname($fifo)) {
217        system("mkdir", "-p", dirname($fifo)) == 0 || die("failed to create fifo directory " . dirname($fifo) . ": $!")
218}
219system("mkfifo", $fifo) == 0 || die("failed to create fifo: $!");
220
221# force a flush right away and after every write or print
222local $| = 1;
223
[257]224# let the kernel raper dead childs
225$SIG{CHLD} = 'IGNORE';
226
[130]227# fork minicom and write to pipe
228defined( my $pid = fork() ) or die "can't fork: $!";
229unless ($pid) {
230        # we're the child
[133]231        # detach from session
[217]232    setsid() or die "can't start a new session: $!";
[257]233    close(STDIN);
234        close(STDOUT);
235        close(STDERR);
[130]236       
237        if (defined($options{'f'})) {
238                open(DATA, "< $options{'f'}") || die("failed to open data file $options{'f'}: $!");
239                open(FIFO, "> $fifo") || die("failed to open fifo for writing: $!");
240                while(my $line = <DATA>) {
241                        chomp($line);
242                        next if ($line =~ /^\s*$/);
243                        print FIFO $line, "\n" || die("failed to execute child command: $!");
244                }
245                close(DATA);
246                close(FIFO);
247        } else {
[226]248                exec("exec minicom -t xterm-color -C $fifo> /dev/null 2>&1") == 0 || die("failed to run minicom: $!");
[130]249        }
250        exit 0;
251}
252
253# create UserAgent object
254$ua = new LWP::UserAgent;
255my $buf;
256# read data from pipe
257open(FIFO, "< $fifo") || die("failed to open fifo for reading: $!");
[212]258my $i = 0;
[130]259while(my $record = <FIFO>) {
[251]260        # send only n-th record to the server (option -s)
261        if ($i % $skip == 0) {
262                chomp($record);
263                logit("TRACE", $record);
264                $buf = (defined($buf)) ? "$buf;$record" : $record;
265        }
266       
267        # a GPGGA record terminates the sequence
[130]268        if ($record =~ /^\$GPGGA,/) {
[251]269                if ($i % ($interval * $skip) == 0) {
270                        flush($buf, $config{'url'}) ;
271                        $buf = undef;
272                        sleep($interval * $skip) if (defined($options{'f'}));
[133]273                }
[251]274               
[212]275                $i++;
[130]276        }
277}
278close(FIFO);
279
280cleanup();
281exit 0;
282
Note: See TracBrowser for help on using the repository browser.