Files
paparazzi/sw/logalizer/plot.pl
T
2006-06-03 13:04:49 +00:00

483 lines
15 KiB
Perl
Executable File

#!/usr/bin/perl -w
use Getopt::Long;
use Tk;
package Ploter;
use Tk::LabEntry;
use Tk::FileSelect;
use Tk::DialogBox;
use XML::Parser;
use XML::DOM;
use Expect;
use Data::Dumper;
use Getopt::Long;
my $paparazzi_lib;
BEGIN {
$paparazzi_lib = (defined $ENV{PAPARAZZI_SRC}) ?
$ENV{PAPARAZZI_SRC}."/sw/lib/perl" : "/usr/lib/paparazzi/";
}
use lib ($paparazzi_lib);
#use ChildrenSpawner;
@ISA = qw(Subject);
use strict;
use warnings;
use diagnostics;
use Paparazzi::Environment;
use Subject;
my $log_date;
my $log_duration;
my $log_filename;
my $time_range="[]";
my $tr_entry;
my $pi = 3.14159;
sub populate {
my ($self, $args) = @_;
my $paparazzi_src = Paparazzi::Environment::paparazzi_src();
my $paparazzi_home = Paparazzi::Environment::paparazzi_home();
Paparazzi::Environment::check_paparazzi_home();
# $args->{-variables} = {paparazzi_home => $paparazzi_home}; #A quoi ça sert ?
# $args->{-bin_base_dir} = $paparazzi_src; #A quoi ça sert ?
# $self->SUPER::populate($args); #A quoi ça sert ?
$self->configspec(-log => [S_NOINIT, S_PASSIVE, S_RDWR, S_OVRWRT, S_NOPRPG, undef]);
$self->configspec(-log_start_date => [S_NOINIT, S_PASSIVE, S_RDWR, S_OVRWRT, S_NOPRPG, undef]);
$self->configspec(-protocol => [S_NOINIT, S_PASSIVE, S_RDWR, S_OVRWRT, S_NOPRPG, undef]);
$self->configspec(-listbox => [S_NOINIT, S_PASSIVE, S_RDWR, S_OVRWRT, S_NOPRPG, undef]);
$self->configspec(-gnuplots => [S_NOINIT, S_PASSIVE, S_RDWR, S_OVRWRT, S_NOPRPG, undef]);
$self->configspec(-variables => [S_SUPER, S_SUPER, S_SUPER, S_SUPER, S_SUPER, {}]);
# print("in Ploter::populate\n");
}
sub completeinit {
my $self = shift;
$self->SUPER::completeinit;
$self->configure('-protocol' => undef); #A retirer je suppose
$self->build_gui();
# print("in Ploter::completeinit\n");
}
#
# XML
#
sub parse_messages_xml() {
my $self = shift;
my $filename = Paparazzi::Environment::paparazzi_src()."/conf/messages.xml";
my $parser = XML::DOM::Parser->new();
my $doc = $parser->parsefile($filename);
$self->{messages} = $doc;
print STDOUT "successfully parsed $filename\n";
return $doc;
}
#
# Menus
#
sub build_menu_msg() {
my ($self, $doc, $menubar) = @_;
my $data_menu = $menubar->cascade(-label => "~Data");
$self->{data_menu} = $data_menu;
}
sub update_menu_msg() {
my ($self, $ac_name, $ac_id) = @_;
my $data_menu = $self->{data_menu};
my $doc = $self->{messages};
my $menubar = $self->{menubar};
my $ac_menu = $data_menu->cascade(-label => "$ac_name ($ac_id)");
$self->parse_protocol($doc, $ac_menu, $ac_id);
}
sub parse_protocol() {
my ($self, $doc, $ac_menu, $ac_id) = @_;
my $protocol = $doc->getElementsByTagName('protocol')->[0];
print "found protocol \n";
$self->parse_class($protocol, $ac_menu, $ac_id);
}
sub parse_class() {
my ($self, $protocol, $ac_menu, $ac_id) = @_;
my $class_name;
foreach my $class ($protocol->getElementsByTagName('class')) {
$class_name = $class->getAttribute('name');
if ($class_name eq "telemetry") {
print "found telemetry class \n";
$self->parse_msg($class, $ac_menu, $ac_id);
}
}
}
sub parse_msg() {
my ($self, $class, $ac_menu, $ac_id) = @_;
my $msg_name;
foreach my $message ($class->getElementsByTagName('message')) {
$msg_name = $message->getAttribute('name');
# print "found message $msg_name \n";
my $msg_menu = $ac_menu->cascade(-label => $msg_name);
$self->build_field_commands($message, $msg_name, $msg_menu, $ac_id);
}
}
sub build_field_commands() {
my ($self, $message, $msg_name, $msg_menu, $ac_id) = @_;
my $no_field = 0;
foreach my $field ($message->getElementsByTagName('field')) {
my $field_name = $field->getAttribute('name');
my $field_unit = $field->getAttribute('unit');
# print "found field $field_name \n";
my $no_field1 = $no_field;
my $file_menu = $msg_menu->command(-label => $field_name,
-command => sub {on_plot($self, $msg_name, $field_name, $no_field1, $ac_id, $field_unit)});
$no_field++;
}
}
sub build_log_menu() {
my ($self, $mainwindow, $menubar) = @_;
my $log_menu = $menubar->cascade(-label => "~Log",);
my $new_log_menu = $log_menu->command(-label => "~Open new log",
-command => sub { on_new_log($self, $mainwindow)});
# my $add_log_menu = $log_menu->command(-label => "~Add new log",
# -command => sub { on_add_log($self, $mainwindow)});
}
sub build_gui() {
my ($self) = @_;
my $width = 700;
my $height = 300;
my $mw = MainWindow->new;
$mw->geometry(sprintf("%dx%d", $width, $height));
$mw->title("Paparazzi (gnu)plotter");
my $mb = $mw->Menu();
$self->{menubar} = $mb;
$self->build_log_menu($mw, $mb);
# my $log_menu = $mb->command(-label => "~Log",
# -command => sub { on_load($self, $mw)});
$self->build_menu_msg($self->parse_messages_xml(), $mb);
# $self->build_compiled_msg_menu($mb);
$mw->configure(-menu => $mb);
my $padx = 10;
my $filename_label = $self->add_label("filename :", \$log_filename, 0, $padx, $mw);
my $date_label = $self->add_label("date :", \$log_date, 1, $padx, $mw);
my $duration_label = $self->add_label("duration :", \$log_duration, 2, $padx, $mw);
my $time_range_label = $mw->Label( -text => "time range")->pack(-side=>'left');
$time_range_label->grid (-column=>0, -row=>3, -ipadx=>$padx);
$tr_entry = $mw->Entry(-width => 25);
$tr_entry->grid (-column=>1, -row=>3, -ipadx=>$padx);
$tr_entry->insert(0, $time_range);
my $button = $mw->Button (-text => "update",
-command=> sub { update_time_range($self)},
);
$button->grid (-column=>2, -row=>3, -ipadx=>$padx);
my $listbox = $mw->Listbox();
$listbox->grid (-column=>0, -columnspan => 3, -row=>4, -ipadx=>$padx);
$listbox->bind('<Double-1>', sub {$self->on_list_clicked($listbox, $mw)});
$self->configure('-listbox' => $listbox);
}
sub add_label() {
my ($self, $text, $text_variable, $row, $padx, $mw) = @_;
my $label1 = $mw->Label( -text => $text);
$label1->grid (-column=>0, -row=>$row, -ipadx=>$padx, -sticky => 'e' );
my $label2 = $mw->Label( -textvariable => $text_variable);
$label2->grid (-column=>1, -row=>$row, -ipadx=>$padx, -sticky => 'w');
return $label2;
}
sub on_list_clicked() {
my ($self, $listbox, $mw) = @_;
my $key = $listbox->get('active');
my $gnuplots = $self->get('-gnuplots');
my $gnuplot = $gnuplots->{$key};
my $plot_cmd = $gnuplot->{'plot_cmd'};
my $dialog;
if ($gnuplot->{'normal'}) {
$dialog = $mw->DialogBox( -title => "Plot command",
-buttons => [ "Replot", "Print", "Remove", "Add points", "Cancel" ],
); }
else {
$dialog = $mw->DialogBox( -title => "Plot command",
-buttons => [ "Replot", "Remove", "Cancel" ],
); }
$dialog->add("Label", -text => "Plot command")->pack();
# print "plot_cmd $plot_cmd\n";
my $entry = $dialog->add("Entry", -width => 150)->pack();
$entry->insert(0,$plot_cmd);
print "selected key $key\n";
my $answer = $dialog->Show();
print "selected $answer\n";
my ($new_plot_cmd, $exp);
my $timeout = 1;
if ($answer eq "Add points") {
$key =~ /([^\.]+).([^\.]+).([^\.]+).([^\.]+)/ or return;
my ($msg_name, $ac_id, $field_name, $field_pos) = ($1, $2, $3, $4);
my $rpos = $field_pos + 3;
my $last_plot_cmd = $entry->get();
my $plot_points_cmd = "\"/tmp/plot_data.$msg_name.$ac_id\" using 1:$rpos w p not";
$new_plot_cmd = $last_plot_cmd.", ".$plot_points_cmd;
$entry->configure(-text => $new_plot_cmd);
print("add points to $key \n");
$gnuplot->{'plot_cmd'} = $new_plot_cmd;
$exp = $gnuplot->{'exp'};
$exp->send($new_plot_cmd."\n");
$exp->expect($timeout);
}
if ($answer eq "Replot") {
$new_plot_cmd = $entry->get();
print("new_plot_cmd $new_plot_cmd \n");
$gnuplot->{'plot_cmd'} = $new_plot_cmd;
$exp = $gnuplot->{'exp'};
# print "exp $exp\n";
$exp->send($new_plot_cmd."\n");
$exp->expect($timeout);
}
if ($answer eq "Print") {
$self->print_plot($key, $entry);
}
if ($answer eq "Remove") {
$self->remove_plot($key);
}
}
sub update_time_range() {
my ($self) = @_;
my $gnuplots = $self->get('-gnuplots');
$time_range = $tr_entry->get();
foreach my $key (keys %{$gnuplots}) {
my $gnuplot = $gnuplots->{$key};
my $exp = $gnuplot->{'exp'};
if (defined $exp) {
print "update_range_for_key $key ($gnuplots->{$key})\n";
my $plot_cmd = $gnuplot->{'plot_cmd'};
print "plot_cmd $time_range [$plot_cmd]\n";
$plot_cmd =~ s/\[.*\]/$time_range/;
print "new_plot_cmd $plot_cmd\n\n";
$gnuplot->{'plot_cmd'} = $plot_cmd;
$exp->send($plot_cmd."\n");
my $timeout = 1;
$exp->expect($timeout);
}
}
}
sub open_log() {
my ($self, $mw) = @_;
my $fs = $mw->FileSelect(-directory => Paparazzi::Environment::paparazzi_home()."/var/logs");
$fs->geometry("600x350");
my $file_name = $fs->Show();
if (defined $file_name) {
print "file_name: $file_name\n";
$self->load_log($file_name);
}
}
sub on_plot() {
my ($self, $msg_name, $field_name, $field_pos, $ac_id, $field_unit) = @_;
print "in on_plot msg_name $msg_name, field_name $field_name, field_pos $field_pos, ac_id $ac_id, field_unit $field_unit \n";
my $key = $msg_name.".".$ac_id.".".$field_name.".".$field_pos;
$self->gen_data_file($msg_name, $ac_id);
$self->add_plot($key, $field_unit);
}
sub on_new_log() {
my ($self, $mainwindow) = @_;
my $log;
push (@{$log}, {date=>"", ac_id=>"", type=>"", args=>""});
$self->configure('-log' => $log);
$self->open_log($mainwindow);
}
sub load_log() {
my ($self, $filename) = @_;
$log_filename = $filename;
my $nb_lines = 0;
open(INFILE, $filename) or die print STDERR "Cant open $filename: $!";
my $log = $self->get('-log');
$log_date = undef;
my $ac_list;
my $line;
while ($line = <INFILE>) { # assigns each line in turn to $_
if ($line =~ /(^\d+\.\d+) (\d+) (\w+) (.+)/) {
$log_date = $1 unless defined $log_date;
my $rel_date = $1 - $log_date;
push (@{$log}, {date=>$rel_date, ac_id=>$2, type=>$3, args=>$4});
$self->{ac_list}->{name}->{$2} = 1;
# push (@{$self->{ac_list}->{$2}}, {date=>$rel_date, type=>$3, args=>$4});
$nb_lines++;
}
}
close INFILE;
$self->configure('-log' => $log);
$self->configure( '-log_start_date' => $log_date);
$log_duration = "aaa";
print STDERR "read $nb_lines lines\n";
$self->parse_conf();
}
sub parse_conf() {
my ($self) = @_;
my $filename = Paparazzi::Environment::paparazzi_src()."/conf/conf.xml";
my $parser = XML::DOM::Parser->new();
my $doc = $parser->parsefile($filename);
my ($aircraft_name, $aircraft_id);
my $aircraft_no = 0;
foreach my $aircraft ($doc->getElementsByTagName('aircraft')) {
$aircraft_name = $aircraft->getAttribute('name');
$aircraft_id = $aircraft->getAttribute('ac_id');
# print "search aircraft $aircraft_name with id $aircraft_ac_id... \n";
if (defined $self->{ac_list}->{name}->{$aircraft_id}) {
print "found aircraft $aircraft_name \n";
$self->{ac_list}->{name}->{$aircraft_id} = $aircraft_name;
$aircraft_no++;
$self->update_menu_msg($aircraft_name, $aircraft_id);
}
}
print "=> found $aircraft_no aircrafts \n";
}
sub add_plot() {
my ($self, $data_key, $field_unit) = @_;
$data_key =~ /([^\.]+).([^\.]+).([^\.]+).([^\.]+)/ or return;
my ($msg_name, $ac_id, $field_name, $field_pos) = ($1, $2, $3, $4);
my $ac_name = $self->{ac_list}->{name}->{$ac_id};
my $gnuplots = $self->get('-gnuplots');
my $exp = new Expect();
$exp->raw_pty(1);
my $rpos = $field_pos + 3;
my $nb_plots = scalar(keys(%{$gnuplots}));
my $h = $nb_plots * 260;
my $timeout = 1;
#Do not open again already open keys. Just show it.
if (defined $gnuplots->{$data_key}) {
print ("$data_key is already open \n");
$exp = $gnuplots->{$data_key}->{'exp'};
$exp->send($gnuplots->{$data_key}->{'plot_cmd'}."\n");
$exp->expect($timeout);
}
#Key is not in current list. Show it.
else {
my $plot_cmd = "plot $time_range \"/tmp/plot_data.$msg_name.$ac_id\" using 1:$rpos t \"$ac_name : $field_name ($field_unit)\" w l";
my $pid = $exp->spawn("/usr/bin/gnuplot", ("-geometry", "1350x200+0+$h", "-title", "$msg_name.$field_name"));
$pid->log_stdout(0);
my $gnuplot = { 'plot_cmd' => $plot_cmd,
'exp' => $exp,
'normal' => 1
};
$gnuplots->{$data_key} = $gnuplot;
$self->configure('-gnuplots' => $gnuplots);
$exp->send($plot_cmd."\n");
$exp->expect($timeout);
my $listbox = $self->get('-listbox');
$listbox->insert('end', "$data_key");
}
}
sub print_plot() {
my ($self, $data_key, $entry) = @_;
$data_key =~ /([^\.]+).([^\.]+).([^\.]+).([^\.]+)/ or return;
my ($msg_name, $ac_id, $field_name, $field_pos) = ($1, $2, $3, $4);
my $ac_name = $self->{ac_list}->{name}->{$ac_id};
my $set_terminal_cmd = "set terminal jpeg giant size 1350,400";
my $set_output_cmd = "set output \"$log_filename\_print/$ac_name.$msg_name.$field_name.jpg\"";
my $plot_cmd = $entry->get();
my $print_cmd = "$set_terminal_cmd; $set_output_cmd; $plot_cmd";
mkdir $log_filename."_print/";
my $gnuplots = $self->get('-gnuplots');
my $exp = new Expect();
$exp->raw_pty(1);
my $pid = $exp->spawn("/usr/bin/gnuplot", ("-geometry", "1x1+0+0")) or die "Don't find gnuplot";
$pid->log_stdout(0);
print("Printing $plot_cmd \n");
$exp->send($print_cmd."\n");
my $timeout = 1;
$exp->expect($timeout);
$exp->hard_close();
}
sub remove_plot() {
my ($self, $data_key) = @_;
my $gnuplots = $self->get('-gnuplots');
my $gnuplot = $gnuplots->{$data_key};
my $exp = $gnuplot->{'exp'};
# $exp->soft_close();
$exp->hard_close();
$gnuplots->{$data_key} = undef;
$self->configure('-gnuplots' => $gnuplots);
my $listbox = $self->get('-listbox');
print "remove $data_key\n";
my $idx = $listbox->index('active');
$listbox->delete($idx);
}
sub gen_data_file() {
my ($self, $msg_name, $ac_id) = @_;
my $nb_msgs = 0;
my $tmp_file = "/tmp/plot_data.$msg_name.$ac_id";
open(OUTFILE, ">".$tmp_file) or die "Can t open $tmp_file: $!";
foreach (@{$self->get('-log')}) {
# print "$_->{type} eq $msg_name \n";
if (($_->{type} eq $msg_name && $_->{ac_id} == $ac_id) || $_->{type} eq " ") {
print OUTFILE "$_->{date} $_->{type} $_->{args}\n";
$nb_msgs++;
}
}
close OUTFILE;
print STDERR "Number of messages $nb_msgs for $msg_name \n";
}
sub catchSigTerm() {
my ($self) = @_;
printf("in catchSigTerm\n");
my %gnuplots = $self->get('-gnuplots');
foreach my $key (keys %gnuplots) {
print ("killing $key (%gnuplots{$key}->{'pid'})\n");
$self->kill_gnuplot($key);
}
}
sub tan { sin($_[0]) / cos($_[0]) }
$SIG{TERM} = \&catchSigTerm ;
#$SIG{KILL} = \&catchSigTerm ;
my $ploter = Ploter->new();
#$ploter->load_log("../../var/log_05_08_04__12_50_09");
Tk::MainLoop();
$ploter->catchSigTerm();
printf STDOUT "ploter over\n";
1;