/* 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.Collections.Generic; using System.Diagnostics; using System.Collections.ObjectModel; using System.Threading; using System.Xml; namespace DMX2 { public class Conduite : IDisposable { // Conduite courante static Conduite courante = null; public static Conduite Courante { get { return courante; } } Thread tickThread = null; // Thread de Mise a jour en arriere plan DateTime dernierTick; // Derniere execution du timer DateTime derniereMaj; // Derniere MAJ de l'affichage string _name; // Nom de la conduite int master=100; readonly List circuits = new List(); readonly List univers = new List(); readonly List drivers = new List(); readonly List sequenceurs= new List(); readonly SequenceurMaitre seqmaitre = new SequenceurMaitre(); readonly EventManager eventManager = new EventManager(); // Gestion des fournisseurs d'evenements actionEventTargetEx masterEventTarget; // Recepteur d'evenements pour le master readonly MidiEventProvider midip=null; bool running=true; public Conduite (): this(true) { } Conduite (bool startthread) { // Conduite courante => la derniere instanciee if (courante != null) courante.Dispose (); courante = this; // Les ID reprennent à 1 Circuit.maxid = 1; Sequenceur.maxid = 1; UniversDMX.maxid = 1; // Crée l'univers par défaut var u = new UniversDMX (); Patches.Add (u); u.Nom = "Univers par Défaut"; // La conduite peux recevoir des evenements midi midip = new MidiEventProvider (eventManager); masterEventTarget = new actionEventTargetEx ( delegate(EventData data) { Master = 100 * data.value / 255; return true; }, delegate{ return master * 255 /100; }, false ); if (startthread) StartThread(); } void StartThread() { if(tickThread != null) return; // Démarrage du thread tickThread = new Thread (new ThreadStart (ThreadLoop)); tickThread.Start (); } public MidiEventProvider Midi { get { return midip; } } public EventManager EventManager { get { return eventManager; } /* set { eventManager = value; }*/ } public ReadOnlyCollection Circuits { get { return circuits.AsReadOnly(); } } public Circuit NouveauCircuit () { lock (this) { Circuit c = new Circuit (); circuits.Add (c); return c; } } public Circuit GetCircuitByID (int i) { foreach(Circuit c in circuits) if(c.ID == i) return c; return null; } public string Name { get { return _name; } set { _name = value; } } public int Master { get { return master; } set { master= value; } } public bool Pause { get; set; } public bool BlackOut { get; set ; } public SequenceurMaitre SequenceurMaitre { get { return seqmaitre; } } public List Drivers { get { return drivers; } } public void DriversAdd (DriverDMX drv) { drivers.Add(drv); IEventProvider evp = drv as IEventProvider; if(evp!=null) eventManager.RegisterProvider(evp); } public void DriversRemove (DriverDMX drv) { drivers.Remove(drv); IEventProvider evp = drv as IEventProvider; if(evp!=null) eventManager.UnregisterProvider(evp); } public DriverDMX GetDriverByID(string ID){ foreach (var driver in drivers) if(ID== driver.ID) return driver; return null; } public ReadOnlyCollection Sequenceurs { get { return sequenceurs.AsReadOnly(); } } public void AjoutSequenceur (Sequenceur seq) { lock (this) { sequenceurs.Add(seq); seq.Renamed += SequenceurRenomme; } } void SequenceurRenomme (object sender, Sequenceur.SeqRenamedEventArgs e) { MainWindow.Win.NextUpdateFull(); } public Sequenceur GetSeqByID (int i) { foreach(Sequenceur seq in sequenceurs) if(seq.ID == i) return seq; return null; } public List Patches { get { return univers; } } Circuit circuitTelecomande=null; int circuitTelecomandeVal=0; public Circuit CircuitTelecomande { get { return circuitTelecomande; } set { circuitTelecomande = value; } } public int CircuitTelecomandeVal { get { return circuitTelecomandeVal; } set { circuitTelecomandeVal = value; } } // On utilise un thread qui boucle au lieu d'un timer. // C'est un peu moins précis, mais ca consomme beaucoup moins de ressources const int LOOPTIME = 10; void ThreadLoop () { Thread.CurrentThread.Priority = ThreadPriority.AboveNormal; DateTime next; dernierTick = DateTime.Now; while (running) { try{ Tick(); } catch (Exception ex) // Gestion d'erreurs ... au cas ou ... on affiche et on continue { Info.Publish(ex.ToString()); } next = dernierTick.AddMilliseconds (LOOPTIME); while (DateTime.Now< next) Thread.Sleep(1); // Version plus précise, si besoin : // next = dernierTick.AddMilliseconds (LOOPTIME-1); // while (DateTime.Now< next) Thread.Sleep(1); // next = dernierTick.AddMilliseconds (LOOPTIME); // while (DateTime.Now TimeSpan.FromMilliseconds (LOOPTIME+2)) Info.Publish(string.Format ("{0}", deltaT)); lock (this) { seqmaitre.Tick (deltaT); // 'Actionne' les sequenceurs foreach (var seq in sequenceurs) { seq.Tick (deltaT); } // Mets a jour les valeurs circuits. if(BlackOut) { foreach (var c in circuits) c.ValeurCourante = 0; } else if(master != 100){ foreach (var c in circuits) { int val = 0; foreach (var seq in sequenceurs) { val = Math.Max (val, seq.ValeurCircuit (c)); } c.ValeurCourante = val * master / 100; } } else { foreach (var c in circuits) { int val = 0; if(circuitTelecomande==c) val = circuitTelecomandeVal; //else foreach (var seq in sequenceurs) { val = Math.Max (val, seq.ValeurCircuit (c)); } c.ValeurCourante = val; } } EventManager.ProcessEvents(); } } // Cette fonction retourne quasi immédiatement, même si il y'a beaucoup a faire sur l'affichage if(tickTime - derniereMaj > TimeSpan.FromMilliseconds(50)){ MainWindow.Win.ScheduleUpdate(); derniereMaj = DateTime.Now; } } public void BindMaster (string eventId) { if (eventId.Length == 0) { Conduite.Courante.EventManager.Unbind (masterEventTarget); return; } Conduite.Courante.EventManager.Bind(eventId,masterEventTarget); } #region IDisposable implementation bool disposed=false; public void Dispose () { if (disposed) return; if (courante == this) courante = null; if (tickThread != null) { running = false; tickThread.Join (50); tickThread.Abort (); } foreach (var seq in sequenceurs) { IDisposable idis = seq as IDisposable; if(idis!=null) idis.Dispose(); } foreach(var driver in Drivers) driver.Dispose(); tickThread=null; Midi.Dispose(); disposed=true; } #endregion #region Sauvegarde public XmlDocument Save () { XmlDocument xmlDoc = new XmlDocument (); XmlElement xmlRoot = xmlDoc.CreateElement ("Conduite"); xmlDoc.AppendChild (xmlRoot); xmlRoot.SetAttribute ("nom", this.Name); XmlElement xmlCircuits = xmlDoc.CreateElement ("Circuits"); xmlRoot.AppendChild (xmlCircuits); foreach (Circuit c in circuits) { c.Save (xmlCircuits); } XmlElement xmlSequenceurs = xmlDoc.CreateElement ("Sequenceurs"); xmlRoot.AppendChild (xmlSequenceurs); foreach (Sequenceur seq in sequenceurs) { seq.Save (xmlSequenceurs); } XmlElement xmlUniversList = xmlDoc.CreateElement ("ListeUnivers"); xmlRoot.AppendChild (xmlUniversList); foreach (UniversDMX univ in univers) { univ.Save (xmlUniversList); } XmlElement xmlDriverList = xmlDoc.CreateElement ("ListeDrivers"); xmlRoot.AppendChild (xmlDriverList); foreach (DriverDMX drv in drivers) { drv.Save(xmlDriverList); } XmlElement xmlMaster = xmlDoc.CreateElement("Master"); xmlMaster.SetAttribute("value",master.ToString()); EventManager.SaveBindings(xmlMaster,masterEventTarget); xmlRoot.AppendChild(xmlMaster); Midi.Save(xmlRoot); seqmaitre.Save(xmlRoot); return xmlDoc; } public Conduite (XmlDocument doc) : this(false) { //TODO : Gestion d'erreurs XmlElement root = doc.DocumentElement; _name = root.Attributes ["nom"].Value; foreach (var xc in root["Circuits"].ChildNodes) { Circuit c = Circuit.Load (xc as XmlElement); if (c != null) circuits.Add (c); } foreach (var xs in root["Sequenceurs"].ChildNodes) { Sequenceur s = Sequenceur.Load (this, xs as XmlElement); if (s != null) { sequenceurs.Add (s); s.Renamed += SequenceurRenomme; } } univers.Clear (); foreach (var xu in root["ListeUnivers"].ChildNodes) { UniversDMX u = UniversDMX.Load (this, xu as XmlElement); if (u != null) univers.Add (u); } if(root["ListeDrivers"]!=null) foreach (var xd in root["ListeDrivers"].ChildNodes) { DriverDMX drv = DriverDMX.Load(this,xd as XmlElement); if(drv != null) DriversAdd(drv); } XmlElement xmlMaster; if((xmlMaster = root["Master"])!=null) { master = int.Parse( xmlMaster.GetAttribute("value")); foreach(string id in EventManager.LoadBindings(xmlMaster)) BindMaster(id); } Midi.Load(root["Midi"]); seqmaitre = SequenceurMaitre.Load(this,root["SequenceurMaitre"]); StartThread(); } #endregion } public class Circuit { public static int maxid=1; string name; public const int SNLen= 8; public Circuit() { id=maxid++; Name = "Circuit n°" + id.ToString(); ShortName = "Cir" + id.ToString(); } public string Name { get { return name; } set { name = value; } } string shortName; public string ShortName { get { return shortName; } set { if (value.Length > SNLen) shortName = value.Substring(0,SNLen); else shortName = value; } } int id; public int ID { get { return id; } } int _curval; public int ValeurCourante { get { return _curval; } set { _curval = value; } } public void Save (XmlElement parent) { XmlElement el= parent.OwnerDocument.CreateElement ("Circuit"); parent.AppendChild(el); el.SetAttribute("ID",id.ToString()); el.SetAttribute("name",name); el.SetAttribute("shortName",shortName); } public static Circuit Load (XmlElement xc) { if(xc.Name!="Circuit") throw new ErreurLectureFichier(" attendu."); return new Circuit(xc); } private Circuit(XmlElement xc) { id = int.Parse(xc.GetAttribute("ID")); maxid = Math.Max (maxid,id+1); name = xc.GetAttribute("name"); shortName = xc.GetAttribute("shortName"); } } }