/* Copyright (C) Arnaud Houdelette 2012-2014 Copyright (C) Emmanuel Langlois 2012-2014 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ using System; using System.Threading; using System.IO.Ports; using System.Xml; using System.Collections.Generic; namespace DMX2 { public class DriverBoitierV2 : DriverDMX, IEventProvider { struct dmxState { public dmxState(int _dmx, byte _value, byte _lastvalue){ value=_value; dmx=_dmx; lastvalue = _lastvalue; } public int dmx; public byte value; public byte lastvalue; } enum etatAutomate { Deconnecte, Transmission, Erreur, Fin } const int timeout = 200; // tampons Entrée/Sortie public byte[] inputbuffer = new byte[532]; byte[] outputbuffer = new byte[1026]; //Thread de boucle Thread loopthread=null; public UniversDMX patch1=null; public UniversDMX patch2=null; string portname = ""; SerialPort serial = null; int brk = 150; int mab = 50; int nbc1 = 512; byte flag_merge1 = 1; byte flag_merge2 = 1; bool reinit=false ; public void ReInit () { reinit=true; } public int Break { get { return brk; } } public int Mab { get { return mab; } } public byte Flag_merge1 { get { return flag_merge1; } } public byte Flag_merge2 { get { return flag_merge2; } } public DriverBoitierV2 (string serialport, string id): base(id) { portname = serialport; outputbuffer[0]=27; outputbuffer[1]=68; Start(); } bool paramFlag = false; public void SetBreak( int _brk, int _mab, byte _merge1, byte _merge2) { brk = _brk; mab = _mab; flag_merge1 = _merge1; flag_merge2 = _merge2; paramFlag = true; } void Start () { if (loopthread == null) { loopthread = new Thread(new ThreadStart(MainLoop)); loopthread.Start(); } } void Connection () { #if DEBUG Console.WriteLine ("DriverV2.Connection()"); #endif if (serial != null) { serial.Close (); serial.Dispose (); serial = null; } if (!System.IO.File.Exists (portname)) { Thread.Sleep (200); return; } serial = new SerialPort (portname, 460800, Parity.None, 8, StopBits.One); try { serial.Open (); Attente(DateTime.Now.AddMilliseconds(2000)); if(Synchronisation()) etat = etatAutomate.Transmission; else { serial.Close(); etat = etatAutomate.Deconnecte; } } #if DEBUG catch (Exception ex) { Console.WriteLine("DriverV2:Connection : {0}",ex); #else catch { #endif etat = etatAutomate.Deconnecte; Thread.Sleep (500); } } /// /// Synchronise le pilote et le boitier ... /// Après connexion ou erreur /// bool Synchronisation () { //return true; #if DEBUG Console.WriteLine ("DriverV2.Synchronisation()"); #endif if (serial == null) return false; if (!serial.IsOpen) return false; // Au cas ou le boitier attends une fin de commande : envoi 1030 octets a 0 (le boitier ignorera tout seul la suite) byte[] tmpBuffer = new byte[1030]; serial.Write (tmpBuffer, 0, 1030); // On attends un peu Thread.Sleep (300); // Vide le buffer d'entree if (serial.BytesToRead > 0) serial.ReadExisting (); if(serial.BytesToWrite > 0) Console.WriteLine("Les infos partent pas ..."); // on envoie Esc 'A' tmpBuffer [0] = 27; tmpBuffer [1] = 65; serial.Write (tmpBuffer, 0, 2); // On attends un peu if(!WaitForData (1)) { return false; } paramFlag = true; serial.Read(tmpBuffer,0,1); if(tmpBuffer[0] == 65) return true; return false; } volatile etatAutomate etat = etatAutomate.Deconnecte; DateTime finAttente = DateTime.Now; int compteErreur = 0; void MainLoop () { //DateTime t = DateTime.Now; while (!disposed && etat != etatAutomate.Fin) { try { switch (etat) { case etatAutomate.Deconnecte: Connection (); compteErreur = 0; break; case etatAutomate.Transmission: finAttente = DateTime.Now.AddMilliseconds (22); //Console.WriteLine(DateTime.Now-t); //t = DateTime.Now; EnvoiTrame (); Reception (); Attente (finAttente); if (paramFlag) Parametrage (); if (reinit) EnvoieReInit(); break; case etatAutomate.Erreur: #if DEBUG Console.WriteLine("DriverV2 : etatAutomate.Erreur"); #endif compteErreur ++; Deconnecte (); Attente (DateTime.Now.AddSeconds (2)); break; // case etatAutomate.Parametrage: // EnvoiParam(); // break; // case etatAutomate.Reset: // EnvoiReset(); // break; } } catch (Exception ex) { Console.WriteLine("Exception dans DriverV2 : {0}",ex); if(etat != etatAutomate.Fin) etat = etatAutomate.Erreur; } } Deconnecte(); } void Attente (DateTime date) { int sleeptime = (int) (date - DateTime.Now).TotalMilliseconds-1; if(sleeptime>2) Thread.Sleep(sleeptime); while (DateTime.Now 0) Thread.Sleep(1); serial.Write(outputbuffer,0,outputbuffer.Length); } catch (Exception ex) { Console.WriteLine("Exception Envoi {0}",ex); etat = etatAutomate.Erreur; } } bool WaitForData (int len) { int wcnt =0 ; while (serial.BytesToRead < len) { Thread.Sleep (1); if (++wcnt > timeout) return false; } return true; } // void EnvoiParam () // { // throw new NotImplementedException (); // } // // void EnvoiReset () // { // throw new NotImplementedException (); // } byte flag_input; void Reception () { try { if(!serial.IsOpen || etat == etatAutomate.Erreur) { etat = etatAutomate.Erreur; return; } if(!WaitForData (inputbuffer.Length)) { etat = etatAutomate.Erreur; Console.WriteLine("DriverV2.Reception : attente depassee"); return ; } serial.Read(inputbuffer,0,inputbuffer.Length); if (flag_input != inputbuffer[0]) { flag_input = inputbuffer[0]; Console.WriteLine(flag_input ); } if(serial.BytesToRead>0) serial.ReadExisting (); ProcessData(); compteErreur= 0; } catch (Exception ex) { Console.WriteLine(serial.BytesToRead); Console.WriteLine("Exception Reception {0}",ex); etat = etatAutomate.Erreur; } } void Parametrage () { paramFlag = false; if (!serial.IsOpen) { etat = etatAutomate.Erreur; return; } byte[] tmpBuffer = new byte[6]; tmpBuffer [0] = 27; // Esc tmpBuffer [1] = 66; // 'B' tmpBuffer [2] = // nb circuits (byte)(nbc1 / 2 - 1); tmpBuffer [3] = (byte)brk; tmpBuffer [4] = (byte)mab; tmpBuffer [5] = (byte) (flag_merge1 + flag_merge2 * 2); serial.Write (tmpBuffer, 0, tmpBuffer.Length); if(!WaitForData (1)) { etat = etatAutomate.Erreur; return ; } serial.Read(tmpBuffer,0,1); if(tmpBuffer[0] != 66) etat = etatAutomate.Erreur; } void EnvoieReInit() { reinit = false; if (!serial.IsOpen) { etat = etatAutomate.Erreur; return; } byte[] tmpBuffer = new byte[2]; tmpBuffer [0] = 27; // Esc tmpBuffer [1] = 67; // 'B' serial.Write (tmpBuffer, 0, tmpBuffer.Length); if(!WaitForData (1)) { etat = etatAutomate.Erreur; return ; } serial.Read(tmpBuffer,0,1); if(tmpBuffer[0] != 67) etat = etatAutomate.Erreur; } void ProcessData () { lock(watchdmx) foreach (int dmx in watchdmx) { if( inputbuffer[dmx-1]!= lastVal[dmx]){ eventsPending.Enqueue(new dmxState(dmx,inputbuffer[dmx-1],lastVal[dmx])); lastVal[dmx] = inputbuffer[dmx-1]; } } } public override void Dispose () { disposed = true; etat = etatAutomate.Fin; if (loopthread != null) { loopthread.Join (); loopthread = null; } if(serial != null) serial.Dispose(); } #region implemented abstract members of DMX2.DriverDMX public override Gtk.Widget GetUI () { return new DriverBoitierV2UI(this); } #endregion #region IEventProvider implementation List watchdmx = new List(); Dictionary lastVal = new Dictionary(); static System.Text.RegularExpressions.Regex regexEventID = new System.Text.RegularExpressions.Regex( @"BV2-D(?\d+)?", System.Text.RegularExpressions.RegexOptions.Compiled); System.Collections.Concurrent.ConcurrentQueue eventsPending = new System.Collections.Concurrent.ConcurrentQueue(); bool IEventProvider.Bind (string eventId) { var res = regexEventID.Match (eventId); if (res.Success) { int dmx = int.Parse (res.Groups ["dmx"].Value); if(dmx<0||dmx>511) return false; lock(watchdmx){ if(!watchdmx.Contains(dmx)) { watchdmx.Add(dmx); lastVal[dmx] = 0; } } return true; } return false; } void IEventProvider.Unbind (string eventId) { var res = regexEventID.Match (eventId); if (res.Success) { int dmx = int.Parse (res.Groups ["dmx"].Value); if(dmx<1||dmx>512) return; lock(watchdmx) watchdmx.Remove (dmx); return; } return; } Gtk.Menu IEventProvider.GetProviderSubMenu (EventManager.EventMenuData state, Gtk.ButtonPressEventHandler handler) { Gtk.Menu retmenu = new Gtk.Menu (); Gtk.MenuItem evmenuitem = new Gtk.MenuItem ("Entrée DMX"); retmenu.Add (evmenuitem); Gtk.Menu evmenu = new Gtk.Menu (); evmenuitem.Submenu = evmenu; for (int c = 0; c<512; c+=100) { Gtk.MenuItem citem = new Gtk.MenuItem(string.Format("De {0} à {1}", c==0?1:c, c==500?512:c+99 )); Gtk.Menu cmenu = new Gtk.Menu(); citem.Submenu = cmenu; evmenu.Add(citem); for(int d=0 ; d<100;d+=10){ if(c+d>512) break; Gtk.MenuItem ditem = new Gtk.MenuItem(string.Format("De {0} à {1}", c+d==0?1:c+d, c+d==510?512:c+d+9 )); Gtk.Menu dmenu = new Gtk.Menu(); ditem.Submenu = dmenu; cmenu.Add(ditem); for(int u=0; u<10;u++){ if(c+d+u==0) continue; if(c+d+u>512) break; Gtk.MenuItem uitem = new Gtk.MenuItem(string.Format("Entrée DMX {0}",c+d+u)); uitem.Data[EventManager.EventIdKey] = string.Format("BV2-D{0}",c+d+u); uitem.Data[EventManager.StateKey] = state; uitem.ButtonPressEvent += handler; dmenu.Add (uitem); } } } return retmenu; } void IEventProvider.ProcessEvents (EventManagerCallback callback) { dmxState dmxs; EventData evd; while (eventsPending.TryDequeue(out dmxs)) { evd.id= string.Format("BV2-D{0}",dmxs.dmx ); evd.value = dmxs.value; evd.prev_value = dmxs.lastvalue; callback(evd); } } string IEventProvider.MenuName { get { return "Boitier V2"; } } IFeedbackInfo IEventProvider.GetFeedbackInfo (string eventId) { return null; } #endregion #region implemented abstract members of DMX2.DriverDMX public override void Save (System.Xml.XmlElement parent) { System.Xml.XmlElement el = parent.OwnerDocument.CreateElement ("DriverBoitierV2"); parent.AppendChild (el); el.SetAttribute ("portname", portname.ToString ()); el.SetAttribute ("id", ID); if(patch1!=null) el.SetAttribute ("univers1", patch1.Nom); if(patch2!=null) el.SetAttribute ("univers2", patch2.Nom); el.SetAttribute("mab",mab.ToString()); el.SetAttribute("brk",brk.ToString()); el.SetAttribute("merge1",(flag_merge1!=0).ToString()); el.SetAttribute("merge2",(flag_merge2!=0).ToString()); } #endregion public static new DriverDMX Load(Conduite conduite, XmlElement el) { //System.Xml.XmlElement xmlE; string port = el.GetAttribute("portname"); if(! System.IO.File.Exists(port)) return null; string id = el.GetAttribute("id"); DriverBoitierV2 drv = new DriverBoitierV2(port,id); if(el.HasAttribute("univers1")) { string univ = el.GetAttribute("univers1"); foreach (UniversDMX u in conduite.Patches){ if(u.Nom== univ){ drv.patch1 = u; break; } } } if(el.HasAttribute("univers2")) { string univ = el.GetAttribute("univers2"); foreach (UniversDMX u in conduite.Patches){ if(u.Nom== univ){ drv.patch2 = u; break; } } } int mab,brk; byte merge1,merge2; mab = int.Parse(el.TryGetAttribute("mab","150")); brk = int.Parse(el.TryGetAttribute("brk","50")); merge1 = (byte)( bool.Parse(el.TryGetAttribute("merge1","True"))?1:0 ); merge2 = (byte)( bool.Parse(el.TryGetAttribute("merge2","True"))?1:0 ); drv.SetBreak(brk,mab,merge1,merge2); return drv; } } }