/// François-Régis Colin /// http://www.tls.cena.fr/products/ivy/ /// * /// (C) CENA /// * namespace IvyProbeConsole { using System; using System.Collections; using System.Collections.Generic; using System.Threading; using System.Text.RegularExpressions; using System.IO; using System.Configuration; using System.Diagnostics; using IvyBus; using Gnu; /// Console implementation of the ivyprobe test program. /// /// Mainly used for testing and debugging purpose public class IvyProbeConsole { public virtual bool ExitOnDie { set { exitOnDie = value; } } public void BindFromFile(string name) { string line; try { FileStream file = new FileStream(name, FileMode.Open, FileAccess.Read); TextReader reader = new StreamReader(file); while ((line = reader.ReadLine()) != null) { bus.BindMsg(line, receive); } } catch (FileNotFoundException e) { Console.WriteLine(e.Message); } } public const string helpCommands = "Available commands:\n"+ ".die CLIENTNAME sends a die message\n" + ".direct CLIENTNAME ID MESSAGE sends the direct message to the client, with a message id set to the numerical ID\n" + ".bye quits the application\n" + ".quit idem\n" + ".list lists the available clients\n" + ".ping sends a ping request if IVY_PING is enabled\n" + ".bind REGEXP binds to a regexp at runtime\n" + ".unbind REGEXP unbinds to a regexp at runtime"; public const String helpmsg = "usage: IvyProbe [options] [regexp]\n" + "\t-b BUS\tspecifies the Ivy bus domain\n" + "\t-n ivyname (default JPROBE)\n" + "\t-q\tquiet, no tty output\n" + "\t-d\tdebug\n" + "\t-t\ttime stamp each message\n" + "\t-h\thelp\n\n" + "\t regexp is a Perl5 compatible regular expression"; private TextReader in_Renamed; private volatile Thread looperThread; private Ivy bus; private bool timestamp, quiet, debug, exitOnDie = false; private Regex directMsgRE; [STAThread] public static void Main(String[] args) { string name = "C#PROBE"; string fname = null; bool quiet = false; bool timestamp = false; string domain = Ivy.GetDomain(null); bool debug = false; domain = Properties.Settings.Default.domain; name = Properties.Settings.Default.name; quiet = Properties.Settings.Default.quiet; timestamp = Properties.Settings.Default.timestamp; debug = Properties.Settings.Default.IvyDebug; GetOpt opt = new GetOpt(args, "f:n:b:dqht"); Arg a; while ((a = opt.NextArg()) != null) { switch (a.Flag) { case 'd': debug = true; break; case 'b': domain = a.Parameter; break; case 'n': name = a.Parameter; break; case 'f': fname = a.Parameter; break; case 'q': quiet = true; break; case 't': timestamp = true; break; case 'h': default: System.Console.Out.WriteLine(helpmsg); System.Environment.Exit(0); break; } } IvyProbeConsole p = new IvyProbeConsole(System.Console.In, timestamp, quiet, debug ); p.ExitOnDie = true; Ivy bus = new Ivy(name, name + " ready"); p.start(bus); if (fname != null) p.BindFromFile(fname); foreach(string ex in opt.Extras) { if (!quiet) System.Console.Out.WriteLine("you want to subscribe to " + ex); bus.BindMsg(ex, p.receive); } if (!quiet) System.Console.Out.WriteLine(Ivy.Domains(domain)); bus.Start(domain); } public IvyProbeConsole(TextReader in_Renamed, bool timestamp, bool quiet, bool debug) { this.in_Renamed = in_Renamed; this.timestamp = timestamp; this.quiet = quiet; this.debug = debug; try { directMsgRE = new Regex("^\\.direct ([^ ]*) ([0-9]+) (.*)"); } catch (ArgumentException ree) { System.Console.Error.WriteLine("Regexp fatal error in the Probe program."); System.Console.Error.WriteLine(ree.StackTrace); System.Environment.Exit(0); } } public virtual void start(Ivy bus) { if (looperThread != null) throw new IvyException("Probe already started"); this.bus = bus; bus.ClientConnected += connect; bus.ClientDisconnected += disconnect; bus.DieReceived += die ; bus.DirectMessageReceived += directMessage; bus.BindingAdd += new EventHandler(bus_BindingAdd); bus.BindingRemove += new EventHandler(bus_BindingRemove); bus.BindingFilter += new EventHandler(bus_BindingFilter); looperThread = new Thread(new ThreadStart(Run)); looperThread.Name = "Keyboard Input Thread"; looperThread.Start(); } void bus_BindingFilter(object sender, IvyEventArgs e) { println("filtred regexp {0} from {1}", e.Argument, e.Client.ApplicationName); } void bus_BindingRemove(object sender, IvyEventArgs e) { println("Removed regexp {0} from {1}", e.Argument, e.Client.ApplicationName); } void bus_BindingAdd(object sender, IvyEventArgs e) { println("Added regexp {0} from {1}", e.Argument, e.Client.ApplicationName); } public void Run() { traceDebug("Thread started"); Thread thisThread = Thread.CurrentThread; String s; // "infinite" loop on keyboard input while (looperThread == thisThread) { s = in_Renamed.ReadLine(); if (s == null) break; parseCommand(s); } println("End of input. Good bye !"); bus.Stop(); traceDebug("Probe Thread stopped"); } internal void parseCommand(String s) { Match result; traceDebug("parsing the [" + s + "] (length " + s.Length + ") string"); // crude parsing of the ".xyz" commands // TODO use regexps instends of String.lastIndexOf(String). Example // provided with .direct ! if (s.Length == 0) { println("-> Sent to " + bus.SendMsg(s) + " peers"); } else if ((result = directMsgRE.Match(s)).Success) { String target = result.Groups[1].ToString(); ushort id = ushort.Parse(result.Groups[2].ToString()); String message = result.Groups[3].ToString(); List v = bus.GetClientsByName(target); if (v.Count == 0) println("no Ivy client with the name \"" + target + "\""); for (int i = 0; i < v.Count; i++) v[i].SendDirectMsg(id, message); return ; } else if (s.LastIndexOf(".die ") >= 0) { String target = s.Substring(5); if (bus.Die(target, ".die command") == 0) println("no Ivy client with the name \"" + target + "\""); } else if (s.LastIndexOf(".unbind ") >= 0) { String regexp = s.Substring(8); if (bus.UnbindMsg(regexp)) { println("you want to unsubscribe to " + regexp); } else { println("you can't unsubscribe to " + regexp + ", your're not subscribed to it"); } } else if (s.LastIndexOf(".bind ") >= 0) { String regexp = s.Substring(6); println("you want to subscribe to " + regexp); bus.BindMsg(regexp, receive); } else if (s.LastIndexOf(".ping ") >= 0) { String target = s.Substring(6); if (bus.Ping( target, "test") == 0) { println("no Ivy client with the name \"" + target + "\""); } } else if ((s.LastIndexOf(".quit") >= 0) || (s.LastIndexOf(".bye") >= 0)) { bus.Stop(); System.Environment.Exit(0); } else if (s.LastIndexOf(".list") >= 0) { println(bus.IvyClients.Count + " clients on the bus"); foreach (IvyClient client in bus.IvyClients ) { println("-> " + client.ApplicationName); } } else if (s.LastIndexOf(".help") >= 0) { println(helpCommands); } else if (s[0] == '.') { println("this command is not recognized"); println(helpCommands); } else { println("-> Sent to " + bus.SendMsg(s) + " peers"); } } // parseCommand private void connect(object sender, IvyEventArgs e) { println(e.Client.ApplicationName + " connected from "+e.Client.RemoteAddress+":"+e.Client.RemotePort); } private void disconnect(object sender, IvyEventArgs e) { println(e.Client.ApplicationName + " disconnected "); } private void die(object sender, IvyDieEventArgs e) { println("received die msg from " + e.Client.ApplicationName + " good bye cause: "+e.Argument); /* I cannot stop the readLine(), because it is native code */ if (exitOnDie) System.Environment.Exit(0); } private void directMessage(object sender, IvyEventArgs e) { println(e.Client.ApplicationName + " direct Message " + e.Id + e.Argument); } private void receive(object sender, IvyMessageEventArgs e) { String s = e.Client.ApplicationName + " sent "; // Copy the collection to a new array starting at index 0. String[] args = new String[e.Arguments.Count]; e.Arguments.CopyTo(args, 0); s += string.Join(",", args); println(s); } [Conditional("DEBUG")] private void traceDebug(String s) { Trace.WriteLineIf(debug, "-->Probe<-- " + s); } private void println(string format, params object[] args) { if (!quiet) { if (timestamp) System.Console.Out.Write(string.Format("[ {0|HH:mm:ss.fff} ]",System.DateTime.Now )); System.Console.Out.WriteLine(string.Format(format,args)); } } } }