/// 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));
}
}
}
}