diff options
Diffstat (limited to 'src/ivymon')
-rwxr-xr-x | src/ivymon | 755 |
1 files changed, 755 insertions, 0 deletions
diff --git a/src/ivymon b/src/ivymon new file mode 100755 index 0000000..e6f3ceb --- /dev/null +++ b/src/ivymon @@ -0,0 +1,755 @@ +#!/usr/bin/perl + +use Tk; +use Tk::Font; +use Ivy; +use strict 'vars'; +use Getopt::Long; +use vars qw/$opt_help $opt_b $opt_history/; + +# geometrie +my $appliFrameWidth = 150; +my $appliFrameHeight = 200; +my $bindingsFrameWidth = 400; + + +my $ivy_port = "127.255.255.255:2010"; +my $history = 10000; + +# divers +my %connectedClients; +my %clientsIndex; +my %bindings; +my %bindingsIndex; +my $selectedClient; + +my $duration = 0; +my $time = 0; +my $fmtduration = '00:00'; +my $bytes = 0 ; +my $messagesNumber = 0; +my $bindingsFlag = 0; +my $stopFlag = 0; + +my $mw = MainWindow->new; +$mw->geometry('850x768'); +$mw->minsize(850, 768); + + +if (not GetOptions('-help', '-history=s', '-b=s') or $opt_help) { + print "Usage: ivymon [options]\n"; + print "Options : \n"; + print " -s <[addr]:port> bus port(default $ivy_port)\n"; + print " -history <number> messages history length (default $history)\n"; + print " -help help message \n"; + exit ; +} + +$ivy_port = $opt_b if $opt_b and + $opt_b =~ /^(\d+\.\d+\.\d+\.\d+)?(,\d+\.\d+\.\d+\.\d+)*:\d+/; +$history = $opt_history if $opt_history; +$mw->title("IvyMon ($ivy_port)"); + +#---------------------------------------------------------------------------------- +# 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 +#---------------------------------------------------------------------------------- +my $frame1left = + $mw->Frame()->pack(-fill => 'both', + -expand => 1, + -side => 'left'); + +my $frame11messages = + $frame1left->Frame()->pack(-fill => 'both', + -expand => 1, + -padx => 5, -pady => 5); +my $frame12 = + $frame1left->Frame(-height => $appliFrameHeight)->pack(-fill => 'both', + -side => 'bottom', + -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); +my $frame122search = + $frame12->Frame(-relief => 'groove', + -borderwidth => 3, + -height => $appliFrameHeight)->pack(-fill => 'both', + -padx => 5, -pady => 5, + -side => 'left', + -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'); +my $frame21appli = + $frame2right->Frame(-width => $appliFrameWidth)->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); +#---------------------------------------------------------------------------------- +# Description de la zone Messages +#---------------------------------------------------------------------------------- +my $messagesLabel = + $frame11messages->Label(-text => "Messages :")->pack(-side => 'top', + -anchor => 'w'); + +my $textFont = $mw->fontCreate('H_Normal', + -family => 'Helvetica', + -size => 10); +my $textItalicFont = $mw->fontCreate('H_Italic', + -family => 'Helvetica', + -size => 10, + -slant => 'italic'); +my $textBoldFont = $mw->fontCreate('H_Bold', + -family => 'Helvetica', + -size => 10, + -weight => 'bold'); + +my $messagesText = + $frame11messages->Scrolled(Text, + -scrollbars => 'e', + -font => 'H_Bold', + -spacing1 => 2, + -spacing2 => 0, + -spacing3 => 2, + -state => 'disabled')->pack(-fill => 'both', + -expand => 1, + -side => 'bottom'); +my $messagesBg = $messagesText->cget(-background); +$messagesText->tagConfigure('sender', + -background => 'gray50', + -foreground => 'gray90'); +$messagesText->tagConfigure('info', -foreground => 'sienna'); +$messagesText->bind('<KeyPress>', sub { + my $index = $messagesText->index('insert'); + #print "index = $index\n"; + $messagesText->tagAdd('info', "$index - 1 chars", $index); +}); + +#---------------------------------------------------------------------------------- +# Description de la zone Clients +#---------------------------------------------------------------------------------- +my $clientsLabel = + $frame21appli->Label(-text => "Applications :")->pack(-side => 'top', + -anchor => 'w'); +my $clientsListbox = + $frame21appli->Listbox()->pack(-fill => 'both', + -expand => 1, + -side => 'bottom'); +$clientsListbox->bind('<1>', [\&selectClient]); + +#---------------------------------------------------------------------------------- +# Description de la zone Abonnements +#---------------------------------------------------------------------------------- +my $bindingsLabel = + $frame121bindings->Label(-text => 'Bindings : ')->pack(-side => 'top', + -anchor => 'w'); + +my $bindingsEntry = + $frame121bindings->Entry(-width => 50)->pack(-fill => 'x', + -ipady => 3, + -expand => 0, + -padx => 5, -pady => 5); +$bindingsEntry->bind('<Escape>' => [\&addBindingExpression]); +$bindingsEntry->bind('<Return>' => [\&addBinding, 1]); +$bindingsEntry->focus; + +my $frame1211 = + $frame121bindings->Frame()->pack(-fill => 'x', + -side => 'bottom', + -expand => 1); +my $bindingsList = + $frame1211->Listbox(-width => 40)->pack(-fill => 'y', + -side => 'left', + -expand => 1); +$bindingsList->bind('<1>', [\&selectBinding]); + +my $frame12111 = + $frame1211->Frame()->pack(-fill => 'y', + -side => 'right', + -expand => 1); +my $bindingsClear = + $frame12111->Button(-text => 'Clear', + -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); +my $bindingsRemove = + $frame12111->Button(-text => 'Remove', + -state => 'disabled', + -command => [\&removeBinding])->pack(-fill => 'both', + -side => 'bottom', + -expand => 1); + +#---------------------------------------------------------------------------------- +# Description de la zone Recherche +#---------------------------------------------------------------------------------- +my $searchLabel = + $frame122search->Label(-text => 'Search : ')->pack(-side => 'top', + -anchor => 'w'); +my $searchEntry = + $frame122search->Entry(-width => 20)->pack(-fill => 'x', + -ipady => 3, + -expand => 0, + -padx => 5, -pady => 5); +$searchEntry->bind('<Return>' => [\&searchNext, 1]); + +my $frame1222 = + $frame122search->Frame()->pack(-fill => 'x', + -padx => 10, -pady => 10, + -side => 'bottom', + -expand => 0); +my $frame1221 = + $frame122search->Frame()->pack(-fill => 'x', + -padx => 10, + -side => 'bottom', + -expand => 0); +my $searchPrev = + $frame1221->Button(-text => 'Previous', + -height => 3, + -width => 4, + -command => [\&searchPrev])->pack(-fill => 'x', + -side => 'left', + -expand => 1); +my $searchNext = + $frame1221->Button(-text => 'Next', + -width => 4, + -height => 3, + -command => [\&searchNext])->pack(-fill => 'x', + -side => 'right', + -expand => 1); +my $searchClear = + $frame1222->Button(-text => 'Clear', + -width => 4, + -height => 3, + -command => [\&clearSearch])->pack(-fill => 'x', + -side => 'left', + -expand => 1); +my $searchAll = + $frame1222->Button(-text => 'All', + -width => 4, + -height => 3, + -command => [\&searchAll])->pack(-fill => 'x', + -side => 'right', + -expand => 1); + + +#---------------------------------------------------------------------------------- +# Description de a la zone Commande +#---------------------------------------------------------------------------------- +# les champs de statistiques +my $frame211control = + $frame21control->Frame()->pack(-side => 'top', + -fill => 'both', + -expand => 1); +my $frame2111control = + $frame211control->Frame()->pack(-side => 'left', + -padx => 5, + -fill => 'both', + -expand => 1); +my $frame2112control = + $frame211control->Frame()->pack(-side => 'right', + -fill => 'both', + -expand => 1); + + +$frame2111control->Label(-text => "Duration :")->pack(-side => 'top', + -pady => 3, + -anchor => 'w', + ); +$frame2111control->Label(-text => "Messages :")->pack(-side => 'top', + -pady => 2, + -anchor => 'w'); +$frame2111control->Label(-text => "Bytes :")->pack(-side => 'top', + -pady => 3, + -anchor => 'w'); +$frame2112control->Label(-textvariable => \$fmtduration)->pack(-side => 'top', + -pady => 3, + -anchor => 'w'); +$frame2112control->Label(-textvariable => \$messagesNumber)->pack(-side => 'top', + -pady => 3, + -anchor => 'w'); +$frame2112control->Label(-textvariable => \$bytes)->pack(-side => 'top', + -pady => 3, + -anchor => 'w'); + +# 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, + -padx => 5, -pady => 5, + -expand => 1); +my $frame2122control = + $frame212control->Frame()->pack(-side => 'right', + -padx => 5, -pady => 5, + -ipady => 10, + -fill => 'both', + -expand => 1); +my $startButton = + $frame2121control->Button(-height => 3, + -width => 6, + -command => [\&start, 1], + -state => 'disabled', + -text => 'Start')->pack(-fill => 'both', + -side => 'top', + -expand => 0); +my $clearButton = + $frame2121control->Button(-height => 3, + -width => 6, + -command => [\&clearMessages], + -text => "Clear")->pack(-fill => 'both', + -side => 'bottom', + -expand => 0); + +my $stopButton = + $frame2122control->Button(-height => 3, + -width => 6, + -command => [\&stop, 1], + -text => 'Stop')->pack(-fill => 'both', + -side => 'top', + -expand => 0); +my $exitButton = + $frame2122control->Button(-height => 3, + -width => 6, + -command => [\&bye], + -text => 'Exit')->pack(-fill => 'both', + -side => 'bottom', + -expand => 0); + + + + +#---------------------------------------------------------------------------------- +# Demarrage d'Ivy et abonnements +#---------------------------------------------------------------------------------- + +Ivy->init(-loopMode => 'TK', + -appName => "IVYMON", + -ivyBus => $ivy_port, + ); +my $ivy = Ivy->new(-statusFunc => \&checkClientsStatus); +$ivy->start; + +$mw->repeat(1000, \&updateDuration); + +MainLoop; + +#================================================================================== +# FONCTIONS IVY +#================================================================================== +sub addIvyBinding { + my $binding = shift; + #print "in addIvyBinding $binding\n"; + return if $stopFlag; + $ivy->bindRegexp($binding, [\&updateMessages]); +} + +sub removeIvyBinding { + my $binding = shift; + $ivy->bindRegexp($binding); +} + +sub stopIvyBindings { + for (keys(%bindings)) { + $ivy->bindRegexp($_); + } +} + +sub startIvyBindings { + for (keys(%bindings)) { + $ivy->bindRegexp($_, [\&updateMessages]); + } +} + +#================================================================================== +# CALLBACK TK +#================================================================================== + +#---------------------------------------------------------------------------------- +# Fonctions associees a la liste des applications connectees +#---------------------------------------------------------------------------------- +sub checkClientsStatus { + my ($present, $absent) = @_; + #print "in checkClientsStatus\n"; + my %present; + for (@$present) { + $present{$_} = 1; + unless ($connectedClients{$_}) { + $connectedClients{$_} = 1; + &addClient($_); + } + } + for (keys(%connectedClients)) { + unless ($present{$_}) { + delete $connectedClients{$_}; + &removeClient($_); + } + } +} + +sub addClient { + my $client = shift; + $clientsListbox->insert('end', $client); + &clientsGenList; + +} + +sub removeClient { + my $client = shift; + my $index = $clientsIndex{$client}; + $clientsListbox->delete($index) if $index >= 0; + &clientsGenList; +} + +sub clientsGenList { + my $i = 0; + for ($clientsListbox->get(0, 'end')) { + $clientsIndex{$_} = $i++; + } +} + +sub selectClient { + $messagesText->tagConfigure($selectedClient, -background => $messagesBg) + if $selectedClient; + my $selindex = $clientsListbox->curselection; + my $client = $clientsListbox->get($selindex); + if ($selectedClient eq $client) { + $selectedClient = undef; + $clientsListbox->selectionClear($selindex); + return; + } + $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$/); + chomp($message); + my $text = "$sender $message\n"; + $messagesText->configure(-state => 'normal'); + # on teste la taille de la fenetre de messages + my ($linesNb) = split(/\./, $messagesText->index('end')); + #print "linesNb=$linesNb history=$history\n"; + if ($linesNb > $history) { + my $dl = $linesNb - $history; + $messagesText->delete('1.0', "1.0 + $dl lines"); + } + # on extrait les index de debut et de fin de l'application emettrice + my $index1 = $messagesText->index('end'); + $index1 = "$index1 - 1 lines"; + my $senderlen = length($sender); + my $index2 = "$index1 + $senderlen chars"; + $messagesText->insert('end', $text); + $messagesText->tagAdd('sender', $index1, $index2); + # on extrait les index de debut et de fin du message + my $msglen = length($message); + $index1 = "$index2 + 1 chars"; + $index2 = "$index1 + $msglen chars"; + $messagesText->tagAdd($sender, $index1, $index2); + $messagesText->configure(-state => 'disabled'); + $messagesText->see('end'); + $messagesNumber++; + $bytes += length($message); + $mw->update; +} + +sub printInfo { + my $info = shift; + $messagesText->insert('end', "<< $info >>\n"); + my $index = $messagesText->index('end'); + my $len = length($info) + 7; + my $index1 = "$index - 1 lines"; + my $index0 = "$index1 - $len chars"; + $messagesText->tagAdd('info', $index0, $index1); + $messagesText->see('end'); + $messagesText->markSet('insert', 'end'); +} + +sub addBindingExpression { + $bindingsEntry->insert('insert', '(.*)'); + $bindingsEntry->xview('end'); +} + +sub clearMessages { + &stop; + $messagesText->configure(-state => 'normal'); + $messagesText->delete('0.0', 'end'); + $messagesText->configure(-state => 'disabled'); + &start; +} + +#---------------------------------------------------------------------------------- +# Fonctions associees a la gestion des abonnements +#---------------------------------------------------------------------------------- + +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'); + $bindingsRemove->configure(-state => 'disabled'); +} + +sub addBinding { + my $entry = $bindingsEntry->get; + return unless $entry; + return if $bindings{$entry}; + $bindingsList->insert('end', $entry); + # on ajoute l'abonnement ivy + &addIvyBinding($entry); + # on remet a jour la liste des bindings + &bindingsGenList; +} + +sub selectBinding { + my $selindex = $bindingsList->curselection; + my $selected = $bindingsList->get($selindex); + # on active les boutons Change et Remove + $bindingsChange->configure(-state => 'normal'); + $bindingsRemove->configure(-state => 'normal'); + # on met a jour le champ de saisie + $bindingsEntry->delete(0, 'end'); + $bindingsEntry->insert(0, $selected); +} + +sub removeBinding { + my $selindex = $bindingsList->curselection; + return if $selindex eq ""; + my $selected = $bindingsList->get($selindex); + # on supprime l'abonnement ivy + &removeIvyBinding($selected); + # on enleve l'item de la liste + $bindingsList->delete($selindex); + # on remet a jour la liste des bindings + &bindingsGenList; + # on desactive les boutons Change et Remove + $bindingsChange->configure(-state => 'disabled'); + $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; + for (keys %bindingsIndex) { + delete $bindingsIndex{$_}; + delete $bindings{$_}; + } + for ($bindingsList->get(0, 'end')) { + $found = 1; + $bindingsIndex{$_} = $i++; + $bindings{$_} = 1 + } + $bindingsFlag = $found; + #print "bindingsFlag = $bindingsFlag\n"; +} + +#---------------------------------------------------------------------------------- +# Fonctions associees au panneau de recherche +#---------------------------------------------------------------------------------- +my $searchIndex; +sub clearSearch { + # on vide la le champ de saisie + $searchEntry->delete(0, 'end'); + &highlightStringOff; +} + +sub searchNext { + my $string = $searchEntry->get; + my $strlen = length($string); + return unless $string; + my $index0 = ($searchIndex) ? "$searchIndex + 1 chars": '0.0'; + my $index = $messagesText->search(-forwards, $string, $index0); + $searchIndex = $index; + return unless $index; + &highlightStringOff; + &highlightString($index, "$index + $strlen chars"); + $messagesText->see($index); + +} + +sub searchPrev { + my $string = $searchEntry->get; + my $strlen = length($string); + return unless $string; + #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); + +} + +sub searchAll { + my $string = $searchEntry->get; + my $strlen = length($string); + return unless $string; + $messagesText->tagConfigure('found', + -background => 'sienna', + -foreground => 'ivory'); + my $index = '0.0'; + while ($index) { + $index = $messagesText->search(-forwards, $string, $index, 'end'); + return unless $index; + $messagesText->tagAdd('found', $index, "$index + $strlen chars"); + $index = "$index + 1 chars"; + } + +} + +sub highlightString { + my ($i1, $i2) = @_; + $messagesText->tagConfigure('found', + -background => 'sienna', + -foreground => 'ivory'); + $messagesText->tagAdd('found', $i1, $i2); +} + +sub highlightStringOff { + $messagesText->tagDelete('found'); + $messagesText->tagConfigure('found', + -background => 'sienna', + -foreground => 'ivory'); +} + +#---------------------------------------------------------------------------------- +# Fonctions associees au panneau de commande +#---------------------------------------------------------------------------------- +sub bye { + #$ivy->stop; + exit; +} + +sub stop { + my $flag = shift; + $stopFlag = 1; + $startButton->configure(-state => 'normal'); + $stopButton->configure(-state => 'disabled'); + &stopIvyBindings; + if ($flag) { + &setEditMode; + my $fmttime = &formattime($time); + &printInfo("Stopped at $fmttime"); + } + +} + +sub start { + my $flag = shift; + $stopFlag = 0; + $stopButton->configure(-state => 'normal'); + $startButton->configure(-state => 'disabled'); + &startIvyBindings; + if ($flag) { + my $fmttime = &formattime($time); + &printInfo("Restarted at $fmttime"); + &unsetEditMode; + } +} + +sub updateDuration { + return unless $bindingsFlag; + $time++; + return if $stopFlag; + $duration++; + $fmtduration = &formattime($duration); +} + +sub formattime { + my $duration = shift; + my $hour = sprintf("%02d", int($duration/3600)); + my $sec = $duration % 3600; + my $min = sprintf("%02d", int($sec/60)); + my $sec = sprintf("%02d", $sec % 60); + if ($hour eq '00') { + return "$min:$sec"; + } else { + return "$hour:$min:$sec"; + } +} + +# sub Ivy::exit { +# print "exit\n"; +# } + +sub setEditMode { + #print "setEditMode\n"; + $messagesText->configure(-state => 'normal'); + $messagesText->focus; +} + +sub unsetEditMode { + #print "unsetEditMode\n"; + $messagesText->configure(-state => 'disabled'); + $bindingsEntry->focus; +} |