2 using System.Collections.Generic;
4 using System.Reflection;
5 using System.Threading;
6 using DirectOutput.Cab;
8 using DirectOutput.General;
9 using DirectOutput.GlobalConfiguration;
10 using DirectOutput.LedControl.Loader;
11 using DirectOutput.PinballSupport;
12 using DirectOutput.Scripting;
13 using DirectOutput.Table;
14 using DirectOutput.General.Statistics;
16 namespace DirectOutput
41 get {
return _Scripts; }
42 private set { _Scripts = value; }
57 get {
return _Table; }
58 private set { _Table = value; }
70 get {
return _Cabinet; }
71 private set { _Cabinet = value; }
86 get {
return _Alarms; }
87 private set { _Alarms = value; }
100 get {
return _GlobalConfig; }
101 private set { _GlobalConfig = value; }
109 #region Init & Finish
116 public void Init(
string GlobalConfigFilename =
"",
string TableFilename =
"",
string RomName =
"")
119 bool GlobalConfigLoaded =
true;
125 if (!GlobalConfigFilename.IsNullOrWhiteSpace())
127 FileInfo GlobalConfigFile =
new FileInfo(GlobalConfigFilename);
133 GlobalConfigLoaded =
false;
150 throw new Exception(
"DirectOutput framework could initialize global config.\n Inner exception: {0}".Build(E.Message), E);
165 throw new Exception(
"DirectOutput framework could initialize the log file.\n Inner exception: {0}".Build(E.Message), E);
172 if (GlobalConfigLoaded)
174 Log.
Write(
"Global config loaded from: {0}".Build(GlobalConfigFilename));
178 if (!GlobalConfigFilename.IsNullOrWhiteSpace())
180 Log.
Write(
"Could not find or load theGlobalConfig file {0}".Build(GlobalConfigFilename));
184 Log.
Write(
"No GlobalConfig file loaded. Using newly instanciated GlobalConfig object instead.");
202 if (!TableFilename.IsNullOrWhiteSpace())
215 Log.
Write(
"Will load cabinet config file: {0}".Build(CCF.FullName));
222 Log.
Write(
"Cabinet config file has AutoConfig feature enable. Calling AutoConfig.");
225 Log.
Write(
"Cabinet config loaded successfully from {0}".Build(CCF.FullName));
229 Log.
Exception(
"A exception occured when load cabinet config file: {0}".Build(CCF.FullName), E);
236 Log.
Write(
"No cabinet config file loaded. Will use AutoConfig.");
251 if (!TableFilename.IsNullOrWhiteSpace())
253 FileInfo TableFile =
new FileInfo(TableFilename);
257 Log.
Write(
"Will load table config from {0}".Build(TCF.FullName));
262 Log.
Write(
"Table config loaded successfully from {0}".Build(TCF.FullName));
266 Log.
Exception(
"A exception occured when loading table config: {0}".Build(TCF.FullName), E);
270 Log.
Write(
"Table config allows mix with ledcontrol configs.");
275 Log.
Warning(
"No table config file found. Will try to load config from LedControl file(s).");
280 Log.
Write(
"No TableFilename specified, will use empty tableconfig");
284 if (!RomName.IsNullOrWhiteSpace())
286 Log.
Write(
"Will try to load configs from DirectOutput.ini or LedControl.ini file(s) for RomName {0}".Build(RomName));
291 Log.
Write(
"Will try to load table config from LedControl file(s) specified in global config.");
296 bool FoundIt =
false;
297 List<string> LookupPaths =
new List<string>();
298 if (!TableFilename.IsNullOrWhiteSpace())
300 if (
new FileInfo(TableFilename).Directory.Exists)
302 LookupPaths.Add(
new FileInfo(TableFilename).Directory.FullName);
309 string[] LedControlFilenames = {
"directoutputconfig",
"ledcontrol" };
311 foreach (
string LedControlFilename
in LedControlFilenames)
313 foreach (
string P
in LookupPaths)
315 DirectoryInfo DI =
new DirectoryInfo(P);
317 List<FileInfo> Files =
new List<FileInfo>();
318 foreach (FileInfo FI
in DI.EnumerateFiles())
320 if (FI.Name.ToLower().StartsWith(LedControlFilename.ToLower()) && FI.Name.ToLower().EndsWith(
".ini"))
327 foreach (FileInfo FI
in Files)
329 if (
string.Equals(FI.Name,
"{0}.ini".Build(LedControlFilename), StringComparison.OrdinalIgnoreCase))
331 LedControlIniFiles.Add(FI.FullName, 1);
336 string F = FI.Name.Substring(LedControlFilename.Length, FI.Name.Length - LedControlFilename.Length - 4);
340 if (
int.TryParse(F, out LedWizNr))
342 if (!LedControlIniFiles.Contains(LedWizNr))
344 LedControlIniFiles.Add(FI.FullName, LedWizNr);
364 Log.
Write(
"{0} directoutput.ini or ledcontrol.ini files loaded.".Build(LedControlIniFiles.Count));
368 Log.
Write(
"No directoutput.ini or ledcontrol.ini files found. No directoutput.ini or ledcontrol.ini configs will be loaded.");
373 Log.
Write(
"No config for table found in LedControl data for RomName {0}.".Build(RomName));
377 Log.
Write(
"Config for RomName {0} exists in LedControl data. Updating cabinet and config.".Build(RomName));
379 DirectOutput.LedControl.Setup.Configurator C =
new DirectOutput.LedControl.Setup.Configurator();
391 Log.
Write(
"Cant load config from directoutput.ini or ledcontrol.ini file(s) since no RomName was supplied. No ledcontrol config will be loaded.");
397 if (!TableFilename.IsNullOrWhiteSpace())
399 Table.
TableName = Path.GetFileNameWithoutExtension(
new FileInfo(TableFilename).FullName);
401 else if (!RomName.IsNullOrWhiteSpace())
406 if (!TableFilename.IsNullOrWhiteSpace())
410 if (!RomName.IsNullOrWhiteSpace())
414 Log.
Write(
"Table config loading finished");
438 Log.
Write(
"Framework initialized.");
445 Log.
Exception(
"A eception occured during initialization", E);
446 throw new Exception(
"DirectOutput framework has encountered a exception during initialization.\n Inner exception: {0}".Build(E.Message), E);
469 Log.
Write(
"DirectOutput framework finished.");
470 Log.
Write(
"Bye and thanks for using!");
475 Log.
Exception(
"A exception occured while finishing the DirectOutput framework.", E);
476 throw new Exception(
"DirectOutput framework has encountered while finishing.\n Inner exception: {0}".Build(E.Message), E);
489 private void InitMainThread()
494 KeepMainThreadAlive =
true;
497 MainThread =
new Thread(MainThreadDoIt);
498 MainThread.Name =
"DirectOutput MainThread ";
505 Log.Exception(
"DirectOutput MainThread could not start.", E);
506 throw new Exception(
"DirectOutput MainThread could not start.", E);
515 private void FinishMainThread()
517 if (MainThread != null)
521 KeepMainThreadAlive =
false;
522 lock (MainThreadLocker)
524 Monitor.Pulse(MainThreadLocker);
526 if (!MainThread.Join(1000))
534 Log.Exception(
"A error occured during termination of DirectOutput MainThread", E);
535 throw new Exception(
"A error occured during termination of DirectOutput MainThread", E);
548 if (MainThread != null)
550 if (MainThread.IsAlive)
562 private void MainThreadSignal()
564 lock (MainThreadLocker)
566 Monitor.Pulse(MainThreadLocker);
571 private Thread MainThread {
get;
set; }
572 private object MainThreadLocker =
new object();
573 private bool KeepMainThreadAlive =
true;
576 const int MaxInputDataProcessingTimeMs = 10;
584 private void MainThreadDoIt()
590 while (KeepMainThreadAlive)
592 bool UpdateRequired =
false;
593 DateTime Start = DateTime.Now;
596 while (
InputQueue.
Count > 0 && (DateTime.Now - Start).Milliseconds <= MaxInputDataProcessingTimeMs && KeepMainThreadAlive)
603 DateTime StartTime = DateTime.Now;
605 UpdateRequired |=
true;
610 Log.Exception(
"A unhandled exception occured while processing data for table element {0} {1} with value {2}".Build(D.
TableElementType, D.
Number, D.
Value), E);
616 if (KeepMainThreadAlive)
625 Log.Exception(
"A unhandled exception occured while executing timer events.", E);
632 if (UpdateRequired && KeepMainThreadAlive)
640 Log.Exception(
"A unhandled exception occured while updating the output controllers", E);
645 if (KeepMainThreadAlive)
651 lock (MainThreadLocker)
653 while (
InputQueue.
Count == 0 && NextAlarm > DateTime.Now && KeepMainThreadAlive)
655 int TimeOut = ((int)(NextAlarm - DateTime.Now).TotalMilliseconds).Limit(1, 50);
657 Monitor.Wait(MainThreadLocker, TimeOut);
668 Log.Exception(
"A unexpected exception occured in the DirectOutput MainThread", E);
677 private Dictionary<TableElementTypeEnum, TimeSpanStatisticsItem> TableElementCallStatistics =
new Dictionary<TableElementTypeEnum, TimeSpanStatisticsItem>();
680 private void InitStatistics()
682 Log.Debug(
"Initializing table element statistics");
686 TableElementCallStatistics =
new Dictionary<TableElementTypeEnum, TimeSpanStatisticsItem>();
689 TSI =
new TimeSpanStatisticsItem() { Name =
"{0}".Build(T.ToString()), GroupName =
"Pinball - Table element update calls" };
690 TableElementCallStatistics.Add(T, TSI);
696 Log.Debug(
"Table element statistics initialized");
709 TableElementCallStatistics[TableElementData.
TableElementType].AddDuration(Duration);
713 Log.
Exception(
"Could not update TimeSpanStatistics for Pinball table element type {0} ({1})".Build(TableElementData.ToString(), TableElementData), E);
726 string LastGroupName =
"";
751 public void ReceiveData(
char TableElementTypeChar,
int Number,
int Value)
753 InputQueue.
Enqueue(TableElementTypeChar, Number, Value);
765 InputQueue.
Enqueue(TableElementData);
781 string S = this.GetType().FullName +
" {\n";
782 S +=
" GlobalConfig {\n";
792 S +=
" Table Effects count: " +
Table.
Effects.Count +
"\n";
797 S +=
" Output toys count: " +
Cabinet.
Toys.Count +
"\n";