summaryrefslogtreecommitdiff
path: root/Ivy
diff options
context:
space:
mode:
authorfcolin2007-02-01 12:04:16 +0000
committerfcolin2007-02-01 12:04:16 +0000
commit92757a8d629812303ff3665343bd098917cca611 (patch)
treecd995c9863aa6fc4c32ec5ce247e4c3119eb44a3 /Ivy
parent98ab5d0164040427f7c554febae125686284e2a7 (diff)
downloadivy-csharp-92757a8d629812303ff3665343bd098917cca611.zip
ivy-csharp-92757a8d629812303ff3665343bd098917cca611.tar.gz
ivy-csharp-92757a8d629812303ff3665343bd098917cca611.tar.bz2
ivy-csharp-92757a8d629812303ff3665343bd098917cca611.tar.xz
modification structure svn
Diffstat (limited to 'Ivy')
-rw-r--r--Ivy/Ivy.cs1183
-rw-r--r--Ivy/Ivy.csproj189
-rw-r--r--Ivy/Ivy.csproj.vspscc10
-rw-r--r--Ivy/Ivy.snippet43
-rw-r--r--Ivy/IvyApplicationBinding.cs186
-rw-r--r--Ivy/IvyBinding.cs118
-rw-r--r--Ivy/IvyBindingAttribute.cs31
-rw-r--r--Ivy/IvyClient.cs592
-rw-r--r--Ivy/IvyDomain.Designer.cs75
-rw-r--r--Ivy/IvyDomain.cs60
-rw-r--r--Ivy/IvyDomain.resx120
-rw-r--r--Ivy/IvyEventArgs.cs84
-rw-r--r--Ivy/IvyException.cs19
-rw-r--r--Ivy/IvyProtocol.cs25
-rw-r--r--Ivy/IvyTCPStream.cs20
-rw-r--r--Ivy/IvyTCPStreamV3.cs254
-rw-r--r--Ivy/IvyTCPStreamV4.cs273
-rw-r--r--Ivy/IvyUDPStream.cs66
-rw-r--r--Ivy/IvyUDPStreamV3.cs104
-rw-r--r--Ivy/IvyUDPStreamV4.cs80
-rw-r--r--Ivy/IvyWatcher.cs179
-rw-r--r--Ivy/Properties/AssemblyInfo.cs62
-rw-r--r--Ivy/Properties/Settings.Designer.cs80
-rw-r--r--Ivy/Properties/Settings.settings24
-rw-r--r--Ivy/Settings.cs28
-rw-r--r--Ivy/app.config30
26 files changed, 3935 insertions, 0 deletions
diff --git a/Ivy/Ivy.cs b/Ivy/Ivy.cs
new file mode 100644
index 0000000..1c38396
--- /dev/null
+++ b/Ivy/Ivy.cs
@@ -0,0 +1,1183 @@
+/// François-Régis Colin
+/// http://www.tls.cena.fr/products/ivy/
+/// *
+/// (C) CENA
+/// *
+///
+
+namespace IvyBus
+{
+ using System;
+ using System.IO;
+ using System.Collections;
+ using System.Collections.Specialized;
+ using System.Collections.Generic;
+ using System.Net;
+ using System.Net.Sockets;
+ using System.Threading;
+ using System.Configuration;
+ using System.Globalization;
+ using System.Text.RegularExpressions;
+ using System.Text;
+ using System.Reflection;
+ using System.ComponentModel;
+ using System.Diagnostics;
+
+#if (!PocketPC)
+ using System.ComponentModel.Design;
+#endif
+// using System.Drawing.Design;
+
+
+ /// <summary> The Main bus Class
+ /// </summary>
+ ///
+#if (!PocketPC)
+ // Show this property in the property grid.
+ [ToolboxItemFilter("System.Windows.Forms.Form", ToolboxItemFilterType.Allow)]
+ [Description("IVY Main API")]
+#endif
+ [DesignerCategory("Component")]
+ public class Ivy : System.ComponentModel.Component, ISupportInitialize
+ {
+ /* Event */
+ /// <summary>fires when a new client connect to the bus</summary>
+ public event EventHandler<IvyEventArgs> ClientConnected;
+ /// <summary>fires when a client discconnect from the bus</summary>
+ public event EventHandler<IvyEventArgs> ClientDisconnected;
+ /// <summary>fires when a client receive a direct message from another client</summary>
+ public event EventHandler<IvyEventArgs> DirectMessageReceived;
+ /// <summary>fires when somebody ask for killing every client on the bus</summary>
+ public event EventHandler<IvyDieEventArgs> DieReceived;
+ /// <summary>fires when a client receive a add binding from another client</summary>
+ public event EventHandler<IvyEventArgs> BindingAdd;
+ /// <summary>fires when a client receive a remove binding from another client</summary>
+ public event EventHandler<IvyEventArgs> BindingRemove;
+ /// <summary>fires when a client receive a binding from another client and it as been filtered </summary>
+ public event EventHandler<IvyEventArgs> BindingFilter;
+ /// <summary>fires when a client receive a remove binding from another client</summary>
+ public event EventHandler<IvyEventArgs> ErrorMessage;
+
+#if (!PocketPC)
+ [Bindable(true)]
+ [Category("Ivy")]
+#endif
+ [DefaultValue(false)]
+ public static bool DebugProtocol
+ {
+ get
+ {
+ return debugProtocol;
+ }
+ set
+ {
+ debugProtocol = value;
+ }
+
+ }
+#if (!PocketPC)
+ [Category("Ivy")]
+#endif
+ public CultureInfo Culture
+ {
+ get
+ {
+ return culture;
+ }
+ set
+ {
+ culture = value;
+ }
+
+ }
+
+ /// <summary>IvyClients accesses the list of the connected clients</summary>
+#if (!PocketPC)
+ [Browsable(false)]
+#endif
+ public List<IvyClient> IvyClients
+ {
+ get
+ {
+ return clients;
+ }
+
+ }
+
+ /// <summary>AppName the application name</summary>
+
+#if (!PocketPC)
+ [Category("Ivy")]
+ [Bindable(true)]
+#endif
+ [DefaultValue(null)]
+ public string AppName
+ {
+ set
+ {
+ appName = value;
+ }
+ get
+ {
+ return appName;
+ }
+
+ }
+ /// <summary>AppId the Application Unique ID</summary>
+
+#if (!PocketPC)
+ [Browsable(false)]
+#endif
+ public string AppId
+ {
+ get
+ {
+ return applicationUniqueId;
+ }
+
+ }
+ /// <summary>AppPriority the Application Priority: the clients list is sorted against priority</summary>
+#if (!PocketPC)
+ [Category("Ivy")]
+#endif
+ [DefaultValue(DEFAULT_PRIORITY)]
+ public ushort AppPriority
+ {
+ set
+ {
+ applicationPriority = value;
+ lock (clients)
+ {
+ foreach (IvyClient client in clients)
+ {
+ client.stream.TokenApplicationId(applicationPriority, AppId);
+ }
+ }
+
+ }
+ get
+ {
+ return applicationPriority;
+ }
+
+ }
+
+#if (!PocketPC)
+ [Browsable(false)]
+#endif
+ public int ProtocolVersion
+ {
+ get
+ {
+ return protocolVersion;
+ }
+
+ }
+
+ /// <summary>IsRunning is the bus Started and connected ?</summary>
+
+#if (!PocketPC)
+ [Browsable(false)]
+#endif
+ public bool IsRunning
+ {
+ get
+ {
+ return !stopped;
+ }
+
+ }
+ ///<summary>SentMessageClasses the first word token of sent messages
+ ///<remarks> optimise the parsing process when sending messages </remarks>
+ ///</summary>
+#if (!PocketPC)
+ [Category("Ivy")]
+
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
+ // sinon bug System.String constructor not found !
+ [Editor("System.Windows.Forms.Design.StringCollectionEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")]
+#endif
+ public List<string> SentMessageFilter
+ {
+ get
+ {
+ return sent_messageFilter;
+ }
+ }
+ /// <summary>ReadyMessage message send when Application receive all the regexp at the connection of the client</summary>
+
+#if (!PocketPC)
+ [Bindable(true)]
+ [Category("Ivy")]
+#endif
+ [DefaultValue(null)]
+ public string ReadyMessage
+ {
+ get { return ready_message; }
+ set { ready_message = value; }
+ }
+
+
+
+
+#if (PocketPC)
+#if (!PocketPC)
+ [Category("Ivy")]
+#endif
+ [DefaultValue(null)]
+ public System.Windows.Forms.ContainerControl ContainerControl
+ {
+ get { return parentControl; }
+ set
+ {
+ parentControl = value;
+ }
+ }
+#endif
+#if (!PocketPC)
+ [Category("Ivy")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
+#endif
+ public List<IvyApplicationBinding> Bindings
+ {
+ get { return app_bindings; }
+ }
+
+ internal class MyTcpListener : TcpListener
+ {
+ public MyTcpListener(IPAddress address, int port)
+ : base(address, port)
+ {
+ }
+ public bool IsActive()
+ {
+ return this.Active;
+ }
+ }
+ /// the name of the application on the bus
+ internal string appName;
+ /// the port for the UDP rendez vous, if none is supplied
+ internal const ushort DEFAULT_PORT = 2010;
+ // client default priority
+ internal const ushort DEFAULT_PRIORITY = 100;
+ /// the domain for the UDP rendez vous
+ private static readonly string DEFAULT_DOMAIN = "127.255.255.255:" + DEFAULT_PORT;
+ internal int protocolVersion = 3;
+ private static bool debugProtocol; // false by default runtime
+ private static ushort serial; /* an unique ID for each regexp */ // 0 by default runtime
+ private MyTcpListener app;
+ private List<IvyWatcher> watchers;
+ private volatile Thread serverThread; // to ensure quick communication of the end
+
+ internal Dictionary<int, IvyApplicationBinding> bindings;
+ //TODO should be remove samve as above
+ private List<IvyApplicationBinding> app_bindings;
+ private List<IvyClient> clients;
+ private List<string> sent_messageFilter;
+ private bool stopped = true;
+ internal ushort applicationPort; /* Application port number */
+ internal IPAddress applicationHost; /* Application host number */
+ internal string applicationUniqueId; /* identifier Application unique timestamp-ipaddress-port */
+ internal ushort applicationPriority = DEFAULT_PRIORITY;
+ private string ready_message;
+ private CultureInfo culture = new CultureInfo("en-us");
+ // for synchronous event
+#if (PocketPC)
+ private System.Windows.Forms.ContainerControl parentControl;
+#else
+ private readonly SynchronizationContext syncContext;
+#endif
+
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:Ivy"/> class.
+ /// </summary>
+ public Ivy()
+ {
+#if (!PocketPC)
+ syncContext = SynchronizationContext.Current;
+#endif
+ clients = new List<IvyClient>();
+ bindings = new Dictionary<int, IvyApplicationBinding>();
+ app_bindings = new List<IvyApplicationBinding>();
+ sent_messageFilter = new List<string>();
+#if (!PocketPC)
+ debugProtocol = Properties.Settings.Default.IvyDebug;
+ protocolVersion = Properties.Settings.Default.IvyProtocolVersion;
+#endif
+ // get binding from Attribute IvyBinding
+ //TODO Autobinding attribute
+#if (PocketPC)
+ if (parentControl != null)
+ BindAttibute(parentControl);
+#endif
+ Assembly assembly = Assembly.GetCallingAssembly();
+ if ( assembly != this.GetType().Assembly )
+ BindAttibute(assembly);
+ }
+ public Ivy(IContainer container)
+ : this()
+ {
+ container.Add(this);
+ // get binding from Attribute IvyBinding
+ //TODO Autobinding attribute
+ Assembly assembly = Assembly.GetCallingAssembly();
+ if (assembly != this.GetType().Assembly)
+ BindAttibute(assembly);
+ }
+ /// <summary>
+ /// Readies the structures for the software bus connexion.
+ /// </summary>
+ /// <example> This sample shows how to start working with Ivy.
+ /// <code>
+ /// Ivy bus = new Ivy("Dummy agent","ready");
+ /// bus.bindMsg("(.*)",myMessageListener);
+ /// bus.start(null);
+ /// </code>
+ /// How to send & receive:
+ /// the Ivy agent A performs <c>b.bindMsg("^Hello (*)",cb);</c>
+ /// the Ivy agent B performs <c>b2.sendMsg("Hello world");</c>
+ /// a thread in A will run the callback cb with its second argument set
+ /// to a array of string, with one single element, "world"
+ /// </example>
+ /// <remarks>
+ /// the real work begin in the start() method
+ /// </remarks>
+ /// <seealso cref=" Ivy.start"/>
+ /// <param name='name'>The name of your Ivy agent on the software bus
+ /// </param>
+ /// <param name='message'>The hellow message you will send once ready
+ /// </param>
+ public Ivy(string name, string rdy_message)
+ : this()
+ {
+ appName = name;
+ ready_message = rdy_message;
+ // get binding from Attribute IvyBinding
+ //TODO Autobinding attribute
+ Assembly assembly = Assembly.GetCallingAssembly();
+ if (assembly != this.GetType().Assembly)
+ BindAttibute(assembly);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (disposing)
+ {
+ Stop();
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+ }
+ }
+
+ // Autobinding on static method
+ public void BindAttibute(Type type)
+ {
+ if (type == null) return;
+ //Get instance of the IvyBindingAttribute.
+ foreach (MethodInfo m in type.GetMethods(BindingFlags.Static | BindingFlags.NonPublic))
+ {
+ foreach (IvyBindingAttribute attr in Attribute.GetCustomAttributes(m, typeof(IvyBindingAttribute)))
+ {
+ //TODO check paramater type MessageHandler
+ Debug.WriteLine("IvyBinding '" + attr.GetExpression(null) + "' to Method " + m.Name);
+ EventHandler<IvyMessageEventArgs> handler;
+#if (PocketPC)
+ //Createdelegate mydlg = new Createdelegate(m, null, EventArgs.Empty);
+ //bindMsg(attr.GetExpression(null), mydlg);
+ handler = null;
+#else
+ handler = (EventHandler<IvyMessageEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<IvyMessageEventArgs>), m);
+#endif
+ BindMsg(attr.GetExpression(null), handler);
+ }
+ }
+ }
+ // Autobinding on instance method
+ public void BindAttibute(object obj)
+ {
+ if (obj == null) return;
+ Type type = obj.GetType();
+ //Get instance of the IvyBindingAttribute.
+ foreach (MethodInfo m in type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic))
+ {
+ foreach (IvyBindingAttribute attr in Attribute.GetCustomAttributes(m, typeof(IvyBindingAttribute)))
+ {
+ //TODO check paramater type MessageHandler
+ Ivy.traceProtocol("Ivy", "BindAttibute " + attr.GetExpression(obj) + "' to Method " + m.Name);
+ EventHandler<IvyMessageEventArgs> handler;
+#if (PocketPC)
+ handler = null; // TODO
+#else
+ handler = (EventHandler<IvyMessageEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<IvyMessageEventArgs>), obj, m);
+#endif
+ BindMsg(attr.GetExpression(obj), handler);
+ }
+ }
+
+ }
+ // Autobinding on IvyBindingAttribute method
+ public void BindAttibute(Assembly assy)
+ {
+ foreach (Type typ in assy.GetTypes())
+ {
+ BindAttibute(typ);
+ }
+
+ }
+ /// <summary>
+ /// connects the Ivy bus to a domain or list of domains.
+ /// </summary>
+ /// <param name="domainbus">a domain of the form 10.0.0:1234, it is similar to the
+ /// netmask without the trailing .255. This will determine the meeting point
+ /// of the different applications. Right now, this is done with an UDP
+ /// broadcast. Beware of routing problems ! You can also use a comma
+ /// separated list of domains.</param>
+ /// <remarks>
+ /// One thread (IvyWatcher) for each traffic rendezvous (either UDP broadcast or TCP Multicast).
+ /// One thread (serverThread/Ivy) to accept incoming connexions on server socket.
+ /// a thread for each IvyClient when the connexion has been done.
+ /// </remarks>
+ public void Start(string domainbus)
+ {
+ domainbus = GetDomain(domainbus);
+ try
+ {
+ long seed = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
+ Random rand = new Random( (int)seed );
+
+ app = new MyTcpListener(IPAddress.Any, 0);
+ app.Start(); // should be started before getting port
+ applicationHost = GetLocalIP();
+ applicationPort = (ushort)((IPEndPoint)app.LocalEndpoint).Port;
+ applicationUniqueId = string.Format(culture,"{0}:{1}:{2}",
+ rand.Next(),
+ seed,
+ //applicationHost.ToString().Replace(".", ""),
+ applicationPort);
+
+ }
+ catch (IOException e)
+ {
+ throw new IvyException("can't open TCP service socket " + e);
+ }
+ Ivy.traceProtocol("BindAttibute", "Ivy protocol: " + ProtocolVersion + " TCP service open on port " + applicationPort);
+ watchers = new List<IvyWatcher>();
+
+ Domain[] d = parseDomains(domainbus);
+ // readies the rendezvous : an IvyWatcher (thread) per domain bus
+ for (int index = 0; index < d.Length; index++)
+ {
+ IvyWatcher watcher = new IvyWatcher(this, d[index].Domainaddr, d[index].Port);
+ watchers.Add(watcher);
+ }
+ serverThread = new Thread(new ThreadStart(this.Run));
+ serverThread.Name = "Ivy Tcp Server Thread";
+ stopped = false;
+ serverThread.Start();
+
+#if (PocketPC )
+ Ivy.traceProtocol("Ivy", "Threading start in progress...");
+ Thread.Sleep(100);
+#else
+ // Wait for readyness
+ while ( serverThread.ThreadState != System.Threading.ThreadState.Running || !app.IsActive())
+ {
+ Ivy.traceError("BindAttibute", " Ivy Threading start in progress..." );
+ Thread.Sleep( 100 );
+ }
+#endif
+ // sends the broadcasts and listen to incoming connexions
+ for (int i = 0; i < watchers.Count; i++)
+ {
+ watchers[i].start();
+ }
+ }
+ internal void SortClients()
+ {
+ lock (clients)
+ {
+ //clients.Sort(new IvyClient.IvyClientPriority());
+ clients.Sort();
+ }
+ }
+ /* a small private method for debbugging purposes */
+
+ public static string Domains(string toparse)
+ {
+ string domainbus = GetDomain(toparse);
+ StringBuilder s = new StringBuilder("broadcasting on ");
+ Ivy.Domain[] d = parseDomains(domainbus);
+ for (int index = 0; index < d.Length; index++)
+ {
+ s.Append(d[index].Domainaddr);
+ s.Append(":");
+ s.Append(d[index].Port);
+ s.Append(" ");
+ }
+ return s.ToString();
+ }
+
+ internal static Domain[] parseDomains(string domainbus)
+ {
+ string[] st = domainbus.Split(',');
+ Domain[] d = new Domain[st.Length];
+ for (int i = 0; i < st.Length; i++)
+ {
+ d[i] = new Domain(st[i]);
+ }
+ return d;
+ }
+
+ /// <summary>
+ /// disconnects from the Ivy bus
+ /// </summary>
+ public void Stop()
+ {
+ if (stopped)
+ return;
+ lock (app)
+ {
+ stopped = true;
+ Ivy.traceProtocol("Ivy", "beginning stopping the bus");
+ try
+ {
+ // stopping the serverThread
+ if (serverThread != null)
+ {
+ app.Stop();
+ // Wait for Thread to end.
+ bool term = serverThread.Join(10000);
+ if (!term && (serverThread != null)) serverThread.Abort();
+ serverThread = null;
+ }
+ // The serverThread might be stopped even before having been created
+ if (app != null)
+ app.Stop();
+ // stopping the IvyWatchers
+ if (watchers != null)
+ for (int i = 0; i < watchers.Count; i++)
+ {
+ ((IvyWatcher)watchers[i]).stop();
+ }
+ // stopping the remaining IvyClients
+ // copy the values in temporary variable to eliminate Thread modifying collection
+ if (clients.Count != 0)
+ {
+ IvyClient[] copyClient;
+ copyClient = new IvyClient[clients.Count];
+ lock (clients)
+ {
+ clients.CopyTo(copyClient, 0);
+ }
+ foreach (IvyClient client in copyClient)
+ {
+ client.close(true); // will notify the reading thread
+ //removeClient(client); already donne in the thread
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ Ivy.traceError("Ivy", "IOexception Stop " + e.Message);
+ }
+ Ivy.traceProtocol("Ivy", "the bus should have stopped so far");
+ }
+ }
+
+
+ /// <summary>
+ /// Send a formated message to someone on the bus
+ /// </summary>
+ /// <remarks>
+ /// Performs a pattern matching according to everyone's regexps, and sends
+ /// the results to the relevant ivy agents.
+ /// There is one thread for each client connected, we could also
+ /// create another thread each time we send a message.
+ /// </remarks>
+ /// <param name='format'>A string message format to build the message
+ /// <param name='args'>args used in message format
+ /// <returns>
+ /// the number of messages actually sent
+ /// </returns>
+ public int SendMsg(string format, params object[] args)
+ {
+ string msg = string.Format(culture, format, args);
+ int count = 0;
+ // an alternate implementation would one sender thread per client
+ // instead of one for all the clients. It might be a performance issue
+ lock (clients)
+ {
+ // hash message in V4 protocol only
+ if (ProtocolVersion == 4)
+ IvyBindingSimple.Prepare(msg);
+ foreach (IvyClient client in clients)
+ {
+ count += client.sendMsg(msg);
+ }
+ }
+ return count;
+ }
+ //
+ public ushort BindMsg(IvyApplicationBinding newbind)
+ {
+ newbind.Key = serial++;
+
+ lock (bindings) bindings.Add(newbind.Key, newbind);
+ // notifies the other clients this new regexp
+ lock (clients)
+ {
+ foreach (IvyClient c in clients)
+ {
+ c.stream.TokenAddBinding(newbind.Binding, newbind.Key, newbind.FormatedExpression);
+ }
+ }
+ return newbind.Key;
+ }
+ /// <summary>
+ /// Subscribes to a regular expression.
+ /// </summary>
+ /// <param name="regexp">a regular expression, groups are done with parenthesis</param>
+ /// <param name="callback">any objects implementing the Ivy.MessageListener</param>
+ /// <param name="args">The args.</param>
+ /// <returns>the id of the regular expression</returns>
+ /// <remarks>
+ /// The callback will be executed with
+ /// the saved parameters of the regexp as arguments when a message will sent
+ /// by another agent. A program doesn't receive its own messages.
+ /// </remarks>
+ //
+ public ushort BindMsg(string regexp, EventHandler<IvyMessageEventArgs> callback, params object[] args)
+ {
+ // creates a new binding (regexp,callback)
+ IvyApplicationBinding newbind;
+ newbind = new IvyApplicationBinding();
+ newbind.Binding = BindingType.Regexp;
+ newbind.Expression = regexp;
+ newbind.Callback += callback;
+ newbind.Args = args;
+ return BindMsg(newbind);
+ }
+
+ /// <summary>
+ /// unsubscribes a regular expression
+ /// </summary>
+ /// <param name="id">the id of the regular expression, returned when it was bound</param>
+ public void UnbindMsg(ushort id)
+ {
+ if (!bindings.ContainsKey(id))
+ {
+ throw new IvyException("client wants to remove an unexistant regexp " + id);
+ }
+ lock (clients)
+ {
+ foreach (IvyClient c in clients)
+ {
+ c.stream.TokenDelBinding(id);
+ }
+ }
+ lock (bindings) bindings.Remove(id);
+ }
+
+ /// <summary>
+ /// unsubscribes a regular expression
+ /// </summary>
+ /// <param name="re">the string for the regular expression</param>
+ /// <returns>
+ /// a boolean, true if the regexp existed, false otherwise or
+ /// whenever an exception occured during unbinding
+ /// </returns>
+ public bool UnbindMsg(string re)
+ {
+ foreach (IvyApplicationBinding bind in bindings.Values)
+ {
+ if (bind.Expression == re)
+ {
+ try
+ {
+ UnbindMsg(bind.Key);
+ }
+ catch (IvyException)
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /// <summary>
+ /// Subscribes to a simple expression ( msg ar1 arg2 arg3 etc).
+ /// </summary>
+ /// <remarks>
+ /// The callback will be executed with
+ /// the saved parameters of the regexp as arguments when a message will sent
+ /// by another agent. A program doesn't receive its own messages.
+ /// </remarks>
+ ///
+ /// <param name='regexp'>a regular expression, groups are done with parenthesis
+ /// <param name='callback'>any objects implementing the EventHandler<IvyMessageEventArgs>
+ /// <returns>
+ /// the id of the regular expression
+ /// </returns>
+ public int BindSimpleMsg(string expression, EventHandler<IvyMessageEventArgs> callback, params object[] args)
+ {
+ // creates a new binding (regexp,callback)
+ IvyApplicationBinding newbind;
+ newbind = new IvyApplicationBinding();
+ newbind.Binding = BindingType.Simple;
+ newbind.Expression = expression;
+ newbind.Callback += callback;
+ newbind.Args = args;
+ return BindMsg(newbind);
+ }
+ /// <summary>
+ /// Dies the specified target.
+ /// </summary>
+ /// <param name="target">The target.</param>
+ /// <param name="message">The reason message.</param>
+ /// <returns></returns>
+ public int Die(string target, string message)
+ {
+ List<IvyClient> v = GetClientsByName(target);
+ for (int i = 0; i < v.Count; i++)
+ v[i].stream.TokenDie(0, message);
+ return v.Count;
+ }
+ /// <summary>
+ /// Pings the specified target.
+ /// </summary>
+ /// <param name="target">The target.</param>
+ /// <param name="message">The message.</param>
+ /// <returns></returns>
+ public int Ping(string target, string message)
+ {
+ List<IvyClient> v = GetClientsByName(target);
+ for (int i = 0; i < v.Count; i++)
+ v[i].stream.TokenPing(message);
+ return v.Count;
+ }
+#if (PocketPC)
+ internal virtual void FireEvent(EventHandler<IvyEventArgs> ev, IvyEventArgs e)
+ {
+ if (ev != null)
+ {
+ if (parentControl != null)
+ {
+ parentControl.Invoke(ev, this, e);
+ }
+ else
+ ev(this, e);
+ }
+ }
+#else
+ internal virtual void FireEvent(EventHandler<IvyEventArgs> ev, IvyEventArgs e)
+ {
+ if (ev != null)
+ {
+ if (syncContext != null)
+ {
+ SendOrPostCallback update = delegate(object state)
+ {
+ IvyEventArgs args = (IvyEventArgs)state;
+ ev(this, args);
+ };
+ syncContext.Post(update, e);
+ }
+ else
+ ev(this, e);
+ }
+ }
+
+#endif
+ internal virtual void OnDirectMessage(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = DirectMessageReceived;
+ FireEvent(temp,e);
+ }
+ internal virtual void OnClientConnected(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = ClientConnected;
+ FireEvent(temp,e);
+ }
+ internal virtual void OnClientDisconnected(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = ClientDisconnected;
+ try
+ {
+ FireEvent(temp,e);
+ }
+#if (!PocketPC)
+ catch (SynchronizationLockException ex)
+ {
+ // protect terminaison
+ }
+#endif
+ catch (ObjectDisposedException)
+ {
+ // protect terminaison
+ }
+
+ }
+ internal virtual void OnClientAddBinding(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = BindingAdd;
+ FireEvent(temp,e);
+ }
+ internal virtual void OnClientRemoveBinding(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = BindingRemove;
+ FireEvent(temp,e);
+ }
+ internal virtual void OnClientFilterBinding(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = BindingFilter;
+ FireEvent(temp,e);
+ }
+ internal virtual void OnError(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = ErrorMessage;
+ FireEvent(temp, e);
+ }
+#if (PocketPC)
+ internal virtual void OnDie(IvyDieEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyDieEventArgs> temp = DieReceived;
+ if (temp != null)
+ {
+ if (parentControl != null)
+ {
+ parentControl.Invoke(temp, this, e);
+ }
+ else
+ temp(this, e);
+ }
+ }
+
+#else
+ internal virtual void OnDie(IvyDieEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyDieEventArgs> temp = DieReceived;
+ if (temp != null)
+ {
+ if (syncContext != null)
+ {
+ SendOrPostCallback update = delegate(object state)
+ {
+ IvyDieEventArgs args = (IvyDieEventArgs)state;
+ temp(this, args);
+ };
+ syncContext.Post(update, e);
+ }
+ else
+ temp(this, e);
+ }
+ }
+#endif
+ internal void OnMessage(IvyMessageEventArgs e)
+ {
+ IvyApplicationBinding bind = bindings[e.Id];
+
+ if (bind == null)
+ {
+ throw new IvyException("(callCallback) Not regexp matching id " + e.Id);
+ }
+#if(PocketPC)
+ bind.Firevent(parentControl, e);
+#else
+ bind.Firevent(syncContext, e);
+#endif
+ }
+
+ /*
+ * removes a client from the list
+ */
+ internal void removeClient(IvyClient c)
+ {
+ lock (clients)
+ {
+ clients.Remove(c);
+ }
+ }
+
+
+ /// <summary>
+ /// gives a list of IvyClient(s) with the name given in parameter
+ /// </summary>
+ /// <param name="name">The name of the Ivy agent you're looking for</param>
+ /// <returns></returns>
+ public List<IvyClient> GetClientsByName(string name)
+ {
+ List<IvyClient> v = new List<IvyClient>();
+ foreach (IvyClient ic in clients)
+ {
+ if (ic.ApplicationName.CompareTo(name) == 0)
+ v.Add(ic);
+ }
+ return v;
+ }
+
+ /////////////////////////////////////////////////////////////////:
+ //
+ // Protected methods
+ //
+ /////////////////////////////////////////////////////////////////:
+
+ internal void addClient(Socket socket, string appname)
+ {
+ if (stopped)
+ return;
+ IvyClient client = new IvyClient(this, socket, appname);
+ lock (clients)
+ {
+ clients.Add(client);
+ }
+ client.SendBindings();
+ }
+
+
+ public static IPAddress GetLocalIP()
+ {
+ IPAddress returnaddr = null;
+ //TODO remove ALL reverse DNS search !!!!
+ IPHostEntry ip = Dns.GetHostEntry(Dns.GetHostName());
+ foreach (IPAddress addr in ip.AddressList)
+ {
+ returnaddr = addr;
+ if (IPAddress.IsLoopback(addr)) continue;
+ break;
+ }
+ return returnaddr;
+ }
+
+ public static string GetDomain(string domainbus)
+ {
+#if (PocketPC) // TODO integrate in normal version
+ if (domainbus == null || domainbus.Length == 0)
+ {
+ IPAddress addr = GetLocalIP();
+ //TODO Find Braodcast addr from IP;
+ byte[] bytes = addr.GetAddressBytes();
+ if (IPAddress.IsLoopback(addr))
+ {
+ // 127.255.255.255
+ bytes[1] = 255;
+ bytes[2] = 255;
+ bytes[3] = 255;
+ }
+ else
+ {
+ // else assume class C network
+ // TODO find exact netmask
+ bytes[3] = 255;
+ }
+ IPAddress bcast = new IPAddress(bytes);
+ domainbus = bcast + ":" + Domain.getPort(domainbus);
+ }
+#else
+ if (domainbus == null || domainbus.Length == 0 )
+ {
+ domainbus = Environment.GetEnvironmentVariable("IVYBUS");
+ }
+
+ if (domainbus == null || domainbus.Length == 0)
+ {
+ domainbus = Properties.Settings.Default.IvyBus;
+ }
+#endif
+ if (domainbus == null || domainbus.Length == 0)
+ domainbus = DEFAULT_DOMAIN;
+ return domainbus;
+ }
+
+
+ /// checks the "validity" of a regular expression. //TODO put in IvyBinding
+ internal bool CheckRegexp(string exp)
+ {
+ bool regexp_ok = true;
+ // Attention Bug
+ // ClockStop ClockStart & ^Clock(Start|Pause)
+ // should Stop to the first parent
+ if ((sent_messageFilter.Count != 0) && exp.StartsWith("^"))
+ {
+ regexp_ok = false;
+ // extract first word from regexp...
+ string token = Regex.Replace(exp, @"^\^(?<token>[a-zA-Z_0-9-]+).*", @"${token}");
+
+ foreach (string exp_class in sent_messageFilter)
+ {
+ if (exp_class.StartsWith(token))
+ return true;
+ }
+ }
+ return regexp_ok;
+ }
+
+ /*
+ * prevents two clients from connecting to each other at the same time
+ * there might still be a lingering bug here, that we could avoid with the
+ * SchizoToken.
+ */
+ //TODO bug multiple instance Reco car en 127.0.0.1
+ internal bool checkConnected(IvyClient clnt)
+ {
+ if (clnt.AppPort == 0)
+ return false;
+ lock (clients)
+ {
+ foreach (IvyClient client in clients)
+ {
+ if (clnt != client && client.sameClient(clnt))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * the service socket thread reader main loop
+ */
+ private void Run()
+ {
+ Ivy.traceProtocol("Ivy", "Ivy service Thread started");
+ bool running = true;
+ while (running)
+ {
+ try
+ {
+ Socket socket = app.AcceptSocket();
+ if (stopped)
+ break;
+ // early disconnexion
+ addClient(socket, "Unkown(waiting for name reception)"); // the peer called me
+ }
+ catch (IOException e)
+ {
+ Ivy.traceError("Ivy","Ivy server socket reader caught an exception: " + e.Message);
+ }
+ catch (SocketException e)
+ {
+ Ivy.traceError("Ivy","my server socket has been closed " + e.Message);
+ running = false;
+ }
+ }
+ Ivy.traceProtocol("Ivy", "Ivy service Thread stopped");
+ }
+
+ [Conditional("DEBUG")]
+ internal static void traceProtocol(string name, string message)
+ {
+ if ( debugProtocol )
+ Console.WriteLine( "-->{0}<-- {1}", name, message);
+ }
+ internal static void traceError(string name, string message)
+ {
+ Console.WriteLine("-->{0}<-- {1}", name, message);
+ }
+
+ internal class Domain
+ {
+ public virtual string Domainaddr
+ {
+ get
+ {
+ return domainaddr;
+ }
+
+ }
+ public virtual int Port
+ {
+ get
+ {
+ return port;
+ }
+
+ }
+ private string domainaddr;
+ private int port;
+ public Domain(string net)
+ {
+ this.domainaddr = getDomain(net);
+ this.port = getPort(net);
+ }
+ public Domain(string domainaddr, int port)
+ {
+ this.domainaddr = domainaddr; this.port = port;
+ }
+ public override string ToString()
+ {
+ return domainaddr + ":" + port;
+ }
+ public static string getDomain(string net)
+ {
+ int sep_index = net.LastIndexOf(":");
+ if (sep_index != -1)
+ {
+ net = net.Substring(0, (sep_index) - (0));
+ }
+ try
+ {
+ net += ".255.255.255";
+ Regex exp = new Regex("^(\\d+\\.\\d+\\.\\d+\\.\\d+).*");
+ net = exp.Replace(net, "$1");
+ }
+ catch (ArgumentException e)
+ {
+ Ivy.traceError("Ivy","Bad broascat addr " + net + "error " + e.Message);
+ return null;
+ }
+ return net;
+ }
+
+ public static int getPort(string net)
+ {
+ if (net == null) return Ivy.DEFAULT_PORT;
+ int sep_index = net.LastIndexOf(":");
+ int port = (sep_index == -1) ? Ivy.DEFAULT_PORT : Int32.Parse(net.Substring(sep_index + 1));
+ return port;
+ }
+ }
+
+
+ public static bool ValidatingDomain(string p)
+ {
+ // domain if of the form ip1[:port][,ip2[:port]] with ip of the form n1.n2.n3.n4
+ return Regex.IsMatch(p, @"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+");
+ }
+
+ #region ISupportInitialize Members
+
+ public void BeginInit()
+ {
+
+ }
+
+ public void EndInit()
+ {
+ // TODO ugly should be added directly the bindings Dictionary !
+ foreach (IvyApplicationBinding bind in app_bindings)
+ {
+ BindMsg(bind);
+ }
+ }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/Ivy/Ivy.csproj b/Ivy/Ivy.csproj
new file mode 100644
index 0000000..1944b08
--- /dev/null
+++ b/Ivy/Ivy.csproj
@@ -0,0 +1,189 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <ProjectType>Local</ProjectType>
+ <ProductVersion>8.0.50727</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{F2F03CF7-0087-4EDB-AD15-80C9E8DA2617}</ProjectGuid>
+ <SccProjectName>SAK</SccProjectName>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccProvider>SAK</SccProvider>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ApplicationIcon>
+ </ApplicationIcon>
+ <AssemblyKeyContainerName>
+ </AssemblyKeyContainerName>
+ <AssemblyName>Ivy</AssemblyName>
+ <AssemblyOriginatorKeyFile>
+ </AssemblyOriginatorKeyFile>
+ <DefaultClientScript>JScript</DefaultClientScript>
+ <DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
+ <DefaultTargetSchema>IE50</DefaultTargetSchema>
+ <DelaySign>false</DelaySign>
+ <OutputType>Library</OutputType>
+ <RootNamespace>IvyBus</RootNamespace>
+ <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
+ <StartupObject>
+ </StartupObject>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <UpgradeBackupLocation>
+ </UpgradeBackupLocation>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <OutputPath>bin\Debug\</OutputPath>
+ <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
+ <BaseAddress>285212672</BaseAddress>
+ <CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
+ <ConfigurationOverrideFile>
+ </ConfigurationOverrideFile>
+ <DefineConstants>
+ </DefineConstants>
+ <DocumentationFile>
+ </DocumentationFile>
+ <DebugSymbols>true</DebugSymbols>
+ <FileAlignment>4096</FileAlignment>
+ <NoStdLib>false</NoStdLib>
+ <NoWarn>
+ </NoWarn>
+ <Optimize>false</Optimize>
+ <RegisterForComInterop>false</RegisterForComInterop>
+ <RemoveIntegerChecks>false</RemoveIntegerChecks>
+ <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
+ <WarningLevel>1</WarningLevel>
+ <DebugType>full</DebugType>
+ <ErrorReport>prompt</ErrorReport>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <OutputPath>bin\Release\</OutputPath>
+ <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
+ <BaseAddress>285212672</BaseAddress>
+ <CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
+ <ConfigurationOverrideFile>
+ </ConfigurationOverrideFile>
+ <DefineConstants>
+ </DefineConstants>
+ <DocumentationFile>
+ </DocumentationFile>
+ <DebugSymbols>false</DebugSymbols>
+ <FileAlignment>4096</FileAlignment>
+ <NoStdLib>false</NoStdLib>
+ <NoWarn>
+ </NoWarn>
+ <Optimize>false</Optimize>
+ <RegisterForComInterop>false</RegisterForComInterop>
+ <RemoveIntegerChecks>false</RemoveIntegerChecks>
+ <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
+ <WarningLevel>1</WarningLevel>
+ <DebugType>none</DebugType>
+ <ErrorReport>prompt</ErrorReport>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\x86\Debug\</OutputPath>
+ <BaseAddress>285212672</BaseAddress>
+ <WarningLevel>1</WarningLevel>
+ <DebugType>full</DebugType>
+ <PlatformTarget>x86</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <DefineConstants>
+ </DefineConstants>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <OutputPath>bin\x86\Release\</OutputPath>
+ <BaseAddress>285212672</BaseAddress>
+ <WarningLevel>1</WarningLevel>
+ <DebugType>
+ </DebugType>
+ <PlatformTarget>x86</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="mscorlib">
+ <Name>mscorlib</Name>
+ </Reference>
+ <Reference Include="System">
+ <Name>System</Name>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Design">
+ <Name>System.Design</Name>
+ </Reference>
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Management">
+ <Name>System.Management</Name>
+ </Reference>
+ <Reference Include="System.Windows.Forms">
+ <Name>System.Windows.Forms</Name>
+ </Reference>
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="IvyApplicationBinding.cs">
+ <SubType>Component</SubType>
+ </Compile>
+ <Compile Include="IvyBindingAttribute.cs" />
+ <Compile Include="IvyDomain.cs">
+ <SubType>UserControl</SubType>
+ </Compile>
+ <Compile Include="IvyDomain.Designer.cs">
+ <DependentUpon>IvyDomain.cs</DependentUpon>
+ </Compile>
+ <Compile Include="IvyEventArgs.cs" />
+ <Compile Include="IvyProtocol.cs" />
+ <Compile Include="IvyTCPStreamV3.cs" />
+ <Compile Include="IvyUDPStream.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="Ivy.cs">
+ <SubType>Component</SubType>
+ </Compile>
+ <Compile Include="IvyBinding.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="IvyClient.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="IvyException.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="IvyTCPStreamV4.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="IvyUDPStreamV3.cs" />
+ <Compile Include="IvyUDPStreamV4.cs" />
+ <Compile Include="IvyWatcher.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="Properties\Settings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTimeSharedInput>True</DesignTimeSharedInput>
+ <DependentUpon>Settings.settings</DependentUpon>
+ </Compile>
+ <Compile Include="Settings.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="app.config" />
+ <None Include="Ivy.snippet" />
+ <None Include="Properties\Settings.settings">
+ <Generator>SettingsSingleFileGenerator</Generator>
+ <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="IvyDomain.resx">
+ <SubType>Designer</SubType>
+ <DependentUpon>IvyDomain.cs</DependentUpon>
+ </EmbeddedResource>
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <PropertyGroup>
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <PostBuildEvent>
+ </PostBuildEvent>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/Ivy/Ivy.csproj.vspscc b/Ivy/Ivy.csproj.vspscc
new file mode 100644
index 0000000..831ab8f
--- /dev/null
+++ b/Ivy/Ivy.csproj.vspscc
@@ -0,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = "relative:Ivy"
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
+}
diff --git a/Ivy/Ivy.snippet b/Ivy/Ivy.snippet
new file mode 100644
index 0000000..5319177
--- /dev/null
+++ b/Ivy/Ivy.snippet
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<CodeSnippets
+ xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
+ <CodeSnippet Format="1.0.0">
+ <Header>
+ <Title>
+ Ivy Message Callback
+ </Title>
+ </Header>
+ <Snippet>
+ <References>
+ <Reference>
+ <Assembly>Ivy.dll</Assembly>
+ </Reference>
+ </References>
+ <Imports>
+ <Import>
+ <Namespace>IvyBus</Namespace>
+ </Import>
+ </Imports>
+ <Declarations>
+ <Literal>
+ <ID>CallbackName</ID>
+ <ToolTip>Replace with a function name.</ToolTip>
+ <Default>bus_receive</Default>
+ </Literal>
+ <Literal>
+ <ID>Expression</ID>
+ <ToolTip>Replace with a regular expression.</ToolTip>
+ <Default>"^(.*)"</Default>
+ </Literal>
+ </Declarations>
+ <Code Language="CSharp">
+ <![CDATA[
+ [IvyBinding($Expression$)]
+ private void $CallbackName$(object sender, IvyMessageEventArgs e)
+ {
+ }
+ ]]>
+ </Code>
+ </Snippet>
+ </CodeSnippet>
+</CodeSnippets>
diff --git a/Ivy/IvyApplicationBinding.cs b/Ivy/IvyApplicationBinding.cs
new file mode 100644
index 0000000..9183850
--- /dev/null
+++ b/Ivy/IvyApplicationBinding.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Text;
+using System.ComponentModel;
+using System.Threading;
+
+namespace IvyBus
+{
+ /* This is the Application side of binding storage */
+ /* association of a generated Key and a delegate and the expression */
+ /* this is SEND to other client */
+#if (!PocketPC)
+ [PropertyTab(typeof(System.Windows.Forms.Design.EventsTab), PropertyTabScope.Component)]
+ [DefaultEvent("Callback")]
+#endif
+ [DesignerCategory("Component")]
+ [DesignTimeVisible(false)] /* should be added via Ivy component */
+ public class IvyApplicationBinding : System.ComponentModel.Component
+ {
+ private BindingType binding;
+
+#if (!PocketPC)
+ [Category("Ivy")]
+#endif
+ public BindingType Binding
+ {
+ get { return binding; }
+ set { binding = value; }
+ }
+ private ushort key;
+
+ #if (!PocketPC)
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public ushort Key
+ {
+ get { return key; }
+ set { key = value; }
+ }
+
+ private object[] args;
+#if (!PocketPC)
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public object[] Args
+ {
+ get { return args; }
+ set { args = value; }
+ }
+ private string expression;
+#if (!PocketPC)
+ [Category("Ivy")]
+#endif
+ [DefaultValue(null)]
+ public string Expression
+ {
+ get { return expression; }
+ set { expression = value; }
+ }
+ private string formated_expression;
+ public string FormatedExpression
+ {
+ get
+ {
+ FormatExpression();
+ return formated_expression;
+ }
+ }
+
+ private List<string> arguments;
+ ///<summary>SentMessageClasses the first word token of sent messages
+ ///<remarks> optimise the parsing process when sending messages </remarks>
+ ///</summary>
+#if (!PocketPC)
+ [Category("Ivy")]
+
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
+
+ // sinon bug System.String constructor not found !
+ [Editor(
+ "System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
+ "System.Drawing.Design.UITypeEditor,System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
+ )]
+
+ [Description("Arguments used when formating the expression")]
+#endif
+ public List<string> Arguments
+ {
+ get
+ {
+ return arguments;
+ }
+ }
+
+#if (!PocketPC)
+ [Category("Ivy")]
+ [Description("Event fired when Message Matching expression received")]
+#endif
+ public event EventHandler<IvyMessageEventArgs> Callback;
+
+ public IvyApplicationBinding()
+ {
+ arguments = new List<string>();
+ }
+ public IvyApplicationBinding(IContainer container)
+ : this()
+ {
+ container.Add(this);
+ }
+ // translate part of expression to object property
+ public void FormatExpression()
+ {
+ //// Safely :
+#if (!PocketPC)//TODO Pocket PC doesn't have Target Member
+ EventHandler<IvyMessageEventArgs> temp = Callback;
+ if (temp != null)
+ {
+ //TODO Pocket PC doesn't have Target Member
+ object target = temp.Target;
+ if (args == null)
+ {
+ args = new object[arguments.Count];
+ for (int i = 0; i < arguments.Count; i++)
+ {
+ System.Reflection.PropertyInfo prop = target.GetType().GetProperty(arguments[i]);
+ if (prop != null)
+ args[i] = prop.GetValue(target, null);
+ else //TODO what else BUG msgbox in desing mode !!!
+ args[i] = arguments[i];
+ }
+ }
+ formated_expression = string.Format(expression, args);
+ }
+ else //TODO Abnormal condition Design Time
+#endif
+ formated_expression = expression;
+
+ }
+
+#if ( PocketPC )
+ internal void Firevent(System.Windows.Forms.Control control, IvyMessageEventArgs e)
+ {
+ //// Safely invoke an event:
+ EventHandler<IvyMessageEventArgs> temp = Callback;
+
+ if (temp == null)
+ {
+ throw new IvyException("(callCallback) Not callback for id " + e.Id);
+ }
+ if (control != null)
+ {
+ control.Invoke(temp, this, e);
+ }
+ else
+ temp(this, e);
+ }
+#else
+ internal void Firevent(System.Threading.SynchronizationContext syncContext, IvyMessageEventArgs e)
+ {
+ //// Safely invoke an event:
+ EventHandler<IvyMessageEventArgs> temp = Callback;
+
+ if (temp == null)
+ {
+ throw new IvyException("(callCallback) Not callback for id " + e.Id);
+ }
+ if (syncContext != null)
+ {
+ SendOrPostCallback update = delegate(object state)
+ {
+ IvyMessageEventArgs args = (IvyMessageEventArgs)state;
+ temp(this, args);
+ };
+ syncContext.Post(update, e);
+ }
+ else
+ temp(this, e);
+ }
+
+#endif
+ }
+
+}
diff --git a/Ivy/IvyBinding.cs b/Ivy/IvyBinding.cs
new file mode 100644
index 0000000..624b401
--- /dev/null
+++ b/Ivy/IvyBinding.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Text.RegularExpressions;
+using System.Diagnostics;
+
+namespace IvyBus
+{
+ /* This is the Client side of binding storage */
+ /* association of a generated Key and the expression and a compiled Expression matching */
+ /* this is RECEIVED from other client */
+
+ /// <summary>
+ /// Description résumée de IvyBinding.
+ /// </summary>
+ internal abstract class IvyBindingBase
+ {
+
+ private ushort key;
+
+ internal ushort Key
+ {
+ get { return key; }
+ }
+ protected string expression;
+
+ internal string Expression
+ {
+ get { return expression; }
+ }
+
+ internal IvyBindingBase(ushort id, string exp)
+ {
+ key = id;
+ expression = exp;
+ }
+ internal abstract string[] Match(string message);
+
+ }
+ internal class IvyBindingRegexp : IvyBindingBase
+ {
+ internal Regex regexp;
+
+ public IvyBindingRegexp(ushort id, string exp)
+ : base(id, exp)
+ {
+ regexp = new Regex(expression, /* RegexOptions.Compiled | */RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
+ //regexp.Match("###"); // for really compile the expression really slow on 3000 expression
+ }
+ internal override string[] Match(string message)
+ {
+ string[] args = null;
+ // use of regexp to extract info
+ Match result = regexp.Match(message);
+ if (result.Success)
+ {
+ // Start at 1 because group 0 represent entire matching
+ args = new string[result.Groups.Count-1];
+ for (int sub = 1; sub < result.Groups.Count; sub++)
+ {
+ args[sub-1] = result.Groups[sub].Value;
+ }
+ }
+ return args;
+ }
+ }
+ internal class IvyBindingSimple : IvyBindingBase
+ {
+ internal string msgname; // message name
+ internal string[] msgargs; // list of message args names
+ static string msgtag; // send message name
+ static StringDictionary args_values; // send message args[name]=value
+
+ internal IvyBindingSimple(ushort id, string exp)
+ : base(id, exp)
+ {
+ string[] expr = expression.Split( ' ' );
+ msgname = expr[0];
+ msgargs = new string[ expr.Length -1 ];
+ for ( int i = 1; i < expr.Length; i++ )
+ msgargs[i-1] = expr[i];
+ }
+ static internal void Prepare(string message)
+ {
+ string[] msg = message.Split(' ');
+ msgtag = msg[0];
+ args_values = new StringDictionary();
+ for( int sub=1 ; sub < msg.Length; sub++ )
+ {
+ string[] arg = msg[sub].Split('='); // champ = valeur
+ if ( arg.Length == 2 )
+ args_values[arg[0]] = arg[1];
+ else
+ {
+ Ivy.traceError("IvyBindingSimple" , "abnormally Formed message expected 'msg champ=valeur champ=valeur....' :" + message);
+ }
+ }
+
+ }
+ internal override string[] Match(string message)
+ {
+ // the message is already parsed by prepare
+ //
+ string[] args = null;
+
+ if (msgtag == msgname)
+ {
+ args = new string[msgargs.Length];
+ for( int sub= 0; sub < msgargs.Length; sub++)
+ {
+ args[sub] = args_values[msgargs[sub]];
+ }
+ }
+ return args;
+ }
+
+ }
+}
diff --git a/Ivy/IvyBindingAttribute.cs b/Ivy/IvyBindingAttribute.cs
new file mode 100644
index 0000000..90c44ce
--- /dev/null
+++ b/Ivy/IvyBindingAttribute.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace IvyBus
+{
+ [AttributeUsage(AttributeTargets.Method,AllowMultiple = true)]
+ public sealed class IvyBindingAttribute : Attribute
+ {
+ private string expression;
+ private string[] args;
+
+ // translate part of expression to object property
+ public string GetExpression(object obj)
+ {
+ if (obj == null) return string.Format(expression);
+ object[] values = new object[args.Length];
+ for (int i = 0; i < args.Length; i++)
+ {
+ values[i] = obj.GetType().GetProperty(args[i]).GetValue(obj,null);
+ }
+ return string.Format(expression,values);
+ }
+
+ public IvyBindingAttribute(string expression, params string[] args)
+ {
+ this.expression = expression;
+ this.args = args;
+ }
+ }
+}
diff --git a/Ivy/IvyClient.cs b/Ivy/IvyClient.cs
new file mode 100644
index 0000000..3db9f0f
--- /dev/null
+++ b/Ivy/IvyClient.cs
@@ -0,0 +1,592 @@
+/// François-Régis Colin
+/// http://www.tls.cena.fr/products/ivy/
+/// *
+/// (C) CENA
+/// *
+namespace IvyBus
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Specialized;
+ using System.Collections.Generic;
+ using System.Threading;
+ using System.Text;
+ using System.IO;
+ using System.Net;
+ using System.Net.Sockets;
+ using System.Configuration;
+ using System.Diagnostics;
+
+ /// <summary> A Class for the the peers on the bus.
+ /// </summary>
+ /// <remarks>
+ /// each time a connexion is made with a remote peer, the regexp are exchanged
+ /// once ready, a ready message is sent, and then we can send messages,
+ /// die messages, direct messages, add or remove regexps, or quit. A thread is
+ /// created for each remote client.
+ /// </remarks>
+ public class IvyClient : IvyProtocol, IComparable<IvyClient>, IDisposable
+ {
+ public int CompareTo(IvyClient other)
+ {
+ return (other.clientPriority - clientPriority);
+ }
+
+ public String ApplicationName
+ {
+ get
+ {
+ return appName;
+ }
+
+ }
+
+ public List<string> Regexps
+ {
+ get
+ {
+ List<string> tab = new List<string>();
+ lock (bindings)
+ {
+ foreach (IvyBindingBase bind in bindings.Values)
+ tab.Add(bind.Expression);
+ }
+ return tab;
+ }
+
+ }
+ internal int AppPort
+ {
+ get
+ {
+ return appPort;
+ }
+
+ }
+ public IPAddress RemoteAddress
+ {
+ get
+ {
+ return remoteHost;
+ }
+
+ }
+ public int RemotePort
+ {
+ get
+ {
+ return remotePort;
+ }
+
+ }
+
+ private Ivy bus;
+ private Dictionary<ushort,IvyBindingBase> bindings;
+ private int appPort;
+ private string clientId; /* an unique ID for each IvyClient */
+ private int clientPriority; /* client priority */
+
+ private volatile Thread clientThread; // volatile to ensure the quick communication
+ private bool doping; // false by runtime default
+ private const int PINGTIMEOUT = 5000;
+ private volatile Thread pingerThread;
+
+ private int remotePort;
+ private IPAddress remoteHost;
+
+ // protected variables
+ internal String appName;
+ internal IvyProtocol stream;
+
+ internal IvyClient(Ivy bus, Socket socket, string appname)
+ {
+ bindings = new Dictionary<ushort,IvyBindingBase>();
+ appName = appname;
+ this.bus = bus;
+
+ IPEndPoint endpoint = (IPEndPoint)socket.RemoteEndPoint;
+
+ remoteHost = endpoint.Address;
+ remotePort = endpoint.Port;
+
+#if (!PocketPC )
+ socket.SetSocketOption( SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, 1 );
+#endif
+
+ if ( bus.ProtocolVersion == 4 )
+ stream = new IvyTCPStreamV4( socket, this );
+ else
+ stream = new IvyTCPStreamV3(socket, this);
+
+ clientPriority = Ivy.DEFAULT_PRIORITY;
+ // spawns a thread to manage the incoming traffic on this
+ // socket. We should be ready to receive messages now.
+ clientThread = new Thread(new ThreadStart(this.Run));
+ clientThread.Name = "Ivy Tcp Client Reader Thread ("+appname+")";
+ clientThread.Start();
+
+ }
+
+ internal void SendBindings()
+ {
+ try
+ {
+ stream.TokenApplicationId(bus.applicationPriority, bus.AppId);
+
+ // sends our ID, whether we initiated the connexion or not
+ // the ID is the couple "host name,application Port", the host name
+ // information is in the socket itself, the port is not known if we
+ // initiate the connexion
+ stream.TokenStartRegexp(bus.applicationPort, bus.appName);
+ // sends our regexps to the peer
+ lock (bus.bindings)
+ {
+ foreach (IvyApplicationBinding bind in bus.bindings.Values)
+ {
+ stream.TokenAddBinding(bind.Binding, bind.Key, bind.FormatedExpression);
+ }
+ }
+ stream.TokenEndRegexp();
+
+#if (!PocketPC)
+ doping = Properties.Settings.Default.IvyPing;
+#endif
+ if (doping)
+ {
+ pingerThread = new Thread(new ThreadStart(PingerRun));
+ pingerThread.Name = "Ivy Pinger Thread";
+ pingerThread.Start();
+ }
+
+
+ }
+ catch (IOException ex)
+ { // the client nous a coupé l'herbe sous le pied
+ Ivy.traceError("IvyClient","I can't send my message to this client. He probably left " + ex.Message);
+ // invokes the Disconnected applicationListeners
+ //bus.OnClientDisconnected(new IvyEventArgs(this,id, message ));
+ // called by the receiver Thread
+ close(false);
+ }
+ }
+
+ /// <summary> returns the name of the remote agent.
+ /// allow an Ivy package class to access the list of regexps at a
+ /// given time.
+ /// perhaps we should implement a new IvyApplicationListener method to
+ /// allow the notification of regexp addition and deletion
+ /// </summary>
+
+ /// <summary> sends a direct message to the peer
+ /// </summary>
+ /// <param name='id'>the numeric value provided to the remote client
+ /// </param>
+ /// <param name='message'>the string that will be match-tested
+ ///
+ /// </param>
+ public void SendDirectMsg(ushort id, string message)
+ {
+ try
+ {
+ stream.TokenDirectMsg( id, message);
+ }
+ catch (IOException ex)
+ {
+ Ivy.traceError("IvyClient","I can't send my message to this client. He probably left "+ex.Message);
+ // first, I'm not a first class IvyClient any more
+ bus.removeClient(this);
+ // invokes the Disconnected applicationListeners
+ //bus.OnClientDisconnected(new IvyEventArgs(this,id, message ));
+ // should be called by receiver thread
+ close(false);
+ }
+ }
+
+ /// <summary> closes the connexion to the peer.
+ /// </summary>
+ /// <param name='notify'>should I send Bye message ?
+ /// the thread managing the socket is stopped
+ ///
+ /// </param>
+ internal void close(bool notify)
+ {
+ Ivy.traceProtocol("IvyClient","closing connexion to " + appName);
+ if (doping )
+ {
+ StopPinging();
+ }
+ if (notify)
+ try
+ {
+ stream.TokenBye(0, "hasta la vista");
+ }
+ catch (IOException ioe)
+ {
+ throw new IvyException(ioe.Message);
+ }
+ // stop the thread and close the stream
+ if (clientThread == null)
+ return;
+ // Tell Thread to stop.
+ if (stream != null)
+ {
+ try
+ {
+ stream.Close(); // should stop the Reading Client Thread
+ }
+ catch (IOException ioe)
+ {
+ throw new IvyException(ioe.Message);
+ }
+ //socket.Close(); // pris en charge par stream ( NetWorkStream )
+ stream = null;
+ }
+ // Potential dead lok when thread issue ClientDisconnected event
+ //if (Thread.CurrentThread != clientThread && (clientThread != null))
+ //{
+ // // Wait for Thread to end.
+ // bool term = clientThread.Join(10000);
+ // if (!term && (clientThread != null)) clientThread.Abort();
+ //}
+
+ clientThread = null;
+
+
+ }
+
+ /// <summary> sends the substrings of a message to the peer for each matching regexp.
+ /// </summary>
+ /// <param name='message'>the string that will be match-tested
+ /// </param>
+ /// <returns>the number of messages sent to the peer
+ ///
+ /// </returns>
+ internal int sendMsg(String message)
+ {
+ int count = 0;
+
+ lock( bindings )
+ {
+ try
+ {
+ foreach (IvyBindingBase bind in bindings.Values)
+ {
+ string[] args = bind.Match(message);
+ if (stream != null && args != null)
+ {
+ stream.TokenMsg(bind.Key, args);
+ count++;
+ }
+ }
+ }
+ catch (IOException ex)
+ {
+ Ivy.traceError("IvyClient","I can't send my message to this client. He probably left " + ex.Message);
+ // first, I'm not a first class IvyClient any more
+ bus.removeClient(this);
+ // invokes the Disconnected applicationListeners
+ // in the receiver thread
+ close(false);
+ }
+
+
+ }
+ return count;
+ }
+
+ /// <summary> compares two peers the id is the couple (host,service port).
+ /// </summary>
+ /// <param name='clnt'>the other peer
+ /// </param>
+ /// <returns>true if the peers are similir. This should not happen, it is bad
+ /// © ® (tm)
+ ///
+ /// </returns>
+ internal bool sameClient(IvyClient clnt)
+ {
+ return (appPort != 0 && appPort == clnt.appPort) && (RemoteAddress == clnt.RemoteAddress);
+ }
+
+ /// <summary> the code of the thread handling the incoming messages.
+ /// </summary>
+ private void Run()
+ {
+ Ivy.traceProtocol("IvyClient","Connected from " + RemoteAddress + ":" + RemotePort);
+
+ Ivy.traceProtocol("IvyClient","Thread started");
+
+ bool running = true;
+ while ( running && (stream != null) )
+ {
+ try
+ {
+ if ( stream.receiveMsg() )
+ {
+ // early stop during readLine()
+ if (doping && (pingerThread != null))
+ pingerThread.Abort();
+ }
+ else
+ {
+ Ivy.traceProtocol("IvyClient","receiveMsg false ! leaving the thread");
+ running = false;
+ break;
+ }
+ }
+ catch ( ObjectDisposedException ex )
+ {
+ Ivy.traceError("IvyClient", "socket closed "+ex.Message );
+ running = false;
+ break;
+ }
+ catch (IvyException ie)
+ {
+ Ivy.traceError("IvyClient","socket closed IvyException" + ie.Message);
+ running = false;
+ break;
+ }
+ catch (SocketException se)
+ {
+ Ivy.traceError("IvyClient", "socket closed "+se.Message );
+ running = false;
+ break;
+ }
+ catch (IOException ex)
+ {
+ if ( ex.InnerException is SocketException )
+ {
+ Ivy.traceProtocol("IvyClient", "socket closed" );
+
+ }
+ else
+ {
+ Ivy.traceError("IvyClient","abnormally Disconnected from " + RemoteAddress + ":" + RemotePort);
+ }
+ running = false;
+ break;
+ }
+ }
+ Ivy.traceProtocol("IvyClient","normally Disconnected from " + appName);
+ Ivy.traceProtocol("IvyClient","Thread stopped");
+ // invokes the Disconnected applicationListeners
+ bus.OnClientDisconnected(new IvyEventArgs(this,0, "" ));
+ // first, I'm not a first class IvyClient any more
+ if (stream != null)
+ {
+ stream.Close();
+ stream = null;
+ }
+ bus.removeClient(this);
+
+ }
+ void IvyProtocol.Close()
+ {
+ // never call in this side
+ }
+ bool IvyProtocol.receiveMsg()
+ {
+ // nerver call in this side
+ return false;
+ }
+ void IvyProtocol.TokenDie(ushort id, string arg)
+ {
+ Ivy.traceProtocol("IvyClient","received die Message from " + appName + "Raison: "+ arg);
+ // invokes the die applicationListeners
+ IvyDieEventArgs ev = new IvyDieEventArgs(this, id, arg);
+ bus.OnDie(ev);
+ // first, I'm not a first class IvyClient any more
+ bus.removeClient(this);
+ // makes the bus die
+ bus.Stop();
+ close(false);
+ if (ev.ForceExit)
+#if (PocketPC)
+ System.Windows.Forms.Application.Exit();
+#else
+ System.Environment.Exit(0);
+#endif
+ }
+ void IvyProtocol.TokenBye(ushort err, string arg)
+ {
+ // the peer quits
+ Ivy.traceProtocol("IvyClient","received bye Message from " + appName + "Raison: "+ arg);
+ // first, I'm not a first class IvyClient any more
+ //bus.removeClient(this); // this is done in the receive thread terminaison!!
+ // invokes the disconnect applicationListeners
+ //bus.FireClientDisconnected(this); done in Running Thread
+ close(false); // will fire disconnected
+ }
+
+ void IvyProtocol.TokenAddBinding(BindingType type, ushort id, string expression)
+ {
+
+ if (type == BindingType.Regexp && !bus.CheckRegexp(expression))
+ {
+ bus.OnClientFilterBinding(new IvyEventArgs(this, id, expression ));
+ return;
+ }
+ IvyBindingBase bind = null;
+ try
+ {
+ switch (type)
+ {
+ case BindingType.Regexp:
+ bind = new IvyBindingRegexp(id, expression);
+ break;
+ case BindingType.Simple:
+ bind = new IvyBindingSimple(id, expression);
+ break;
+ }
+ lock (bindings)
+ {
+ bindings.Add(id, bind);
+ }
+
+ bus.OnClientAddBinding(new IvyEventArgs(this, id, expression));
+
+ }
+ catch (ArgumentException ex)
+ {
+ throw new IvyException("binding expression error " + ex.Message);
+ }
+
+ }
+ void IvyProtocol.TokenDelBinding(ushort id)
+ {
+ lock( bindings )
+ {
+ try
+ {
+ IvyBindingBase bind = bindings[id];
+ bus.OnClientRemoveBinding(new IvyEventArgs(this, bind.Key, bind.Expression));
+ bindings.Remove(id);
+ }
+ catch (KeyNotFoundException ex)
+ {
+ Ivy.traceError("IvyClient","DelBinding " + ex.Message);
+ }
+ }
+ }
+ void IvyProtocol.TokenMsg(ushort id, string[] args)
+ {
+ bus.OnMessage(new IvyMessageEventArgs(this, id, args));
+ }
+ void IvyProtocol.TokenError(ushort id, string arg)
+ {
+ bus.OnError(new IvyEventArgs(this, id, arg));
+ Ivy.traceError("IvyClient","Error msg " + id + " " + arg);
+ }
+ void IvyProtocol.TokenApplicationId(ushort id, string arg)
+ {
+ clientId = arg;
+ if ( clientPriority != id )
+ {
+ clientPriority = id;
+ bus.SortClients();
+ }
+ }
+ void IvyProtocol.TokenEndRegexp()
+ {
+ /*
+ * the peer is perhaps not ready to handle this message
+ * an assymetric processing should be written
+ */
+ if (bus.ReadyMessage != null)
+ sendMsg(bus.ReadyMessage);
+ bus.OnClientConnected(new IvyEventArgs(this, 0, ""));
+ }
+ void IvyProtocol.TokenStartRegexp(ushort id, string arg)
+ {
+ appName = arg;
+ appPort = id;
+ if (bus.checkConnected(this))
+ {
+ close(false);
+ throw new IvyException("Rare ! A concurrent connect occured");
+ }
+
+ }
+ void IvyProtocol.TokenDirectMsg(ushort id, string arg)
+ {
+ bus.OnDirectMessage(new IvyEventArgs(this,id,arg));
+ }
+ void IvyProtocol.TokenPing(string arg)
+ {
+ // I receive a ping. I can answer a pong.
+ Ivy.traceProtocol("IvyClient","Ping msg from " + appName + " : " + arg );
+ stream.TokenPong(arg);
+ }
+ void IvyProtocol.TokenPong(string arg)
+ {
+ Ivy.traceProtocol("IvyClient","Ping msg from " + appName + " : " + arg);
+ }
+
+
+
+ public override String ToString()
+ {
+ return "IvyClient " + bus.appName + ":" + appName;
+ }
+
+ /* is the Pinging Thread Runninng */
+ internal bool isPinging;
+
+ private void PingerRun()
+ {
+ isPinging = true;
+ Ivy.traceProtocol("IvyClient","Pinger Thread started");
+ while (isPinging)
+ {
+ try
+ {
+ Thread.Sleep(PINGTIMEOUT);
+ stream.TokenPing("are you here ?");
+ }
+ catch (ThreadAbortException ie)
+ {
+ Ivy.traceError("IvyClient","Pinger Thread killed "+ie.Message);
+ }
+ }
+ Ivy.traceProtocol("IvyClient","Pinger Thread stopped");
+ }
+ public virtual void StopPinging()
+ {
+ isPinging = false;
+ //pingerThread.Interrupt();
+ pingerThread.Abort();
+ }
+
+ #region IDisposable Members
+
+ //Implement IDisposable.
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // Free other state (managed objects).
+ }
+ // Free your own state (unmanaged objects).
+ // Set large fields to null.
+ if (stream != null)
+ {
+ stream.Close();
+ stream = null;
+ }
+ }
+
+ // Use C# destructor syntax for finalization code.
+ ~IvyClient()
+ {
+ // Simply call Dispose(false).
+ Dispose(false);
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Ivy/IvyDomain.Designer.cs b/Ivy/IvyDomain.Designer.cs
new file mode 100644
index 0000000..3f89b6a
--- /dev/null
+++ b/Ivy/IvyDomain.Designer.cs
@@ -0,0 +1,75 @@
+namespace IvyBus
+{
+ partial class IvyDomain
+ {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.label1 = new System.Windows.Forms.Label();
+ this.ivybus = new System.Windows.Forms.TextBox();
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)));
+ this.label1.Location = new System.Drawing.Point(0, 5);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(24, 13);
+ this.label1.Text = "Ivy:";
+ //
+ // ivybus
+ //
+ this.ivybus.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.ivybus.Location = new System.Drawing.Point(30, 0);
+ this.ivybus.Name = "ivybus";
+ this.ivybus.Size = new System.Drawing.Size(129, 20);
+ this.ivybus.TabIndex = 2;
+ this.ivybus.Validated += new System.EventHandler(this.ivybus_Validated);
+ this.ivybus.Validating += new System.ComponentModel.CancelEventHandler(this.ivybus_Validating);
+ //
+ // IvyDomain
+ //
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.ivybus);
+ this.Name = "IvyDomain";
+ this.Size = new System.Drawing.Size(159, 22);
+ this.ResumeLayout(false);
+#if (!PocketPC)
+ this.PerformLayout();
+#endif
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TextBox ivybus;
+ }
+}
diff --git a/Ivy/IvyDomain.cs b/Ivy/IvyDomain.cs
new file mode 100644
index 0000000..6c43b73
--- /dev/null
+++ b/Ivy/IvyDomain.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+using System.Windows.Forms;
+
+namespace IvyBus
+{
+ public partial class IvyDomain : UserControl
+ {
+ private string domain = "";
+ public event EventHandler DomainChanged;
+#if (!PocketPC)
+ [Category("Ivy")]
+ [DefaultValue("")]
+ [Bindable(true)]
+#endif
+ public string Domain
+ {
+ get { return domain; }
+ set {
+ if (domain != value)
+ {
+ domain = value;
+ ivybus.Text = domain;
+ if (DomainChanged != null) DomainChanged(this, EventArgs.Empty);
+ }
+ }
+ }
+
+ public IvyDomain()
+ {
+ InitializeComponent();
+ }
+ public void SetDefault()
+ {
+ if (IsEmpty())
+ {
+ domain = Ivy.GetDomain(domain);
+ ivybus.Text = domain;
+ }
+ }
+ public bool IsEmpty()
+ {
+ return String.IsNullOrEmpty( domain );
+ }
+
+ private void ivybus_Validating(object sender, CancelEventArgs e)
+ {
+ e.Cancel = !Ivy.ValidatingDomain(ivybus.Text);
+ }
+
+ private void ivybus_Validated(object sender, EventArgs e)
+ {
+ if ( domain != ivybus.Text )
+ Domain = ivybus.Text;
+ }
+
+ }
+}
diff --git a/Ivy/IvyDomain.resx b/Ivy/IvyDomain.resx
new file mode 100644
index 0000000..ff31a6d
--- /dev/null
+++ b/Ivy/IvyDomain.resx
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+</root> \ No newline at end of file
diff --git a/Ivy/IvyEventArgs.cs b/Ivy/IvyEventArgs.cs
new file mode 100644
index 0000000..e394802
--- /dev/null
+++ b/Ivy/IvyEventArgs.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace IvyBus
+{
+ /// <summary> The EventArgs Classes
+ /// </summary>
+ ///
+ public class IvyEventArgs : EventArgs
+ {
+ private IvyClient client;
+ private int id;
+ private string arg;
+
+ public IvyClient Client
+ {
+ get { return client; }
+ }
+
+ public int Id
+ {
+ get { return id; }
+ }
+
+ public string Argument
+ {
+ get { return arg; }
+ }
+ public IvyEventArgs(IvyClient app, int id, string arg)
+ {
+ this.client = app;
+ this.id = id;
+ this.arg = arg;
+ }
+ }
+ public class IvyDieEventArgs : IvyEventArgs
+ {
+ /* return value for Die Event */
+ private bool forceExit;
+
+ public bool ForceExit
+ {
+ get { return forceExit; }
+ set { forceExit = value; }
+ }
+ public IvyDieEventArgs(IvyClient app, int id, string arg)
+ : base(app, id, arg)
+ {
+ forceExit = true;
+ }
+ }
+ public class IvyMessageEventArgs : EventArgs
+ {
+ private IvyClient client;
+ private int id;
+ private string[] args;
+
+ public IvyClient Client
+ {
+ get { return client; }
+ }
+
+ public int Id
+ {
+ get { return id; }
+ }
+
+ public string[] Arguments
+ {
+ get { return args; }
+ }
+ public string this[int i]
+ {
+ get { return args[i]; }
+ }
+ public IvyMessageEventArgs(IvyClient app, int id, string[] args)
+ {
+ this.client = app;
+ this.id = id;
+ this.args = args;
+ }
+ }
+}
diff --git a/Ivy/IvyException.cs b/Ivy/IvyException.cs
new file mode 100644
index 0000000..90a91b8
--- /dev/null
+++ b/Ivy/IvyException.cs
@@ -0,0 +1,19 @@
+/// François-Régis Colin
+/// http://www.tls.cena.fr/products/ivy/
+/// *
+/// (C) CENA
+/// *
+namespace IvyBus
+{
+ using System;
+
+ /// <summary> signals that an unrecoverrable Ivy exception has occured.
+ /// </summary>
+
+ public class IvyException:System.Exception
+ {
+ public IvyException(System.String s):base(s)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Ivy/IvyProtocol.cs b/Ivy/IvyProtocol.cs
new file mode 100644
index 0000000..1b1f3aa
--- /dev/null
+++ b/Ivy/IvyProtocol.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Text;
+
+namespace IvyBus
+{
+ public enum BindingType { Regexp, Simple };
+
+ internal interface IvyProtocol
+ {
+ void Close();
+ bool receiveMsg();
+ void TokenStartRegexp(ushort port, string appName);
+ void TokenEndRegexp();
+ void TokenApplicationId(ushort priority, string appId);
+ void TokenAddBinding(BindingType type, ushort id, string expression);
+ void TokenDelBinding(ushort bind);
+ void TokenDirectMsg(ushort id, string message);
+ void TokenPong(string s);
+ void TokenPing(string s);
+ void TokenBye(ushort id, string message);
+ void TokenDie(ushort err, string message);
+ void TokenMsg(ushort key, string[] args);
+ void TokenError(ushort id, string message);
+ }
+}
diff --git a/Ivy/IvyTCPStream.cs b/Ivy/IvyTCPStream.cs
new file mode 100644
index 0000000..b8fee34
--- /dev/null
+++ b/Ivy/IvyTCPStream.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Specialized;
+using System.Text;
+using System.Net;
+using System.Net.Sockets;
+using System.IO;
+
+namespace IvyBus
+{
+ abstract class IvyTCPStream : NetworkStream
+ {
+ public IvyTCPStream(Socket socket)
+ : base(socket, true)
+ {
+ }
+
+ abstract internal bool receiveMsg();
+
+ }
+}
diff --git a/Ivy/IvyTCPStreamV3.cs b/Ivy/IvyTCPStreamV3.cs
new file mode 100644
index 0000000..4eef12c
--- /dev/null
+++ b/Ivy/IvyTCPStreamV3.cs
@@ -0,0 +1,254 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Collections;
+using System.Collections.Specialized;
+using System.IO;
+
+namespace IvyBus
+{
+ /// <summary>
+ /// Description résumée de IvyStream.
+ /// </summary>
+ internal class IvyTCPStreamV3 : NetworkStream, IvyProtocol
+ {
+ StreamReader input;
+ StreamWriter output;
+ IvyProtocol receiver;
+
+ /// the protocol separator
+ internal const char ARG_START = '\x02';
+ internal const char ARG_END = '\x03';
+ internal const char MSG_END = '\n';
+
+ internal IvyTCPStreamV3(Socket socket, IvyProtocol _receiver) : base ( socket )
+ {
+ output = new StreamWriter(this, Encoding.ASCII);
+ output.NewLine = MSG_END.ToString();
+ input = new StreamReader(this, Encoding.ASCII);
+ receiver = _receiver;
+ }
+ /* the protocol magic numbers */
+ internal enum MessageType : ushort
+ {
+ Bye = 0, /* end of the peer */
+ AddRegexp = 1, /* the peer adds a regexp */
+ Msg = 2, /* the peer sends a message */
+ Error = 3, /* error message */
+ DelBinding = 4, /* the peer removes one of his regex */ // OLD DelRegexp rename to DelBinding
+ EndRegexp = 5, /* no more regexp in the handshake */
+ StartRegexp = 6, /* avoid race condition in concurrent connexions */
+ DirectMsg = 7, /* the peer sends a direct message */
+ Die = 8, /* the peer wants us to quit */
+ Ping = 9, /* checks the presence of the other */
+ Pong = 10, /* checks the presence of the other */
+ };
+
+ /*
+ * message Syntax:
+ * this is text formated message 'type id STX ARG0 {[ETX] ARG1 [ETX] ARGn}\n'
+ *
+ * message Format:
+ MessageType, id , length, string
+ */
+
+ private void sendMsg(MessageType msgType, ushort msgId, string msgData)
+ {
+ // IOException Should be traited upstairs
+ output.Write((ushort)msgType);
+ output.Write(' ');
+ output.Write(msgId);
+ output.Write(ARG_START);
+ output.Write(msgData);
+ output.Write(MSG_END);
+ output.Flush();
+
+ }
+ void IvyProtocol.TokenStartRegexp(ushort port, string appName)
+ {
+ sendMsg(MessageType.StartRegexp, port, appName);
+ }
+ void IvyProtocol.TokenEndRegexp()
+ {
+ sendMsg(MessageType.EndRegexp, 0, "");
+ }
+ void IvyProtocol.TokenApplicationId(ushort priority, string appId)
+ {
+ // NOt implemented in this protocol version
+ }
+
+ void IvyProtocol.TokenAddBinding(BindingType type, ushort id, string expression)
+ {
+ switch (type)
+ {
+ case BindingType.Regexp:
+ sendMsg(MessageType.AddRegexp, id, expression); /* perhaps we should perform some checking here */
+ break;
+ case BindingType.Simple:
+ // NO Simple Binding in this protocol
+ break;
+ }
+ }
+
+ void IvyProtocol.TokenDelBinding(ushort id)
+ {
+ sendMsg(MessageType.DelBinding, id, "");
+ }
+
+ void IvyProtocol.TokenDirectMsg(ushort id, string message)
+ {
+ sendMsg(MessageType.DirectMsg, id, message);
+ }
+ void IvyProtocol.TokenPong(string s)
+ {
+ sendMsg(MessageType.Pong, 0, s);
+ }
+ void IvyProtocol.TokenPing(string s)
+ {
+ sendMsg(MessageType.Ping, 0, s);
+ }
+
+ void IvyProtocol.TokenBye(ushort id, string message)
+ {
+ sendMsg(MessageType.Bye, id, message);
+ }
+
+ void IvyProtocol.TokenDie(ushort id, string message)
+ {
+ sendMsg(MessageType.Die, id, message);
+ }
+
+ void IvyProtocol.TokenMsg(ushort key, string[] args)
+ {
+ string delimiter = "" + ARG_END;
+ string data = string.Join(delimiter, args);
+ // a bad protocol implementation in C add a delimiter to the end of each arg
+ // we must add a delimiter to the end
+ data += delimiter;
+ sendMsg(MessageType.Msg, key, data);
+ }
+
+ void IvyProtocol.TokenError(ushort key, string arg)
+ {
+ sendMsg(MessageType.Msg, key, arg);
+ }
+
+ private ushort DeserializeShort()
+ {
+ int read;
+ ushort ret = 0;
+ char digit;
+ // this will eat next non digit char ie space
+ do
+ {
+ read = input.Read();
+ if (read < 0)
+ throw new EndOfStreamException();
+ digit = (char)read;
+ if (Char.IsDigit(digit))
+ ret = (ushort)(ret * 10 + (digit - 0x30));
+ } while (Char.IsDigit(digit));
+ return ret;
+ }
+ private string DeserializeString(char sep)
+ {
+ int read;
+ char car;
+ StringBuilder str = new StringBuilder();
+ // this will eat next non separator char
+ do
+ {
+ read = input.Read();
+ if (read < 0)
+ throw new EndOfStreamException();
+ car = (char)read;
+ if (car != sep ) str.Append(car);
+ } while (car != sep);
+ return str.ToString();
+ }
+
+ bool IvyProtocol.receiveMsg()
+ {
+ MessageType msgType = MessageType.Die;
+ ushort msgId = 0;
+ string msgData = null;
+
+ try
+ {
+ msgType = (MessageType)DeserializeShort();
+ msgId = DeserializeShort();
+ msgData = DeserializeString(MSG_END);
+
+ switch (msgType)
+ {
+ case MessageType.Die:
+ receiver.TokenDie(msgId, msgData);
+ break;
+
+ case MessageType.Bye:
+ receiver.TokenBye(msgId, msgData);
+ break;
+
+ case MessageType.AddRegexp:
+ receiver.TokenAddBinding(BindingType.Regexp, msgId, msgData);
+ break;
+
+ case MessageType.DelBinding:
+ receiver.TokenDelBinding(msgId);
+ break;
+
+ case MessageType.EndRegexp:
+ receiver.TokenEndRegexp();
+ break;
+
+ case MessageType.Msg:
+ // a bad protocol implementation in C add a delimiter to the end of each arg
+ // we must remove a delimiter to the end
+ if ( msgData.Length > 0 )
+ msgData = msgData.Remove(msgData.Length - 1,1);
+ receiver.TokenMsg(msgId, msgData.Split( ARG_END ));
+ break;
+
+ case MessageType.Pong:
+ receiver.TokenPong(msgData);
+ break;
+
+ case MessageType.Ping:
+ receiver.TokenPing(msgData);
+ break;
+
+ case MessageType.Error:
+ receiver.TokenError(msgId, msgData);
+ break;
+
+ case MessageType.StartRegexp:
+ receiver.TokenStartRegexp(msgId, msgData);
+ break;
+
+ case MessageType.DirectMsg:
+ receiver.TokenDirectMsg(msgId, msgData);
+ break;
+ default:
+ throw new IvyException("protocol error, unknown message type " + msgType);
+
+ }
+
+
+
+ }
+ catch (EndOfStreamException)
+ {
+ return false;
+ }
+ catch (FormatException)
+ {
+ throw new IvyException("protocol error on msgType");
+ }
+
+
+ return true;
+ }
+
+ }
+}
diff --git a/Ivy/IvyTCPStreamV4.cs b/Ivy/IvyTCPStreamV4.cs
new file mode 100644
index 0000000..9a96d62
--- /dev/null
+++ b/Ivy/IvyTCPStreamV4.cs
@@ -0,0 +1,273 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Collections;
+using System.Collections.Specialized;
+using System.IO;
+
+namespace IvyBus
+{
+ /// <summary>
+ /// Description résumée de IvyStream.
+ /// </summary>
+ internal class IvyTCPStreamV4 : NetworkStream , IvyProtocol
+ {
+ BinaryReader input;
+ BinaryWriter output;
+ IvyProtocol receiver;
+
+ /* the protocol magic numbers */
+ internal enum MessageType : ushort
+ {
+ Bye = 0, /* end of the peer */
+ AddRegexp = 1, /* the peer adds a regexp */
+ Msg = 2, /* the peer sends a message */
+ Error = 3, /* error message */
+ DelBinding = 4, /* the peer removes one of his regex */ // OLD DelRegexp rename to DelBinding
+ EndRegexp = 5, /* no more regexp in the handshake */
+ StartRegexp = 6, /* avoid race condition in concurrent connexions */
+ DirectMsg = 7, /* the peer sends a direct message */
+ Die = 8, /* the peer wants us to quit */
+ Ping = 9, /* checks the presence of the other */
+ Pong = 10, /* checks the presence of the other */
+ ApplicationId = 11, /* on start send my ID and priority */
+ AddBinding = 12, /* other methods for binding message based on hash table */
+
+ };
+
+ internal IvyTCPStreamV4(Socket socket, IvyProtocol _receiver) : base( socket )
+ {
+
+ input = new BinaryReader(this, Encoding.ASCII);
+ output = new BinaryWriter(this, Encoding.ASCII);
+ receiver = _receiver;
+ }
+
+
+ /*
+ * message Syntax:
+ * this is a binary formated message use of network representation
+ *
+ * message Format:
+ MessageType, id , length, string
+ */
+ private void Serialize(short arg)
+ {
+ output.Write((ushort)IPAddress.HostToNetworkOrder(arg));
+ }
+ private void Serialize(string arg)
+ {
+ short length = arg != null ? (short)arg.Length : (short)0;
+ Serialize(length);
+ if (length != 0)
+ output.Write(arg.ToCharArray());
+ }
+ private void Serialize(string[] arg)
+ {
+
+ /* serialize count */
+ Serialize((short)arg.Length);
+
+ for (int i = 0; i < arg.Length; i++)
+ {
+ Serialize(arg[i]);
+ }
+ }
+ private void sendMsg(MessageType type, int id, params string[] arg)
+ {
+
+ Serialize( (short)type );
+ Serialize( (short)id );
+ Serialize(arg);
+ output.Flush();
+
+ }
+ void IvyProtocol.TokenStartRegexp(ushort port, string appName)
+ {
+ sendMsg(MessageType.StartRegexp, port, appName);
+ }
+ void IvyProtocol.TokenEndRegexp()
+ {
+ sendMsg(MessageType.EndRegexp, 0, "");
+ }
+ void IvyProtocol.TokenApplicationId(ushort priority, string appId)
+ {
+ sendMsg(MessageType.ApplicationId, priority, appId);
+ }
+
+ void IvyProtocol.TokenAddBinding(BindingType type, ushort id, string expression)
+ {
+ switch (type)
+ {
+ case BindingType.Regexp:
+ sendMsg(MessageType.AddRegexp, id, expression); /* perhaps we should perform some checking here */
+ break;
+ case BindingType.Simple:
+ sendMsg(MessageType.AddBinding, id, expression); /* perhaps we should perform some checking here */
+ break;
+ }
+ }
+
+ void IvyProtocol.TokenDelBinding(ushort id)
+ {
+ sendMsg(MessageType.DelBinding, id, null );
+ }
+
+ void IvyProtocol.TokenDirectMsg(ushort id, string message)
+ {
+ sendMsg(MessageType.DirectMsg, id, message);
+ }
+ void IvyProtocol.TokenPong(string s)
+ {
+ sendMsg(MessageType.Pong, 0, s);
+ }
+ void IvyProtocol.TokenPing(string s)
+ {
+ sendMsg(MessageType.Ping, 0, s);
+ }
+
+ void IvyProtocol.TokenBye(ushort id, string message)
+ {
+ sendMsg(MessageType.Bye, id, message);
+ }
+
+ void IvyProtocol.TokenDie(ushort id, string message)
+ {
+ sendMsg(MessageType.Die, id, message);
+ }
+
+ void IvyProtocol.TokenMsg(ushort key, string[] args)
+ {
+ sendMsg(MessageType.Msg, key, args );
+ }
+
+ void IvyProtocol.TokenError(ushort key, string arg)
+ {
+ sendMsg(MessageType.Msg, key, arg);
+ }
+
+ private short DeserializeShort()
+ {
+ return IPAddress.NetworkToHostOrder((short)input.ReadUInt16());
+ }
+ private string DeserializeString()
+ {
+ string arg;
+ int val_len;
+ char[] data;
+ val_len = (ushort)DeserializeShort();
+ if (val_len != 0)
+ {
+ data = input.ReadChars(val_len);
+ arg = new String(data);
+ }
+ else
+ arg = "";
+ return arg;
+ }
+
+
+ private string[] DeserializeArgument()
+ {
+ int nb_children;
+ string[] arg;
+
+ /* Deserialize childrens */
+ nb_children = (ushort)DeserializeShort();
+ /* deserialize Value */
+ arg = new string[nb_children];
+
+ for (int i = 0; i < nb_children; i++)
+ {
+ arg[i] = DeserializeString();
+ }
+ return arg;
+ }
+ bool IvyProtocol.receiveMsg()
+ {
+ MessageType msgType = MessageType.Die;
+ ushort msgId = 0;
+ string[] msgData = null;
+
+ try
+ {
+ msgType = (MessageType)(ushort)DeserializeShort();
+ msgId = (ushort)DeserializeShort();
+ msgData = DeserializeArgument();
+
+ switch (msgType)
+ {
+ case MessageType.Die:
+ receiver.TokenDie(msgId, msgData[0]);
+ break;
+
+ case MessageType.Bye:
+ receiver.TokenBye(msgId, msgData[0]);
+ break;
+
+ case MessageType.AddRegexp:
+ receiver.TokenAddBinding(BindingType.Regexp, msgId, msgData[0]);
+ break;
+
+ case MessageType.AddBinding:
+ receiver.TokenAddBinding(BindingType.Simple, msgId, msgData[0]);
+ break;
+
+ case MessageType.DelBinding:
+ receiver.TokenDelBinding(msgId);
+ break;
+
+ case MessageType.EndRegexp:
+ receiver.TokenEndRegexp();
+ break;
+
+ case MessageType.Msg:
+ receiver.TokenMsg( msgId, msgData );
+ break;
+
+ case MessageType.Pong:
+ receiver.TokenPong(msgData[0]);
+ break;
+
+ case MessageType.Ping:
+ receiver.TokenPing(msgData[0]);
+ break;
+
+ case MessageType.Error:
+ receiver.TokenError(msgId, msgData[0]);
+ break;
+
+ case MessageType.StartRegexp:
+ receiver.TokenStartRegexp(msgId, msgData[0]);
+ break;
+
+ case MessageType.DirectMsg:
+ receiver.TokenDirectMsg(msgId, msgData[0]);
+ break;
+ case MessageType.ApplicationId:
+ receiver.TokenApplicationId(msgId, msgData[0]);
+ break;
+ default:
+ throw new IvyException("protocol error, unknown message type " + msgType);
+
+ }
+
+
+
+ }
+ catch (EndOfStreamException)
+ {
+ return false;
+ }
+ catch (FormatException)
+ {
+ throw new IvyException("protocol error on msgType");
+ }
+
+
+ return true;
+ }
+
+ }
+}
diff --git a/Ivy/IvyUDPStream.cs b/Ivy/IvyUDPStream.cs
new file mode 100644
index 0000000..0e3f517
--- /dev/null
+++ b/Ivy/IvyUDPStream.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Net;
+using System.Net.Sockets;
+using System.IO;
+
+namespace IvyBus
+{
+ abstract class IvyUDPStream
+ {
+ Socket socket;
+ byte[] buffer;
+
+ protected MemoryStream out_stream;
+ protected MemoryStream in_stream;
+
+ ushort protocol_version;
+
+ public ushort ProtocolVersion
+ {
+ get { return protocol_version; }
+ }
+
+ public IvyUDPStream(Socket _socket, ushort protocol)
+ {
+ socket = _socket;
+ buffer = new byte[4096];
+ in_stream = new MemoryStream(buffer);
+ out_stream = new MemoryStream();
+ protocol_version = protocol;
+ }
+ internal void Close()
+ {
+ in_stream.Close();
+ out_stream.Close();
+ socket.Shutdown(SocketShutdown.Both);
+ socket.Close();
+ }
+ internal void receiveMsg(out IPEndPoint remote, out ushort version, out ushort port, out string appId, out string appName)
+ {
+ int len;
+ IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
+ EndPoint tempRemoteEP = (EndPoint)remoteEP;
+ remoteEP = null;
+ len = socket.ReceiveFrom(buffer, ref tempRemoteEP);
+ remote = (IPEndPoint)tempRemoteEP;
+ in_stream.Position = 0;
+ in_stream.SetLength(len);
+ in_stream.Seek(0, SeekOrigin.Begin);
+ //Call Deserialization
+ Deserialize( out version, out port, out appId, out appName );
+ }
+ internal void sendMsg(IPEndPoint EPhost, ushort port, string appId, string appName)
+ {
+ // Call Serialisation
+ Serialize(port, appId, appName);
+
+ byte[] hellob = out_stream.GetBuffer();
+ socket.SendTo(hellob, (int)out_stream.Length, 0, EPhost);
+ }
+ abstract internal void Serialize(ushort port, string appId, string appName);
+ abstract internal void Deserialize(out ushort version, out ushort port, out string appId, out string appName);
+
+ }
+}
diff --git a/Ivy/IvyUDPStreamV3.cs b/Ivy/IvyUDPStreamV3.cs
new file mode 100644
index 0000000..63a8679
--- /dev/null
+++ b/Ivy/IvyUDPStreamV3.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Net;
+using System.Net.Sockets;
+using System.IO;
+
+namespace IvyBus
+{
+ class IvyUDPStreamV3 : IvyUDPStream
+ {
+ StreamReader input;
+ StreamWriter output;
+
+ /// the protocol version number
+ internal const int PROCOCOLVERSION = 3;
+
+ public IvyUDPStreamV3(Socket _socket) : base( _socket , PROCOCOLVERSION )
+ {
+ input = new StreamReader(in_stream, Encoding.ASCII);
+ output = new StreamWriter(out_stream, Encoding.ASCII);
+ }
+ /*
+ * message Syntax:
+ * this is a text formated message
+ *
+ * message Format:
+ protocol_version, TCP server port , appId, appName
+ */
+ private ushort DeserializeShort()
+ {
+ int read;
+ ushort ret = 0;
+ char digit;
+ // this will eat next non digit car ie space
+ do
+ {
+ read = input.Read();
+ if (read < 0)
+ throw new EndOfStreamException();
+ digit = (char)read;
+ if ( Char.IsDigit(digit) )
+ ret = (ushort)(ret * 10 + (digit-0x30));
+ } while (Char.IsDigit(digit));
+ return ret;
+ }
+ private string DeserializeString(char sep)
+ {
+ int read;
+ char car;
+ StringBuilder str = new StringBuilder();
+ // this will eat next non digit car ie space
+ do
+ {
+ read = input.Read();
+ if (read < 0)
+ throw new EndOfStreamException();
+ if (read == 0) break;
+ car = (char)read;
+ if (car != sep)
+ str.Append(car);
+ } while (car != sep);
+ return str.ToString();
+ }
+
+ internal override void Deserialize(out ushort version, out ushort port, out string appId, out string appName)
+ {
+ version = 0;
+ port = 0;
+ appId = "";
+ appName = "";
+ try {
+ version = DeserializeShort();
+ port = DeserializeShort();
+ //Optionel in V3 protocol depend on client version
+ appId = DeserializeString(' ');
+ appName = DeserializeString('\n');
+ }
+ catch( EndOfStreamException )
+ {
+ // Bad protocol message receive or without appId and appName
+ }
+ input.DiscardBufferedData();
+ }
+ private void Serialize(ushort arg, char sep)
+ {
+ output.Write(arg);
+ output.Write(sep);
+ }
+ private void Serialize(string arg, char sep)
+ {
+ output.Write(arg);
+ output.Write(sep);
+ }
+ internal override void Serialize(ushort port, string appId, string appName)
+ {
+ Serialize(PROCOCOLVERSION, ' ');
+ Serialize(port,' ');
+ Serialize(appId,' '); //No AppId in V3
+ Serialize(appName, '\n'); //No Appname in V3
+ output.Flush();
+ }
+ }
+}
diff --git a/Ivy/IvyUDPStreamV4.cs b/Ivy/IvyUDPStreamV4.cs
new file mode 100644
index 0000000..b8d329a
--- /dev/null
+++ b/Ivy/IvyUDPStreamV4.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Net;
+using System.Net.Sockets;
+using System.IO;
+
+namespace IvyBus
+{
+ class IvyUDPStreamV4 : IvyUDPStream
+ {
+
+ BinaryReader input;
+ BinaryWriter output;
+
+ /// the protocol version number
+ internal const ushort PROCOCOLVERSION = 4;
+ public IvyUDPStreamV4(Socket _socket) : base ( _socket, PROCOCOLVERSION)
+ {
+ input = new BinaryReader( in_stream,Encoding.ASCII);
+ output = new BinaryWriter(out_stream, Encoding.ASCII);
+ }
+ /*
+ * message Syntax:
+ * this is a binary formated message use of network representation
+ *
+ * message Format:
+ protocol_version, TCP server port , lenAppId, appId, lenAppNameId, appName
+ */
+ private ushort DeserializeShort()
+ {
+ return (ushort)IPAddress.NetworkToHostOrder((ushort)input.ReadUInt16());
+ }
+ private string DeserializeString()
+ {
+ string arg;
+ int val_len;
+ char[] data;
+ val_len = (ushort)IPAddress.NetworkToHostOrder((ushort)input.ReadUInt16());
+ if (val_len != 0)
+ {
+ data = input.ReadChars(val_len);
+ arg = new String(data);
+ }
+ else
+ arg = "";
+ return arg;
+ }
+
+ internal override void Deserialize(out ushort version, out ushort port, out string appId, out string appName)
+ {
+ version = DeserializeShort();
+ port = DeserializeShort();
+ appId = DeserializeString();
+ appName = DeserializeString();
+
+ }
+ private void Serialize(ushort arg)
+ {
+ output.Write((ushort)IPAddress.HostToNetworkOrder(arg));
+ }
+ private void Serialize(string arg)
+ {
+ ushort length = arg != null ? (ushort)arg.Length : (ushort)0;
+ Serialize(length);
+ if (length != 0)
+ output.Write(arg.ToCharArray());
+ }
+
+ internal override void Serialize(ushort port, string appId, string appName)
+ {
+ Serialize(PROCOCOLVERSION );
+ Serialize(port);
+ Serialize(appId);
+ Serialize(appName);
+ output.Flush();
+ }
+
+ }
+}
diff --git a/Ivy/IvyWatcher.cs b/Ivy/IvyWatcher.cs
new file mode 100644
index 0000000..5a3afc8
--- /dev/null
+++ b/Ivy/IvyWatcher.cs
@@ -0,0 +1,179 @@
+
+/// François-Régis Colin
+/// http://www.tls.cena.fr/products/ivy/
+/// *
+/// (C) CENA
+/// *
+
+namespace IvyBus
+{
+ using System;
+ using System.Threading;
+ using System.IO;
+ using System.Net;
+ using System.Net.Sockets;
+ using System.Text.RegularExpressions;
+ using System.Configuration;
+ using System.Text;
+ using System.Diagnostics;
+
+ /// <summary> IvyWatcher, A private Class for the Ivy rendezvous
+ /// </summary>
+ /// <remarks> right now, the rendez vous is either an UDP socket or a TCP multicast.
+ /// The watcher will answer to
+ /// each peer advertising its arrival on the bus. The intrinsics of Unix are so
+ /// that the broadcast is done using the same socket, which is not a good
+ /// thing.
+ /// </remarks>
+ internal class IvyWatcher
+ {
+ private Ivy bus; /* master bus controler */
+ private int port;
+ private volatile Thread listenThread;
+ private IPAddress group;
+ private IvyUDPStream stream;
+
+ /// <summary> creates an Ivy watcher
+ /// </summary>
+ /// <param name='bus'>the bus
+ /// </param>
+ /// <param name='domainaddr'>the domain
+ /// </param>
+ /// <param name='port'>the port number
+ /// </param>
+ internal IvyWatcher(Ivy bus, String domainaddr, int port)
+ {
+ this.bus = bus;
+ this.port = port;
+ listenThread = new Thread(new ThreadStart(this.Run));
+ listenThread.Name = "Ivy UDP Listener Thread";
+ try
+ {
+ group = IPAddress.Parse(domainaddr);
+ /* supervision socket */
+ // To do reuseaddr we must use a Socket not a udp client
+ Socket broadcast = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+ IPEndPoint EPhost = new IPEndPoint(IPAddress.Any, port);
+ broadcast.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast,1);
+ broadcast.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress,1);
+ broadcast.Bind(EPhost);
+
+ //test isMulticastAddress // TODO better check
+ //if ( group.IsIPv6Multicast ) yes but in IPV4 how to do
+ byte[] addr = group.GetAddressBytes();
+ if ((addr[0] & 0xf0) == 0xe0)
+ {
+ broadcast.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption( group ));
+ }
+ // TODO support the Two protocol
+ if (bus.protocolVersion == 4)
+ stream = new IvyUDPStreamV4(broadcast);
+ else
+ stream = new IvyUDPStreamV3(broadcast);
+ }
+ catch (IOException e)
+ {
+ throw new IvyException("IvyWatcher I/O error" + e);
+ }
+ }
+
+ /// <summary> the behaviour of each thread watching the UDP socket.
+ /// </summary>
+ public void Run()
+ {
+ Ivy.traceProtocol("IvyWatcher", "beginning of a watcher Thread");
+
+ try
+ {
+ bool running = true;
+ while (running)
+ {
+ ushort version;
+ ushort appPort;
+ string appId;
+ string appName;
+ IPEndPoint remoteEP;
+
+ stream.receiveMsg(out remoteEP, out version, out appPort, out appId, out appName);
+ IPAddress remotehost = remoteEP.Address;
+
+ Ivy.traceProtocol("IvyWatcher", "Receive Broadcast from " + Dns.GetHostEntry(remotehost).HostName + ":" + remoteEP.Port);
+
+ //TODO if ( !isInDomain( remotehost ) ) continue;
+
+ if (version != stream.ProtocolVersion)
+ {
+ Ivy.traceError("IvyWatcher","Ignoring bad protocol version " + version + " expected " + stream.ProtocolVersion);
+ continue;
+ }
+
+ // filtrage des self Broadcast
+ if (appId == bus.AppId)
+ continue;
+ if ((appPort == bus.applicationPort) && (remotehost.Equals(bus.applicationHost)))
+ continue;
+
+ Ivy.traceProtocol("IvyWatcher", "reponse au Broadcast de " + Dns.GetHostEntry(remotehost).HostName + ":" + remoteEP.Port + " port " + appPort +
+ " version " + version +
+ " id " + appId +
+ " name " + appName);
+
+ try
+ {
+ Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+ IPEndPoint hostEndPoint = new IPEndPoint(remoteEP.Address, appPort);
+ socket.Blocking = true;
+ socket.Connect(hostEndPoint);
+ bus.addClient(socket, appName);
+ }
+ catch (Exception e)
+ {
+ Ivy.traceError("IvyWatcher","can't connect to " + remotehost + " port " + appPort + " \n" + e.Message);
+ }
+
+ } // while
+ }
+ catch (SocketException se)
+ {
+ Ivy.traceError("IvyWatcher","watcher socket closed: " + se.Message);
+ }
+ catch (IOException ioe)
+ {
+ Ivy.traceError("IvyWatcher","watcher thread ended: " + ioe.Message);
+ }
+ Ivy.traceProtocol("IvyWatcher", "end of a watcher thread");
+ }
+
+ /// <summary> stops the thread waiting on the broadcast socket
+ /// </summary>
+ internal virtual void stop()
+ {
+ lock (stream)
+ {
+ Ivy.traceProtocol("IvyWatcher", "begining stopping an IvyWatcher");
+ stream.Close();
+ if (listenThread != null)
+ {
+ // Wait for Thread to end.
+ bool term = listenThread.Join(10000);
+ if (!term && (listenThread != null)) listenThread.Abort();
+ listenThread = null;
+ }
+ // it might not even have been created
+ Ivy.traceProtocol("IvyWatcher", "ending stopping an IvyWatcher");
+ }
+ }
+
+ internal virtual void start()
+ {
+ lock (stream)
+ {
+ listenThread.Start();
+ IPEndPoint EPhost = new IPEndPoint(group, port);
+ stream.sendMsg(EPhost, bus.applicationPort, bus.AppId, bus.AppName);// notifies our arrival on each domain: protocol version + port
+ }
+ }
+
+
+ }
+} \ No newline at end of file
diff --git a/Ivy/Properties/AssemblyInfo.cs b/Ivy/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..a3ce834
--- /dev/null
+++ b/Ivy/Properties/AssemblyInfo.cs
@@ -0,0 +1,62 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+[assembly: AssemblyTitle("Ivy")]
+[assembly: AssemblyDescription("Dll de communications sur le bus IVY")]
+[assembly: AssemblyCompany("DTI/SDER PII")]
+[assembly: AssemblyProduct("Ivy")]
+[assembly: AssemblyCopyright("SDER")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Revision
+// Build Number
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("2.0.*")]
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the
+// Microsoft .NET Framework documentation for more information on assembly signing.
+//
+// Use the attributes below to control which key is used for signing.
+//
+// Notes:
+// (*) If no key is specified, the assembly is not signed.
+// (*) KeyName refers to a key that has been installed in the Crypto Service
+// Provider (CSP) on your machine. KeyFile refers to a file which contains
+// a key.
+// (*) If the KeyFile and the KeyName values are both specified, the
+// following processing occurs:
+// (1) If the KeyName can be found in the CSP, that key is used.
+// (2) If the KeyName does not exist and the KeyFile does exist, the key
+// in the KeyFile is installed into the CSP and used.
+// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
+// When specifying the KeyFile, the location of the KeyFile should be
+// relative to the project output directory which is
+// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
+// located in the project directory, you would specify the AssemblyKeyFile
+// attribute as [assembly: AssemblyKeyFile("..\..\mykey.snk")]
+// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+// documentation for more information on this.
+//
+
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
+[assembly: AssemblyKeyName("")]
+
+
+[assembly: ComVisibleAttribute(false)]
diff --git a/Ivy/Properties/Settings.Designer.cs b/Ivy/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..96b0f2d
--- /dev/null
+++ b/Ivy/Properties/Settings.Designer.cs
@@ -0,0 +1,80 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.42
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace IvyBus.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("3")]
+ public int IvyProtocolVersion {
+ get {
+ return ((int)(this["IvyProtocolVersion"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool IvyPing {
+ get {
+ return ((bool)(this["IvyPing"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool IvyDebug {
+ get {
+ return ((bool)(this["IvyDebug"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string IvyBus {
+ get {
+ return ((string)(this["IvyBus"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string AppName {
+ get {
+ return ((string)(this["AppName"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string ReadyMessage {
+ get {
+ return ((string)(this["ReadyMessage"]));
+ }
+ }
+ }
+}
diff --git a/Ivy/Properties/Settings.settings b/Ivy/Properties/Settings.settings
new file mode 100644
index 0000000..f23b946
--- /dev/null
+++ b/Ivy/Properties/Settings.settings
@@ -0,0 +1,24 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="IvyBus.Properties" GeneratedClassName="Settings">
+ <Profiles />
+ <Settings>
+ <Setting Name="IvyProtocolVersion" Type="System.Int32" Scope="Application">
+ <Value Profile="(Default)">3</Value>
+ </Setting>
+ <Setting Name="IvyPing" Type="System.Boolean" Scope="Application">
+ <Value Profile="(Default)">False</Value>
+ </Setting>
+ <Setting Name="IvyDebug" Type="System.Boolean" Scope="Application">
+ <Value Profile="(Default)">False</Value>
+ </Setting>
+ <Setting Name="IvyBus" Type="System.String" Scope="Application">
+ <Value Profile="(Default)" />
+ </Setting>
+ <Setting Name="AppName" Type="System.String" Scope="Application">
+ <Value Profile="(Default)" />
+ </Setting>
+ <Setting Name="ReadyMessage" Type="System.String" Scope="Application">
+ <Value Profile="(Default)" />
+ </Setting>
+ </Settings>
+</SettingsFile> \ No newline at end of file
diff --git a/Ivy/Settings.cs b/Ivy/Settings.cs
new file mode 100644
index 0000000..17744f8
--- /dev/null
+++ b/Ivy/Settings.cs
@@ -0,0 +1,28 @@
+namespace IvyBus.Properties {
+
+
+ // This class allows you to handle specific events on the settings class:
+ // The SettingChanging event is raised before a setting's value is changed.
+ // The PropertyChanged event is raised after a setting's value is changed.
+ // The SettingsLoaded event is raised after the setting values are loaded.
+ // The SettingsSaving event is raised before the setting values are saved.
+ internal sealed partial class Settings {
+
+ public Settings() {
+ // // To add event handlers for saving and changing settings, uncomment the lines below:
+ //
+ // this.SettingChanging += this.SettingChangingEventHandler;
+ //
+ // this.SettingsSaving += this.SettingsSavingEventHandler;
+ //
+ }
+
+ private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) {
+ // Add code to handle the SettingChangingEvent event here.
+ }
+
+ private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) {
+ // Add code to handle the SettingsSaving event here.
+ }
+ }
+}
diff --git a/Ivy/app.config b/Ivy/app.config
new file mode 100644
index 0000000..e5b53b3
--- /dev/null
+++ b/Ivy/app.config
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+ <configSections>
+ <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
+ <section name="IvyBus.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
+ </sectionGroup>
+ </configSections>
+ <applicationSettings>
+ <IvyBus.Properties.Settings>
+ <setting name="IvyProtocolVersion" serializeAs="String">
+ <value>3</value>
+ </setting>
+ <setting name="IvyPing" serializeAs="String">
+ <value>False</value>
+ </setting>
+ <setting name="IvyDebug" serializeAs="String">
+ <value>False</value>
+ </setting>
+ <setting name="IvyBus" serializeAs="String">
+ <value />
+ </setting>
+ <setting name="AppName" serializeAs="String">
+ <value />
+ </setting>
+ <setting name="ReadyMessage" serializeAs="String">
+ <value />
+ </setting>
+ </IvyBus.Properties.Settings>
+ </applicationSettings>
+</configuration> \ No newline at end of file