diff options
-rwxr-xr-x | src/ivymon | 835 |
1 files changed, 661 insertions, 174 deletions
@@ -5,13 +5,11 @@ use Tk::Font; use Ivy; use strict 'vars'; use Getopt::Long; -use vars qw/$opt_help $opt_b $opt_history/; +use vars qw/$opt_help $opt_b $opt_history @opt_bind @opt_send/; # geometrie -my $appliFrameWidth = 150; -my $appliFrameHeight = 200; -my $bindingsFrameWidth = 400; - +my $minW = 860; +my $minH = 768; my $ivy_port = "127.255.255.255:2010"; my $history = 10000; @@ -22,7 +20,23 @@ my %clientsIndex; my %bindings; my %bindingsIndex; my $selectedClient; +my %msgToSend; +my %msgToSendIndex; +my $msgToSendFlag = 0; +my $searchIndex; + +my %searchHistory; +my @searchHistory; +my $searchHistoryIndex = -1; + +my %sendHistory; +my @sendHistory; +my $sendHistoryIndex = -1; +my %bindHistory; +my @bindHistory; +my $bindHistoryIndex = -1; + my $duration = 0; my $time = 0; my $fmtduration = '00:00'; @@ -31,17 +45,47 @@ my $messagesNumber = 0; my $bindingsFlag = 0; my $stopFlag = 0; -my $mw = MainWindow->new; -$mw->geometry('850x768'); -$mw->minsize(850, 768); +my $appname = 'IvyMon'; + +my @send_def = + ('CLOCK Pause', + 'CLOCK Start', + 'CLOCK Start time=', + 'CLOCK Stop', + 'CLOCK rate=', + 'CLOCK Tick time=', + ); +my @bind_def = ('(.*)'); -if (not GetOptions('-help', '-history=s', '-b=s') or $opt_help) { - print "Usage: ivymon [options]\n"; - print "Options : \n"; - print " -b <[addr]:port> bus port(default $ivy_port)\n"; - print " -history <number> messages history length (default $history)\n"; - print " -help help message \n"; +my @send = @send_def; +my @bind = @bind_def; + +my $mw = MainWindow->new; +$mw->geometry($minW.'x'.$minH); +$mw->minsize($minW, $minH); + + +if (not GetOptions('-help', '-history=s', '-b=s', '-bind=s@', '-send=s@') or $opt_help) { + print "Usage: ivymon [-b bus] [-help] [-history length]\n"; + print " [-bind regexp1] ... [-bind regexpN] \n"; + print " [-send message1] ... [-send messageN] \n"; + print "\n"; + print "Options :\n"; + print " -b <[addr]:port> Ivy bus (default is $ivy_port)\n"; + print " -history <lenght> Messages list history length (default is $history)\n"; + print " -bind <regexp> Ivy binding regular expression\n"; + print " -send <string> Ivy message to send\n"; + print "\n"; + print "Default bindings are : '", $bind_def[0], "'\n"; + for (my $i=1; $i < @bind_def; $i++) { + print " '", $bind_def[$i], "'\n"; + } + print "Default messages are : '", $send_def[0], "'\n"; + for (my $i=1; $i < @send_def; $i++) { + print " '", $send_def[$i], "'\n"; + } + exit ; } @@ -50,10 +94,12 @@ $ivy_port = $opt_b if $opt_b and $history = $opt_history if $opt_history; $mw->title("IvyMon ($ivy_port)"); +push(@bind, @opt_bind); +push(@send, @opt_send); #---------------------------------------------------------------------------------- # La Frame de gauche prend toute la hauteur de l'appli et est composee d'une frame -# ou sont affiches les Messages et une frame ou sont saisis les Abonnements et les -# instructions de Recherche +# ou sont affiches les Messages et une frame ou sont saisis les Abonnements, les +# instructions de Recherche, et les messages a emettre #---------------------------------------------------------------------------------- my $frame1left = $mw->Frame()->pack(-fill => 'both', @@ -65,46 +111,49 @@ my $frame11messages = -expand => 1, -padx => 5, -pady => 5); my $frame12 = - $frame1left->Frame(-height => $appliFrameHeight)->pack(-fill => 'both', - -side => 'bottom', - -expand => 0); + $frame1left->Frame()->pack(-fill => 'both', + -side => 'top', + -expand => 0); my $frame121bindings = - $frame12->Frame(-height => $appliFrameHeight, - -relief => 'groove', - -borderwidth => 3, - -width => $bindingsFrameWidth)->pack(-fill => 'both', - -ipady => 20, -ipadx => 20, - -padx => 5, -pady => 5, - -side => 'left', - -expand => 0); + $frame12->Frame()->pack(-fill => 'y', + -anchor => 'w', + -padx => 5, -pady => 5, + -side => 'left', + -expand => 0); my $frame122search = - $frame12->Frame(-relief => 'groove', - -borderwidth => 3, - -height => $appliFrameHeight)->pack(-fill => 'both', - -padx => 5, -pady => 5, - -side => 'left', - -expand => 0); - - + $frame12->Frame()->pack(-fill => 'y', + -anchor => 'w', + -padx => 5, -pady => 5, + -side => 'left', + -expand => 0); + +my $frame13send = + $frame1left->Frame()->pack(-fill => 'y', + -anchor => 'w', + -padx => 5, -pady => 5, + -side => 'bottom', + -expand => 0); + #---------------------------------------------------------------------------------- # La Frame de droite prend toute la hauteur de l'appli et est composee d'une frame # ou sont affiches les Applications connectees et une frame contenant les boutons # de commande #---------------------------------------------------------------------------------- my $frame2right = - $mw->Frame(-width => $appliFrameWidth)->pack(-fill => 'both', - -expand => 0, - -side => 'right'); + $mw->Frame()->pack(-fill => 'both', + -expand => 0, + -side => 'right'); my $frame21appli = - $frame2right->Frame(-width => $appliFrameWidth)->pack(-fill => 'both', - -expand => 1, - -padx => 5, -pady => 5); + $frame2right->Frame()->pack(-fill => 'both', + -expand => 1, + -padx => 5, -pady => 5); my $frame21control = - $frame2right->Frame(-width => $appliFrameWidth, - -height => $appliFrameHeight)->pack(-fill => 'both', - -expand => 0, - -padx => 5, -pady => 5); + $frame2right->Frame(-borderwidth => 3, + -relief => 'groove')->pack(-fill => 'both', + -anchor => 's', + -expand => 0, + -padx => 5, -pady => 5); #---------------------------------------------------------------------------------- # Description de la zone Messages #---------------------------------------------------------------------------------- @@ -127,6 +176,7 @@ my $textBoldFont = $mw->fontCreate('H_Bold', my $messagesText = $frame11messages->Scrolled(Text, -scrollbars => 'e', + -height => 18, -font => 'H_Bold', -spacing1 => 2, -spacing2 => 0, @@ -139,9 +189,9 @@ $messagesText->tagConfigure('sender', -background => 'gray50', -foreground => 'gray90'); $messagesText->tagConfigure('info', -foreground => 'sienna'); +$messagesText->tagConfigure($appname, -foreground => 'gray40'); $messagesText->bind('<KeyPress>', sub { my $index = $messagesText->index('insert'); - #print "index = $index\n"; $messagesText->tagAdd('info', "$index - 1 chars", $index); }); @@ -152,90 +202,145 @@ my $clientsLabel = $frame21appli->Label(-text => "Applications :")->pack(-side => 'top', -anchor => 'w'); my $clientsListbox = - $frame21appli->Listbox()->pack(-fill => 'both', - -expand => 1, - -side => 'bottom'); + $frame21appli->Scrolled(Listbox, + -scrollbars => 's')->pack(-fill => 'both', + -expand => 1, + -side => 'top'); $clientsListbox->bind('<1>', [\&selectClient]); + #---------------------------------------------------------------------------------- # Description de la zone Abonnements #---------------------------------------------------------------------------------- +# le label titre my $bindingsLabel = $frame121bindings->Label(-text => 'Bindings : ')->pack(-side => 'top', - -anchor => 'w'); + -anchor => 'w'); +# la frame Abonnements +$frame121bindings = + $frame121bindings->Frame(-relief => 'groove', + -borderwidth => 3)->pack(-fill => 'none', + -side => 'left', + -expand => 1); +# la frame de gauche contenant le champ de saisie et la listbox +my $frame1211 = + $frame121bindings->Frame()->pack(-fill => 'none', + -padx => 10, -pady => 5, + -side => 'left', + -expand => 1); +# la frame de droite contenant les boutons de gestion de la listbox +my $frame1212 = + $frame121bindings->Frame()->pack(-fill => 'none', + -padx => 10, -pady => 5, + -ipady => 13, + -side => 'right', + -expand => 1); +# creation du champ de saisie + bindings associes my $bindingsEntry = - $frame121bindings->Entry(-width => 50)->pack(-fill => 'x', - -ipady => 3, - -expand => 0, - -padx => 5, -pady => 5); + $frame1211->Entry(-width => 40)->pack(-fill => 'x', + -side => 'top', + -anchor => 'w', + -ipady => 3, + -expand => 0, + -pady => 5); $bindingsEntry->bind('<Escape>' => [\&addBindingExpression]); $bindingsEntry->bind('<Return>' => [\&addBinding, 1]); + +$bindingsEntry->bind('<Down>' => [\&bindingNextExpression]); +$bindingsEntry->bind('<Control-n>' => [\&bindingNextExpression]); +$bindingsEntry->bind('<Up>' => [\&bindingPrevExpression]); +$bindingsEntry->bind('<Control-p>' => [\&bindingPrevExpression]); + $bindingsEntry->focus; -my $frame1211 = - $frame121bindings->Frame()->pack(-fill => 'x', - -side => 'bottom', - -expand => 1); +# creation de la liste + bindings associes my $bindingsList = - $frame1211->Listbox(-width => 40)->pack(-fill => 'y', - -side => 'left', - -expand => 1); + $frame1211->Scrolled(Listbox, + -scrollbars => 'e', + -height => 4, + -width => 40)->pack(-fill => 'y', + -side => 'bottom', + -anchor => 'w', + -expand => 1); $bindingsList->bind('<1>', [\&selectBinding]); -my $frame12111 = - $frame1211->Frame()->pack(-fill => 'y', - -side => 'right', - -expand => 1); +# creation des boutons my $bindingsClear = - $frame12111->Button(-text => 'Clear', - -command => [\&clearBinding])->pack(-fill => 'both', - -side => 'top', - -expand => 1); + $frame1212->Button(-text => 'Clear', + -height => 1, + -command => [\&clearBinding])->pack(-fill => 'both', + -side => 'top', + -expand => 1); my $bindingsAdd = - $frame12111->Button(-text => 'Add', - -command => [\&addBinding])->pack(-fill => 'both', - -side => 'top', - -expand => 1); -my $bindingsChange = - $frame12111->Button(-text => 'Change', - -state => 'disabled', - -command => [\&changeBinding])->pack(-fill => 'both', - -side => 'bottom', - -expand => 1); + $frame1212->Button(-text => 'Add', + -height => 1, + -command => [\&addBinding, 1])->pack(-fill => 'both', + -side => 'top', + -expand => 1); my $bindingsRemove = - $frame12111->Button(-text => 'Remove', - -state => 'disabled', - -command => [\&removeBinding])->pack(-fill => 'both', - -side => 'bottom', - -expand => 1); + $frame1212->Button(-text => 'Remove', + -height => 1, + -state => 'disabled', + -command => [\&removeBinding])->pack(-fill => 'both', + -side => 'top', + -expand => 1); + #---------------------------------------------------------------------------------- # Description de la zone Recherche #---------------------------------------------------------------------------------- +# le label titre my $searchLabel = $frame122search->Label(-text => 'Search : ')->pack(-side => 'top', - -anchor => 'w'); + -padx => 15, + -anchor => 'w'); +# la frame Recherche +$frame122search = + $frame122search->Frame(-relief => 'groove', + -borderwidth => 3)->pack(-fill => 'none', + -padx => 15, + -side => 'left', + -expand => 1); + +# le champ de saisie + bindings associes my $searchEntry = - $frame122search->Entry(-width => 20)->pack(-fill => 'x', + $frame122search->Entry(-width => 20)->pack(-fill => 'none', -ipady => 3, - -expand => 0, - -padx => 5, -pady => 5); + -expand => 1, + -padx => 10, -pady => 14); $searchEntry->bind('<Return>' => [\&searchNext, 1]); -$searchEntry->bind('<Shift-Return>' => [\&searchPrev, 1]); +$searchEntry->bind('<Next>' => [\&searchNext, 1]); +$searchEntry->bind('<Control-s>' => [\&searchNext, 1]); -my $frame1222 = - $frame122search->Frame()->pack(-fill => 'x', - -padx => 10, -pady => 10, - -side => 'bottom', - -expand => 0); +$searchEntry->bind('<Shift-Return>' => [\&searchPrev, 1]); +$searchEntry->bind('<Prior>' => [\&searchPrev, 1]); +$searchEntry->bind('<Control-r>' => [\&searchPrev, 1]); + +$searchEntry->bind('<Down>' => [\&searchNextExpression]); +$searchEntry->bind('<Control-n>' => [\&searchNextExpression]); +$searchEntry->bind('<Up>' => [\&searchPrevExpression]); +$searchEntry->bind('<Control-p>' => [\&searchPrevExpression]); + +# la frame comprenant les boutons associes +my $frame1220 = + $frame122search->Frame()->pack(-fill => 'both', + -side => 'top', + -expand => 0, + -padx => 10, -pady => 5); +# la frame comprenant la rangee superieure de bouton Previous et Next my $frame1221 = - $frame122search->Frame()->pack(-fill => 'x', - -padx => 10, - -side => 'bottom', - -expand => 0); + $frame1220->Frame()->pack(-fill => 'x', + -side => 'top', + -expand => 0); +# la frame comprenant la rangee inferieure de bouton Clear et All +my $frame1222 = + $frame1220->Frame()->pack(-fill => 'x', + -side => 'top', + -expand => 0); + my $searchPrev = $frame1221->Button(-text => 'Previous', - -height => 3, + -height => 1, -width => 4, -command => [\&searchPrev])->pack(-fill => 'x', -side => 'left', @@ -243,25 +348,100 @@ my $searchPrev = my $searchNext = $frame1221->Button(-text => 'Next', -width => 4, - -height => 3, + -height => 1, -command => [\&searchNext])->pack(-fill => 'x', -side => 'right', -expand => 1); my $searchClear = $frame1222->Button(-text => 'Clear', -width => 4, - -height => 3, + -height => 1, -command => [\&clearSearch])->pack(-fill => 'x', -side => 'left', -expand => 1); my $searchAll = $frame1222->Button(-text => 'All', -width => 4, - -height => 3, + -height => 1, -command => [\&searchAll])->pack(-fill => 'x', -side => 'right', -expand => 1); +#---------------------------------------------------------------------------------- +# Description de la zone Emission +#---------------------------------------------------------------------------------- +# le label titre +my $sendLabel = + $frame13send->Label(-text => 'Messages to send : ')->pack(-side => 'top', + -anchor => 'w'); +# la frame Emission +$frame13send = + $frame13send->Frame(-relief => 'groove', + -borderwidth => 3)->pack(-fill => 'none', + -side => 'left', + -expand => 1); + + +# la frame de gauche contenant le champ de saisie et la listbox +my $frame131 = + $frame13send->Frame()->pack(-fill => 'none', + -padx => 9, -pady => 5, + -side => 'left', + -expand => 1); +# la frame de droite contenant les boutons de gestion de la listbox +my $frame132 = + $frame13send->Frame()->pack(-fill => 'none', + -padx => 10, -pady => 5, + -ipady => 13, + -side => 'right', + -expand => 1); + +# creation du champ de saisie + bindings associes +my $sendEntry = + $frame131->Entry(-width => 69)->pack(-fill => 'x', + -side => 'top', + -anchor => 'w', + -ipady => 3, + -expand => 0, + -pady => 5); +$sendEntry->bind('<Return>' => [\&addMsgToSend, 1]); + +$sendEntry->bind('<Down>' => [\&sendNextExpression]); +$sendEntry->bind('<Control-n>' => [\&sendNextExpression]); +$sendEntry->bind('<Up>' => [\&sendPrevExpression]); +$sendEntry->bind('<Control-p>' => [\&sendPrevExpression]); + +# creation de la liste + bindings associes +my $sendList = + $frame131->Scrolled(Listbox, + -scrollbars => 'e', + -height => 4, + -width => 69)->pack(-fill => 'y', + -anchor => 'w', + -side => 'bottom', + -expand => 1); +$sendList->bind('<1>', [\&selectMsgToSend]); +$sendList->bind('<Double-1>', [\&addMsgToSend, 1]); +# creation des boutons +my $sendClear = + $frame132->Button(-text => 'Clear', + -height => 1, + -command => [\&clearMsgToSend])->pack(-fill => 'both', + -side => 'top', + -expand => 1); +my $sendAdd = + $frame132->Button(-text => 'Send', + -height => 1, + -command => [\&addMsgToSend, 1])->pack(-fill => 'both', + -side => 'top', + -expand => 1); +my $sendRemove = + $frame132->Button(-text => 'Remove', + -height => 1, + -state => 'disabled', + -command => [\&removeMsgToSend])->pack(-fill => 'both', + -side => 'top', + -expand => 1); #---------------------------------------------------------------------------------- # Description de a la zone Commande @@ -269,6 +449,7 @@ my $searchAll = # les champs de statistiques my $frame211control = $frame21control->Frame()->pack(-side => 'top', + -pady => 47, -fill => 'both', -expand => 1); my $frame2111control = @@ -305,19 +486,18 @@ $frame2112control->Label(-textvariable => \$bytes)->pack(-side => 'top', # les boutons my $frame212control = $frame21control->Frame()->pack(-side => 'bottom', - -pady => 8, -fill => 'both', -expand => 1); my $frame2121control = $frame212control->Frame()->pack(-side => 'left', -fill => 'both', - -ipady => 10, + -ipady => 5, -padx => 5, -pady => 5, -expand => 1); my $frame2122control = $frame212control->Frame()->pack(-side => 'right', -padx => 5, -pady => 5, - -ipady => 10, + -ipady => 5, -fill => 'both', -expand => 1); my $startButton = @@ -340,7 +520,7 @@ my $stopButton = $frame2122control->Button(-height => 3, -width => 6, -command => [\&stop, 1], - -text => 'Stop')->pack(-fill => 'both', + -text => 'Pause')->pack(-fill => 'both', -side => 'top', -expand => 0); my $exitButton = @@ -359,7 +539,7 @@ my $exitButton = #---------------------------------------------------------------------------------- Ivy->init(-loopMode => 'TK', - -appName => "IVYMON", + -appName => $appname, -ivyBus => $ivy_port, ); my $ivy = Ivy->new(-statusFunc => \&checkClientsStatus); @@ -367,9 +547,25 @@ $ivy->start; $mw->repeat(1000, \&updateDuration); -# par defaut on cree l'abonnement (.*) -$bindingsEntry->insert(0, '(.*)'); -&addBinding; +# par defaut on cree les abonnements suivants : +for my $bind (sort(@bind)) { + $bindingsEntry->delete(0, 'end'); + $bindingsEntry->insert(0, $bind); + &addBinding; +} +$bindingsEntry->delete(0, 'end'); + +# par defaut on cree les messages suivants : +for my $msg (sort(@send)) { + $sendEntry->delete(0, 'end'); + $sendEntry->insert(0, $msg); + &addMsgToSend; +} +$sendEntry->delete(0, 'end'); + +# ajout de Ivymon dans la liste des applications connectees +$connectedClients{$appname} = 1; +&addClient($appname); MainLoop; @@ -388,18 +584,6 @@ sub removeIvyBinding { $ivy->bindRegexp($binding); } -sub stopIvyBindings { - for (keys(%bindings)) { - $ivy->bindRegexp($_); - } -} - -sub startIvyBindings { - for (keys(%bindings)) { - $ivy->bindRegexp($_, [\&updateMessages]); - } -} - #================================================================================== # CALLBACK TK #================================================================================== @@ -419,7 +603,7 @@ sub checkClientsStatus { } } for (keys(%connectedClients)) { - unless ($present{$_}) { + unless ($present{$_} or $_ eq $appname) { delete $connectedClients{$_}; &removeClient($_); } @@ -459,14 +643,15 @@ sub selectClient { } $messagesText->tagConfigure($client, -background => 'gray70'); $selectedClient = $client; + } + #---------------------------------------------------------------------------------- # Fonctions associees a la gestion de l'affichage des messages #---------------------------------------------------------------------------------- sub updateMessages { my ($sender, $message) = @_; - return unless $message; - return if ($message =~ /READY$/); + return if $stopFlag or not $message; chomp($message); my $text = "$sender $message\n"; $messagesText->configure(-state => 'normal'); @@ -521,25 +706,81 @@ sub clearMessages { &start; } +sub flashMessages { + $messagesText->configure(-background => 'gray90'); + $messagesText->after(100, sub {$messagesText->configure(-background => + $messagesBg); + }); +} + #---------------------------------------------------------------------------------- # Fonctions associees a la gestion des abonnements #---------------------------------------------------------------------------------- +sub bindingNextExpression { + my $cursorIndex = $bindingsEntry->index('insert'); + $cursorIndex = -1 if $bindingsEntry->index('end') == $cursorIndex; + if ($bindHistoryIndex == -1 or $bindHistoryIndex == @bindHistory - 1 + or @bindHistory <= 1) { + &flashBindingEntry; + } else { + $bindHistoryIndex++; + $bindingsEntry->delete(0, 'end'); + $bindingsEntry->insert(0, $bindHistory[$bindHistoryIndex]); + $bindingsEntry->icursor($cursorIndex) if $cursorIndex >= 0; + } +} + +sub bindingPrevExpression { + my $cursorIndex = $bindingsEntry->index('insert'); + $cursorIndex = -1 if $bindingsEntry->index('end') == $cursorIndex; + if ($bindHistoryIndex == 0 or @bindHistory <= 1) { + &flashBindingEntry; + } else { + if ($bindHistoryIndex == -1) { + $bindHistoryIndex = @bindHistory - 2; + } else { + $bindHistoryIndex--; + } + $bindingsEntry->delete(0, 'end'); + $bindingsEntry->insert(0, $bindHistory[$bindHistoryIndex]); + $bindingsEntry->icursor($cursorIndex) if $cursorIndex >= 0; + } +} + +sub flashBindingEntry { + $bindingsEntry->configure(-background => 'gray90'); + $bindingsEntry->after(100, sub {$bindingsEntry->configure(-background => + $messagesBg); + }); +} + sub clearBinding { # on vide la le champ de saisie $bindingsEntry->delete(0, 'end'); # on enleve la selection dans la listbox $bindingsList->selectionClear(0, 'end'); - # on desactive les boutons Change et Remove - $bindingsChange->configure(-state => 'disabled'); + # on desactive le bouton Remove $bindingsRemove->configure(-state => 'disabled'); } sub addBinding { + my $bindFlag = shift; my $entry = $bindingsEntry->get; return unless $entry; + # on met a jour l'historique + &bindHistoryGenList($entry) if $bindFlag; + # on insere le message dans la liste si celui-ci n'y est pas return if $bindings{$entry}; - $bindingsList->insert('end', $entry); + my @content = $bindingsList->get(0, 'end'); + push (@content, $entry); + my $index = 0; + my $i = 0; + for (sort @content) { + $index = $i if $_ eq $entry; + $i++; + } + $bindingsList->insert($index, $entry); # on ajoute l'abonnement ivy &addIvyBinding($entry); # on remet a jour la liste des bindings @@ -549,8 +790,7 @@ sub addBinding { sub selectBinding { my $selindex = $bindingsList->curselection; my $selected = $bindingsList->get($selindex); - # on active les boutons Change et Remove - $bindingsChange->configure(-state => 'normal'); + # on active le bouton Remove $bindingsRemove->configure(-state => 'normal'); # on met a jour le champ de saisie $bindingsEntry->delete(0, 'end'); @@ -567,38 +807,13 @@ sub removeBinding { $bindingsList->delete($selindex); # on remet a jour la liste des bindings &bindingsGenList; - # on desactive les boutons Change et Remove - $bindingsChange->configure(-state => 'disabled'); + # on desactive le bouton Remove $bindingsRemove->configure(-state => 'disabled'); # on vide le champ de saisie $bindingsEntry->delete(0, 'end'); } -sub changeBinding { - my $selindex = $bindingsList->curselection; - return if $selindex eq ""; - my $selected = $bindingsList->get($selindex); - my $newbinding = $bindingsEntry->get; - return unless $newbinding; - return if $bindings{$newbinding}; - # on supprime l'abonnement ivy - &removeIvyBinding($selected); - # on ajoute le nouvel abonnement ivy - &addIvyBinding($selected); - # on enleve la selection de la liste - $bindingsList->selectionClear(0, 'end'); - # on met a jour l'item de la liste - $bindingsList->delete($selindex); - $bindingsList->insert($selindex, $newbinding); - # on remet a jour la liste des bindings - &bindingsGenList; - # on desactive les boutons Change et Remove - $bindingsChange->configure(-state => 'disabled'); - $bindingsRemove->configure(-state => 'disabled'); - -} - sub bindingsGenList { my $i = 0; my $found = 0; @@ -615,14 +830,196 @@ sub bindingsGenList { #print "bindingsFlag = $bindingsFlag\n"; } +sub bindHistoryGenList { + my $string = shift; + return if $bindHistory{$string}; + $bindHistory{$string} = 1; + push(@bindHistory, $string); + +} + +#---------------------------------------------------------------------------------- +# Fonctions associees a la gestion des envois de messages +#---------------------------------------------------------------------------------- +sub sendNextExpression { + my $cursorIndex = $sendEntry->index('insert'); + $cursorIndex = -1 if $sendEntry->index('end') == $cursorIndex; + if ($sendHistoryIndex == -1 or $sendHistoryIndex == @sendHistory - 1 + or @sendHistory <= 1) { + &flashSendEntry; + } else { + $sendHistoryIndex++; + $sendEntry->delete(0, 'end'); + $sendEntry->insert(0, $sendHistory[$sendHistoryIndex]); + $sendEntry->icursor($cursorIndex) if $cursorIndex >= 0; + } +} + +sub sendPrevExpression { + my $cursorIndex = $sendEntry->index('insert'); + $cursorIndex = -1 if $sendEntry->index('end') == $cursorIndex; + if ($sendHistoryIndex == 0 or @sendHistory <= 1) { + &flashSendEntry; + } else { + if ($sendHistoryIndex == -1) { + $sendHistoryIndex = @sendHistory - 2; + } else { + $sendHistoryIndex--; + } + $sendEntry->delete(0, 'end'); + $sendEntry->insert(0, $sendHistory[$sendHistoryIndex]); + $sendEntry->icursor($cursorIndex) if $cursorIndex >= 0; + } +} + +sub flashSendEntry { + $sendEntry->configure(-background => 'gray90'); + $sendEntry->after(100, sub {$sendEntry->configure(-background => + $messagesBg); + }); +} + +sub clearMsgToSend { + # on vide la le champ de saisie + $sendEntry->delete(0, 'end'); + # on enleve la selection dans la listbox + $sendList->selectionClear(0, 'end'); + # on desactive le bouton Remove + $sendRemove->configure(-state => 'disabled'); +} + +sub addMsgToSend { + my $sendFlag = shift; + my $entry = $sendEntry->get; + return unless $entry; + # on emet le messages + &sendMsg($entry) if $sendFlag; + # on met a jour l'historique + &sendHistoryGenList($entry) if $sendFlag; + # on insere le message dans la liste si celui-ci n'y est pas + return if $msgToSend{$entry}; + my @content = $sendList->get(0, 'end'); + push (@content, $entry); + my $index = 0; + my $i = 0; + for (sort @content) { + $index = $i if $_ eq $entry; + $i++; + } + $sendList->insert($index, $entry); + # on remet a jour la liste des bindings + &sendGenList; +} + +sub sendMsg { + my $entry = shift; + # emission sur le bus + $ivy->sendMsgs($entry); + # mise a jour de la fenetre des messages + &updateMessages($appname, $entry); + + +} +sub selectMsgToSend { + my $selindex = $sendList->curselection; + my $selected = $sendList->get($selindex); + # on active le bouton Remove + $sendRemove->configure(-state => 'normal'); + # on met a jour le champ de saisie + $sendEntry->delete(0, 'end'); + $sendEntry->insert(0, $selected); +} + +sub removeMsgToSend { + my $selindex = $sendList->curselection; + return if $selindex eq ""; + my $selected = $sendList->get($selindex); + # on enleve l'item de la liste + $sendList->delete($selindex); + # on desactive le bouton Remove + $sendRemove->configure(-state => 'disabled'); + # on vide le champ de saisie + $sendEntry->delete(0, 'end'); + +} + + +sub sendGenList { + my $i = 0; + my $found = 0; + for (keys %msgToSendIndex) { + delete $msgToSendIndex{$_}; + delete $msgToSend{$_}; + } + for ($sendList->get(0, 'end')) { + $found = 1; + $msgToSendIndex{$_} = $i++; + $msgToSend{$_} = 1 + } + $msgToSendFlag = $found; +} + +sub sendHistoryGenList { + my $string = shift; + return if $sendHistory{$string}; + $sendHistory{$string} = 1; + push(@sendHistory, $string); + +} + #---------------------------------------------------------------------------------- # Fonctions associees au panneau de recherche #---------------------------------------------------------------------------------- -my $searchIndex; +sub flashSearchEntry { + $searchEntry->configure(-background => 'gray90'); + $searchEntry->after(100, sub {$searchEntry->configure(-background => + $messagesBg); + }); +} + +sub searchNextExpression { + my $cursorIndex = $searchEntry->index('insert'); + $cursorIndex = -1 if $searchEntry->index('end') == $cursorIndex; + if ($searchHistoryIndex == -1 or $searchHistoryIndex == @searchHistory - 1 + or @searchHistory <= 1) { + &flashSearchEntry; + } else { + $searchHistoryIndex++; + $searchEntry->delete(0, 'end'); + $searchEntry->insert(0, $searchHistory[$searchHistoryIndex]); + $searchEntry->icursor($cursorIndex) if $cursorIndex >= 0; + } +} + +sub searchPrevExpression { + my $cursorIndex = $searchEntry->index('insert'); + $cursorIndex = -1 if $searchEntry->index('end') == $cursorIndex; + if ($searchHistoryIndex == 0 or @searchHistory <= 1) { + &flashSearchEntry; + } else { + if ($searchHistoryIndex == -1) { + $searchHistoryIndex = @searchHistory - 2; + } else { + $searchHistoryIndex--; + } + $searchEntry->delete(0, 'end'); + $searchEntry->insert(0, $searchHistory[$searchHistoryIndex]); + $searchEntry->icursor($cursorIndex) if $cursorIndex >= 0; + } +} + sub clearSearch { # on vide la le champ de saisie $searchEntry->delete(0, 'end'); &highlightStringOff; + $searchIndex = undef; +} + +sub searchHistoryGenList { + my $string = shift; + return if $searchHistory{$string}; + $searchHistory{$string} = 1; + push(@searchHistory, $string); } sub searchNext { @@ -631,12 +1028,18 @@ sub searchNext { return unless $string; my $index0 = ($searchIndex) ? "$searchIndex + 1 chars": '0.0'; my $index = $messagesText->search(-forwards, $string, $index0); - $searchIndex = $index; - return unless $index; + #print "index=$index searchIndex=$searchIndex\n"; &highlightStringOff; - &highlightString($index, "$index + $strlen chars"); - $messagesText->see($index); - + if ($index) { + &highlightString($index, "$index + $strlen chars"); + $messagesText->see($index); + &flashMessages if $index <= $searchIndex; + } else { + &flashMessages; + } + $searchIndex = $index; + $searchHistoryIndex = -1; + &searchHistoryGenList($string); } sub searchPrev { @@ -646,14 +1049,20 @@ sub searchPrev { #my $index0 = ($searchIndex) ? "$searchIndex - 1 chars": '0.0'; my $index0 = ($searchIndex) ? $searchIndex : '0.0'; my $index = $messagesText->search(-backwards, $string, $index0); - $searchIndex = $index; - return unless $index; &highlightStringOff; - &highlightString($index, "$index + $strlen chars"); - $messagesText->see($index); - + if ($index) { + &highlightString($index, "$index + $strlen chars"); + $messagesText->see($index); + &flashMessages if $index >= $searchIndex; + } else { + &flashMessages; + } + $searchIndex = $index; + $searchHistoryIndex = -1; + &searchHistoryGenList($string); } + sub searchAll { my $string = $searchEntry->get; my $strlen = length($string); @@ -662,12 +1071,17 @@ sub searchAll { -background => 'sienna', -foreground => 'ivory'); my $index = '0.0'; + my $found = 0; while ($index) { $index = $messagesText->search(-forwards, $string, $index, 'end'); - return unless $index; + last unless $index; $messagesText->tagAdd('found', $index, "$index + $strlen chars"); $index = "$index + 1 chars"; + $found++; } + &flashMessages unless $found; + $searchHistoryIndex = -1; + &searchHistoryGenList($string); } @@ -699,7 +1113,7 @@ sub stop { $stopFlag = 1; $startButton->configure(-state => 'normal'); $stopButton->configure(-state => 'disabled'); - &stopIvyBindings; + #&stopIvyBindings; if ($flag) { &setEditMode; my $fmttime = &formattime($time); @@ -713,7 +1127,7 @@ sub start { $stopFlag = 0; $stopButton->configure(-state => 'normal'); $startButton->configure(-state => 'disabled'); - &startIvyBindings; + #&startIvyBindings; if ($flag) { my $fmttime = &formattime($time); &printInfo("Restarted at $fmttime"); @@ -734,7 +1148,7 @@ sub formattime { my $hour = sprintf("%02d", int($duration/3600)); my $sec = $duration % 3600; my $min = sprintf("%02d", int($sec/60)); - my $sec = sprintf("%02d", $sec % 60); + $sec = sprintf("%02d", $sec % 60); if ($hour eq '00') { return "$min:$sec"; } else { @@ -742,9 +1156,6 @@ sub formattime { } } -# sub Ivy::exit { -# print "exit\n"; -# } sub setEditMode { #print "setEditMode\n"; @@ -757,3 +1168,79 @@ sub unsetEditMode { $messagesText->configure(-state => 'disabled'); $bindingsEntry->focus; } + +__END__ + +=head1 NAME + +ivymon - a graphical application for monitoring Ivy + + +=head1 SYNOPSIS + +B<ivymon> [B<-b> ivybus] [B<-help>] [B<-history> size] + [B<-bind> messageB<1>] ... [B<-bind> messageB<N>] + [B<-send> regexpB<1>] ... [B<-send> regexpB<N>] ... + +=head1 DESCRIPTION + +IvyMon is dedicated to monitor an Ivy bus. It prints out messages that match regular expressions, lets you send messages on bus, and provides a keywords search interface. + +The main area is the window labeled B<Messages> where are printed messages that forward on bus. Each message is preceded by sender application's name. + +The B<Applications> area lists the connected applications names. When you select an item in the listbox, the messages sent by this application are highlighted. + +The B<Bindings> area lets you manage regular expressions used to subscribe to ivy messages. It provides an entry field to enter new regexp, a listbox which contains an alphabetical list of effective bindings, and buttons to manage this list. To validate an entry, press the I<Add> button or the I<Return> key. To remove subscription, select the corresponding item in the listbox, and use the I<Remove> button. Pressing the I<Escape> key inserts I<(.*)> string in entry field. + +The B<Messages to send> area lets you manage a list of predefined messages ready to be sent. It provides an entry field to enter new message, a listbox which contains an alphabetical list of messages, and buttons to manage this list. When you validate an entry (by pressing the I<Send> button or the I<Return> key), the new message is added to the listbox and sent on ivy bus. To send predefined messages, simply I<double-click> on corresponding item in the listbox. + +The B<Search> area provides an interface for searching (non-regexp) pattern in messages window. It provides an entry field to enter new pattern, and control buttons. To highlight all matches, press the I<All> button. To make incremental search, press the I<Next> button, the I<Return>, I<Next> or I<Ctrl-s> keys to proceed forward, and press the I<Previous> button the I<Shift-Return>, I<Prior> or I<Ctrl-r> keys to proceed backward. + +The last area contains statistics data and general control buttons. The I<Clear> button clears out the messages window. The I<Pause> one stops the printing of messages (and their storage by the way!). When you are in this mode, the messages text area becomes editable. You are able to insert free coloured text (particular tags for example), anywhere in the messages window. Then, you can restart monitoring by pressing the I<Start> button. At last, the I<Exit> button closes the application without asking confirmation. + +=head2 ENTRIES HISTORY + +Each entry field use an history mechanism. To scroll history list, use the I<UpArrow> (or I<Ctrl-p>) and I<DownArrow> (or I<Ctrl-n>) keys. + + +=head1 OPTIONS + +=over + +=item B<-b> ip:port + +Sets the bus domain and port number to be used (default: 127.255.255.255:2010) + +=item B<-help> + +Gets some help + +=item B<-history> size + +Sets the history size of messages window (default: 10000) + +=item B<-bind> regular_expression + +Subscribes to ivy messages, by using regular expression. This option may be used several times. + +=item B<-send> ivy_message + +Adds an ivy message in the list of potential messages to send. This option may be used several times. + +=back + + +=head1 EXAMPLE + +ivymon -b 10.192.36.255:3456 -history 20000 -bind 'PLN:(.*) SectorActivation sector=(.*)' -bind 'CLOCK start' -send 'TRAFFIC RadarEnd' -send 'AIRCRAFT: Activated' + + +=head1 AUTHORS + +Daniel Etienne <etienne@cena.fr> + +=head1 ENHANCEMENT + +In pause mode, messages are currently lost. They should be stored in buffer. + +Ivymon should provides 'Save in file' and 'Load file' functions. |