/* 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.Collections.ObjectModel; using System.Linq; using System.Threading; namespace DMX2 { public class SequenceurLineaire : Sequenceur { public class Effet { string _nom; public Effet (string nom, Dictionary valeurs, TimeSpan duree, TimeSpan transition) { _nom = nom; _valeurs = new Dictionary (valeurs); _duree = duree; _transition = transition; } public string Nom { get { return _nom; } set { _nom = value; } } Dictionary _valeurs; public int this [Circuit index] { get { if (!_valeurs.ContainsKey (index)) _valeurs.Add (index, 0); return _valeurs [index]; } } public Dictionary Valeurs { get { return _valeurs; } } public void RetireCircuit (Circuit c) { _valeurs.Remove (c); } TimeSpan _duree = TimeSpan.Zero ; TimeSpan _transition = TimeSpan.Zero; public TimeSpan Duree { get { return _duree; } set { _duree = value; } } public TimeSpan Transition { get { return _transition; } set { _transition = value; } } public void Save (System.Xml.XmlElement parent) { System.Xml.XmlElement el = parent.OwnerDocument.CreateElement ("Effet"); System.Xml.XmlElement xmlVal; parent.AppendChild (el); el.SetAttribute("nom",_nom); el.SetAttribute ("duree", _duree.ToString ()); el.SetAttribute ("transition", _transition.ToString ()); foreach (var valeur in _valeurs) { xmlVal = parent.OwnerDocument.CreateElement("Valeur"); el.AppendChild(xmlVal); xmlVal.SetAttribute("circuit",valeur.Key.ID.ToString()); xmlVal.SetAttribute("valeur",valeur.Value.ToString()); } } public static Effet Load (Conduite conduite, System.Xml.XmlElement el) { Dictionary valeurs = new Dictionary (); foreach (var xv in el.GetElementsByTagName("Valeur")) { System.Xml.XmlElement xval = xv as System.Xml.XmlElement; valeurs.Add( conduite.GetCircuitByID(int.Parse(xval.GetAttribute("circuit"))), int.Parse(xval.GetAttribute("valeur")) ); } return new Effet( el.GetAttribute("nom"),valeurs, TimeSpan.Parse(el.GetAttribute("duree")), TimeSpan.Parse(el.GetAttribute("transition")) ); } } #region EventTargets class circuitEventTarget : IEventTarget { Circuit circuit; Dictionary valeursrecues= new Dictionary(); SequenceurLineaire seq; int max=0, signe=-2; bool attache; public bool Attache { get { return attache; } set { signe=-2; attache = value; } } public circuitEventTarget(SequenceurLineaire _seq, Circuit _c){ seq=_seq; circuit=_c; } bool IEventTarget.FireEvent (EventData data) { valeursrecues[data.id]=data.value; max = valeursrecues.Values.Max(); if (!Attache) { int val = seq.ValeurBruteCircuit (circuit); int cs = Math.Sign (val - max); if (signe == -2) signe=cs; if (cs==0 || cs!=signe) Attache=true; else return true ; } seq.ChangeValeur(circuit,max); return true; } void IEventTarget.Bind (string id) { valeursrecues[id] = 0; } void IEventTarget.Unbind (string id) { valeursrecues.Remove(id); } List feedbacks = new List(); bool IEventTarget.CanFeedback { get { return true; } } void IEventTarget.AddFeedback (IFeedbackInfo info) { feedbacks.Add(info); } public void FeedBack(byte data){ foreach (var fb in feedbacks) fb.FeedBack(data); } /*IEnumerable IEventTarget.IDs { get { return valeursrecues.Keys; } }*/ } #endregion TimeSpan timeStamp = TimeSpan.Zero; TimeSpan tempsTransition = TimeSpan.Zero; List effets = new List (); Effet effetcourrant = null; bool enTransition = false; bool paused=false; List circuitsSeq = new List (); Dictionary valeurscourantes = new Dictionary (); Dictionary valeursinitiales = new Dictionary (); Dictionary valeurschangees = new Dictionary (); Dictionary targets = new Dictionary(); actionEventTarget masterEventTarget=null; actionEventTarget goNextEventTarget=null; actionEventTarget goBackEventTarget=null; SeqLinUI ui = null; int master = 100; public int Master { get { return master; } set { master = value; } } public SequenceurLineaire () { effetcourrant = new Effet ("",valeurscourantes , TimeSpan.Zero, TimeSpan.Zero); masterEventTarget = new actionEventTarget ( delegate(EventData data) { Master = 100 * data.value / 255; return true; } ); goNextEventTarget = new actionEventTarget ( delegate(EventData data) { if(data.value==255) IndexEffetCourrant++; return true; } ); goBackEventTarget = new actionEventTarget ( delegate(EventData data) { if(data.value==255) IndexEffetCourrant--; return true; } ); } public TimeSpan TimeStamp { get { return timeStamp; } } public ReadOnlyCollection Circuits { get { return circuitsSeq.AsReadOnly (); } } public ReadOnlyCollection Effets { get { return effets.AsReadOnly (); } } public TimeSpan TempsTransition { get { return tempsTransition; } } public void ChangeCircuits (System.Collections.Generic.List list) { foreach (var c in circuitsSeq.ToArray()) { if (!list.Contains (c)) lock(this) RetireCircuit (c); } foreach (var c in list) if (!circuitsSeq.Contains (c)) lock(this) AjouteCircuit (c); circuitsSeq = list; } void AjouteCircuit (Circuit c) { valeurscourantes [c] = 0; valeursinitiales [c] = 0; } void RetireCircuit (Circuit c) { foreach (var ef in effets) { ef.RetireCircuit (c); } circuitsSeq.Remove (c); valeurscourantes.Remove (c); valeursinitiales.Remove (c); targets.Remove(c); } public override int ValeurCircuit (Circuit c) { lock(this) { if (!valeurscourantes.ContainsKey (c)) return 0; if (master != 100) return valeurscourantes [c] * master / 100; return valeurscourantes [c]; } } public int ValeurBruteCircuit (Circuit c) { // Appelé par l'interface, donc normalement, uniquement sur les circuits qui composent le sequenceur //if (!valeurscourantes.ContainsKey (c)) // return 0; return valeurscourantes [c]; } public void ChangeValeur (Circuit c, int value) { valeurschangees [c] = true; valeurscourantes [c] = value; lock(this) if(targets.ContainsKey(c)) targets[c].FeedBack((byte)value); } public bool EstChange (Circuit c) { return valeurschangees.ContainsKey (c); } public bool EnTransition (Circuit c) { if(!enTransition) return false ; return valeurscourantes [c] != effetcourrant [c]; } public bool EstCapture (Circuit c) { if(!targets.ContainsKey(c)) return false; return targets[c].Attache; } public bool Paused { get { return paused; } set { paused = value; } } public override void Tick (TimeSpan time) { if (paused) return; timeStamp += time; if (Monitor.TryEnter (this)) try { if (enTransition) { if (timeStamp < tempsTransition) { double progression = timeStamp.TotalMilliseconds / tempsTransition.TotalMilliseconds; foreach (Circuit c in circuitsSeq) { if (valeurscourantes [c] != effetcourrant [c] && !valeurschangees.ContainsKey (c)) { valeurscourantes [c] = (int)(progression * (effetcourrant [c] - valeursinitiales [c]) + valeursinitiales [c]); if(targets.ContainsKey(c)) targets[c].FeedBack((byte)valeurscourantes [c]); } } } else { FinDeTransition (); } } if (effetcourrant.Duree != TimeSpan.Zero && timeStamp >= effetcourrant.Duree) { int index = effets.IndexOf (effetcourrant) + 1; if (index < effets.Count) ChangeEffetCourrant (index); } } finally { Monitor.Exit (this); } } public void FinDeTransition () { lock(this) { enTransition = false; foreach (Circuit c in circuitsSeq) if (!valeurschangees.ContainsKey (c)){ valeurscourantes [c] = effetcourrant [c]; if(targets.ContainsKey(c)) targets[c].FeedBack((byte)valeurscourantes [c]); } } } public SequenceurLineaire.Effet EffetCourrant { get { return effetcourrant; } } public int IndexEffetCourrant { get { return effets.IndexOf(effetcourrant); } set { if(value>=0 && value < effets.Count) ChangeEffetCourrant(value); } } void ChangeEffetCourrant (int index) { lock (this) { effetcourrant = effets [index]; valeurschangees.Clear (); valeursinitiales = new Dictionary (valeurscourantes); tempsTransition = effetcourrant.Transition; enTransition = true; timeStamp = TimeSpan.Zero; if (ui != null) ui.EffetChange (); foreach(var t in targets.Values) t.Attache = false; } } public int SauveEffet (string nom, TimeSpan duree, TimeSpan transition) { lock (this) { effets.Add (effetcourrant = new Effet (nom, valeurscourantes, duree, transition)); valeurschangees.Clear (); return effets.Count-1; } } public int InsereEffetApres (int index, string nom, TimeSpan duree, TimeSpan transition) { lock (this) { int pos = index+1; if (pos >= effets.Count) return SauveEffet(nom,duree,transition); effets.Insert (pos,effetcourrant = new Effet (nom, valeurscourantes, duree, transition)); valeurschangees.Clear (); CommandAdd(index); return pos; } } public void RemplaceEffet (int index) { lock (this) { Effet ef = effets[index]; effets[index] = new Effet (ef.Nom, valeurscourantes, ef.Duree, ef.Transition); effetcourrant = effets[index]; valeurschangees.Clear (); } } public void SupprimeEffet (int index) { lock (this) { effets.RemoveAt (index); CommandRemove(index); } } public int MonteEffet (int index) { lock (this) { if (index >= effets.Count || index < 1) return index; Effet ef = effets [index]; effets.RemoveAt (index); effets.Insert (index - 1, ef); CommandSwap (index - 1); return index - 1; } } public int BaisseEffet (int index) { lock (this) { if (index > effets.Count - 2 || index < 0) return index; Effet ef = effets [index]; effets.RemoveAt (index); effets.Insert (index + 1, ef); CommandSwap(index); return index + 1; } } /* private class circEvTarget : IEventTarget{ Circuit c; SequenceurLineaire seq; int s=-2; bool ok=false; public circEvTarget(SequenceurLineaire _seq, Circuit _c){ c=_c; seq=_seq; } #region IEventTarget implementation bool IEventTarget.FireEvent (EventData data) { if (!ok) { int val = seq.ValeurBruteCircuit (c); int cs = Math.Sign (val - data.value); if (s == -2) s =cs; if (cs==0 || cs!=s) ok=true; else return true ; } seq.ChangeValeur(c,data.value); return true; } #endregion } */ public void BindCircuitEvent (Circuit c, string eventId) { if (eventId.Length == 0) { if(!targets.ContainsKey(c)) return; Conduite.Courante.EventManager.Unbind(targets[c]); targets.Remove(c); return; } if(!targets.ContainsKey(c)) targets[c]=new circuitEventTarget(this,c); Conduite.Courante.EventManager.Bind(eventId,targets[c]); } public void BindMasterEvent (string eventId) { if(eventId.Length==0) { Conduite.Courante.EventManager.Unbind(masterEventTarget); return; } Conduite.Courante.EventManager.Bind(eventId,masterEventTarget); } public void BindEffetSuivantEvent (string eventId) { if (eventId.Length == 0) { Conduite.Courante.EventManager.Unbind (goNextEventTarget); return; } Conduite.Courante.EventManager.Bind(eventId,goNextEventTarget); } public void BindEffetPrecedentEvent (string eventId) { if (eventId.Length == 0) { Conduite.Courante.EventManager.Unbind (goBackEventTarget); return; } Conduite.Courante.EventManager.Bind(eventId,goBackEventTarget); } public override void Save (System.Xml.XmlElement parent) { System.Xml.XmlElement el = parent.OwnerDocument.CreateElement ("SequenceurLineaire"); System.Xml.XmlElement xmlEl; parent.AppendChild (el); el.SetAttribute ("id", ID.ToString ()); el.SetAttribute ("name", Name); //el.SetAttribute ("master", master.ToString ()); el.AppendChild(xmlEl = parent.OwnerDocument.CreateElement ("Master")); xmlEl.SetAttribute("value",master.ToString()); Conduite.Courante.EventManager.SaveBindings(xmlEl,masterEventTarget); xmlEl = parent.OwnerDocument.CreateElement ("EffetSuivant"); if(Conduite.Courante.EventManager.SaveBindings(xmlEl,goNextEventTarget )) el.AppendChild(xmlEl); xmlEl = parent.OwnerDocument.CreateElement ("EffetPrecedent"); if(Conduite.Courante.EventManager.SaveBindings(xmlEl,goBackEventTarget )) el.AppendChild(xmlEl); foreach (Circuit c in circuitsSeq) { el.AppendChild(xmlEl = parent.OwnerDocument.CreateElement ("CircuitSeq")); xmlEl.SetAttribute("id",c.ID.ToString()); if(targets.ContainsKey(c)) Conduite.Courante.EventManager.SaveBindings(xmlEl,targets[c]); } foreach (Effet ef in effets) { ef.Save(el); } } public override SequenceurUI GetUI () { if (ui == null) { ui = new SeqLinUI (this); ui.Destroyed += UiDestroyed; } return ui; } void UiDestroyed (object sender, EventArgs e) { ui = null; } public static new SequenceurLineaire Load (Conduite conduite, System.Xml.XmlElement el) { SequenceurLineaire seq = new SequenceurLineaire(); seq.LoadSeq(conduite,el); return seq; } private void LoadSeq (Conduite conduite, System.Xml.XmlElement el) { System.Xml.XmlElement xmlE; ID = int.Parse (el.GetAttribute ("id")); Name = el.GetAttribute ("name"); if ((xmlE = el["Master"]) != null) { master = int.Parse (xmlE.TryGetAttribute("value","100")); foreach(string id in EventManager.LoadBindings(xmlE)) BindMasterEvent(id); } else master = int.Parse (el.TryGetAttribute("master","100")); if ((xmlE = el["EffetSuivant"])!= null) foreach(string id in EventManager.LoadBindings(xmlE)) BindEffetSuivantEvent(id); if ((xmlE = el["EffetPrecedent"])!= null) foreach(string id in EventManager.LoadBindings(xmlE)) BindEffetPrecedentEvent(id); foreach (var xc in el.GetElementsByTagName("CircuitSeq")) { System.Xml.XmlElement xcir = xc as System.Xml.XmlElement; Circuit c = conduite.GetCircuitByID (int.Parse (xcir.GetAttribute ("id"))); circuitsSeq.Add (c); AjouteCircuit (c); foreach(string id in EventManager.LoadBindings (xcir)) BindCircuitEvent(c,id); } foreach (var xe in el.GetElementsByTagName("Effet")) effets.Add(Effet.Load(conduite,xe as System.Xml.XmlElement)); } static System.Text.RegularExpressions.Regex regexCommandExec = new System.Text.RegularExpressions.Regex( @"(?\d+)(t(?\d+))?", System.Text.RegularExpressions.RegexOptions.Compiled); static System.Text.RegularExpressions.Regex regexCommandProcess = new System.Text.RegularExpressions.Regex( @"(?\d+)(?(t\d+)?)?", System.Text.RegularExpressions.RegexOptions.Compiled); public override void Command (string command) { lock (this) { var cmd = regexCommandExec.Match(command); if (cmd.Success) { if (cmd.Groups ["effet"].Success) { int effet = int.Parse (cmd.Groups ["effet"].Value) - 1; if (effet < effets.Count) ChangeEffetCourrant (effet); } if (cmd.Groups ["transition"].Success) { int transition = int.Parse (cmd.Groups ["transition"].Value); tempsTransition = TimeSpan.FromMilliseconds( transition *100); } } } } void CommandAdd (int index) { lock (Conduite.Courante.SequenceurMaitre) { string[] commands = Conduite.Courante.SequenceurMaitre.GetCommands (this); for (int i = 0; i < commands.Length; i++) { var cmd = regexCommandProcess.Match(commands[i]); if(cmd.Success){ int ef = int.Parse(cmd.Groups["effet"].Value); if (ef-1>index) { ef++; commands[i] = ef.ToString() + cmd.Groups["params"].Value; } } } Conduite.Courante.SequenceurMaitre.SetCommands(this,commands); } } void CommandRemove (int index) { lock (Conduite.Courante.SequenceurMaitre) { string[] commands = Conduite.Courante.SequenceurMaitre.GetCommands (this); for (int i = 0; i < commands.Length; i++) { var cmd = regexCommandProcess.Match(commands[i]); if(cmd.Success){ int ef = int.Parse(cmd.Groups["effet"].Value); if (ef-1 == index) commands[i] = string.Empty; else if (ef-1>index) { ef--; commands[i] = ef.ToString() + cmd.Groups["params"].Value; } } } Conduite.Courante.SequenceurMaitre.SetCommands(this,commands); } } void CommandSwap (int index) { lock (Conduite.Courante.SequenceurMaitre) { string[] commands = Conduite.Courante.SequenceurMaitre.GetCommands (this); // numeros a swapper int a = index+1; int b = index+2; for (int i = 0; i < commands.Length; i++) { var cmd = regexCommandProcess.Match(commands[i]); if(cmd.Success){ int ef = int.Parse(cmd.Groups["effet"].Value); if (ef == a) commands[i] = b.ToString() + cmd.Groups["params"].Value; if (ef == b) commands[i] = a.ToString() + cmd.Groups["params"].Value; } } Conduite.Courante.SequenceurMaitre.SetCommands(this,commands); } } } }