550 lines
11 KiB
C#
550 lines
11 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 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 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;
|
|
}
|
|
}
|
|
|
|
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<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;
|
|
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 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 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);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|