loupiottes/DMX-2.0/Conduite.cs

585 lines
12 KiB
C#

/*
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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Threading;
using System.Xml;
namespace DMX2
{
public sealed 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<Circuit> circuits = new List<Circuit>();
readonly List<UniversDMX> univers = new List<UniversDMX>();
readonly List<DriverDMX> drivers = new List<DriverDMX>();
readonly List<Sequenceur> sequenceurs= new List<Sequenceur>();
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<Circuit> 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<DriverDMX> 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<Sequenceur> 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<UniversDMX> Patches {
get {
return univers;
}
}
HashSet<Circuit> circuitTelecomande=new HashSet<Circuit>();
int circuitTelecomandeVal=0;
public HashSet<Circuit> CircuitTelecomande {
get {
return circuitTelecomande;
}
/*set {
circuitTelecomande = value;
}*/
}
public int CircuitTelecomandeVal {
get {
return circuitTelecomandeVal;
}
set {
circuitTelecomandeVal = value;
}
}
bool IsCircuitTel(Circuit c){
if (circuitTelecomande.Count == 0)
return false;
return circuitTelecomande.Contains (c);
}
// 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<next) Thread.Sleep(0);
}
}
void Tick ()
{
DateTime tickTime = DateTime.Now;
TimeSpan deltaT = tickTime - dernierTick;
dernierTick = tickTime;
if (!Pause) {
if (deltaT > 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));
}
if (IsCircuitTel (c))
val = circuitTelecomandeVal;
c.ValeurCourante = val * master / 100;
}
} else {
foreach (var c in circuits) {
int val = 0;
foreach (var seq in sequenceurs) {
val = Math.Max (val, seq.ValeurCircuit (c));
}
if (IsCircuitTel (c))
val = circuitTelecomandeVal;
c.ValeurCourante = val;
}
}
/*if (circuitTelecomande.Count > 0) {
foreach (Circuit c in circuitTelecomande) {
c.ValeurCourante = circuitTelecomandeVal * master / 100;
}
}*/
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("<Circuit> 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");
}
}
}