loupiottes/DMX-2.0/Conduite.cs

482 lines
9.6 KiB
C#

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;
List<Circuit> circuits = new List<Circuit>();
List<UniversDMX> univers = new List<UniversDMX>();
List<DriverDMX> drivers = new List<DriverDMX>();
List<Sequenceur> sequenceurs= new List<Sequenceur>();
SequenceurMaitre seqmaitre = new SequenceurMaitre();
EventManager eventManager = new EventManager(); // Gestion des fournisseurs d'evenements
actionEventTarget masterEventTarget; // Recepteur d'evenements pour le master
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
MidiEventProvider midip = new MidiEventProvider (eventManager);
masterEventTarget = new actionEventTarget (
delegate(EventData data) {
Master = 100 * data.value / 255;
return true;
}
);
if (startthread) StartThread();
}
void StartThread()
{
if(tickThread != null) return;
// Démarrage du thread
tickThread = new Thread (new ThreadStart (ThreadLoop));
tickThread.Start ();
}
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 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;
}
}
// 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));
}
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));
}
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 driver in Drivers)
driver.Dispose();
tickThread=null;
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 xmlMaster = xmlDoc.CreateElement("Master");
xmlMaster.SetAttribute("value",master.ToString());
EventManager.SaveBindings(xmlMaster,masterEventTarget);
xmlRoot.AppendChild(xmlMaster);
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);
}
XmlElement xmlMaster;
if((xmlMaster = root["Master"])!=null)
{
master = int.Parse( xmlMaster.GetAttribute("value"));
foreach(string id in EventManager.LoadBindings(xmlMaster))
BindMaster(id);
}
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");
}
}
}