From f73271cbff849e59481986aebc3fafb4daeb1ae9 Mon Sep 17 00:00:00 2001 From: etienne Date: Wed, 24 Jan 2007 12:22:47 +0000 Subject: *** empty log message *** --- src/ivyreplay | 768 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 768 insertions(+) create mode 100755 src/ivyreplay (limited to 'src/ivyreplay') diff --git a/src/ivyreplay b/src/ivyreplay new file mode 100755 index 0000000..8d34b61 --- /dev/null +++ b/src/ivyreplay @@ -0,0 +1,768 @@ +#!/usr/bin/perl + + +#================================================================================= +# +# M A I N +# +#================================================================================= + +use Tk; +use Tk::Font; +use Tk::ErrorDialog; +use Tk::Dialog; +use Tk::ProgressBar; +use Time::HiRes qw(gettimeofday); +use Ivy; +use Carp; +use strict; +use Getopt::Long; +use Tk::CmdLine; +use vars qw/$opt_help $opt_b $opt_bus + $opt_size $opt_undersize $opt_autostart + $opt_repeat $opt_startregexp + $opt_stopregexp $opt_timegranularity $opt_debug/; + +# geometry +my $minW = 1024; +my $minH = 768; +my $smallsized = 0; +my $coef = 1; + +my $ivy_port; # undef:=> default value is treated by ivy-perl +my $appname = 'IvyReplay'; +$appname =~ s/ /_/g; + + +# replay +my $replay_current_t0; +my $replay_current_t; +my $replay_data_t0; +my $replay_data_t; +my $replay_maxgap = 0.2; # sec +my $replay_regulpct = [50, 20]; # % +my $replay_factor = 1000; # msec +my $replay_current_factor = $replay_factor; + +my $replay_speed = 1; +my $replay_runnable = 1; +my $replay_running = 0; +my $replay_time_granularity = 1; +my $replay_time_decimalplaces = 0; +my $replay_timer; +my $mw; +my $replay_text; +my $replay_repeat; +my %replay_msg; +my $replay_time; +my $replay_min_time; +my $replay_max_time; +my $replay_last_time; +my $replay_stepbystep; +my $replay_bg = 'gray75'; +my $replay_fg = 'black'; +my $replay_bg_orig; +my $replay_fg_orig; +my $replay_hour; + + +#---------------------------------------------------------------------------------- +# command line options management +#---------------------------------------------------------------------------------- + +Tk::CmdLine::SetArguments(-font => '7x14'); +Tk::CmdLine::SetArguments(); + +if (not GetOptions('-help', '-b=s', '-bus=s', '-size=s', '-undersize', + '-repeat', '-startregexp=s', '-debug', '-timegranularity=s', + '-autostart', '-stopregexp=s') or $opt_help) { + + print "\n"; + print "Usage: ivyreplay [-b[us] bus] [-help]\n"; + print " [-size size] [-undersize] [-autostart]\n"; + print " [-repeat] [-timegranularity float>0]\n"; + print " [-startregexp regexp] [-stopregexp regexp]\n"; + print " [standard X11 options...]\n"; + print " inputfile\n"; + print "\n"; + print "Options :\n"; + print " -b <[addr]:port> Ivy bus (\$IVYBUS by default)\n"; + print " -size Size of the main window\n"; + print " -undersize If set, slightly reduces the main window\n"; + print " -autostart If set, messages are replayed as soon as the\n"; + print " -repeat If set, repeat infinitely the replay sequence\n"; + print " -timegranularity Time granularity (in second) for replaying\n"; + print " messages (1 by default)\n"; + print " -startregexp Regexp to match for starting replay\n"; + print " ('^ClockStart\$'' by default)\n"; + print " -stopregexp Regexp to match for stopping replay\n"; + print " ('^ClockStop\$'' by default)\n"; + print "\n"; + + exit ; + +} + +croak "Error : an input file is expected\n" unless @ARGV > 0; + +$replay_repeat = $opt_repeat; +if ($opt_bus) { + $ivy_port = $opt_bus; +} elsif ($opt_b) { + $ivy_port = $opt_b; +} +my $title; +my $ivy_version = $Ivy::VERSION; +my $ivy_cvsrevision = 0; +my $ivy_versionstring = "v$ivy_version"; +if ($ivy_version =~ s/Revision: (.*)//) { + $ivy_version = $1; + $ivy_cvsrevision = 1; + $ivy_versionstring = "v$ivy_version (cvs revision)"; +} +if ($ivy_port) { + $title = "Ivyreplay ($ivy_port) - Ivy $ivy_versionstring"; +} else { + $title = "Ivyreplay (default port) - Ivy $ivy_versionstring"; +} +$replay_time_granularity = $opt_timegranularity if $opt_timegranularity > 0; +if ($replay_time_granularity >= 1) { + $replay_time_decimalplaces = 0; +} else { + $replay_time_decimalplaces = $replay_time_granularity; + $replay_time_decimalplaces =~ s/^\+?\d*\.//; + $replay_time_decimalplaces = length($replay_time_decimalplaces); +} +$replay_hour = &replayTime(); + + +#=================================== +# +# Size options + +if($opt_size eq "UXGA" || $opt_size eq "1600") { + $minW = 1600; + $minH = 1200; + $smallsized = 0; + $coef = 1.2; + Tk::CmdLine::SetArguments(-font => '10x20'); + Tk::CmdLine::SetArguments(); +} elsif ($opt_size eq "SXGA" || $opt_size eq "1280") { + $minW = 1280; + $minH = 1024; + $smallsized = 0; + $coef = 1.1; + Tk::CmdLine::SetArguments(-font => '8x13'); + Tk::CmdLine::SetArguments(); +} elsif ($opt_size eq "XGA" || $opt_size eq "1024") { + $minW = 1024; + $minH = 768; + $smallsized = 1; + $coef = 1; + Tk::CmdLine::SetArguments(-font => '7x14'); + Tk::CmdLine::SetArguments(); +} elsif ($opt_size eq "SVGA" || $opt_size eq "800") { + $minW = 800; + $minH = 600; + $smallsized = 1; + $coef = 0.64; + Tk::CmdLine::SetArguments(-font => '6x10'); + Tk::CmdLine::SetArguments(); +} elsif ($opt_size eq "VGA" || $opt_size eq "640"){ + $minW = 640; + $minH = 480; + $smallsized = 1; + $coef = 0.48; + Tk::CmdLine::SetArguments(-font => '5x7'); + Tk::CmdLine::SetArguments(); +} + +if ($opt_undersize) { + $minW -= 10; + $minH -= 30; +} + +#================================================================================= +# +# H M I +# +#================================================================================= +my $mw = MainWindow->new(); + +# Set default min size +$mw->geometry($minW."x".$minH); +$mw->minsize($minW, $minH); +$mw->title($title); + +# Key-Tab binding is deactivated, because this event will be used by entries +# for completion functionality +$mw->bind('', sub {Tk->break}); +my $focusedtext; +$mw->bind("Tk::Text", "", [sub { + &clearSearch() if $_[1] ne $focusedtext; + $focusedtext = $_[1];}, Ev('W')]); + + +#---------------------------------------------------------------------------------- +# Progress bar +#---------------------------------------------------------------------------------- +my $tpl = $mw->Toplevel; +$tpl->Popup; +$tpl->raise($mw); +$tpl->title(""); +$tpl->geometry("300x50"); +my $progressbar = + $tpl->ProgressBar(-from => 0, + -length => 200, + -borderwidth => 2, + -colors => [ 0 => 'yellow'], + -relief => 'sunken', + -resolution => 0, + -anchor => 'w', + )->pack(-fill => 'both', + -expand => 1, + ); +$progressbar->value(0); +$tpl->withdraw; + + + +#================================================================================= +# +# Ivy initialisation and Ivy bindings +# +#================================================================================= + +# init Ivy bus and start it. +Ivy->init(-loopMode => 'TK', + -appName => $appname, + -ivyBus => $ivy_port, + -onDieFunc => [\&quit], + ); +my $ivy = Ivy->new(); +$ivy->start; + + +#================================================================================= +# +# Options and arguments test +# +#================================================================================= + +&build(); + +# load input files +my $file = $ARGV[0]; + +if (not open(IN, $file)) { + $mw->Tk::Error("Can't open file '$file' ($!)"); +} else { + &showProgressbar(); + my ($step, $timefound) = &stepsnumber; + if ($timefound) { + &loadfileForReplay($step); + } else { + $mw->Tk::Error("No time information in file '$file'. ". + "Can't be replayed."); + } + &replayStart() if $opt_autostart; + &hideProgressbar(); + close(IN); +} + +MainLoop; + +#================================================================================== +# +# Functions +# +#================================================================================== + + +#---------------------------------------------------------------------------------- +# Functions related to input/output files +#---------------------------------------------------------------------------------- + +sub stepsnumber { + + my $step = 0; + my $timefound; + my $lc = 0; + + # get lines number and test if exists time field + while() { + chomp; + next if (/^applications=/ or /^(marker\d+)$/ or /^(messages_number=)/ + or /^\#/); + my ($sender, $message) = split(/\s+/, $_, 2); + if ($message =~ /^(\d[\d\.]+\d)\s+.*/) { + $timefound = 1; + } + $lc++; + } + $step = int($lc/10); + $progressbar->configure(-to => $step*10); + seek(IN, 0, 0); + return ($step, $timefound); + +} # end stepsnumber + +sub build { + + %replay_msg = (); + $replay_time = undef; + # build replay window + my $ctrl_fm = $mw->Frame()->pack(-side => 'bottom', -pady => 5); + $replay_text = $mw->Scrolled('Text', + -scrollbars => 'e', + -spacing1 => 2, + -spacing2 => 0, + -spacing3 => 2, + )->pack(-fill => 'both', + -expand => 1, + -side => 'bottom'); + # colors + $replay_bg_orig = $replay_text->cget(-background); + $replay_fg_orig = $replay_text->cget(-foreground); + &wheelmousebindings($replay_text); + # build speed control buttons + my $realspeedrate = 1; + my $ctrl_fm1 = $ctrl_fm->Frame()->pack(-side => 'left', -padx => 10); + $ctrl_fm1->Radiobutton(-text => "x0.1", -indicatoron => 0, + -height => 2, + -width => 4, + -value => 0.1, + -variable => \$replay_speed, + -command => sub { $realspeedrate = 0 }, + -selectcolor => 'white')->pack(-side => 'left'); + $ctrl_fm1->Radiobutton(-text => "x0.5", -indicatoron => 0, + -height => 2, + -width => 4, + -value => 0.5, + -variable => \$replay_speed, + -command => sub { $realspeedrate = 0 }, + -selectcolor => 'white')->pack(-side => 'left'); + $ctrl_fm1->Radiobutton(-text => "x1", -indicatoron => 0, + -height => 2, + -width => 4, + -value => 1, + -variable => \$replay_speed, + -command => sub { + return if $realspeedrate == 1; + $realspeedrate = 1; + ®ulationReset(); + }, + -selectcolor => 'white')->pack(-side => 'left'); + $ctrl_fm1->Radiobutton(-text => "x2", -indicatoron => 0, + -height => 2, + -width => 4, + -value => 2, + -variable => \$replay_speed, + -command => sub { $realspeedrate = 0 }, + -selectcolor => 'white')->pack(-side => 'left'); + $ctrl_fm1->Radiobutton(-text => "x5", -indicatoron => 0, + -height => 2, + -width => 4, + -value => 5, + -variable => \$replay_speed, + -command => sub { $realspeedrate = 0 }, + -selectcolor => 'white')->pack(-side => 'left'); + $ctrl_fm1->Radiobutton(-text => "x10", -indicatoron => 0, + -height => 2, + -width => 4, + -value => 10, + -variable => \$replay_speed, + -command => sub { $realspeedrate = 0 }, + -selectcolor => 'white')->pack(-side => 'left'); + # build hour label + my $ctrl_fm2 = $ctrl_fm->Frame()->pack(-side => 'left', -padx => 10); + my $hour_lab = $ctrl_fm2->Label(-borderwidth => 1, -relief => 'ridge', + -height => 2, + -width => ($replay_time_decimalplaces > 0) ? + 10 + $replay_time_decimalplaces : 9, + -textvariable => \$replay_hour, + )->pack(-side => 'left'); + + # build control buttons + my $ctrl_fm3 = $ctrl_fm->Frame()->pack(-side => 'left', -padx => 10); + $ctrl_fm3->Radiobutton(-text => "Play", -indicatoron => 0, + -width => 6, + -height => 2, + -value => 0, + -command => \&replayStart, + -variable => \$replay_runnable, + -selectcolor => 'white')->pack(-side => 'left'); + $ctrl_fm3->Radiobutton(-text => "Pause", -indicatoron => 0, + -width => 6, + -height => 2, + -value => 1, + -command => \&replayStop, + -variable => \$replay_runnable, + -selectcolor => 'white')->pack(-side => 'left'); + # build loop and step by step checkbuttons + my $ctrl_fm4 = + $ctrl_fm->Frame(-relief => 'ridge', -borderwidth => 1 + )->pack(-side => 'left', -padx => 10, -fill => 'y', + -expand => 1); + $ctrl_fm4->Checkbutton(-text => "Repeat", + -variable => \$replay_repeat, + )->pack(-side => 'left'); + + $ctrl_fm4->Checkbutton(-text => "Step by\nstep", + -variable => \$replay_stepbystep, + -command => \&replayStop, + )->pack(-side => 'left', -padx => 10); + + # build close button + $ctrl_fm->Button(-text => "Close", + -command => \&replayClose, + -height => 1)->pack(-side => 'left', -padx => 10, -fill => 'y', + -expand => 1); + $mw->update; + $mw->minsize($mw->width, $mw->height); + +} # end build + + +sub loadfileForReplay { + + my $step = shift; + my $line = 0; + + # display messsages to replay + my ($sender, $time, $message); + while() { + chomp; + next if /^\#/ or /^applications=/ or /^messages_number=/ + or /^\s*$/ or /^(marker\d+)$/; + ($sender, $time, $message) = split(/\s+/, $_, 3); + if ($replay_time_granularity >= 1) { + $time = int($time); + } else { + $time = sprintf("%.".$replay_time_decimalplaces."f", $time); + } + if (defined $replay_max_time) { + $replay_max_time = $time if $time > $replay_max_time; + } else { + $replay_max_time = $time; + } + if (defined $replay_min_time) { + $replay_min_time = $time if $time < $replay_min_time; + } else { + $replay_min_time = $time; + } + $line++; + $message =~ s/^\"//; + $message =~ s/\"$//; + push(@{$replay_msg{$time}}, $message); + $replay_text->insert('end', &replayTime($time)." ".$message."\n", $time); + # when user click on a message the begin time changes. + $replay_text->tagBind($time, '<1>', [sub { + my $ti = $_[1]; + my $replay_was_running; + if ($replay_running) { + $replay_was_running = 1; + &replayStop; + } + $replay_text->tagConfigure($replay_last_time, + -foreground => $replay_fg_orig, + -background => $replay_bg_orig) + if defined $replay_last_time; + $replay_text->tagConfigure($ti, + -foreground => $replay_fg, + -background => $replay_bg); + $replay_last_time = $ti; + $replay_hour = &replayTime($ti); + $replay_time = $ti; + ®ulationReset(); + &replayStart if $replay_was_running; + }, $time]); + &setProgressbar($line, $step); + } + $mw->raise; + $replay_time = $replay_min_time; + $replay_text->configure(-state => 'disabled'); + # ivy bindings + $ivy->bindRegexp($opt_startregexp, [\&replayStart]); + $ivy->bindRegexp($opt_stopregexp, [\&replayStop]); + + +} # end loadfileForReplay + + + +sub showProgressbar { + + $progressbar->value(0); + $progressbar->toplevel->deiconify; + $progressbar->toplevel->raise($mw); + +} # end showProgressbar + + +sub hideProgressbar { + + $progressbar->toplevel->withdraw; + +} # end hideProgressbar + + +sub setProgressbar { + + my ($line, $step) = @_; + $progressbar->value($line); + $progressbar->toplevel->raise($mw); + return unless defined $step; + $progressbar->update if ($step == 0 or $line % $step == 0); + +} # end setProgressbar + + +#---------------------------------------------------------------------------------- +# Functions related to replay +#---------------------------------------------------------------------------------- +sub replayStart { + + my $loopflag = ($_[0] == 1) ? 1 : undef; + $replay_data_t0 = $replay_time unless defined $replay_data_t0; + $replay_current_t0 = gettimeofday() unless defined $replay_current_t0; + my $t = $replay_text; + return if $replay_running and not $loopflag; + return if $loopflag and not $replay_running; + $replay_runnable = 0; + $replay_running = 1; + if ($replay_time > $replay_max_time) { + if ($replay_repeat == 1) { + $replay_time = $replay_min_time; + } else { + $t->tagConfigure($replay_last_time, + -foreground => $replay_fg_orig, + -background => $replay_bg_orig) + if defined $replay_last_time; + $replay_running = 0; + $replay_runnable = 1; + return; + } + } + $replay_hour = &replayTime($replay_time); + my $data_dt = $replay_time - $replay_data_t0; + if (defined $replay_msg{$replay_time}) { + for my $msg (@{$replay_msg{$replay_time}}) { + $ivy->sendMsgs($msg); + $t->tagConfigure($replay_last_time, + -foreground => $replay_fg_orig, + -background => $replay_bg_orig) + if defined $replay_last_time; + $t->tagConfigure($replay_time, + -foreground => $replay_fg, + -background => $replay_bg); + } + $replay_last_time = $replay_time; + my $i = $t->tagRanges($replay_time); + $t->see($i) if defined $i; + } + $replay_time += $replay_time_granularity; + $replay_time = sprintf("%.".$replay_time_decimalplaces."f", $replay_time) if + $replay_time_decimalplaces > 0; + # step by step mode + if ($replay_stepbystep) { + while (not defined $replay_msg{$replay_time}) { + $replay_time += $replay_time_granularity; + $replay_time = sprintf("%.".$replay_time_decimalplaces."f", $replay_time) if + $replay_time_decimalplaces > 0; + last if $replay_time > $replay_max_time; + } + $replay_running = 0; + $replay_runnable = 1; + + # continuous mode + } else { + my $dt = gettimeofday() - $replay_current_t0; + my $dt2 = sprintf("%.2f", $dt); + my $d = sprintf("%.3f", $dt-$data_dt); + + # if speed rate != 1, time regulation is deactivated + if ($replay_speed != 1) { + $replay_current_factor = $replay_factor; + print "replay_speed=$replay_speed => time regulation deactivated\n" + if $opt_debug; + + # if replay is running behind schedule (retard) + } elsif ($dt > $data_dt + $replay_maxgap and + $replay_current_factor <= $replay_factor) { + $replay_current_factor *= (1 - $replay_regulpct->[0]/100); + print "data_dt=$data_dt dt=$dt2 delay=$d [--] ". + "replay_factor=$replay_current_factor\n" if $opt_debug; + + # if replay is getting ahead of schedule (avance) + } elsif ($dt < $data_dt - $replay_maxgap and + $replay_current_factor >= $replay_factor) { + $replay_current_factor *= (1 + $replay_regulpct->[1]/100); + print "data_dt=$data_dt dt=$dt2 delay=$d [++] ". + "replay_factor=$replay_current_factor\n" if $opt_debug; + + # if replay is on time + } else { + print "data_dt=$data_dt dt=$dt2 delay=$d\n" if $opt_debug; + $replay_current_factor = $replay_factor; + } + + $replay_timer = + $mw->after($replay_current_factor*$replay_time_granularity/$replay_speed, + [\&replayStart, 1]); + } + +} # end replayStart + +sub replayStop { + + $replay_running = 0; + $replay_runnable = 1; + $replay_text->afterCancel($replay_timer) if defined $replay_timer; + ®ulationReset(); + +} # end replayStop + +sub replayClose { + + $ivy->bindRegexp($opt_startregexp); + $ivy->bindRegexp($opt_stopregexp); + &replayStop(); + $replay_text->after(400, sub {$mw->destroy}); + $replay_speed = 1; + $replay_repeat = $opt_repeat; + $replay_stepbystep = undef; + $replay_hour = '--:--:--'; + +} # end replayClose + +sub replayTime { + + my $time = shift; + if (defined $time) { + my ($s, $m, $h) = localtime($time); + if ($replay_time_decimalplaces > 0) { + my $dec = + substr($time, -$replay_time_decimalplaces, $replay_time_decimalplaces); + return sprintf("%02d:%02d:%02d.%s", $h, $m, $s, $dec); + } else { + return sprintf("%02d:%02d:%02d", $h, $m, $s); + } + } elsif ($replay_time_decimalplaces > 0) { + return "--:--:--."."-" x $replay_time_decimalplaces; + } else { + return "--:--:--"; + } + +} # end replayTime + +#---------------------------------------------------------------------------------- +# General functions +#---------------------------------------------------------------------------------- + +sub regulationReset { + + $replay_data_t0 = undef; + $replay_current_t0 = undef; + +} # end regulationReset + + +sub wheelmousebindings { + + my $w = shift; + my $count = shift; + my $count = 3 unless $count > 0; + $w->bind('', sub {$w->yview('scroll', -1, 'page')}); + $w->bind('', sub {$w->yview('scroll', -1, 'unit')}); + $w->bind('', sub {$w->yview('scroll', -$count, 'unit')}); + + $w->bind('', sub {$w->yview('scroll', 1, 'page')}); + $w->bind('', sub {$w->yview('scroll', 1, 'unit')}); + $w->bind('', sub {$w->yview('scroll', $count, 'unit')}); + +} # end wheelmousebindings + + +sub quit { + + print "Quit\n"; + exit; + +} # end quit + + +__END__ + +=head1 NAME + +ivyreplay - a graphical application for replaying Ivy messages + + +=head1 SYNOPSIS + +B [B<-b> ivybus] [B<-help>] + [B<-size> window size] [B<-undersize>] + [B<-autostart>] [B<-repeat>] + [B<-timegranularity> float>0] + [B<-startregexp> regexp] + [B<-stopregexp> regexp] + [standard X11 options...] + inputfile + +=head1 DESCRIPTION + +IvyReplay is dedicated to replay Ivy messages. Input file must have been generated with ivymon v1.6 or later (it must contain time information). + + +=head1 OPTIONS + +=over + +=item B<-b> ip:port + +Set the bus domain and port number to be used. Use $IVYBUS variable if defined. Default is 127.255.255.255:2010. + +=item B<-help> + +Get some help + + +=item B<-size> window size + +Set the size of the IvyMon window. Can be VGA or 640, SVGA or 800, XGA or 1024, SXGA or 1280, UXGA or 1600. Default is XGA (1024x768). + +=item B<-undersize> + +Slightly reduce the IvyMon window to fit it in screen with borders of the window manager. Option not set by default. + + +=item B<-repeat> + +If set, the replay sequence will be repetitive. + +=item B<-timegranularity> float>0 + +Specify the time granularity in second for replaying messages. Set to 1 by default. + +=item B<-startregexp> regexp + +Specify the ivy regular expression to match in order to start the replay sequence. Default is '^ClockStart'. You can do manually the same using the B button of the Replay window. + +=item B<-stopregexp> regexp + +Specify the ivy regular expression to match in order to stop the replay sequence. Default is '^ClockStop'. You can do manually the same using the B button of the Replay window. + + +=back + + +=head1 EXAMPLE + +ivyreplay -b 10.192.36.255:3456 -repeat -startregexp '^ReplayOn' -stopregexp '^ReplayOff' /my/dir/log.ivy + + +=head1 AUTHORS + +Daniel Etienne + -- cgit v1.1