diff --git a/InstallForge/2021_1_InstallProject.ifp b/InstallForge/2021_1_InstallProject.ifp new file mode 100644 index 0000000..2d5a883 Binary files /dev/null and b/InstallForge/2021_1_InstallProject.ifp differ diff --git a/Schedulino/Generator/Interval.cs b/McIntyreAFC/Generator/Interval.cs similarity index 90% rename from Schedulino/Generator/Interval.cs rename to McIntyreAFC/Generator/Interval.cs index 0b051c2..13417ef 100644 --- a/Schedulino/Generator/Interval.cs +++ b/McIntyreAFC/Generator/Interval.cs @@ -1,11 +1,7 @@ -using ProtocolMaster.Model.Protocol; +using ProtocolMasterCore.Protocol; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace SchedulinoDriver.Generator +namespace Schedulino.Generator { class Interval : IEquatable, IComparable { diff --git a/Schedulino/Generator/Protocol.cs b/McIntyreAFC/Generator/Protocol.cs similarity index 98% rename from Schedulino/Generator/Protocol.cs rename to McIntyreAFC/Generator/Protocol.cs index b12b7c8..4eee910 100644 --- a/Schedulino/Generator/Protocol.cs +++ b/McIntyreAFC/Generator/Protocol.cs @@ -1,11 +1,8 @@ -using ProtocolMaster.Model.Protocol; +using ProtocolMasterCore.Protocol; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace SchedulinoDriver.Generator +namespace Schedulino.Generator { // Pairing Handler: calls the delivery handler for delegate void SoundHandler(); @@ -147,7 +144,7 @@ private void GenerateSoundsBlock() prevSoundInterval = si; } exp_end = potential_end; - } + } } private void GeneratePreSounds(ref UInt32 timeMs, ref SoundInterval prevSoundInterval) @@ -335,7 +332,7 @@ private void CenteredDelivery(Stimulus stim, UInt32 begin, UInt32 end) totalTime += duration[i] + between[i]; } uint timeMs; - if (end-begin > totalTime) + if (end - begin > totalTime) timeMs = begin + ((end - begin) - totalTime) / 2; else timeMs = begin - (totalTime - (end - begin)) / 2; for (int i = 0; i < stim.stims_per_sound; i++) diff --git a/Schedulino/Generator/Sound.cs b/McIntyreAFC/Generator/Sound.cs similarity index 85% rename from Schedulino/Generator/Sound.cs rename to McIntyreAFC/Generator/Sound.cs index 96c2a08..297d89c 100644 --- a/Schedulino/Generator/Sound.cs +++ b/McIntyreAFC/Generator/Sound.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; -namespace SchedulinoDriver.Generator +namespace Schedulino.Generator { class Sound { diff --git a/Schedulino/Generator/SoundInterval.cs b/McIntyreAFC/Generator/SoundInterval.cs similarity index 81% rename from Schedulino/Generator/SoundInterval.cs rename to McIntyreAFC/Generator/SoundInterval.cs index bd33f33..6cd5181 100644 --- a/Schedulino/Generator/SoundInterval.cs +++ b/McIntyreAFC/Generator/SoundInterval.cs @@ -1,18 +1,14 @@ -using ProtocolMaster.Model.Protocol; -using System; +using ProtocolMasterCore.Protocol; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace SchedulinoDriver.Generator +namespace Schedulino.Generator { class SoundInterval : Interval { public Sound sound; public SoundInterval previous; public SoundInterval next; - public SoundInterval(Sound sound, SoundInterval previous, uint begin, uint end):base(begin, end) + public SoundInterval(Sound sound, SoundInterval previous, uint begin, uint end) : base(begin, end) { this.sound = sound; this.previous = previous; diff --git a/Schedulino/Generator/Stimulus.cs b/McIntyreAFC/Generator/Stimulus.cs similarity index 75% rename from Schedulino/Generator/Stimulus.cs rename to McIntyreAFC/Generator/Stimulus.cs index d7fd57b..24b2771 100644 --- a/Schedulino/Generator/Stimulus.cs +++ b/McIntyreAFC/Generator/Stimulus.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; -namespace SchedulinoDriver.Generator +namespace Schedulino.Generator { class Stimulus { diff --git a/Schedulino/Generator/StimulusInterval.cs b/McIntyreAFC/Generator/StimulusInterval.cs similarity index 88% rename from Schedulino/Generator/StimulusInterval.cs rename to McIntyreAFC/Generator/StimulusInterval.cs index fae483e..4ffdac0 100644 --- a/Schedulino/Generator/StimulusInterval.cs +++ b/McIntyreAFC/Generator/StimulusInterval.cs @@ -1,11 +1,7 @@ -using ProtocolMaster.Model.Protocol; -using System; +using ProtocolMasterCore.Protocol; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace SchedulinoDriver.Generator +namespace Schedulino.Generator { class StimulusInterval : Interval { diff --git a/McIntyreAFC/McIntyreAFC.csproj b/McIntyreAFC/McIntyreAFC.csproj new file mode 100644 index 0000000..fe614c3 --- /dev/null +++ b/McIntyreAFC/McIntyreAFC.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp3.1 + + + + ..\ProtocolMasterWPF\bin\Debug\netcoreapp3.1\Extensions\ + false + 5 + + + + ..\ProtocolMasterWPF\bin\Release\netcoreapp3.1\Extensions\ + false + + + + + + + + + false + + + + diff --git a/Schedulino/SchedulinoDriver.cs b/McIntyreAFC/SchedulinoDriver.cs similarity index 77% rename from Schedulino/SchedulinoDriver.cs rename to McIntyreAFC/SchedulinoDriver.cs index d44bb7d..74fa858 100644 --- a/Schedulino/SchedulinoDriver.cs +++ b/McIntyreAFC/SchedulinoDriver.cs @@ -1,21 +1,16 @@ -using ProtocolMaster.Model.Protocol.Driver; -using ProtocolMaster.Model.Protocol; -using ProtocolMaster.Model.Debug; -using System.ComponentModel.Composition; -using System.IO.Ports; +using ProtocolMasterCore.Prompt; +using ProtocolMasterCore.Protocol; +using ProtocolMasterCore.Protocol.Driver; +using ProtocolMasterCore.Utility; +using System; +using RJCP.IO.Ports; using System.Collections.Generic; using System.Threading; -using System.Net.Http.Headers; -using System.Collections.Concurrent; -using System; -using System.Xml; -using System.Diagnostics; -using ProtocolMaster.Model; -namespace Schedulino +namespace McIntyreAFC { - [DriverMeta("Schedulino", "1.1", "DigitalDuration", "DigitalPulse", "DigitalStringDuration")] - public class SchedulinoDriver : IDriver, ICallDropdown + [DriverMeta("McIntyreAFC", "1.1", "DigitalDuration", "DigitalPulse", "DigitalStringDuration")] + public class Schedulino : IDriver, IPromptUserSelect { List schedule; int scheduleIndex = 0; @@ -24,10 +19,10 @@ private enum ScheduleState { SETUP = 0, RUNNING, DONE } uint _run_time; byte _serial_available; private uint _capacity; - - SerialPort serial; - public SerialPort Serial { get => serial; set => serial = value; } - public CallDropdownHandler CallDropdown { private get; set; } + + SerialPortStream serial; + public SerialPortStream Serial { get => serial; set => serial = value; } + public UserSelectHandler UserSelectPrompt { private get; set; } // Data processing handlers delegate void Handler(ProtocolEvent item); @@ -39,7 +34,7 @@ private enum ScheduleState { SETUP = 0, RUNNING, DONE } readonly Dictionary receivers; readonly Receiver invalidKeyReceiver; - public SchedulinoDriver() + public Schedulino() { schedule = new List(); _state = ScheduleState.SETUP; @@ -68,41 +63,40 @@ public SchedulinoDriver() invalidKeyReceiver = InvalidKeyReceiver; } - public void Cancel() + private bool isCanceled; + public bool IsCanceled { - Log.Error("Cancelling Schedulino"); - //scheduleIndex = schedule.Count; - Serial.Write("X"); - while (serial.BytesToRead >= 1) + get => isCanceled; + set { - int read = Serial.ReadByte(); - if (read != -1) - Receive((char)read); + isCanceled = true; + Log.Error("McInyreAFC CANCELLED"); + Serial.Write("X"); + ReadSerialBuffer(Serial); } - //Serial.Close(); } public bool Setup(List dataList) { - foreach (ProtocolEvent data in dataList) - { - Handle(data); - } + Log.Error("McIntyreAFC SETUP"); + // CONVERT EVENTS TO SCHEDULE + // Generate Schedule + foreach (ProtocolEvent data in dataList) Handle(data); + // Sort schedule schedule.Sort(); - string[] portOptions = SerialPort.GetPortNames(); + // PORT SELECTION + string[] portOptions = SerialPortStream.GetPortNames(); string port; - if (portOptions.Length == 0) - { - return false; - } - else if (portOptions.Length == 1) - port = portOptions[0]; - else - port = CallDropdown(portOptions); - - // SerialPort setup - Serial = new SerialPort + // If there are no available ports, exit + if (portOptions.Length == 0) return false; + // If there is one available port, use it + else if (portOptions.Length == 1) port = portOptions[0]; + // If there are many available ports, allow the user to select + else port = UserSelectPrompt(portOptions); + + // SERIALPORT SETUP + Serial = new SerialPortStream { RtsEnable = true, DtrEnable = true, @@ -110,57 +104,70 @@ public bool Setup(List dataList) BaudRate = 9600, NewLine = "\n" }; + + Serial.DataReceived += DataReceiver; Serial.Open(); // Handshake // Read serial buffer until arduino tells us how much capacity it has while (_state != ScheduleState.DONE && _capacity == 0) { - ReadSerialBuffer(); - } - // Pre-Load as many events as possible - while (_state != ScheduleState.DONE && _capacity > 0 && scheduleIndex < schedule.Count) - { - SendNextEvent(); - ReadSerialBuffer(); + Thread.Sleep(8); } + // Send as many events as possible + while (_capacity > 0 && scheduleIndex < schedule.Count) + while (SendNextEvent(Serial)) ; return true; } public void Start() { + Log.Error("McIntyreAFC START"); // Send start signal if (_state != ScheduleState.DONE) { - while (serial.IsOpen && !TrySendStartSignal()) + while (!TrySendStartSignal()) { - ReadSerialBuffer(); + Thread.Sleep(2); } } + Log.Error($"McIntyreAFC STARTING:[state:{_state}]"); // Send events and send serial data until Done event is recieved while (_state == ScheduleState.RUNNING) { - SendNextEvent(); - ReadSerialBuffer(); + Thread.Sleep(64); + } + // Read remaining buffer + ReadSerialBuffer(Serial); + if (serial.IsOpen) Serial.Close(); + } + void DataReceiver(object sender, SerialDataReceivedEventArgs args) + { + if (_state != ScheduleState.DONE) + { + ReadSerialBuffer(sender as SerialPortStream); + while (_state == ScheduleState.RUNNING && SendNextEvent(sender as SerialPortStream)) ; } - if(serial.IsOpen) Serial.Close(); } - private void SendNextEvent() + private bool SendNextEvent(SerialPortStream port) { - if (serial.IsOpen && _capacity > 0 && _serial_available >= 7 && scheduleIndex < schedule.Count) + if (port.IsOpen && _capacity > 0 && _serial_available >= 7 && scheduleIndex < schedule.Count) { + Log.Error($"McIntyreAFC SENDEVENT:[scheduleIndex:{scheduleIndex}],[pinState:{schedule[scheduleIndex]}]"); byte[] bytes = schedule[scheduleIndex].ToBytes(); - Serial.Write("E"); - Serial.Write(bytes, 0, bytes.Length); + port.Write("E"); + port.Write(bytes, 0, bytes.Length); _serial_available -= 7; _capacity--; scheduleIndex++; + return true; } + else return false; } private bool TrySendStartSignal() { - if (serial.IsOpen && _serial_available > 0) + if (Serial.IsOpen && _serial_available > 0) { Serial.Write("S"); _state = ScheduleState.RUNNING; @@ -169,11 +176,11 @@ private bool TrySendStartSignal() return false; } - private void ReadSerialBuffer() + private void ReadSerialBuffer(SerialPortStream port) { - while (serial.IsOpen && serial.BytesToRead >= 1) + while (port.IsOpen && port.BytesToRead > 0) { - int read = Serial.ReadByte(); + int read = port.ReadByte(); if (read != -1) Receive((char)read); } @@ -346,7 +353,7 @@ private void CapacityReceiver() { _capacity = Convert.ToUInt16(Serial.ReadLine()); _serial_available = Convert.ToByte(Serial.ReadLine()); - //Log.Error("Schedulino CAPACITY\ncapacity:" + _capacity + "\nserial_available:" + _serial_available); + Log.Error($"McIntyreAFC CAPACITY:[capacity:{_capacity}],[serial_available:{_serial_available}]"); } private void ErrorReceiver() { @@ -354,14 +361,13 @@ private void ErrorReceiver() file = Convert.ToByte(Serial.ReadLine()); error = Convert.ToByte(Serial.ReadLine()); ext = Convert.ToByte(Serial.ReadLine()); - //Log.Error("Schedulino ERROR\nfile:" + file + "\nerror:" + error + "\next:" + ext); ; - + Log.Error($"McIntyreAFC ERROR:[file:{file}],[error:{error}],[ext:{ext}]"); } private void DoneReceiver() { _run_time = Convert.ToUInt32(Serial.ReadLine()); _state = ScheduleState.DONE; - //Log.Error("Schedulino DONE\nrun_time:" + _run_time + "\nstate:" + _state); + Log.Error($"McIntyreAFC DONE:[run_time:{_run_time}],[state:{_state}]"); } private void ReportReceiver() { @@ -370,16 +376,16 @@ private void ReportReceiver() byte pin = Convert.ToByte(Serial.ReadLine()); byte pinstate = Convert.ToByte(Serial.ReadLine()); _capacity++; - //Log.Error("Schedulino REPORT\nindex:" + index + "\ntime/otime:" + time + "/" + schedule[index].Time + "\npin:" + pin + "\npinstate:" + pinstate); + Log.Error($"McIntyreAFC REPORT:[index:{index}],[time:{time}],[otime:{schedule[index].Time}],[pin:{pin}],[pinstate:{pinstate}]"); } private void ReplyReceiver() { _serial_available += 7; - //Log.Error("Schedulino REPLY\nserial_available:" + _serial_available); + Log.Error($"McIntyreAFC REPLY:[serial_available:{_serial_available}]"); } private void InvalidKeyReceiver() { - Log.Error("Schedulino UNEXPECTED BYTE"); + Log.Error("McIntyreAFC UNEXPECTED BYTE"); } diff --git a/Schedulino/SpreadsheetAFC.cs b/McIntyreAFC/SpreadsheetAFC.cs similarity index 90% rename from Schedulino/SpreadsheetAFC.cs rename to McIntyreAFC/SpreadsheetAFC.cs index 79c0b8c..7885960 100644 --- a/Schedulino/SpreadsheetAFC.cs +++ b/McIntyreAFC/SpreadsheetAFC.cs @@ -1,22 +1,22 @@ -using ProtocolMaster.Model.Protocol; -using ProtocolMaster.Model.Protocol.Interpreter; +using ProtocolMasterCore.Prompt; +using ProtocolMasterCore.Protocol; +using ProtocolMasterCore.Protocol.Interpreter; +using Schedulino.Generator; using System; using System.Collections.Generic; using System.Linq; -using SchedulinoDriver.Generator; -using ProtocolMaster.Model; -namespace Schedulino +namespace McIntyreAFC { [InterpreterMeta("SpreadsheetAFC", "1.1")] - public class SpreadsheetAFC : ExcelDataInterpreter, IInterpreter, ICallDropdown + public class SpreadsheetAFC : ExcelDataInterpreter, IInterpreter, IPromptUserSelect { private delegate bool RowReader(Dictionary map); Dictionary mappedRowReaders; Dictionary protocols; Protocol baseData; - public CallDropdownHandler CallDropdown { get; set; } + public UserSelectHandler UserSelectPrompt { get; set; } public SpreadsheetAFC() { mappedRowReaders = new Dictionary(){ @@ -28,10 +28,7 @@ public SpreadsheetAFC() }; protocols = new Dictionary(); } - public void Cancel() - { - - } + public bool IsCanceled { get; set; } private Protocol GetOrCreateProtocol(string protocolName) { Protocol result; @@ -45,7 +42,7 @@ private Protocol GetOrCreateProtocol(string protocolName) return result; } } - public List Generate(string protocolName) + public List Generate(string protocolName = null) { if (DataReader == null) return null; do @@ -72,9 +69,20 @@ public List Generate(string protocolName) } while (DataReader.NextResult()); DataReader.Close(); - string dropdownResponse = CallDropdown(protocols.Keys.ToArray()); - if (dropdownResponse != null) - return protocols[dropdownResponse].Generate(); + if (!IsCanceled) + { + if (protocolName == null) + { + string dropdownResponse = UserSelectPrompt(protocols.Keys.ToArray()); + if (dropdownResponse != null) + return protocols[dropdownResponse].Generate(); + else return null; + } + else + { + return protocols[protocolName].Generate(); + } + } else return null; } private bool ReadExperimentRow(Dictionary headerMap) @@ -85,17 +93,17 @@ private bool ReadExperimentRow(Dictionary headerMap) baseData.owner = DataReader.GetValue(headerMap["Owner"]).ToString(); baseData.sound_order = DataReader.GetValue(headerMap["Sound Order"]).ToString(); - string extra_time_str = DataReader.GetValue(headerMap["Extra Time (seconds)"]).ToString(); - string interval_min_str = DataReader.GetValue(headerMap["Inter-sound Interval Minimum (seconds)"]).ToString(); - string interval_max_str = DataReader.GetValue(headerMap["Inter-sound Interval Maximum (seconds)"]).ToString(); + string extra_time_str = DataReader.GetValue(headerMap["Extra Time (ms)"]).ToString(); + string interval_min_str = DataReader.GetValue(headerMap["Inter-sound Interval Minimum (ms)"]).ToString(); + string interval_max_str = DataReader.GetValue(headerMap["Inter-sound Interval Maximum (ms)"]).ToString(); baseData.Sounds.Add(new Sound(DataReader.GetValue(headerMap["Sound A"]).ToString())); if (!DataReader.IsDBNull(headerMap["Sound B"])) baseData.Sounds.Add(new Sound(DataReader.GetValue(headerMap["Sound B"]).ToString())); - baseData.extra_time = Convert.ToUInt32(extra_time_str) * 1000; - baseData.interval_min = Convert.ToUInt32(interval_min_str) * 1000; - baseData.interval_max = Convert.ToUInt32(interval_max_str) * 1000; + baseData.extra_time = Convert.ToUInt32(extra_time_str); + baseData.interval_min = Convert.ToUInt32(interval_min_str); + baseData.interval_max = Convert.ToUInt32(interval_max_str); return true; } @@ -190,9 +198,9 @@ private bool ReadSoundConfigRow(Dictionary headerMap) sound.duration_pin = DataReader.GetValue(headerMap["Duration_Pin"]).ToString(); sound.sound_id = DataReader.GetValue(headerMap["Sound_ID"]).ToString(); - string duration_str = DataReader.GetValue(headerMap["Duration (seconds)"]).ToString(); + string duration_str = DataReader.GetValue(headerMap["Duration (ms)"]).ToString(); - sound.duration = Convert.ToUInt32(duration_str) * 1000; + sound.duration = Convert.ToUInt32(duration_str); } } return true; diff --git a/ProtocolMaster.sln b/ProtocolMaster.sln index 8f0d5ee..006f44f 100644 --- a/ProtocolMaster.sln +++ b/ProtocolMaster.sln @@ -3,24 +3,86 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29609.76 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProtocolMaster", "ProtocolMaster\ProtocolMaster.csproj", "{CE4D7148-D741-4ED7-95A1-DC1BD0532E1B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProtocolMasterCore", "ProtocolMasterCore\ProtocolMasterCore.csproj", "{44917BA5-F366-4054-95A9-A8371FCEBFB7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schedulino", "Schedulino\Schedulino.csproj", "{EDBA6B70-6719-4118-977D-F10BA534B394}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "McIntyreAFC", "McIntyreAFC\McIntyreAFC.csproj", "{4D707372-0064-4BC7-B744-45DD2F63488B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProtocolMasterWPF", "ProtocolMasterWPF\ProtocolMasterWPF.csproj", "{35ACDD96-A12B-4F38-84F1-2E498AF2E914}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CE4D7148-D741-4ED7-95A1-DC1BD0532E1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE4D7148-D741-4ED7-95A1-DC1BD0532E1B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CE4D7148-D741-4ED7-95A1-DC1BD0532E1B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE4D7148-D741-4ED7-95A1-DC1BD0532E1B}.Release|Any CPU.Build.0 = Release|Any CPU - {EDBA6B70-6719-4118-977D-F10BA534B394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EDBA6B70-6719-4118-977D-F10BA534B394}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EDBA6B70-6719-4118-977D-F10BA534B394}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EDBA6B70-6719-4118-977D-F10BA534B394}.Release|Any CPU.Build.0 = Release|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Debug|ARM.ActiveCfg = Debug|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Debug|ARM.Build.0 = Debug|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Debug|ARM64.Build.0 = Debug|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Debug|x64.ActiveCfg = Debug|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Debug|x64.Build.0 = Debug|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Debug|x86.ActiveCfg = Debug|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Debug|x86.Build.0 = Debug|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Release|Any CPU.Build.0 = Release|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Release|ARM.ActiveCfg = Release|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Release|ARM.Build.0 = Release|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Release|ARM64.ActiveCfg = Release|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Release|ARM64.Build.0 = Release|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Release|x64.ActiveCfg = Release|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Release|x64.Build.0 = Release|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Release|x86.ActiveCfg = Release|Any CPU + {44917BA5-F366-4054-95A9-A8371FCEBFB7}.Release|x86.Build.0 = Release|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Debug|ARM.ActiveCfg = Debug|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Debug|ARM.Build.0 = Debug|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Debug|ARM64.Build.0 = Debug|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Debug|x64.ActiveCfg = Debug|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Debug|x64.Build.0 = Debug|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Debug|x86.ActiveCfg = Debug|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Debug|x86.Build.0 = Debug|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Release|Any CPU.Build.0 = Release|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Release|ARM.ActiveCfg = Release|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Release|ARM.Build.0 = Release|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Release|ARM64.ActiveCfg = Release|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Release|ARM64.Build.0 = Release|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Release|x64.ActiveCfg = Release|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Release|x64.Build.0 = Release|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Release|x86.ActiveCfg = Release|Any CPU + {4D707372-0064-4BC7-B744-45DD2F63488B}.Release|x86.Build.0 = Release|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Debug|ARM.ActiveCfg = Debug|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Debug|ARM.Build.0 = Debug|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Debug|ARM64.Build.0 = Debug|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Debug|x64.ActiveCfg = Debug|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Debug|x64.Build.0 = Debug|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Debug|x86.ActiveCfg = Debug|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Debug|x86.Build.0 = Debug|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Release|Any CPU.Build.0 = Release|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Release|ARM.ActiveCfg = Release|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Release|ARM.Build.0 = Release|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Release|ARM64.ActiveCfg = Release|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Release|ARM64.Build.0 = Release|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Release|x64.ActiveCfg = Release|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Release|x64.Build.0 = Release|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Release|x86.ActiveCfg = Release|Any CPU + {35ACDD96-A12B-4F38-84F1-2E498AF2E914}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ProtocolMaster/App.xaml b/ProtocolMaster/App.xaml deleted file mode 100644 index f33256f..0000000 --- a/ProtocolMaster/App.xaml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/ProtocolMaster/App.xaml.cs b/ProtocolMaster/App.xaml.cs deleted file mode 100644 index 6711e05..0000000 --- a/ProtocolMaster/App.xaml.cs +++ /dev/null @@ -1,124 +0,0 @@ -using Microsoft.Win32; -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; - -namespace ProtocolMaster -{ - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application - { - public static App Instance { get { return (App)Application.Current; } } - public static View.MainWindow Window { get { return (View.MainWindow)Application.Current.MainWindow; } } - public bool LoggedIn => Model.Google.GAuth.Instance.IsAuthenticated(); - internal Model.Protocol.ExtensionSystem ExtensionSystem { get; private set; } - - #region Single Process Management - [DllImport("USER32.DLL", CharSet = CharSet.Unicode)] - public static extern IntPtr FindWindow(String lpClassName, String lpWindowName); - - [DllImport("USER32.DLL")] - public static extern bool SetForegroundWindow(IntPtr hWnd); - // Pinvoke declaration for ShowWindow - private const int SW_SHOWMAXIMIZED = 3; - - [DllImport("user32.dll")] - static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); - Mutex pmMutex; - #endregion - void App_Startup(object sender, StartupEventArgs e) - { - bool isNewInstance = false; - pmMutex = new Mutex(true, "ProtocolMaster", out isNewInstance); - if (!isNewInstance) - { - Process current = Process.GetCurrentProcess(); - foreach (Process process in Process.GetProcessesByName(current.ProcessName)) - { - if (process.Id != current.Id) - { - ShowWindow(process.MainWindowHandle, SW_SHOWMAXIMIZED); - SetForegroundWindow(process.MainWindowHandle); - break; - } - } - App.Current.Shutdown(); - } - RegisterUriScheme(); - - ExtensionSystem = new Model.Protocol.ExtensionSystem(); - - Model.Debug.Log.Error("ProtocolMaster Starting up"); - MainWindow = new View.MainWindow(); - MainWindow.Show(); - Model.Debug.Log.Out("Application Data: " + Model.Debug.Log.Instance.AppData); - - ExtensionSystem.LoadExtensions(); - } - - // Full Login Routine - public async Task LogIn() - { - await Model.Google.GAuth.Instance.Authenticate(Model.Google.GDrive.Instance, Model.Google.GSheets.Instance); - } - - public async Task LogOut() - { - await Model.Google.GAuth.Instance.DeAuthenticate(); - } - - public async void Window_Closed() - { - //ExtensionSystem.Terminate(); - await Model.Google.GAuth.Instance.DeAuthenticate(); - - Model.Debug.Log.Error("ProtocolMaster Mainwindow Exited Gracefully"); - Model.Debug.Log.Instance.WriteFiles(); - } - - const string UriScheme = "protocolmaster"; - const string FriendlyName = "Protocol master"; - - public static void RegisterUriScheme() - { - if (!RegistryUriExists()) - { - using (var key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\" + UriScheme)) - { - // Replace typeof(App) by the class that contains the Main method or any class located in the project that produces the exe. - // or replace typeof(App).Assembly.Location by anything that gives the full path to the exe - string applicationLocation = typeof(App).Assembly.Location; - - key.SetValue("", "URL:" + FriendlyName); - key.SetValue("URL Protocol", ""); - - using (var defaultIcon = key.CreateSubKey("DefaultIcon")) - { - defaultIcon.SetValue("", applicationLocation + ",1"); - } - - using (var commandKey = key.CreateSubKey(@"shell\open\command")) - { - commandKey.SetValue("", "\"" + applicationLocation + "\" \"%1\""); - } - } - } - } - - public static bool RegistryUriExists() - { - string pmSubkey = "SOFTWARE\\Classes\\" + UriScheme; - foreach (string subkey in Registry.CurrentUser.GetSubKeyNames()) - { - if (subkey == pmSubkey) - return true; - } - return false; - } - } -} diff --git a/ProtocolMaster/Model/CallHandler.cs b/ProtocolMaster/Model/CallHandler.cs deleted file mode 100644 index 33394c4..0000000 --- a/ProtocolMaster/Model/CallHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ProtocolMaster.View; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.Model -{ - public static class CallHandler - { - public static string CallDropdown(string[] keys) - { - return App.Current.Dispatcher.Invoke(new Func(() => { return PopupDropdown.PopupNow(keys); })); - } - } -} diff --git a/ProtocolMaster/Model/Config/ConfigFile.cs b/ProtocolMaster/Model/Config/ConfigFile.cs deleted file mode 100644 index 680a940..0000000 --- a/ProtocolMaster/Model/Config/ConfigFile.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.Model.Config -{ - class ConfigFile - { - } -} diff --git a/ProtocolMaster/Model/Debug/Log.cs b/ProtocolMaster/Model/Debug/Log.cs deleted file mode 100644 index 4128d70..0000000 --- a/ProtocolMaster/Model/Debug/Log.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; - -namespace ProtocolMaster.Model.Debug -{ - public sealed class Log - { - private static readonly Log instance = new Log(); - // Explicit static constructor to tell C# compiler - // not to mark type as beforefieldinit - static Log() - { - } - - private readonly string appdata; - public string AppData { get { return appdata; } } - private readonly string logdata; - private readonly string archive; - - private readonly int maxUnarchived = 48; - private readonly int minUnarchived = 16; - - private readonly LogFile lfOut; - private readonly LogFile lfErr; - public bool PrintErrors { get; set; } - - private Log() - { - appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\ProtocolMaster"; - logdata = appdata + "\\Log\\"; - archive = logdata + "\\Archive\\"; - - // First two are redundant, but it is prettier this way - Directory.CreateDirectory(appdata); - Directory.CreateDirectory(logdata); - Directory.CreateDirectory(archive); - - string timePrefix = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); - - lfOut = new LogFile(logdata + timePrefix + "_Out.log"); - lfErr = new LogFile(logdata + timePrefix + "_Err.log"); - - PrintErrors = true; - - ArchiveOldest(); - } - public static Log Instance - { - get - { - return instance; - } - } - - public static void Error(string message) => App.Current.Dispatcher.InvokeAsync(new Action(() => { Log.Instance.WriteError(message); })); - public void WriteError(string message) - { - lfErr.Write(message); - if (PrintErrors && App.Window != null && App.Window.LogView != null) - { - App.Window.LogView.LogToView(message.Replace("\t", "\n")); - } - } - public static void Out(string message) => App.Current.Dispatcher.InvokeAsync(new Action(() => { Log.Instance.WriteOut(message); })); - public void WriteOut(string message) - { - lfOut.Write(message); - if (PrintErrors && App.Window != null && App.Window.LogView != null) - { - App.Window.LogView.LogToView(message.Replace("\t", "\n")); - } - } - - public void WriteFiles() - { - lfErr.WriteBuffer(); - lfOut.WriteBuffer(); - } - - public void OpenFolder() - { - WriteFiles(); - System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo() - { - FileName = logdata, - UseShellExecute = true, - Verb = "open" - }); - } - - private void ArchiveOldest() - { - string[] files = Directory.GetFiles(logdata); - List logs = new List(); - foreach (string file in files) - { - if (file.Contains(".log")) logs.Add(file); - } - - if (logs.Count < maxUnarchived) return; - - logs = logs.OrderBy(d => d).Take(logs.Count - minUnarchived).ToList(); - - //final archive name (I use date / time) - string zipFileName = DateTime.Now.ToString("yyyy-MM-dd_hh-mm-ss") + "[" + logs.Count + "].zip"; - - using (MemoryStream zipMS = new MemoryStream()) - { - using (ZipArchive zipArchive = new ZipArchive(zipMS, ZipArchiveMode.Create, true)) - { - //loop through files to add - foreach (string zipTarget in logs) - { - //exclude some files? -I don't want to ZIP other .zips in the folder. - if (new FileInfo(zipTarget).Extension == ".zip") continue; - - //read the file bytes - byte[] fileToZipBytes = System.IO.File.ReadAllBytes(zipTarget); - - //create the entry - this is the zipped filename - //change slashes - now it's VALID - ZipArchiveEntry zipFileEntry = zipArchive.CreateEntry(zipTarget.Replace(logdata, "").Replace('\\', '/')); - - //add the file contents - using (Stream zipEntryStream = zipFileEntry.Open()) - using (BinaryWriter zipFileBinary = new BinaryWriter(zipEntryStream)) - { - zipFileBinary.Write(fileToZipBytes); - } - - //lstLog.Items.Add("zipped: " + fileToZip); - } - } - - using (FileStream finalZipFileStream = new FileStream(archive + zipFileName, FileMode.Create)) - { - zipMS.Seek(0, SeekOrigin.Begin); - zipMS.CopyTo(finalZipFileStream); - } - - //lstLog.Items.Add("ZIP Archive Created."); - - foreach (string log in logs) - { - File.Delete(log); - } - } - } - } -} diff --git a/ProtocolMaster/Model/Debug/LogFile.cs b/ProtocolMaster/Model/Debug/LogFile.cs deleted file mode 100644 index 540f54f..0000000 --- a/ProtocolMaster/Model/Debug/LogFile.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace ProtocolMaster.Model.Debug -{ - class LogFile - { - private List buffer; - private long writeAfter; - private string filePath; - private string tempPath; - bool tempFail; - - public LogFile(string filePath) - { - this.filePath = filePath; - buffer = new List(); - } - - - - public void Write(string message, bool deepWrite = false) - { - StringBuilder builder = new StringBuilder(DateTime.Now.ToString()); - builder.Append("\t"); - builder.Append(message); - string output = builder.ToString(); - - buffer.Add(output); - - if (deepWrite || DateTime.Now.Ticks > writeAfter) - { - WriteBuffer(); - } - } - - public void WriteBuffer() - { - try - { - File.AppendAllLines(filePath, buffer); - if (tempFail) - { - if (File.Exists(tempPath)) - File.Delete(tempPath); - tempFail = false; - } - } - catch (IOException) - { - tempFail = true; - tempPath = filePath + "[temp]"; - Write("Failed to open log at " + filePath + " attempting " + tempPath + " instead."); - try - { - File.AppendAllLines(tempPath, buffer); - } - catch (IOException) - { - Write("Failed to open temporary log " + tempPath + " will wait to write buffer"); - } - } - - - if (!tempFail) buffer = new List(); - - writeAfter = DateTime.Now.Ticks + (5 * 10000000); - } - } -} diff --git a/ProtocolMaster/Model/Google/GAuth.cs b/ProtocolMaster/Model/Google/GAuth.cs deleted file mode 100644 index 7b7bf81..0000000 --- a/ProtocolMaster/Model/Google/GAuth.cs +++ /dev/null @@ -1,91 +0,0 @@ -using Google.Apis.Auth.OAuth2; -using Google.Apis.Auth.OAuth2.Flows; -using Google.Apis.Auth.OAuth2.Requests; -using Google.Apis.Util.Store; -using ProtocolMaster.Model.Debug; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace ProtocolMaster.Model.Google -{ - public sealed class GAuth - { - private static readonly GAuth instance = new GAuth(); - static GAuth() - { - } - private GAuth() - { - } - public static GAuth Instance - { - get - { - return instance; - } - } - - UserCredential credential; - private FileDataStore userStore; - - public async Task Authenticate(params IService[] services) - { - Log.Error("Authenticate(): AUTHENTICATING"); - - userStore = new FileDataStore(Log.Instance.AppData + "\\Auth", true); - ICodeReceiver receiver = new GAuthReceiver(); - CancellationTokenSource cts = new CancellationTokenSource(); - cts.CancelAfter(40000); - - Log.Error("Authenticate(): Launching OAuth"); - - credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( - new ClientSecrets - { - ClientId = "911699926501-6ici7to17is1fet4mr6jn864lrmrsi0g.apps.googleusercontent.com", - ClientSecret = "LbVHZSa-rtPBMl9odwMBkJi_" - }, - CombineServiceTokens(services), - "user", cts.Token, userStore,receiver); - CreateServices(services); - Log.Error("Authenticate(): User Fully Authenticated"); - App.Window.Activate(); - } - private void CreateServices(IService[] services) - { - foreach (IService service in services) - { - service.CreateService(credential); - } - } - - private string[] CombineServiceTokens(IService[] services) - { - List builder = new List(); - foreach (IService service in services) - { - foreach (string token in service.ServiceTokens()) - { - builder.Add(token); - } - } - - return builder.ToArray(); - } - - public async Task DeAuthenticate() - { - if (!IsAuthenticated()) return; - Log.Error("DeAuthenticate(): DEAUTHENTICATING"); - credential = null; - await userStore.ClearAsync(); - Log.Error("Authenticate(): User Fully Deauthenticated"); - } - - public bool IsAuthenticated() - { - return credential != null; - } - } -} diff --git a/ProtocolMaster/Model/Google/GDrive.cs b/ProtocolMaster/Model/Google/GDrive.cs deleted file mode 100644 index 6f7dc1b..0000000 --- a/ProtocolMaster/Model/Google/GDrive.cs +++ /dev/null @@ -1,153 +0,0 @@ -using Google.Apis.Auth.OAuth2; -using Google.Apis.Drive.v3; -using Google.Apis.Drive.v3.Data; -using Google.Apis.Services; -using ProtocolMaster.Model.Debug; -using System.Collections.Generic; -using System.IO; -using File = Google.Apis.Drive.v3.Data.File; - -namespace ProtocolMaster.Model.Google -{ - public sealed class GDrive : IService - { - private static readonly GDrive instance = new GDrive(); - static GDrive() - { - } - private GDrive() - { - } - public static GDrive Instance - { - get - { - return instance; - } - } - - public Stream Download(string fileID) - { - if (fileID != null) - return service.Files.Export(fileID, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet").ExecuteAsStream(); - else return null; - } - - // Core Drive Functionality. - // This all needs to be abstracted somewhere else (Model) - const string ROOT_FOLDER_NAME = "ProtocolMaster"; - private TreeFile root; - private DriveService service; - #region - private void FindRoot() - { - Log.Error("FindRoot(): FINDING ROOT FOLDER"); - FilesResource.ListRequest listRequest = service.Files.List(); - listRequest.PageSize = 20; - listRequest.Fields = "nextPageToken, files(id, name, mimeType)"; - listRequest.Q = - "mimeType = 'application/vnd.google-apps.folder' and " + - "name = '" + ROOT_FOLDER_NAME + "' and" + - "trashed = false"; - - FileList files = listRequest.Execute(); - - Log.Error("FindRoot(): files.Files.Count = " + files.Files.Count); - if (files.Files.Count == 0) - { - // This is an error. Could not find folder - CreateRoot(); - } - else if (files.Files.Count > 1) - { - // Also an error. Too many ProtocolMaster folders! - root = new TreeFile(files.Files[0]); - } - else - { - root = new TreeFile(files.Files[0]); - } - } - private void CreateRoot() - { - root = null; - root = CreateFolder(ROOT_FOLDER_NAME); - } - public TreeFile CreateFolder(string name, TreeFile parent = null) - { - // Prepare the file - File newFile = new File - { - Name = name, - MimeType = "application/vnd.google-apps.folder" - }; - if (parent == null) - { - parent = root; - } - if (parent != null) - { - newFile.Parents = new List() { parent.File.Id }; - } - // Create the file on Google Server - FilesResource.CreateRequest create = service.Files.Create(newFile); - create.Fields = "id, parents, mimeType, name"; - return parent.AddChild(create.Execute()); - } - public List GetChildren(File parent) - { - List list = new List(); - FileList result; - do - { - FilesResource.ListRequest listRequest = service.Files.List(); - listRequest.PageSize = 20; - listRequest.Fields = "nextPageToken, files(id, name, mimeType, parents)"; - listRequest.Q = - "mimeType = 'application/vnd.google-apps.spreadsheet' and " + - "trashed = false"; - result = listRequest.Execute(); - list.AddRange(result.Files); - } while (result.IncompleteSearch.HasValue && result.IncompleteSearch.Equals(true)); - return list; - } - public void Refresh(MarkupCallback callback = null) - { - root = null; - FindRoot(); - if (callback != null) - CallbackPreBF(callback); - } - - public void CallbackPreBF(MarkupCallback callback) - { - if (GAuth.Instance.IsAuthenticated()) - root.CallbackPreBF(callback); - } - #endregion - - // IService Implementation - #region - private static readonly string[] serviceTokens = - { - DriveService.Scope.DriveFile - }; - - - string[] IService.ServiceTokens() { return serviceTokens; } - void IService.CreateService(UserCredential credential) - { - Log.Error("CreateService(): CREATING DRIVE SERVICE"); - // Create the drive service. - service = new DriveService(new BaseClientService.Initializer() - { - HttpClientInitializer = credential, - ApplicationName = "Protocol Master", - }); - - // Find root folder for application. - FindRoot(); - } - #endregion - } -} diff --git a/ProtocolMaster/Model/Google/GSheets.cs b/ProtocolMaster/Model/Google/GSheets.cs deleted file mode 100644 index 6188eaf..0000000 --- a/ProtocolMaster/Model/Google/GSheets.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Google.Apis.Auth.OAuth2; -using Google.Apis.Services; -using Google.Apis.Sheets.v4; -using ProtocolMaster.Model.Debug; - -namespace ProtocolMaster.Model.Google -{ - public sealed class GSheets : IService - { - private static readonly GSheets instance = new GSheets(); - static GSheets() - { - } - private GSheets() - { - } - public static GSheets Instance - { - get - { - return instance; - } - } - // IService Implementation - private static readonly string[] serviceTokens = - { - "https://www.googleapis.com/auth/drive.install" - }; - string[] IService.ServiceTokens() { return serviceTokens; } - private SheetsService service; - void IService.CreateService(UserCredential credential) - { - // Create the drive service. - Log.Error("CreateService(): CREATING SHEETS SERVICE"); - // Create the drive service. - service = new SheetsService(new BaseClientService.Initializer() - { - HttpClientInitializer = credential, - ApplicationName = "Protocol Master", - }); - } - } -} diff --git a/ProtocolMaster/Model/Google/TreeFile.cs b/ProtocolMaster/Model/Google/TreeFile.cs deleted file mode 100644 index df2c654..0000000 --- a/ProtocolMaster/Model/Google/TreeFile.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Google.Apis.Drive.v3.Data; -using ProtocolMaster.Model.Debug; -using System.Collections.Generic; - -namespace ProtocolMaster.Model.Google -{ - public delegate void MarkupCallback(string parentID, string ID, string header); - public class TreeFile - { - private readonly TreeFile parent; - private readonly List children; - private readonly File file; - - public File File => file; - - public TreeFile(File file, TreeFile parent = null) - { - Log.Error("Treefile(): " + file.Name + " created"); - this.file = file; - this.parent = parent; - children = new List(); - FindChildren(); - } - - public TreeFile AddChild(File file) - { - if (file == this.file) - return this; - TreeFile child = new TreeFile(file, this); - children.Add(child); - return child; - } - - private void FindChildren() - { - if (parent == null) - { - List childList = GDrive.Instance.GetChildren(File); - if (childList == null) return; - foreach (File child in childList) - { - children.Add(new TreeFile(child, this)); - } - } - } - - public void CallbackPreBF(MarkupCallback callback) - { - if(parent != null) - Callback(callback); - foreach (TreeFile child in children) - { - child.CallbackPreBF(callback); - } - } - public void Callback(MarkupCallback callback) - { - callback(null, File.Id, File.Name); - } - } -} - diff --git a/ProtocolMaster/Model/ICallDropdown.cs b/ProtocolMaster/Model/ICallDropdown.cs deleted file mode 100644 index 4f7bf60..0000000 --- a/ProtocolMaster/Model/ICallDropdown.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.Model -{ - public delegate string CallDropdownHandler(string[] keys); - public interface ICallDropdown - { - public CallDropdownHandler CallDropdown { set; } - } -} diff --git a/ProtocolMaster/Model/Protocol/Driver/DriverManager.cs b/ProtocolMaster/Model/Protocol/Driver/DriverManager.cs deleted file mode 100644 index 5473922..0000000 --- a/ProtocolMaster/Model/Protocol/Driver/DriverManager.cs +++ /dev/null @@ -1,43 +0,0 @@ -using ProtocolMaster.Model.Debug; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.Composition; -using System.ComponentModel.Composition.Hosting; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Threading; - -namespace ProtocolMaster.Model.Protocol.Driver -{ - internal class DriverManager : ExtensionManager - { - IDriver driver; - - public event EventHandler OnProtocolStart; - public event EventHandler OnProtocolEnd; - protected void ProtocolStart() - { - OnProtocolStart?.Invoke(this, new EventArgs()); - } - protected void ProtocolEnd() - { - OnProtocolEnd?.Invoke(this, new EventArgs()); - } - public void Run(List data) - { - driver = CreateSelectedExtension(); - - if (driver.Setup(data)) - { - UIDispatcher.Invoke(() => { ProtocolStart(); }, DispatcherPriority.Send); - driver.Start(); - UIDispatcher.Invoke(() => { ProtocolEnd(); }, DispatcherPriority.Send); - } - else Log.Error("Could not start device driver"); - DisposeSelectedExtension(); - } - } -} diff --git a/ProtocolMaster/Model/Protocol/IExtension.cs b/ProtocolMaster/Model/Protocol/IExtension.cs deleted file mode 100644 index 9395a58..0000000 --- a/ProtocolMaster/Model/Protocol/IExtension.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.Model.Protocol -{ - public interface IExtension - { - void Cancel(); - } -} diff --git a/ProtocolMaster/Model/Protocol/IExtensionMeta.cs b/ProtocolMaster/Model/Protocol/IExtensionMeta.cs deleted file mode 100644 index a970018..0000000 --- a/ProtocolMaster/Model/Protocol/IExtensionMeta.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.Model.Protocol -{ - interface IExtensionMeta - { - string Name { get; } - string Version { get; } - } -} diff --git a/ProtocolMaster/Model/Protocol/Interpreter/IInterpreter.cs b/ProtocolMaster/Model/Protocol/Interpreter/IInterpreter.cs deleted file mode 100644 index 9b40f7e..0000000 --- a/ProtocolMaster/Model/Protocol/Interpreter/IInterpreter.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Documents; - -namespace ProtocolMaster.Model.Protocol.Interpreter -{ - public interface IInterpreter : IExtension - { - List Generate(string protocolName); - } -} diff --git a/ProtocolMaster/Model/Protocol/Interpreter/InterpreterManager.cs b/ProtocolMaster/Model/Protocol/Interpreter/InterpreterManager.cs deleted file mode 100644 index f2f4a04..0000000 --- a/ProtocolMaster/Model/Protocol/Interpreter/InterpreterManager.cs +++ /dev/null @@ -1,40 +0,0 @@ -using ExcelDataReader; -using ProtocolMaster.Model.Debug; -using ProtocolMaster.Model.Protocol.Interpreter; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.Composition; -using System.ComponentModel.Composition.Hosting; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace ProtocolMaster.Model.Protocol.Interpreter -{ - internal class InterpreterManager : ExtensionManager - { - IInterpreter interpreter; - - public List GenerateData(string selectionID) - { - interpreter = CreateSelectedExtension(); - - if (typeof(ExcelDataInterpreter).IsAssignableFrom(interpreter.GetType())) - { - ExcelDataInterpreter spreadSheetInterpreter = interpreter as ExcelDataInterpreter; - Stream nfs = Model.Google.GDrive.Instance.Download(selectionID); - - if (nfs != null) - spreadSheetInterpreter.SetReader(ExcelReaderFactory.CreateReader(nfs)); - else return null; - } - - // pre-fill event data - List result = interpreter.Generate("Protocol"); - DisposeSelectedExtension(); - return result; - } - } -} diff --git a/ProtocolMaster/Model/Protocol/NullExtensions/NullDriver.cs b/ProtocolMaster/Model/Protocol/NullExtensions/NullDriver.cs deleted file mode 100644 index 60f6477..0000000 --- a/ProtocolMaster/Model/Protocol/NullExtensions/NullDriver.cs +++ /dev/null @@ -1,32 +0,0 @@ -using ProtocolMaster.Model.Protocol; -using ProtocolMaster.Model.Protocol.Driver; -using ProtocolMaster.Model.Debug; -using System.ComponentModel.Composition; -using System.IO.Ports; -using System.Collections.Generic; -using System.Threading; -using System.Net.Http.Headers; -using System.Collections.Concurrent; -using System; -using System.Xml; -using System.Diagnostics; - -namespace ProtocolMaster.Model.Protocol.NullExtensions -{ - [DriverMeta("None", "")] - public class NullDriver : IDriver - { - public void Cancel() - { - } - - public bool Setup(List dataList) - { - return false; - } - - public void Start() - { - } - } -} diff --git a/ProtocolMaster/Model/Protocol/NullExtensions/NullInterpreter.cs b/ProtocolMaster/Model/Protocol/NullExtensions/NullInterpreter.cs deleted file mode 100644 index b7b79c3..0000000 --- a/ProtocolMaster/Model/Protocol/NullExtensions/NullInterpreter.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ProtocolMaster.Model.Protocol.Interpreter; -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.Model.Protocol.NullExtensions -{ - [InterpreterMeta("None", "")] - public class NullInterpreter : IInterpreter - { - public void Cancel() - { - } - - public List Generate(string protocolName) - { - return null; - } - } - - -} diff --git a/ProtocolMaster/Model/Video/BitMapHelper.cs b/ProtocolMaster/Model/Video/BitMapHelper.cs deleted file mode 100644 index 4aeca4f..0000000 --- a/ProtocolMaster/Model/Video/BitMapHelper.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Windows.Media.Imaging; - -namespace ProtocolMaster.Model.Video -{ - static class BitMapHelper - { - public static BitmapImage ToBitmapImage(this Bitmap bitmap) - { - BitmapImage bi = new BitmapImage(); - bi.BeginInit(); - MemoryStream ms = new MemoryStream(); - bitmap.Save(ms, ImageFormat.Bmp); - ms.Seek(0, SeekOrigin.Begin); - bi.StreamSource = ms; - bi.EndInit(); - return bi; - } - } -} diff --git a/ProtocolMaster/Model/Video/WebCam.cs b/ProtocolMaster/Model/Video/WebCam.cs deleted file mode 100644 index 214421f..0000000 --- a/ProtocolMaster/Model/Video/WebCam.cs +++ /dev/null @@ -1,9 +0,0 @@ - - -namespace ProtocolMaster.Model.Video -{ - public class WebCam - { - - } -} diff --git a/ProtocolMaster/ProtocolMaster.csproj b/ProtocolMaster/ProtocolMaster.csproj deleted file mode 100644 index 715a706..0000000 --- a/ProtocolMaster/ProtocolMaster.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - WinExe - net48 - true - ProtocolMaster.ico - 8.0 - - - - bin\Debug\ - - - - bin\Release\ - - - - - - - - - - - - - - - - - - - - ..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.ComponentModel.Composition.dll - - - - - \ No newline at end of file diff --git a/ProtocolMaster/ProtocolMaster.ico b/ProtocolMaster/ProtocolMaster.ico deleted file mode 100644 index b636473..0000000 Binary files a/ProtocolMaster/ProtocolMaster.ico and /dev/null differ diff --git a/ProtocolMaster/Theme/Dark.xaml b/ProtocolMaster/Theme/Dark.xaml deleted file mode 100644 index caf6c46..0000000 --- a/ProtocolMaster/Theme/Dark.xaml +++ /dev/null @@ -1,183 +0,0 @@ - - - - #FF4A5763 - - #FF647687 - - #2D2D30 - #2D2D30 - #FFFFFFFF - - #2D2D30 - - #2D2D30 - - #2D2D30 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - White - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ProtocolMaster/View/Drive.xaml b/ProtocolMaster/View/Drive.xaml deleted file mode 100644 index 4b0c903..0000000 --- a/ProtocolMaster/View/Drive.xaml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Drive - - - - - - - - - - - - - - diff --git a/ProtocolMaster/View/Drive.xaml.cs b/ProtocolMaster/View/Drive.xaml.cs deleted file mode 100644 index 6a1b80b..0000000 --- a/ProtocolMaster/View/Drive.xaml.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Windows; -using System.Windows.Controls; - - -namespace ProtocolMaster.View -{ - /// - /// Interaction logic for Drive.xaml - /// - public partial class Drive : UserControl - { - readonly Dictionary idChildDictionary; - readonly Dictionary childIdDictionary; - - String selectedItemID = null; - public Drive() - { - idChildDictionary = new Dictionary(); - childIdDictionary = new Dictionary(); - InitializeComponent(); - } - - - // Account Sign-in/Sign-out - private async void Account_Click(object sender, RoutedEventArgs e) - { - if (App.Instance.LoggedIn) - { - AccountButton.IsEnabled = false; - AccountButton.ToolTip = "Signing-Out"; - await App.Instance.LogOut(); - AccountButton.Header = "Sign-In"; - AccountButton.ToolTip = "Google Drive"; - AccountButton.IsEnabled = true; - } - else - { - AccountButton.IsEnabled = false; - AccountButton.ToolTip = "Opening Browser"; - try - { - await App.Instance.LogIn(); - } - catch (OperationCanceledException) - { - AccountButton.Header = "Sign-In"; - AccountButton.IsEnabled = true; - return; - } - AccountButton.Header = "Sign-Out"; - AccountButton.ToolTip = "Google Drive"; - AccountButton.IsEnabled = true; - } - Refresh(); - FileTree.IsExpanded = true; - } - - public void Refresh_Click(object sender, RoutedEventArgs e) - { - Refresh(); - } - - public void Refresh() - { - FileTree.Items.Clear(); - idChildDictionary.Clear(); - childIdDictionary.Clear(); - // Refresh Google Drive Private - if (App.Instance.LoggedIn) - { - Model.Google.MarkupCallback callback = Display_Tree_Child; - Model.Google.GDrive.Instance.Refresh(callback); - } - } - - public void Folder_Click(object sender, RoutedEventArgs e) - { - Model.Google.GDrive.Instance.CreateFolder("New Folder").Callback(Display_Tree_Child); - } - - public void Display_Tree_Child(string parentName, string name, string header) - { - TreeViewItem child = new TreeViewItem(); - idChildDictionary.Add(name, child); - childIdDictionary.Add(child, name); - child.Header = header; - // this should be removed if the type is a folder!!! - child.Selected += SelectionHandler; - - if (parentName != null) - { - idChildDictionary.TryGetValue(parentName, out TreeViewItem parent); - if (parent != null) - parent.Items.Add(child); - else - FileTree.Items.Add(child); - } - else - { - FileTree.Items.Add(child); - } - } - - void SelectionHandler(object sender, RoutedEventArgs e) - { - TreeViewItem selected = sender as TreeViewItem; - - selectedItemID = childIdDictionary[selected]; - } - - public string GetSelectedItemID() - { - return selectedItemID; - } - } -} - diff --git a/ProtocolMaster/View/Log.xaml b/ProtocolMaster/View/Log.xaml deleted file mode 100644 index 214b362..0000000 --- a/ProtocolMaster/View/Log.xaml +++ /dev/null @@ -1,24 +0,0 @@ - - - - Log - - - - - - - - - - - - - diff --git a/ProtocolMaster/View/Log.xaml.cs b/ProtocolMaster/View/Log.xaml.cs deleted file mode 100644 index fc8fe6a..0000000 --- a/ProtocolMaster/View/Log.xaml.cs +++ /dev/null @@ -1,35 +0,0 @@ - -using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; - -namespace ProtocolMaster.View -{ - /// - /// Interaction logic for Log.xaml - /// - public partial class Log : UserControl - { - public Log() - { - InitializeComponent(); - } - - // Log Functions - public void LogToView(string logString) - { - Paragraph addLog = new Paragraph(); - addLog.Inlines.Add(logString); - LogDocument.Blocks.Add(addLog); - } - - public void Log_Folder_Click(object sender, RoutedEventArgs e) - { - ProtocolMaster.Model.Debug.Log.Instance.OpenFolder(); - } - public void Log_Test(object sender, RoutedEventArgs e) - { - ProtocolMaster.Model.Debug.Log.Error("Test"); - } - } -} diff --git a/ProtocolMaster/View/MainWindow.xaml b/ProtocolMaster/View/MainWindow.xaml deleted file mode 100644 index 0497046..0000000 --- a/ProtocolMaster/View/MainWindow.xaml +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ProtocolMaster/View/MainWindow.xaml.cs b/ProtocolMaster/View/MainWindow.xaml.cs deleted file mode 100644 index 647cf9c..0000000 --- a/ProtocolMaster/View/MainWindow.xaml.cs +++ /dev/null @@ -1,60 +0,0 @@ -using MahApps.Metro.Controls; -using System; -using System.IO; - -namespace ProtocolMaster.View -{ - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : MetroWindow - { - public MainWindow() - { - InitializeComponent(); - } - - private void Click_Documentation(object sender, EventArgs e) - { - System.Diagnostics.Process.Start("https://sites.google.com/view/protocolmaster/docs"); - } - - private void Click_Extension_Folder(object sender, EventArgs e) - { - System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo() - { - FileName = "C:\\Users\\phili\\source\\repos\\Philip-S-Martin\\ProtocolMaster\\ProtocolMaster\\bin\\Debug\\net48\\Extensions", - UseShellExecute = true, - Verb = "open" - }); - } - - private void Click_Protocol_Folder(object sender, EventArgs e) - { - string target = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\ProtocolMaster\\Protocols"; - Directory.CreateDirectory(target); - System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo() - { - FileName = target, - UseShellExecute = true, - Verb = "open" - }); - } - - private void Click_Log_Folder(object sender, EventArgs e) - { - ProtocolMaster.Model.Debug.Log.Instance.OpenFolder(); - } - - private void OnLoad(object sender, EventArgs e) - { - - } - - // Window Closing Operations - private void Window_Closed(object sender, EventArgs e) - { - App.Instance.Window_Closed(); - } - } -} diff --git a/ProtocolMaster/View/Media.xaml b/ProtocolMaster/View/Media.xaml deleted file mode 100644 index c778084..0000000 --- a/ProtocolMaster/View/Media.xaml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Video Feed - - - - - - - - - - - - - - - - - - - - - diff --git a/ProtocolMaster/View/Media.xaml.cs b/ProtocolMaster/View/Media.xaml.cs deleted file mode 100644 index 107fedc..0000000 --- a/ProtocolMaster/View/Media.xaml.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; -using DirectShowLib; -using Microsoft.Win32; - -namespace ProtocolMaster.View -{ - /// - /// Interaction logic for Media.xaml - /// - public partial class Media : UserControl - { - public Media() - { - InitializeComponent(); - - } - - } -} diff --git a/ProtocolMaster/View/PopupDropdown.xaml b/ProtocolMaster/View/PopupDropdown.xaml deleted file mode 100644 index 52e34c0..0000000 --- a/ProtocolMaster/View/PopupDropdown.xaml +++ /dev/null @@ -1,14 +0,0 @@ - - - - Click Here to Select - - - diff --git a/ProtocolMaster/View/PopupDropdown.xaml.cs b/ProtocolMaster/View/PopupDropdown.xaml.cs deleted file mode 100644 index 41956c0..0000000 --- a/ProtocolMaster/View/PopupDropdown.xaml.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; - -namespace ProtocolMaster.View -{ - /// - /// Interaction logic for PopupDropdown.xaml - /// - public partial class PopupDropdown : Window - { - public PopupDropdown(string[] options) - { - InitializeComponent(); - this.Left = (App.Window.Width - this.Width) / 2; - this.Top = (App.Window.Height - this.Height) / 2; - foreach (string s in options) - Dropdown.Items.Add(s); - Dropdown.SelectionChanged += DropdownSelectionHandler; - } - - private void DropdownSelectionHandler(Object sender, SelectionChangedEventArgs e) - { - this.Close(); - } - - public static string PopupNow(string[] options) - { - PopupDropdown popup = new PopupDropdown(options); - App.Window.IsEnabled = false; - popup.ShowDialog(); - App.Window.IsEnabled = true; - return popup.DropdownResult(); - } - - public string DropdownResult() - { - return Dropdown.SelectedItem as string; - } - } -} diff --git a/ProtocolMaster/View/Properties.xaml b/ProtocolMaster/View/Properties.xaml deleted file mode 100644 index 63dcfcd..0000000 --- a/ProtocolMaster/View/Properties.xaml +++ /dev/null @@ -1,20 +0,0 @@ - - - - Properties - - - - - - This is the properties panel! Hippity hoppity, click and edit properties! - - - diff --git a/ProtocolMaster/View/Properties.xaml.cs b/ProtocolMaster/View/Properties.xaml.cs deleted file mode 100644 index aeaef91..0000000 --- a/ProtocolMaster/View/Properties.xaml.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace ProtocolMaster.View -{ - /// - /// Interaction logic for Properties.xaml - /// - public partial class Properties : UserControl - { - public Properties() - { - InitializeComponent(); - } - } -} diff --git a/ProtocolMaster/View/Timeline.xaml b/ProtocolMaster/View/Timeline.xaml deleted file mode 100644 index 18e024a..0000000 --- a/ProtocolMaster/View/Timeline.xaml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - Timeline - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ProtocolMaster/View/Timeline.xaml.cs b/ProtocolMaster/View/Timeline.xaml.cs deleted file mode 100644 index b65c9ac..0000000 --- a/ProtocolMaster/View/Timeline.xaml.cs +++ /dev/null @@ -1,430 +0,0 @@ -using MahApps.Metro.Controls; -using OxyPlot; -using OxyPlot.Annotations; -using OxyPlot.Axes; -using OxyPlot.Series; -using ProtocolMaster.Model.Debug; -using ProtocolMaster.Model.Protocol; -using ProtocolMaster.Model.Protocol.Driver; -using ProtocolMaster.Model.Protocol.Interpreter; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Security.AccessControl; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; - -namespace ProtocolMaster.View -{ - /// - /// Interaction logic for Timeline.xaml - /// - public partial class Timeline : UserControl - { - public Timeline() - { - InitializeComponent(); - SetUpPlot(); - App.Instance.ExtensionSystem.InterpreterManager.OnOptionsLoaded += LoadInterpreters; - App.Instance.ExtensionSystem.DriverManager.OnOptionsLoaded += LoadDrivers; - - App.Instance.ExtensionSystem.DriverManager.OnProtocolStart += StartAnimation; - App.Instance.ExtensionSystem.DriverManager.OnProtocolEnd += EndAnimation; - } - public void LoadDrivers(object sender, EventArgs e) - { - DriverDropdown.Items.Clear(); - foreach (DriverMeta meta in App.Instance.ExtensionSystem.DriverManager.Options) - { - ListDriver(meta); - } - } - public void ListDriver(DriverMeta data) - { - MenuItem newDriver = new MenuItem - { - Header = data.ToString() - }; - newDriver.Resources.Add("data", data); - newDriver.Click += new RoutedEventHandler(DriverClickHandler); - DriverDropdown.Items.Add(newDriver); - } - - public void DriverClickHandler(object sender, RoutedEventArgs e) - { - MenuItem src = e.Source as MenuItem; - DriverMeta data = src.Resources["data"] as DriverMeta; - - App.Instance.ExtensionSystem.DriverManager.Selected = data; - ShowSelectedDriver(); - } - - public void ShowSelectedDriver() - { - SelectedDriver.Header = "Selected: " + App.Instance.ExtensionSystem.DriverManager.Selected.ToString(); - } - - public void LoadInterpreters(object sender, EventArgs e) - { - InterpreterDropdown.Items.Clear(); - foreach (InterpreterMeta meta in App.Instance.ExtensionSystem.InterpreterManager.Options) - { - ListInterpreter(meta); - } - } - - public void ListInterpreter(InterpreterMeta data) - { - MenuItem newInterpreter = new MenuItem - { - Header = data.ToString() - }; - newInterpreter.Resources.Add("data", data); - newInterpreter.Click += new RoutedEventHandler(InterpreterClickHandler); - InterpreterDropdown.Items.Add(newInterpreter); - } - - public void InterpreterClickHandler(object sender, RoutedEventArgs e) - { - MenuItem src = e.Source as MenuItem; - InterpreterMeta data = src.Resources["data"] as InterpreterMeta; - App.Instance.ExtensionSystem.InterpreterManager.Selected = data; - ShowSelectedInterpreter(); - } - - public void ShowSelectedInterpreter() - { - SelectedInterpreter.Header = "Selected: " + App.Instance.ExtensionSystem.InterpreterManager.Selected.ToString(); - } - - DateTime start; - - public void Load_Click(object sender, RoutedEventArgs e) - { - LoadButton.IsEnabled = true; - StartButton.IsEnabled = true; - CancelButton.IsEnabled = false; - ResetButton.IsEnabled = true; - App.Instance.ExtensionSystem.Interpret(App.Window.DriveView.GetSelectedItemID()); - } - public void Start_Click(object sender, RoutedEventArgs e) - { - LoadButton.IsEnabled = false; - StartButton.IsEnabled = false; - CancelButton.IsEnabled = true; - ResetButton.IsEnabled = false; - - App.Instance.ExtensionSystem.Run(); - } - - public void Cancel_Click(object sender, RoutedEventArgs e) - { - LoadButton.IsEnabled = true; - StartButton.IsEnabled = false; - CancelButton.IsEnabled = false; - ResetButton.IsEnabled = true; - - App.Instance.ExtensionSystem.End(); - } - - public void Reset_Click(object sender, RoutedEventArgs e) - { - LoadButton.IsEnabled = true; - StartButton.IsEnabled = false; - CancelButton.IsEnabled = false; - ResetButton.IsEnabled = false; - - App.Instance.ExtensionSystem.Reset(); - Line.X = 0; - plot.Model.Series.Clear(); - categoryAxis.Labels.Clear(); - dateTimeAxis.AbsoluteMaximum = 1.035; - - categoryAxis.AbsoluteMaximum = 0.6; - categoryAxis.MaximumRange = categoryAxis.AbsoluteMaximum - categoryAxis.AbsoluteMinimum; - categoryAxis.MinimumRange = categoryAxis.AbsoluteMaximum - categoryAxis.AbsoluteMinimum; - plot.Model.InvalidatePlot(true); - } - - private void GeneratePlotModel() - { - - } - - static OxyColor[] pallete = { OxyColors.DarkRed, OxyColors.DodgerBlue, OxyColors.Green }; - - internal class CategoryNode - { - public string Name { get; private set; } - public CategoryNode Parent { get; private set; } - public List Children { get; private set; } - public IntervalBarSeries Series { get; private set; } - public CategoryNode(string name) - { - this.Name = name; - this.Children = new List(); - Series = new IntervalBarSeries() { Title = name, StrokeThickness = 1.5, StrokeColor = OxyColors.Gray, FillColor = OxyColor.FromArgb(255, 16, 16, 16), BarWidth = 1.0, ToolTip = name }; - } - public CategoryNode(string name, CategoryNode parent) : this(name) - { - this.Parent = parent; - Series.BarWidth = 0.35; - parent.Children.Add(this); - } - public void SetSeriesData(int categoryIndex, OxyColor color) - { - Series.StrokeColor = OxyColor.FromArgb(255, (byte)((color.R * 3 + 255) / 4), (byte)((color.G * 3 + 255) / 4), (byte)((color.B * 3 + 255) / 4)); - foreach (IntervalBarItem item in Series.Items) - { - item.CategoryIndex = categoryIndex; - item.Color = color; - } - } - public static void GeneratePlotData(List rootNodes, out List allSeries, out List labels, out List gridLines) - { - double linePos = 0.0; - int fullIndex = 0; - int palleteIndex = 0; - allSeries = new List(); - labels = new List(); - gridLines = new List(); - for (int i = 0; i < rootNodes.Count; i++) - { - CategoryNode root = rootNodes[i]; - root.SetSeriesData(fullIndex++, pallete[palleteIndex]); - allSeries.Add(root.Series); - labels.Add(root.Name); - gridLines.Add(linePos++); - Stack subtree = new Stack(root.Children); - while (subtree.Count != 0) - { - CategoryNode subNode = subtree.Pop(); - subNode.SetSeriesData(fullIndex++, pallete[palleteIndex]); - allSeries.Add(subNode.Series); - labels.Add(subNode.Name); - gridLines.Add(linePos++); - foreach (CategoryNode child in subNode.Children) - subtree.Push(child); - } - if (i < rootNodes.Count - 1) - { - palleteIndex = (palleteIndex + 1) % pallete.Length; - linePos++; - fullIndex++; - labels.Add(""); - } - } - } - // The biggest ugliest mess in the universe! - public static List BuildTrees(List eventList) - { - Dictionary nodeDictionary = new Dictionary(); - List rootNodes = new List(); - Queue eventQueue = new Queue(eventList); - if (eventList != null) - { - while (eventQueue.Count != 0) - { - ProtocolEvent plotEvent = eventQueue.Dequeue(); - if (plotEvent.HasCategory()) - { - CategoryNode targetNode; - if (plotEvent.HasParent()) - { - if (nodeDictionary.ContainsKey(plotEvent.ParentLabel)) - { - if (!nodeDictionary.TryGetValue(plotEvent.FullLabel(), out targetNode)) - { - targetNode = new CategoryNode(plotEvent.CategoryLabel, nodeDictionary[plotEvent.ParentLabel]); - nodeDictionary.Add(plotEvent.FullLabel(), targetNode); - } - } - else - { - eventQueue.Enqueue(plotEvent); - continue; - } - } - else - { - if (!nodeDictionary.TryGetValue(plotEvent.FullLabel(), out targetNode)) - { - targetNode = new CategoryNode(plotEvent.CategoryLabel); - nodeDictionary.Add(plotEvent.FullLabel(), targetNode); - rootNodes.Add(targetNode); - } - } - targetNode.Series.Items.Add(new IntervalBarItem - { - Start = new DateTime(Convert.ToInt64(plotEvent.Arguments["TimeStartMs"]) * 10000).ToOADate(), - End = new DateTime(Convert.ToInt64(plotEvent.Arguments["TimeEndMs"]) * 10000).ToOADate() - }); - } - } - } - return rootNodes; - } - } - - LineAnnotation Line; - CategoryAxis categoryAxis; - DateTimeAxis dateTimeAxis; - public void LoadPlotData(List eventList) - { - List allSeries; - List labels; - List gridLines; - CategoryNode.GeneratePlotData(CategoryNode.BuildTrees(eventList), out allSeries, out labels, out gridLines); - - // GENERATE LABELS, GRIDLINES, ETC. FROM TREE! - categoryAxis.Labels.Clear(); - categoryAxis.Labels.AddRange(labels); - gridLines.CopyTo(categoryAxis.ExtraGridlines, 0); - - plot.Model.Series.Clear(); - foreach (IntervalBarSeries series in allSeries) - plot.Model.Series.Add(series); - plot.ResetAllAxes(); - dateTimeAxis.Minimum = 0; - categoryAxis.AbsoluteMaximum = gridLines[gridLines.Count - 1] + 0.6; - categoryAxis.MaximumRange = categoryAxis.AbsoluteMaximum - categoryAxis.AbsoluteMinimum; - categoryAxis.MinimumRange = categoryAxis.AbsoluteMaximum - categoryAxis.AbsoluteMinimum; - plot.Model.InvalidatePlot(true); - } - private void SetUpPlot() - { - var model = new PlotModel - { - IsLegendVisible = false - }; - - dateTimeAxis = new DateTimeAxis() - { - Position = AxisPosition.Bottom, - AxislineThickness = 1.5, - ExtraGridlineThickness = 1.5, - MajorGridlineThickness = 1.5, - MinorGridlineThickness = 1.5, - TicklineColor = OxyColors.Gray, - AxislineColor = OxyColors.Gray, - MinorTicklineColor = OxyColors.Gray, - TextColor = OxyColors.WhiteSmoke, - TitleColor = OxyColors.WhiteSmoke, - ExtraGridlineColor = OxyColors.Gray, - MinorGridlineColor = OxyColors.Gray, - MajorGridlineColor = OxyColors.Gray, - StartPosition = 0, - AbsoluteMinimum = 0, - AbsoluteMaximum = 1.035, - }; - model.Axes.Add(dateTimeAxis); - categoryAxis = new CategoryAxis() - { - Position = AxisPosition.Left, - AxislineThickness = 1.5, - ExtraGridlineThickness = 1.5, - MajorGridlineThickness = 1.5, - MinorGridlineThickness = 1.5, - TicklineColor = OxyColors.Transparent, - AxislineColor = OxyColors.Gray, - MinorTicklineColor = OxyColors.Transparent, - TextColor = OxyColors.WhiteSmoke, - TitleColor = OxyColors.WhiteSmoke, - ExtraGridlineColor = OxyColors.Gray, - MinorGridlineColor = OxyColors.Gray, - MajorGridlineColor = OxyColors.Gray, - GapWidth = 0.0f, - ExtraGridlines = new double[32], - AbsoluteMinimum = -0.6, - AbsoluteMaximum = 0.6, - MaximumRange = 1.2, - MinimumRange = 1.2, - }; - model.Axes.Add(categoryAxis); - - model.DefaultColors = new List - { - OxyColors.Gray, - OxyColors.Gray, - OxyColors.Gray, - OxyColors.Gray - }; - - model.TextColor = OxyColors.White; - model.PlotAreaBorderColor = OxyColors.Transparent; - plot.ActualController.UnbindMouseDown(OxyMouseButton.Left); - plot.ActualController.BindMouseDown(OxyMouseButton.Left, PlotCommands.PanAt); - plot.ActualController.UnbindMouseDown(OxyMouseButton.Right); - plot.ActualController.BindMouseDown(OxyMouseButton.Right, PlotCommands.ResetAt); - - Line = new LineAnnotation() - { - StrokeThickness = 1.5, - Color = OxyColors.Green, - Type = LineAnnotationType.Vertical, - LineStyle = LineStyle.Solid, - X = 0, - Y = 0 - }; - - model.Annotations.Add(Line); - dateTimeAxis.Minimum = 0; - plot.Model = model; - } - - Task bgWorker; - Progress animationProgress; - CancellationTokenSource tokenSource; - public void StartAnimation(object sender, EventArgs e) - { - - animationProgress = new Progress(); - tokenSource = new CancellationTokenSource(); - CancellationToken cancelToken = tokenSource.Token; - animationProgress.ProgressChanged += bgWorker_ProgressChanged; - bgWorker = new Task(() => - { - bgWorker_DoWork(animationProgress, cancelToken); - }, cancelToken); - bgWorker.Start(); - start = DateTime.Now; - } - public void EndAnimation(object sender, EventArgs e) - { - tokenSource.Cancel(); - } - - void bgWorker_ProgressChanged(object sender, int e) - { - //Log.Error(e.UserState.GetType().ToString()); - Line.X = (DateTime.Now.ToOADate() - start.ToOADate()); - plot.InvalidatePlot(); - } - - void bgWorker_DoWork(IProgress progress, CancellationToken cancelToken) - { - DateTime workerstart = DateTime.Now; - DateTime end = workerstart.AddSeconds(120); - DateTime now = workerstart; - DateTime nextFrame = workerstart.AddTicks(250000); - while (now < end && !cancelToken.IsCancellationRequested) - { - while (now < nextFrame) - { - Thread.Sleep(5); - now = DateTime.Now; - } - progress.Report(1); - nextFrame = now.AddMilliseconds(200); - } - progress.Report(0); - } - private void MenuItem_Click(object sender, System.Windows.RoutedEventArgs e) - { - - } - } -} diff --git a/ProtocolMaster/ViewModel/DriveViewModel.cs b/ProtocolMaster/ViewModel/DriveViewModel.cs deleted file mode 100644 index 505b98b..0000000 --- a/ProtocolMaster/ViewModel/DriveViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.ViewModel -{ - class DriveViewModel - { - } -} diff --git a/ProtocolMaster/ViewModel/LogViewModel.cs b/ProtocolMaster/ViewModel/LogViewModel.cs deleted file mode 100644 index e46fb83..0000000 --- a/ProtocolMaster/ViewModel/LogViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.ViewModel -{ - class LogViewModel - { - } -} diff --git a/ProtocolMaster/ViewModel/MainWindowViewModel.cs b/ProtocolMaster/ViewModel/MainWindowViewModel.cs deleted file mode 100644 index f128de8..0000000 --- a/ProtocolMaster/ViewModel/MainWindowViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.ViewModel -{ - class MainWindowViewModel - { - } -} diff --git a/ProtocolMaster/ViewModel/MediaViewModel.cs b/ProtocolMaster/ViewModel/MediaViewModel.cs deleted file mode 100644 index 2e61e26..0000000 --- a/ProtocolMaster/ViewModel/MediaViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.ViewModel -{ - class MediaViewModel - { - } -} diff --git a/ProtocolMaster/ViewModel/PropertiesViewModel.cs b/ProtocolMaster/ViewModel/PropertiesViewModel.cs deleted file mode 100644 index 4a8d018..0000000 --- a/ProtocolMaster/ViewModel/PropertiesViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.ViewModel -{ - class PropertiesViewModel - { - } -} diff --git a/ProtocolMaster/ViewModel/TimelineViewModel.cs b/ProtocolMaster/ViewModel/TimelineViewModel.cs deleted file mode 100644 index 6eda002..0000000 --- a/ProtocolMaster/ViewModel/TimelineViewModel.cs +++ /dev/null @@ -1,29 +0,0 @@ -using ProtocolMaster.Model.Protocol.Driver; -using ProtocolMaster.Model.Protocol.Interpreter; -using ProtocolMaster.View; -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; - -namespace ProtocolMaster.ViewModel -{ - public class TimelineViewModel : ViewModelBase - { - //public List InterpreterOptions { get; set; } - public TimelineViewModel() - { - //App.Instance.Extensions.Interpreters.Interpreters.CollectionChanged += LoadInterpreterOptions; - } - - public void LoadInterpreterOptions(object sender, NotifyCollectionChangedEventArgs e) - { - - } - public List Options { get; set; } - } -} diff --git a/ProtocolMaster/ViewModel/ViewModelBase.cs b/ProtocolMaster/ViewModel/ViewModelBase.cs deleted file mode 100644 index 5f10fd9..0000000 --- a/ProtocolMaster/ViewModel/ViewModelBase.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; - -namespace ProtocolMaster.ViewModel -{ - public class ViewModelBase: INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected bool SetProperty(ref T field, T newValue, [CallerMemberName]string propertyName = null) - { - if (!EqualityComparer.Default.Equals(field, newValue)) - { - field = newValue; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - return true; - } - return false; - } - } -} diff --git a/ProtocolMasterCore/Prompt/DefaultPrompts.cs b/ProtocolMasterCore/Prompt/DefaultPrompts.cs new file mode 100644 index 0000000..9c572fb --- /dev/null +++ b/ProtocolMasterCore/Prompt/DefaultPrompts.cs @@ -0,0 +1,12 @@ +namespace ProtocolMasterCore.Prompt +{ + public static class DefaultPrompts + { + public static string UserSelect(string[] options) + { + if (options != null && options.Length > 0 && options[0] != null) + return options[0]; + return "null"; + } + } +} diff --git a/ProtocolMasterCore/Prompt/IPromptUserSelect.cs b/ProtocolMasterCore/Prompt/IPromptUserSelect.cs new file mode 100644 index 0000000..2dd4334 --- /dev/null +++ b/ProtocolMasterCore/Prompt/IPromptUserSelect.cs @@ -0,0 +1,8 @@ +namespace ProtocolMasterCore.Prompt +{ + public delegate string UserSelectHandler(string[] keys); + public interface IPromptUserSelect + { + public UserSelectHandler UserSelectPrompt { set; } + } +} diff --git a/ProtocolMasterCore/Prompt/PromptTargetStore.cs b/ProtocolMasterCore/Prompt/PromptTargetStore.cs new file mode 100644 index 0000000..a5ffee1 --- /dev/null +++ b/ProtocolMasterCore/Prompt/PromptTargetStore.cs @@ -0,0 +1,11 @@ +namespace ProtocolMasterCore.Prompt +{ + public class PromptTargetStore + { + public UserSelectHandler UserSelect { get; set; } + public PromptTargetStore() + { + UserSelect = DefaultPrompts.UserSelect; + } + } +} diff --git a/ProtocolMasterCore/Protocol/Driver/DriverManager.cs b/ProtocolMasterCore/Protocol/Driver/DriverManager.cs new file mode 100644 index 0000000..5f2b12b --- /dev/null +++ b/ProtocolMasterCore/Protocol/Driver/DriverManager.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +namespace ProtocolMasterCore.Protocol.Driver +{ + public delegate void DriverTimeEvent(); + public class DriverManager : ExtensionManager + { + IDriver driver; + public DriverTimeEvent OnProtocolStart; + public DriverTimeEvent OnProtocolEnd; + internal bool Run(List data) + { + bool didStart = false; + driver = CreateSelectedExtension(); + if (driver.Setup(data)) + { + didStart = true; + OnProtocolStart?.Invoke(); + driver.Start(); + OnProtocolEnd?.Invoke(); + } + DisposeSelectedExtension(); + return didStart; + } + } +} diff --git a/ProtocolMaster/Model/Protocol/Driver/DriverMeta.cs b/ProtocolMasterCore/Protocol/Driver/DriverMeta.cs similarity index 77% rename from ProtocolMaster/Model/Protocol/Driver/DriverMeta.cs rename to ProtocolMasterCore/Protocol/Driver/DriverMeta.cs index 0c8ec15..7f5c356 100644 --- a/ProtocolMaster/Model/Protocol/Driver/DriverMeta.cs +++ b/ProtocolMasterCore/Protocol/Driver/DriverMeta.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; +using System.Diagnostics.CodeAnalysis; -namespace ProtocolMaster.Model.Protocol.Driver +namespace ProtocolMasterCore.Protocol.Driver { [MetadataAttribute] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] @@ -18,6 +19,9 @@ public DriverMeta(string name, string version, params string[] eventNames) : bas this.Version = version; this.HandlerLabels = eventNames; } + public DriverMeta() + { + } public DriverMeta(IDictionary inputs) { @@ -30,5 +34,10 @@ public override string ToString() { return Name + " " + Version; } + + public bool Equals([AllowNull] IExtensionMeta other) + { + return this.Name == other.Name && this.Version == other.Version; + } } } diff --git a/ProtocolMaster/Model/Protocol/Driver/IDriver.cs b/ProtocolMasterCore/Protocol/Driver/IDriver.cs similarity index 87% rename from ProtocolMaster/Model/Protocol/Driver/IDriver.cs rename to ProtocolMasterCore/Protocol/Driver/IDriver.cs index c4aea35..a3b01ea 100644 --- a/ProtocolMaster/Model/Protocol/Driver/IDriver.cs +++ b/ProtocolMasterCore/Protocol/Driver/IDriver.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; +using System.Collections.Generic; -namespace ProtocolMaster.Model.Protocol.Driver +namespace ProtocolMasterCore.Protocol.Driver { public enum DriverProgress { diff --git a/ProtocolMaster/Model/Protocol/ExtensionManager.cs b/ProtocolMasterCore/Protocol/ExtensionManager.cs similarity index 53% rename from ProtocolMaster/Model/Protocol/ExtensionManager.cs rename to ProtocolMasterCore/Protocol/ExtensionManager.cs index 664ef00..b7f498d 100644 --- a/ProtocolMaster/Model/Protocol/ExtensionManager.cs +++ b/ProtocolMasterCore/Protocol/ExtensionManager.cs @@ -1,28 +1,25 @@ -using System; +using ProtocolMasterCore.Prompt; +using ProtocolMasterCore.Utility; +using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Navigation; -using System.Windows.Threading; -namespace ProtocolMaster.Model.Protocol +namespace ProtocolMasterCore.Protocol { - abstract class ExtensionManager where T : IExtensionMeta where E : IExtension + public delegate void OptionsLoadedCallback(List options); + public abstract class ExtensionManager where T : IExtensionMeta where E : IExtension { [ImportMany] - IEnumerable> AvailableExtensions { get; set; } + IEnumerable> Extensions { get; set; } ExportFactory extensionFactory; ExportLifetimeContext extensionContext; E extension; - public bool IsRunning { get => !isDisposed; } + internal bool IsRunning { get => !isDisposed; } bool isDisposed = true; - - public event EventHandler OnOptionsLoaded; - protected Dispatcher UIDispatcher { get; set; } + public OptionsLoadedCallback OnOptionsLoaded { get; set; } + public PromptTargetStore PromptTargets { get => _promptTargets; set => _promptTargets = value; } + private PromptTargetStore _promptTargets = new PromptTargetStore(); public void LoadOptions(CompositionContainer container) { try @@ -31,32 +28,35 @@ public void LoadOptions(CompositionContainer container) } catch (CompositionException compositionException) { - Debug.Log.Error(compositionException.ToString()); + Log.Error(compositionException.ToString()); } - foreach (ExportFactory i in AvailableExtensions) + var extensionMeta = new List(); + foreach (ExportFactory i in Extensions) { - if (i.Metadata.Name == "None" && i.Metadata.Version == "") + if (i.Metadata.Name == "None Selected" && i.Metadata.Version == "") { Selected = i.Metadata; } - Debug.Log.Error(typeof(E).ToString() + " found: '" + i.Metadata.Name + "' version: '" + i.Metadata.Version + "'"); + extensionMeta.Add(i.Metadata); + Log.Error($"{typeof(E)} found: '{i.Metadata.Name}' version: '{i.Metadata.Version}'"); } - UIDispatcher = Dispatcher.CurrentDispatcher; - OnOptionsLoaded?.Invoke(this, new EventArgs()); + OnOptionsLoaded?.Invoke(extensionMeta); } public IExtensionMeta Selected { get { return extensionFactory.Metadata; } set { - foreach (ExportFactory i in AvailableExtensions) + foreach (ExportFactory i in Extensions) { - if (i.Metadata.Name == value.Name && i.Metadata.Version == value.Version) + if (i.Metadata.Equals(value)) { extensionFactory = i; + return; } } + throw new ArgumentException($"No extension of type {typeof(E)} of with metadata {value} is loaded"); } } public IEnumerable Options @@ -64,7 +64,7 @@ public IEnumerable Options get { List optionsList = new List(); - foreach (ExportFactory i in AvailableExtensions) + foreach (ExportFactory i in Extensions) { optionsList.Add(i.Metadata); } @@ -78,9 +78,9 @@ protected E CreateSelectedExtension() isDisposed = false; extensionContext = extensionFactory.CreateExport(); extension = extensionContext.Value; - if (typeof(ICallDropdown).IsAssignableFrom(extension.GetType())) + if (typeof(IPromptUserSelect).IsAssignableFrom(extension.GetType())) { - (extension as ICallDropdown).CallDropdown = CallHandler.CallDropdown; + (extension as IPromptUserSelect).UserSelectPrompt = PromptTargets.UserSelect; } return extension; } @@ -94,9 +94,10 @@ protected void DisposeSelectedExtension() extensionContext.Dispose(); isDisposed = true; } - public void Cancel() + + public void CancelRunningExtension() { - extension.Cancel(); + extension.IsCanceled = true; DisposeSelectedExtension(); } } diff --git a/ProtocolMasterCore/Protocol/IExtension.cs b/ProtocolMasterCore/Protocol/IExtension.cs new file mode 100644 index 0000000..6e74348 --- /dev/null +++ b/ProtocolMasterCore/Protocol/IExtension.cs @@ -0,0 +1,7 @@ +namespace ProtocolMasterCore.Protocol +{ + public interface IExtension + { + bool IsCanceled { get; set; } + } +} diff --git a/ProtocolMasterCore/Protocol/IExtensionMeta.cs b/ProtocolMasterCore/Protocol/IExtensionMeta.cs new file mode 100644 index 0000000..7bd48c6 --- /dev/null +++ b/ProtocolMasterCore/Protocol/IExtensionMeta.cs @@ -0,0 +1,14 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Xml.Serialization; + +namespace ProtocolMasterCore.Protocol +{ + public interface IExtensionMeta : IEquatable + { + string Name { get; } + string Version { get; } + } + +} diff --git a/ProtocolMaster/Model/Protocol/ExtensionSystem.cs b/ProtocolMasterCore/Protocol/InterpretAndDriveProtocol.cs similarity index 53% rename from ProtocolMaster/Model/Protocol/ExtensionSystem.cs rename to ProtocolMasterCore/Protocol/InterpretAndDriveProtocol.cs index be78ca5..e2d4289 100644 --- a/ProtocolMaster/Model/Protocol/ExtensionSystem.cs +++ b/ProtocolMasterCore/Protocol/InterpretAndDriveProtocol.cs @@ -1,23 +1,15 @@ -using ProtocolMaster.Model.Debug; +using ProtocolMasterCore.Protocol.Driver; +using ProtocolMasterCore.Protocol.Interpreter; using System; using System.Collections.Generic; -using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.IO; -using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Windows.Documents; -using ProtocolMaster.Model.Protocol.Interpreter; -using ProtocolMaster.Model.Protocol.Driver; -using System.ComponentModel; -using System.Collections.ObjectModel; -namespace ProtocolMaster.Model.Protocol +namespace ProtocolMasterCore.Protocol { - - internal class ExtensionSystem + public class InterpretAndDriveProtocol { // Import All Extensions so that they can be Composed (ComposeParts()) public DriverManager DriverManager { get; private set; } @@ -29,12 +21,10 @@ internal class ExtensionSystem bool isRunning; bool isReady; - Task> generator; - private Task runTask; private CancellationToken cancelToken; private CancellationTokenSource tokenSource; - public ExtensionSystem() + public InterpretAndDriveProtocol(string directory) { isRunning = false; isReady = false; @@ -42,15 +32,12 @@ public ExtensionSystem() DriverManager = new DriverManager(); InterpreterManager = new InterpreterManager(); - string targetDir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + "\\Extensions"; AggregateCatalog catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(typeof(IExtension).Assembly)); - catalog.Catalogs.Add(new DirectoryCatalog(targetDir)); + catalog.Catalogs.Add(new DirectoryCatalog(directory)); _container = new CompositionContainer(catalog); - Debug.Log.Error("Extension Location: " + targetDir); } - - ~ExtensionSystem() + ~InterpretAndDriveProtocol() { Terminate(); } @@ -59,7 +46,7 @@ public void LoadExtensions() DriverManager.LoadOptions(_container); InterpreterManager.LoadOptions(_container); } - public void Interpret(string selectionID) + public void Interpret(Stream stream, string argument = null) { if (isRunning) { @@ -71,30 +58,7 @@ public void Interpret(string selectionID) //then fall through } isReady = true; - tokenSource = new CancellationTokenSource(); - cancelToken = tokenSource.Token; - generator = Task.Factory.StartNew>(() => - { - cancelToken.Register(new Action(() => - { - if (InterpreterManager.IsRunning) - { - InterpreterManager.Cancel(); - } - })); - return InterpreterManager.GenerateData(selectionID); - }, TaskCreationOptions.LongRunning); - - - Task UITask = generator.ContinueWith((data) => - { - List result = generator.Result; - Data = result; - if (result != null) - { - App.Window.TimelineView.LoadPlotData(result); - } - }, TaskScheduler.FromCurrentSynchronizationContext()); + Data = InterpreterManager.GenerateData(stream, argument); } public void Run() { @@ -111,26 +75,10 @@ public void Run() isReady = false; isRunning = true; Progress driverProgress = new Progress(); - - Task UITask = generator.ContinueWith((data) => - { - runTask = Task.Run(new Action(() => - { - cancelToken.Register(new Action(() => - { - if (DriverManager.IsRunning) - { - DriverManager.Cancel(); - } - })); - DriverManager.Run(Data); - }), tokenSource.Token); - - }, TaskScheduler.FromCurrentSynchronizationContext()); + DriverManager.Run(Data); } } - - public void End() + public void Cancel() { if (!isRunning) { @@ -146,8 +94,16 @@ public void Reset() } Terminate(); } - public void Terminate() + private void Terminate() { + if (InterpreterManager.IsRunning) + { + InterpreterManager.CancelRunningExtension(); + } + if (DriverManager.IsRunning) + { + DriverManager.CancelRunningExtension(); + } if (tokenSource != null && !tokenSource.IsCancellationRequested) tokenSource.Cancel(); isRunning = false; diff --git a/ProtocolMaster/Model/Protocol/Interpreter/ExcelDataInterpreter.cs b/ProtocolMasterCore/Protocol/Interpreter/ExcelDataInterpreter.cs similarity index 61% rename from ProtocolMaster/Model/Protocol/Interpreter/ExcelDataInterpreter.cs rename to ProtocolMasterCore/Protocol/Interpreter/ExcelDataInterpreter.cs index 7d10404..3d9c738 100644 --- a/ProtocolMaster/Model/Protocol/Interpreter/ExcelDataInterpreter.cs +++ b/ProtocolMasterCore/Protocol/Interpreter/ExcelDataInterpreter.cs @@ -1,11 +1,6 @@ using ExcelDataReader; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace ProtocolMaster.Model.Protocol.Interpreter +namespace ProtocolMasterCore.Protocol.Interpreter { public abstract class ExcelDataInterpreter { diff --git a/ProtocolMasterCore/Protocol/Interpreter/IInterpreter.cs b/ProtocolMasterCore/Protocol/Interpreter/IInterpreter.cs new file mode 100644 index 0000000..7694210 --- /dev/null +++ b/ProtocolMasterCore/Protocol/Interpreter/IInterpreter.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace ProtocolMasterCore.Protocol.Interpreter +{ + public interface IInterpreter : IExtension + { + List Generate(string protocolName); + } +} diff --git a/ProtocolMasterCore/Protocol/Interpreter/InterpreterManager.cs b/ProtocolMasterCore/Protocol/Interpreter/InterpreterManager.cs new file mode 100644 index 0000000..cb09088 --- /dev/null +++ b/ProtocolMasterCore/Protocol/Interpreter/InterpreterManager.cs @@ -0,0 +1,32 @@ +using ExcelDataReader; +using System.Collections.Generic; +using System.IO; + +namespace ProtocolMasterCore.Protocol.Interpreter +{ + public delegate void ProtocolEventsLoader(List events); + public class InterpreterManager : ExtensionManager + { + IInterpreter interpreter; + public ProtocolEventsLoader OnEventsLoaded; + internal List GenerateData(Stream stream, string argument = null) + { + interpreter = CreateSelectedExtension(); + + if (typeof(ExcelDataInterpreter).IsAssignableFrom(interpreter.GetType())) + { + System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); + ExcelDataInterpreter spreadSheetInterpreter = interpreter as ExcelDataInterpreter; + if (stream != null) + spreadSheetInterpreter.SetReader(ExcelReaderFactory.CreateReader(stream)); + else return null; + } + // pre-fill event data + List result = interpreter.Generate(argument); + if (interpreter.IsCanceled) return null; + DisposeSelectedExtension(); + OnEventsLoaded?.Invoke(result); + return result; + } + } +} diff --git a/ProtocolMaster/Model/Protocol/Interpreter/InterpreterMeta.cs b/ProtocolMasterCore/Protocol/Interpreter/InterpreterMeta.cs similarity index 80% rename from ProtocolMaster/Model/Protocol/Interpreter/InterpreterMeta.cs rename to ProtocolMasterCore/Protocol/Interpreter/InterpreterMeta.cs index e54600e..9ff7096 100644 --- a/ProtocolMaster/Model/Protocol/Interpreter/InterpreterMeta.cs +++ b/ProtocolMasterCore/Protocol/Interpreter/InterpreterMeta.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; +using System.Diagnostics.CodeAnalysis; -namespace ProtocolMaster.Model.Protocol.Interpreter +namespace ProtocolMasterCore.Protocol.Interpreter { [MetadataAttribute] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] @@ -29,5 +30,10 @@ public override string ToString() { return Name + " " + Version; } + + public bool Equals([AllowNull] IExtensionMeta other) + { + return this.Name == other.Name && this.Version == other.Version; + } } } diff --git a/ProtocolMasterCore/Protocol/NullExtensions/NullDriver.cs b/ProtocolMasterCore/Protocol/NullExtensions/NullDriver.cs new file mode 100644 index 0000000..db73ef0 --- /dev/null +++ b/ProtocolMasterCore/Protocol/NullExtensions/NullDriver.cs @@ -0,0 +1,21 @@ +using ProtocolMasterCore.Protocol.Driver; +using System.Collections.Generic; + +namespace ProtocolMasterCore.Protocol.NullExtensions +{ + [DriverMeta("None Selected", "")] + public class NullDriver : IDriver + { + public bool IsCanceled { get; set; } + + + public bool Setup(List dataList) + { + return false; + } + + public void Start() + { + } + } +} diff --git a/ProtocolMasterCore/Protocol/NullExtensions/NullInterpreter.cs b/ProtocolMasterCore/Protocol/NullExtensions/NullInterpreter.cs new file mode 100644 index 0000000..fd0b03e --- /dev/null +++ b/ProtocolMasterCore/Protocol/NullExtensions/NullInterpreter.cs @@ -0,0 +1,17 @@ +using ProtocolMasterCore.Protocol.Interpreter; +using System.Collections.Generic; + +namespace ProtocolMasterCore.Protocol.NullExtensions +{ + [InterpreterMeta("None Selected", "")] + public class NullInterpreter : IInterpreter + { + public bool IsCanceled { get; set; } + public List Generate(string protocolName) + { + return null; + } + } + + +} diff --git a/ProtocolMaster/Model/Protocol/ProtocolEvent.cs b/ProtocolMasterCore/Protocol/ProtocolEvent.cs similarity index 95% rename from ProtocolMaster/Model/Protocol/ProtocolEvent.cs rename to ProtocolMasterCore/Protocol/ProtocolEvent.cs index d4becd5..f9010db 100644 --- a/ProtocolMaster/Model/Protocol/ProtocolEvent.cs +++ b/ProtocolMasterCore/Protocol/ProtocolEvent.cs @@ -1,20 +1,20 @@ using System.Collections.Generic; -namespace ProtocolMaster.Model.Protocol +namespace ProtocolMasterCore.Protocol { public class ProtocolEvent { public string Handler { get; private set; } public string CategoryLabel { get; private set; } public string ParentLabel { get; private set; } - + public Dictionary Arguments { get; private set; } public ProtocolEvent(string handler, params KeyValuePair[] args) { this.Handler = handler; Arguments = new Dictionary(); - foreach(KeyValuePair arg in args) + foreach (KeyValuePair arg in args) { Arguments.Add(arg.Key, arg.Value); } diff --git a/ProtocolMasterCore/ProtocolMasterCore.csproj b/ProtocolMasterCore/ProtocolMasterCore.csproj new file mode 100644 index 0000000..a8e4bb7 --- /dev/null +++ b/ProtocolMasterCore/ProtocolMasterCore.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + diff --git a/ProtocolMasterCore/Utility/AppEnvironment.cs b/ProtocolMasterCore/Utility/AppEnvironment.cs new file mode 100644 index 0000000..c7bce25 --- /dev/null +++ b/ProtocolMasterCore/Utility/AppEnvironment.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace ProtocolMasterCore.Utility +{ + public sealed class AppEnvironment + { + private static readonly AppEnvironment instance = new AppEnvironment(); + // Explicit static constructor to tell C# compiler + // not to mark type as beforefieldinit + static AppEnvironment() + { + } + + Dictionary locations; + string appData; + string documents; + string assembly; + + private AppEnvironment() + { + locations = new Dictionary(); + appData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtocolMaster"); + documents = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "ProtocolMaster"); + assembly = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); + Directory.CreateDirectory(appData); + Directory.CreateDirectory(documents); + Directory.CreateDirectory(assembly); + } + public static bool TryAddLocationAppData(string label, string subPath, out string fullPath) + => instance.TryAddLocation(label, instance.appData, subPath, out fullPath); + public static bool TryAddLocationDocuments(string label, string subPath, out string fullPath) + => instance.TryAddLocation(label, instance.documents, subPath, out fullPath); + public static bool TryAddLocationAssembly(string label, string subPath, out string fullPath) + => instance.TryAddLocation(label, instance.assembly, subPath, out fullPath); + private bool TryAddLocation(string label, string basePath, string subPath, out string fullPath) + { + fullPath = Path.Combine(basePath, subPath); + if (locations.TryAdd(label, fullPath)) + { + Directory.CreateDirectory(fullPath); + return true; + } + else return false; + } + + public static bool TryGetLocation(string label, out string fullPath) => instance.I_TryGetLocation(label, out fullPath); + private bool I_TryGetLocation(string label, out string fullPath) => locations.TryGetValue(label, out fullPath); + + public static string GetLocation(string label) => instance.I_GetLocation(label); + private string I_GetLocation(string label) => locations[label]; + + } +} diff --git a/ProtocolMasterCore/Utility/Archiver.cs b/ProtocolMasterCore/Utility/Archiver.cs new file mode 100644 index 0000000..0f4e329 --- /dev/null +++ b/ProtocolMasterCore/Utility/Archiver.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; + +namespace ProtocolMasterCore.Utility +{ + public static class Archiver + { + public static void ArchiveOldestInDirectory(string path, string archivePath, string extension, int unarchivedMax, int unarchivedMin) + { + // Get all files of type + IEnumerable zipTargets = + new DirectoryInfo(path) + .GetFileSystemInfos() + .Where(fi => fi.Name.Contains(extension)); + // Exit if bounds not met + if (zipTargets.Count() < unarchivedMax) + return; + // Refine target list to oldest + zipTargets = + zipTargets.OrderBy(fi => fi.CreationTime) + .Take(zipTargets.Count() - unarchivedMin); + // final archive name (I use date / time) + string archiveName = $"{DateTime.Now.ToString("yyyy-MM-dd_hh-mm-ss")}[{zipTargets.Count()}].zip"; + // zip loop + using (MemoryStream zipMS = new MemoryStream()) + { + using (ZipArchive zipArchive = new ZipArchive(zipMS, ZipArchiveMode.Create, true)) + { + // loop through files to add + foreach (FileInfo zipTarget in zipTargets) + { + // read the file bytes + byte[] fileToZipBytes = File.ReadAllBytes(zipTarget.FullName); + // create the entry - this is the zipped filename + // change slashes - now it's VALID + ZipArchiveEntry zipFileEntry = zipArchive.CreateEntry(zipTarget.Name.Replace(path, "").Replace('\\', '/')); + // add the file contents + using Stream zipEntryStream = zipFileEntry.Open(); + using BinaryWriter zipFileBinary = new BinaryWriter(zipEntryStream); + zipFileBinary.Write(fileToZipBytes); + } + } + using (FileStream finalZipFileStream = new FileStream(Path.Combine(archivePath, archiveName), FileMode.Create)) + { + zipMS.Seek(0, SeekOrigin.Begin); + zipMS.CopyTo(finalZipFileStream); + } + foreach (FileInfo target in zipTargets) + { + File.Delete(target.FullName); + } + } + } + } +} diff --git a/ProtocolMasterCore/Utility/Log.cs b/ProtocolMasterCore/Utility/Log.cs new file mode 100644 index 0000000..4058f0e --- /dev/null +++ b/ProtocolMasterCore/Utility/Log.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace ProtocolMasterCore.Utility +{ + public delegate void LogPrinter(string message); + public sealed class Log + { + private static readonly Log instance = new Log(); + // Explicit static constructor to tell C# compiler + // not to mark type as beforefieldinit + static Log() + { + } + + public static LogPrinter OutputPrinter; + public static LogPrinter ErrorPrinter; + + private readonly string logdata; + private readonly string archive; + + private readonly int maxUnarchived = 20; + private readonly int minUnarchived = 16; + + private readonly LogFile lfOut; + private readonly LogFile lfErr; + public bool PrintErrors { get; set; } + public bool PrintOutput { get; set; } + + private Log() + { + if (AppEnvironment.TryAddLocationDocuments("Log", "Log", out logdata)) + { } + if (AppEnvironment.TryAddLocationDocuments("LogArchive", "Log/Archive", out archive)) + { } + + string timePrefix = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); + lfOut = new LogFile(Path.Combine(logdata, $"{timePrefix}_Out.log")); + lfErr = new LogFile(Path.Combine(logdata, $"{timePrefix}_Err.log")); + + PrintErrors = true; + ArchiveOldest(); + } + public static Log Instance + { + get + { + return instance; + } + } + + public static void Error(object message) => Instance.I_Error(message == null ? "NULL" : message.ToString()); + public void I_Error(string message) + { + string toWrite = $"E:{DateTime.Now}:\t{message}"; + lfErr.Write(toWrite); + if (PrintErrors) + { + ErrorPrinter(toWrite); + } + } + public static void Out(object message) => Log.Instance.I_Out(message == null ? "NULL" : message.ToString()); + public void I_Out(string message) + { + string toWrite = $"O:{DateTime.Now}:\t{message}"; + lfOut.Write(toWrite); + if (PrintErrors) + { + OutputPrinter(toWrite); + } + } + + public void WriteFiles() + { + lfErr.WriteBuffer(); + lfOut.WriteBuffer(); + } + + public void OpenFolder() + { + WriteFiles(); + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo() + { + FileName = logdata, + UseShellExecute = true, + Verb = "open" + }); + } + + private void ArchiveOldest() => Archiver.ArchiveOldestInDirectory(logdata, archive, ".log", maxUnarchived, minUnarchived); + + } +} diff --git a/ProtocolMasterCore/Utility/LogFile.cs b/ProtocolMasterCore/Utility/LogFile.cs new file mode 100644 index 0000000..91622e3 --- /dev/null +++ b/ProtocolMasterCore/Utility/LogFile.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ProtocolMasterCore.Utility +{ + class LogFile + { + ConcurrentQueue inputbuffer; + private string filePath; + private string tempPath; + bool tempFail; + + public LogFile(string filePath) + { + this.filePath = filePath; + inputbuffer = new ConcurrentQueue(); + Task fileWriter = Task.Factory.StartNew(() => + { + while (true) + { + if (tempFail || !inputbuffer.IsEmpty) WriteBuffer(); + else Thread.Sleep(1000); + } + }, TaskCreationOptions.LongRunning); + } + + public void Write(string message) + { + inputbuffer.Enqueue(message); + } + + public void WriteBuffer() + { + try + { + StreamWriter writer = File.AppendText(filePath); + if (tempFail) + { + using (Stream input = File.OpenRead(tempPath)) + { + input.CopyTo(writer.BaseStream); // Using .NET 4 + } + if (File.Exists(tempPath)) + File.Delete(tempPath); + tempFail = false; + } + while (inputbuffer.TryDequeue(out string append)) + writer.WriteLine(append); + writer.Close(); + } + catch (IOException) + { + tempFail = true; + tempPath = $"[temp]{filePath}"; + Log.Out($"Failed to open log at {filePath} attempting {tempPath} instead."); + try + { + StreamWriter writer = File.AppendText(tempPath); + while (inputbuffer.TryDequeue(out string append)) + writer.WriteLine(append); + writer.Close(); + } + catch (IOException) + { + Log.Out($"Failed to open temporary log {tempPath} will wait to write buffer"); + } + } + } + } +} diff --git a/ProtocolMasterWPF/App.xaml b/ProtocolMasterWPF/App.xaml new file mode 100644 index 0000000..2a767e6 --- /dev/null +++ b/ProtocolMasterWPF/App.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ProtocolMasterWPF/App.xaml.cs b/ProtocolMasterWPF/App.xaml.cs new file mode 100644 index 0000000..d440726 --- /dev/null +++ b/ProtocolMasterWPF/App.xaml.cs @@ -0,0 +1,68 @@ +using ProtocolMasterCore.Utility; +using ProtocolMasterWPF.Model.Google; +using ProtocolMasterWPF.ViewModel; +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using System.Windows; + +namespace ProtocolMasterWPF +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + public LogViewModel LogVM { get; private set; } + private void Application_Startup(object sender, StartupEventArgs e) + { + InitializeLog(); + GAuth.Instance.PostAuthentication += AuthenticationRefocus; + } + private void InitializeLog() + { + LogVM = new LogViewModel(); + Log.ErrorPrinter += AddTextDispatched; + Log.OutputPrinter += AddTextDispatched; + Log.Out("Output Log Running"); + Log.Error("Error Log Running"); + Task logThreadTask = new Task(() => Log.Error("Log working in parallel thread")); + logThreadTask.Start(); + } + + private void AddTextDispatched(string text)=>App.Current.Dispatcher.Invoke(() => { LogVM.LogText.Add(text); }); + + private void AuthenticationRefocus(object sender, EventArgs e) + { + MainWindow.Activate(); + } + + public async void GoogleAuthenticate() + { + await GAuth.Instance.Authenticate(GDrive.Instance); + } + public async void GoogleDeauthenticate() + { + await GAuth.Instance.DeAuthenticate(); + } + + public static void TryOpenURI(object sender, params string[] uris) + { + for (int i = 0; i < uris.Length; i++) + { + try + { + ProcessStartInfo psi = new ProcessStartInfo + { + FileName = uris[i], + UseShellExecute = true + }; + Process.Start(psi); + return; + } + catch (Exception e) { Log.Error($"Failed to open {(sender as DependencyObject).GetValue(FrameworkElement.NameProperty)} URI. Exception:{e}"); } + } + Log.Error($"Failed to open any URIs for {(sender as DependencyObject).GetValue(FrameworkElement.NameProperty)}, please contact the developer."); + } + } +} diff --git a/ProtocolMaster/AssemblyInfo.cs b/ProtocolMasterWPF/AssemblyInfo.cs similarity index 100% rename from ProtocolMaster/AssemblyInfo.cs rename to ProtocolMasterWPF/AssemblyInfo.cs diff --git a/ProtocolMasterWPF/Assets/Logo/Logo120.png b/ProtocolMasterWPF/Assets/Logo/Logo120.png new file mode 100644 index 0000000..f8223d8 Binary files /dev/null and b/ProtocolMasterWPF/Assets/Logo/Logo120.png differ diff --git a/ProtocolMasterWPF/Assets/Logo/Logo240.png b/ProtocolMasterWPF/Assets/Logo/Logo240.png new file mode 100644 index 0000000..9c530a3 Binary files /dev/null and b/ProtocolMasterWPF/Assets/Logo/Logo240.png differ diff --git a/ProtocolMasterWPF/Assets/Logo/Logo360.png b/ProtocolMasterWPF/Assets/Logo/Logo360.png new file mode 100644 index 0000000..4d6bb8d Binary files /dev/null and b/ProtocolMasterWPF/Assets/Logo/Logo360.png differ diff --git a/ProtocolMasterWPF/Assets/Logo/Logo480.png b/ProtocolMasterWPF/Assets/Logo/Logo480.png new file mode 100644 index 0000000..3071782 Binary files /dev/null and b/ProtocolMasterWPF/Assets/Logo/Logo480.png differ diff --git a/ProtocolMasterWPF/Assets/Logo/LogoIcon.ico b/ProtocolMasterWPF/Assets/Logo/LogoIcon.ico new file mode 100644 index 0000000..bf24339 Binary files /dev/null and b/ProtocolMasterWPF/Assets/Logo/LogoIcon.ico differ diff --git a/ProtocolMasterWPF/Assets/Logo/LogoSquare480.png b/ProtocolMasterWPF/Assets/Logo/LogoSquare480.png new file mode 100644 index 0000000..9b69e09 Binary files /dev/null and b/ProtocolMasterWPF/Assets/Logo/LogoSquare480.png differ diff --git a/ProtocolMasterWPF/Assets/Logo/LogoSquareIcon.ico b/ProtocolMasterWPF/Assets/Logo/LogoSquareIcon.ico new file mode 100644 index 0000000..08d1d76 Binary files /dev/null and b/ProtocolMasterWPF/Assets/Logo/LogoSquareIcon.ico differ diff --git a/ProtocolMasterWPF/Helpers/CategoryNode.cs b/ProtocolMasterWPF/Helpers/CategoryNode.cs new file mode 100644 index 0000000..6068f56 --- /dev/null +++ b/ProtocolMasterWPF/Helpers/CategoryNode.cs @@ -0,0 +1,133 @@ +using OxyPlot; +using OxyPlot.Series; +using ProtocolMasterCore.Protocol; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProtocolMasterWPF.Helpers +{ + internal class CategoryNode + { + static OxyColor[] pallete = { OxyColors.DarkRed, OxyColors.DodgerBlue, OxyColors.Green }; + public string Name { get; private set; } + public CategoryNode Parent { get; private set; } + public List Children { get; private set; } + public IntervalBarSeries Series { get; private set; } + public CategoryNode(string name) + { + this.Name = name; + this.Children = new List(); + Series = new IntervalBarSeries() + { + Title = name, + StrokeThickness = 1.5, + StrokeColor = OxyColors.Gray, + FillColor = OxyColor.FromArgb(255, 16, 16, 16), + BarWidth = 1.0, + ToolTip = name + }; + } + public CategoryNode(string name, CategoryNode parent) : this(name) + { + this.Parent = parent; + Series.BarWidth = 0.35; + parent.Children.Add(this); + } + public void SetSeriesData(int categoryIndex, OxyColor color) + { + Series.StrokeColor = OxyColor.FromArgb(255, (byte)((color.R * 3 + 255) / 4), (byte)((color.G * 3 + 255) / 4), (byte)((color.B * 3 + 255) / 4)); + foreach (IntervalBarItem item in Series.Items) + { + item.CategoryIndex = categoryIndex; + item.Color = color; + } + } + public static void GeneratePlotData(List rootNodes, out List allSeries, out List labels, out List gridLines) + { + double linePos = 0.0; + int fullIndex = 0; + int palleteIndex = 0; + allSeries = new List(); + labels = new List(); + gridLines = new List(); + if (rootNodes != null) + for (int i = 0; i < rootNodes.Count; i++) + { + CategoryNode root = rootNodes[i]; + root.SetSeriesData(fullIndex++, pallete[palleteIndex]); + allSeries.Add(root.Series); + labels.Add(root.Name); + gridLines.Add(linePos++); + Stack subtree = new Stack(root.Children); + while (subtree.Count != 0) + { + CategoryNode subNode = subtree.Pop(); + subNode.SetSeriesData(fullIndex++, pallete[palleteIndex]); + allSeries.Add(subNode.Series); + labels.Add(subNode.Name); + gridLines.Add(linePos++); + foreach (CategoryNode child in subNode.Children) + subtree.Push(child); + } + if (i < rootNodes.Count - 1) + { + palleteIndex = (palleteIndex + 1) % pallete.Length; + linePos++; + fullIndex++; + labels.Add(""); + } + } + } + // The biggest ugliest mess in the universe! + public static List BuildTrees(List eventList) + { + if (eventList == null) return null; + Dictionary nodeDictionary = new Dictionary(); + List rootNodes = new List(); + Queue eventQueue = new Queue(eventList); + if (eventList != null) + { + while (eventQueue.Count != 0) + { + ProtocolEvent plotEvent = eventQueue.Dequeue(); + if (plotEvent.HasCategory()) + { + CategoryNode targetNode; + if (plotEvent.HasParent()) + { + if (nodeDictionary.ContainsKey(plotEvent.ParentLabel)) + { + if (!nodeDictionary.TryGetValue(plotEvent.FullLabel(), out targetNode)) + { + targetNode = new CategoryNode(plotEvent.CategoryLabel, nodeDictionary[plotEvent.ParentLabel]); + nodeDictionary.Add(plotEvent.FullLabel(), targetNode); + } + } + else + { + eventQueue.Enqueue(plotEvent); + continue; + } + } + else + { + if (!nodeDictionary.TryGetValue(plotEvent.FullLabel(), out targetNode)) + { + targetNode = new CategoryNode(plotEvent.CategoryLabel); + nodeDictionary.Add(plotEvent.FullLabel(), targetNode); + rootNodes.Add(targetNode); + } + } + targetNode.Series.Items.Add(new IntervalBarItem + { + Start = new DateTime(Convert.ToInt64(plotEvent.Arguments["TimeStartMs"]) * 10000).ToOADate(), + End = new DateTime(Convert.ToInt64(plotEvent.Arguments["TimeEndMs"]) * 10000).ToOADate() + }); + } + } + } + return rootNodes; + } + } +} diff --git a/ProtocolMasterWPF/Helpers/ClockAnimator.cs b/ProtocolMasterWPF/Helpers/ClockAnimator.cs new file mode 100644 index 0000000..224a17e --- /dev/null +++ b/ProtocolMasterWPF/Helpers/ClockAnimator.cs @@ -0,0 +1,73 @@ +using ProtocolMasterCore.Protocol; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ProtocolMasterWPF.Helpers +{ + internal delegate void ClockUpdate(double elapsed, double duration); + internal class ClockAnimator + { + public ClockUpdate OnUpdate; + public DateTime StartDateTime { get; private set; } + public double StartTime { get; private set; } + public double Duration { get; private set; } + public Task AnimTask { get; private set; } + Progress animationProgress; + CancellationTokenSource tokenSource; + + public ClockAnimator() + { + PrepAnimator(); + } + public void FindMaxTime(List events) + { + if (events == null) return; + double milliseconds = events.Where(i => i.Arguments.ContainsKey("TimeEndMs")).Max(i => Convert.ToDouble( i.Arguments["TimeEndMs"])); + Duration = TimeSpan.FromMilliseconds(milliseconds).TotalDays; + } + public void PrepAnimator() + { + animationProgress = new Progress(); + tokenSource = new CancellationTokenSource(); + CancellationToken cancelToken = tokenSource.Token; + animationProgress.ProgressChanged += AnimatorProgress; + AnimTask = new Task(() => + { + AnimatorLoop(animationProgress, cancelToken); + }, cancelToken); + } + public void StartAnimator(DateTime startTime) + { + StartDateTime = startTime; + StartTime = StartDateTime.ToOADate(); + AnimTask.Start(); + } + public void StartAnimatorNow() => StartAnimator(DateTime.Now); + public void StopAnimator() + { + tokenSource?.Cancel(); + PrepAnimator(); + } + void AnimatorProgress(object sender, int e) + { + if (!tokenSource.IsCancellationRequested) + { + double elapsed = (DateTime.Now.ToOADate() - StartTime); + OnUpdate?.Invoke(elapsed, Duration); + } + } + void AnimatorLoop(IProgress progress, CancellationToken cancelToken) + { + while (!cancelToken.IsCancellationRequested) + { + Thread.Sleep(192); + progress.Report(1); + } + progress.Report(0); + } + } +} diff --git a/ProtocolMasterWPF/Helpers/IsEnabledConverter.cs b/ProtocolMasterWPF/Helpers/IsEnabledConverter.cs new file mode 100644 index 0000000..0c27658 --- /dev/null +++ b/ProtocolMasterWPF/Helpers/IsEnabledConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Controls; +using System.Windows.Data; + +namespace ProtocolMasterWPF.Helpers +{ + public class IsEnabledConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (values.LongLength > 0) + { + foreach (var value in values) + { + if (value is bool && (bool)value) + { + return false; + } + } + } + return true; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/ProtocolMasterWPF/Helpers/NotEmptyValidationRule.cs b/ProtocolMasterWPF/Helpers/NotEmptyValidationRule.cs new file mode 100644 index 0000000..1000e92 --- /dev/null +++ b/ProtocolMasterWPF/Helpers/NotEmptyValidationRule.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Controls; + +namespace ProtocolMasterWPF.Helpers +{ + public class NotEmptyValidationRule : ValidationRule + { + public override ValidationResult Validate(object value, CultureInfo cultureInfo) + { + return string.IsNullOrWhiteSpace((value ?? "").ToString()) + ? new ValidationResult(false, "Field is required.") + : ValidationResult.ValidResult; + } + } +} diff --git a/ProtocolMasterWPF/Helpers/NotNullToBoolConverter.cs b/ProtocolMasterWPF/Helpers/NotNullToBoolConverter.cs new file mode 100644 index 0000000..0766dc8 --- /dev/null +++ b/ProtocolMasterWPF/Helpers/NotNullToBoolConverter.cs @@ -0,0 +1,19 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace ProtocolMasterWPF.Helpers +{ + public class NotNullToBoolConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value != null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + } +} diff --git a/ProtocolMasterWPF/Helpers/NullOrEmptyStringToBoolConverter.cs b/ProtocolMasterWPF/Helpers/NullOrEmptyStringToBoolConverter.cs new file mode 100644 index 0000000..65f35aa --- /dev/null +++ b/ProtocolMasterWPF/Helpers/NullOrEmptyStringToBoolConverter.cs @@ -0,0 +1,19 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace ProtocolMasterWPF.Helpers +{ + public class NullOrEmptyStringToBoolConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return !String.IsNullOrEmpty(value as string); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return !String.IsNullOrEmpty(value as string); + } + } +} diff --git a/ProtocolMasterWPF/MainWindow.xaml b/ProtocolMasterWPF/MainWindow.xaml new file mode 100644 index 0000000..1c97f52 --- /dev/null +++ b/ProtocolMasterWPF/MainWindow.xaml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/MainWindow.xaml.cs b/ProtocolMasterWPF/MainWindow.xaml.cs new file mode 100644 index 0000000..66feff5 --- /dev/null +++ b/ProtocolMasterWPF/MainWindow.xaml.cs @@ -0,0 +1,15 @@ +using System.Windows; + +namespace ProtocolMasterWPF +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + public MainWindow() + { + InitializeComponent(); + } + } +} diff --git a/ProtocolMasterWPF/Model/Camera.cs b/ProtocolMasterWPF/Model/Camera.cs new file mode 100644 index 0000000..184bd5a --- /dev/null +++ b/ProtocolMasterWPF/Model/Camera.cs @@ -0,0 +1,115 @@ +using ProtocolMasterCore.Utility; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Windows.Devices.Enumeration; +using Windows.Media; +using Windows.Media.Capture; +using Windows.Media.MediaProperties; +using Windows.Storage; + +namespace ProtocolMasterWPF.Model +{ + internal class Camera : INotifyPropertyChanged + { + private StorageFolder videoStore; + private string storagePath; + public MediaCapture MediaCap { get; private set; } + + public Camera(DeviceInformation videoDevice, DeviceInformation audioDevice) + { + AppEnvironment.TryAddLocationDocuments("Video", "Video", out storagePath); + InitVideoStore(); + MediaCap = new MediaCapture(); + InitializeCap(videoDevice, audioDevice); + } + private async void InitVideoStore() + { + videoStore = await StorageFolder.GetFolderFromPathAsync(storagePath); + } + public void InitializeCap(DeviceInformation videoDevice, DeviceInformation audioDevice) + { + if(videoDevice == null) + Log.Error($"Video Device null"); + if (audioDevice == null) + { + try + { + MediaCap.InitializeAsync(new MediaCaptureInitializationSettings() + { + VideoDeviceId = videoDevice.Id + }).AsTask().Wait(); + } + catch (UnauthorizedAccessException ex) + { + Log.Error($"The app was denied access to the camera: {ex}"); + } + } + else + { + try + { + MediaCap.InitializeAsync(new MediaCaptureInitializationSettings() + { + VideoDeviceId = videoDevice.Id, + AudioDeviceId = audioDevice.Id + }).AsTask().Wait(); + } + catch (UnauthorizedAccessException ex) + { + Log.Error($"The app was denied access to the camera: {ex}"); + } + } + } + public async void StartPreview() + { + try + { + await MediaCap.StartPreviewAsync(); + } + catch (System.IO.FileLoadException ex) + { + Log.Error(ex.ToString()); + } + } + public bool IsRecording { get; private set; } + public async void StartRecord() + { + try + { + StorageFile file = await videoStore.CreateFileAsync("Recording (1).wmv", CreationCollisionOption.GenerateUniqueName); + await MediaCap.StartRecordToStorageFileAsync(MediaEncodingProfile.CreateWmv(VideoEncodingQuality.Auto), file); + IsRecording = true; + Log.Error("Began Recording"); + } + catch (Exception ex) + { + Log.Error($"Failed to record:\t{ex}"); + } + } + public async void StopRecord() + { + if (IsRecording) + { + try + { + await MediaCap.StopRecordAsync(); + IsRecording = false; + Log.Error("Finished Recording"); + } + catch (Exception ex) + { + Log.Error($"Failed to save recording:\t{ex}"); + } + } + } + public event PropertyChangedEventHandler PropertyChanged; + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/ProtocolMasterWPF/Model/CameraContainer.cs b/ProtocolMasterWPF/Model/CameraContainer.cs new file mode 100644 index 0000000..a51e9c5 --- /dev/null +++ b/ProtocolMasterWPF/Model/CameraContainer.cs @@ -0,0 +1,91 @@ +using ProtocolMasterCore.Utility; +using ProtocolMasterWPF.Properties; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.Devices.Enumeration; + +namespace ProtocolMasterWPF.Model +{ + internal class CameraContainer : Observable + { + public CameraContainer() + { + InitDefaultDevices(); + ResetCam(); + } + public Camera Cam + { + get => _cam; + private set + { + _cam = value; + NotifyProperty(); + } + } + private Camera _cam; + public DeviceInformation VideoDevice + { + get => _videoDevice; + set + { + _videoDevice = value; + NotifyProperty(); + ResetCam(); + if (value.Id != Settings.Default.CameraID) + { + Settings.Default.CameraID = value.Id; + Settings.Default.Save(); + } + } + } + private DeviceInformation _videoDevice; + public DeviceInformation AudioDevice + { + get => _audioDevice; + set + { + _audioDevice = value; + NotifyProperty(); + ResetCam(); + if(value.Id != Settings.Default.MicrophoneID) + { + Settings.Default.MicrophoneID = value.Id; + Settings.Default.Save(); + } + } + } + private DeviceInformation _audioDevice; + public void StartRecord() => Cam.StartRecord(); + public void StopRecord() => Cam.StopRecord(); + + private void ResetCam() + { + Cam = new Camera(VideoDevice, AudioDevice); + } + + public void InitDefaultDevices() + { + try + { + _videoDevice = MediaDevices.Instance.VideoDeviceByID(Settings.Default.CameraID); + } + catch (Exception e) + { + Log.Error($"Default Camera could not be seleted, exception: {e}"); + VideoDevice = MediaDevices.Instance.VideoDevices.First(); + } + try + { + _audioDevice = MediaDevices.Instance.AudioDeviceByID(Settings.Default.MicrophoneID); + } + catch (Exception e) + { + Log.Error($"Default Microphone could not be seleted, exception: {e}"); + AudioDevice = MediaDevices.Instance.AudioDevices.First(); + } + } + } +} diff --git a/ProtocolMasterWPF/Model/ExtensionMetaSetting.cs b/ProtocolMasterWPF/Model/ExtensionMetaSetting.cs new file mode 100644 index 0000000..1d6318d --- /dev/null +++ b/ProtocolMasterWPF/Model/ExtensionMetaSetting.cs @@ -0,0 +1,27 @@ +using ProtocolMasterCore.Protocol; +using ProtocolMasterCore.Protocol.Driver; +using ProtocolMasterCore.Protocol.Interpreter; +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Diagnostics.CodeAnalysis; +using System.Text; + +namespace ProtocolMasterWPF.Model +{ + [Serializable] + public sealed class ExtensionMetaSetting : IExtensionMeta + { + public string Name { get; set; } + public string Version { get; set; } + public ExtensionMetaSetting() { } + public ExtensionMetaSetting(IExtensionMeta from) + { + Name = from.Name; Version = from.Version; + } + public bool Equals([AllowNull] IExtensionMeta other) + { + return this.Name == other.Name && this.Version == other.Version; + } + } +} diff --git a/ProtocolMasterWPF/Model/Google/GAuth.cs b/ProtocolMasterWPF/Model/Google/GAuth.cs new file mode 100644 index 0000000..2fb6bcc --- /dev/null +++ b/ProtocolMasterWPF/Model/Google/GAuth.cs @@ -0,0 +1,138 @@ +using Google.Apis.Auth.OAuth2; +using Google.Apis.Util.Store; +using ProtocolMasterCore.Utility; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace ProtocolMasterWPF.Model.Google +{ + public enum GAuthState + { + PreAuth, + DoAuth, + PostAuth + } + public sealed class GAuth : INotifyPropertyChanged + { + private static readonly GAuth instance; + private static readonly string authDir; + public event EventHandler PostAuthentication, PreAuthentication; + private UserCredential credential; + private UserCredential Credential { get => credential; set => credential = value; } + private FileDataStore userStore; + private GAuthState _state; + private GAuthState State { get => _state; set { _state = value; OnPropertyChanged("IsPreAuth"); OnPropertyChanged("IsDoAuth"); OnPropertyChanged("IsPostAuth"); } } + public bool IsPreAuth { get => State == GAuthState.PreAuth; } + public bool IsDoAuth { get => State == GAuthState.DoAuth; } + public bool IsPostAuth { get => State == GAuthState.PostAuth; } + + static GAuth() + { + if (!AppEnvironment.TryAddLocationAppData("Auth", "Auth", out authDir)) + { throw new System.Exception("Could not create authenticaton directory"); } + Log.Error($"AuthDir: {authDir}"); + instance = new GAuth(); + } + private GAuth() + { + State = GAuthState.PreAuth; + // Make sure there are no remaining credentials. + DirectoryInfo directory = new DirectoryInfo(authDir); + foreach (FileInfo file in directory.GetFiles()) + file.Delete(); + } + public static GAuth Instance + { + get + { + return instance; + } + } + public async Task Authenticate(params IService[] services) + { + if (PreAuthentication != null) + PreAuthentication.Invoke(this, new EventArgs()); + State = GAuthState.DoAuth; + userStore = new FileDataStore(authDir, true); + + Log.Error("Authenticate(): AUTHENTICATING"); + ICodeReceiver receiver = new GAuthReceiver(); + CancellationTokenSource cts = new CancellationTokenSource(); + cts.CancelAfter(40000); + + Log.Error("Authenticate(): Launching OAuth"); + + try + { + Credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( + new ClientSecrets + { + ClientId = "911699926501-6ici7to17is1fet4mr6jn864lrmrsi0g.apps.googleusercontent.com", + ClientSecret = "LbVHZSa-rtPBMl9odwMBkJi_" + }, + CombineServiceTokens(services), + "user", cts.Token, userStore, receiver); + CreateServices(services); + } + catch (Exception e) + { + State = GAuthState.PreAuth; + Log.Error($"Authentication Exception: {e.Message}"); + return; + } + if (cts.Token.IsCancellationRequested) + { + State = GAuthState.PreAuth; + Log.Error("Authentication Failed"); + } + else + { + State = GAuthState.PostAuth; + Log.Error("Authenticate(): User Fully Authenticated"); + if (PostAuthentication != null) + PostAuthentication.Invoke(this, new EventArgs()); + } + } + private void CreateServices(IService[] services) + { + foreach (IService service in services) + { + service.CreateService(Credential); + } + } + + private string[] CombineServiceTokens(IService[] services) + { + List builder = new List(); + foreach (IService service in services) + { + foreach (string token in service.ServiceTokens()) + { + builder.Add(token); + } + } + return builder.ToArray(); + } + + public async Task DeAuthenticate() + { + if (!IsPostAuth) return; + Log.Error("DeAuthenticate(): DEAUTHENTICATING"); + Credential = null; + await userStore.ClearAsync(); + State = GAuthState.PreAuth; + Log.Error("Authenticate(): User Fully Deauthenticated"); + } + + public event PropertyChangedEventHandler PropertyChanged; + private void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/ProtocolMaster/Model/Google/GAuthReceiver.cs b/ProtocolMasterWPF/Model/Google/GAuthReceiver.cs similarity index 99% rename from ProtocolMaster/Model/Google/GAuthReceiver.cs rename to ProtocolMasterWPF/Model/Google/GAuthReceiver.cs index 89dc893..2d6b718 100644 --- a/ProtocolMaster/Model/Google/GAuthReceiver.cs +++ b/ProtocolMasterWPF/Model/Google/GAuthReceiver.cs @@ -12,12 +12,11 @@ using System.Linq; using System.Net; using System.Net.Sockets; -using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; -namespace ProtocolMaster.Model.Google +namespace ProtocolMasterWPF.Model.Google { /// /// OAuth 2.0 verification code receiver that runs a local server on a free port and waits for a call with the @@ -464,7 +463,12 @@ private async Task GetResponseFromListener(HttpLis private bool OpenBrowser(string url) { - Process.Start(url); + ProcessStartInfo psi = new ProcessStartInfo + { + FileName = url, + UseShellExecute = true + }; + Process.Start(psi); return true; } diff --git a/ProtocolMasterWPF/Model/Google/GDrive.cs b/ProtocolMasterWPF/Model/Google/GDrive.cs new file mode 100644 index 0000000..d7b15d3 --- /dev/null +++ b/ProtocolMasterWPF/Model/Google/GDrive.cs @@ -0,0 +1,92 @@ +using Google.Apis.Auth.OAuth2; +using Google.Apis.Drive.v3; +using Google.Apis.Drive.v3.Data; +using Google.Apis.Services; +using ProtocolMasterCore.Utility; +using System.Collections.ObjectModel; +using System.IO; +using File = Google.Apis.Drive.v3.Data.File; + +namespace ProtocolMasterWPF.Model.Google +{ + public sealed class GDrive : IService + { + private static readonly GDrive instance = new GDrive(); + static GDrive() + { + } + private GDrive() + { + AvailableFiles = new ObservableCollection(); + } + public static GDrive Instance + { + get + { + return instance; + } + } + + public Stream Download(string fileID) + { + if (fileID != null) + return service.Files.Export(fileID, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet").ExecuteAsStream(); + else return null; + } + + // Core Drive Functionality. + // This all needs to be abstracted somewhere else (Model) + const string ROOT_FOLDER_NAME = "ProtocolMaster"; + public ObservableCollection AvailableFiles { get; private set; } + FileList driveFiles; + private DriveService service; + #region + private void LoadAvailable(bool clear = false) + { + AvailableFiles.Clear(); + FileList result; + do + { + FilesResource.ListRequest listRequest = service.Files.List(); + listRequest.PageSize = 20; + listRequest.Fields = "nextPageToken, files(id, name, mimeType, trashed)"; + listRequest.Q = + "mimeType = 'application/vnd.google-apps.spreadsheet' and " + + "trashed = false"; + result = listRequest.Execute(); + + foreach (File file in result.Files) + AvailableFiles.Add(new GFileStreamer(file)); + } while (result.IncompleteSearch.HasValue && result.IncompleteSearch.Equals(true)); + } + public void RefreshAvailable() + { + LoadAvailable(true); + } + #endregion + + // IService Implementation + #region + private static readonly string[] serviceTokens = + { + DriveService.Scope.DriveFile + }; + + + string[] IService.ServiceTokens() { return serviceTokens; } + void IService.CreateService(UserCredential credential) + { + Log.Error("CreateService(): CREATING DRIVE SERVICE"); + // Create the drive service. + service = new DriveService(new BaseClientService.Initializer() + { + HttpClientInitializer = credential, + ApplicationName = "Protocol Master", + }); + + // Find root folder for application. + LoadAvailable(); + } + #endregion + } +} diff --git a/ProtocolMasterWPF/Model/Google/GFileStreamer.cs b/ProtocolMasterWPF/Model/Google/GFileStreamer.cs new file mode 100644 index 0000000..407a148 --- /dev/null +++ b/ProtocolMasterWPF/Model/Google/GFileStreamer.cs @@ -0,0 +1,38 @@ +using Google.Apis.Drive.v3.Data; +using ProtocolMasterCore.Utility; +using System; +using System.IO; +using File = Google.Apis.Drive.v3.Data.File; + +namespace ProtocolMasterWPF.Model.Google +{ + public class GFileStreamer : IStreamStarter + { + public string Name { get => GFile.Name; } + public string ID { get => GFile.Id; } + public File GFile { get; private set; } + public GFileStreamer(File gfile) + { + GFile = gfile; + } + public override string ToString() + { + return Name; + } + + public Stream StartStream() + { + Stream result; + try + { + result = GDrive.Instance.Download(ID); + } + catch (Exception e) + { + result = null; + Log.Error($"Could not open GDrive File {Name}, Exception: {e}"); + } + return result; + } + } +} diff --git a/ProtocolMaster/Model/Google/IService.cs b/ProtocolMasterWPF/Model/Google/IService.cs similarity index 80% rename from ProtocolMaster/Model/Google/IService.cs rename to ProtocolMasterWPF/Model/Google/IService.cs index 533609a..5cee04c 100644 --- a/ProtocolMaster/Model/Google/IService.cs +++ b/ProtocolMasterWPF/Model/Google/IService.cs @@ -1,6 +1,6 @@ using Google.Apis.Auth.OAuth2; -namespace ProtocolMaster.Model.Google +namespace ProtocolMasterWPF.Model.Google { public interface IService { diff --git a/ProtocolMasterWPF/Model/IStreamStarter.cs b/ProtocolMasterWPF/Model/IStreamStarter.cs new file mode 100644 index 0000000..71366ad --- /dev/null +++ b/ProtocolMasterWPF/Model/IStreamStarter.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace ProtocolMasterWPF.Model +{ + public interface IStreamStarter + { + public Stream StartStream(); + } +} diff --git a/ProtocolMasterWPF/Model/LocalFileStore.cs b/ProtocolMasterWPF/Model/LocalFileStore.cs new file mode 100644 index 0000000..28a3b77 --- /dev/null +++ b/ProtocolMasterWPF/Model/LocalFileStore.cs @@ -0,0 +1,44 @@ +using ProtocolMasterCore.Utility; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Text; + +namespace ProtocolMasterWPF.Model +{ + internal class LocalFileStore : Observable + { + private static LocalFileStore instance = new LocalFileStore(); + public static LocalFileStore Instance + { + get + { + return instance; + } + } + static LocalFileStore() + { + } + private string _directory; + public string Directory { get =>_directory; private set { _directory = value; NotifyProperty(); } } + public ObservableCollection LocalFiles { get; private set;} + private LocalFileStore() + { + AppEnvironment.TryAddLocationDocuments("Protocols", "Protocols", out string dirResult); + Directory = dirResult; + + LocalFiles = new ObservableCollection(); + RefreshFiles(); + } + public void RefreshFiles() + { + LocalFiles.Clear(); + DirectoryInfo dir = new DirectoryInfo(Directory); + foreach (FileInfo info in dir.GetFiles()) + { + LocalFiles.Add(new LocalFileStreamer(info)); + } + } + } +} diff --git a/ProtocolMasterWPF/Model/LocalFileStreamer.cs b/ProtocolMasterWPF/Model/LocalFileStreamer.cs new file mode 100644 index 0000000..2d8beba --- /dev/null +++ b/ProtocolMasterWPF/Model/LocalFileStreamer.cs @@ -0,0 +1,36 @@ +using ProtocolMasterCore.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace ProtocolMasterWPF.Model +{ + internal class LocalFileStreamer : IStreamStarter + { + public string Name { get => LocalFile.Name; } + public FileInfo LocalFile { get; private set; } + public LocalFileStreamer(FileInfo source) + { + LocalFile = source; + } + public Stream StartStream() + { + Stream result; + try + { + result = LocalFile.Open(FileMode.Open); + } + catch(Exception e) + { + Log.Error($"Could not open Local File {Name}, Exception: {e}"); + result = null; + } + return result; + } + public override string ToString() + { + return Name; + } + } +} diff --git a/ProtocolMasterWPF/Model/MediaDevices.cs b/ProtocolMasterWPF/Model/MediaDevices.cs new file mode 100644 index 0000000..de01729 --- /dev/null +++ b/ProtocolMasterWPF/Model/MediaDevices.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.Devices.Enumeration; + +namespace ProtocolMasterWPF.Model +{ + internal class MediaDevices : Observable + { + private static MediaDevices instance = new MediaDevices(); + public static MediaDevices Instance { get => instance; } + static MediaDevices() { } + private MediaDevices() + { + RefreshDevices(); + } + private DeviceInformationCollection _videoDevices; + + public DeviceInformationCollection VideoDevices + { + get => _videoDevices; + set + { + _videoDevices = value; + NotifyProperty(); + } + } + public void RefreshDevices() + { + Task task = DeviceInformation.FindAllAsync(DeviceClass.VideoCapture).AsTask(); + task.Wait(); + VideoDevices = task.Result; + task.Dispose(); + + task = DeviceInformation.FindAllAsync(DeviceClass.AudioCapture).AsTask(); + task.Wait(); + AudioDevices = task.Result; + } + private DeviceInformationCollection _audioDevices; + public DeviceInformationCollection AudioDevices + { + get => _audioDevices; + set + { + _audioDevices = value; + NotifyProperty(); + } + } + public DeviceInformation AudioDeviceByID(string id) + { + return AudioDevices.First(a => a.Id == id); + } + public DeviceInformation VideoDeviceByID(string id) + { + return VideoDevices.First(a => a.Id == id); + } + } +} diff --git a/ProtocolMasterWPF/Model/NoFileSelection.cs b/ProtocolMasterWPF/Model/NoFileSelection.cs new file mode 100644 index 0000000..42d540b --- /dev/null +++ b/ProtocolMasterWPF/Model/NoFileSelection.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace ProtocolMasterWPF.Model +{ + public class NoFileSelection : IStreamStarter + { + public Stream StartStream() + { + return null; + } + public override string ToString() + { + return "No File Selected"; + } + } +} diff --git a/ProtocolMasterWPF/Model/Observable.cs b/ProtocolMasterWPF/Model/Observable.cs new file mode 100644 index 0000000..cf36885 --- /dev/null +++ b/ProtocolMasterWPF/Model/Observable.cs @@ -0,0 +1,14 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace ProtocolMasterWPF.Model +{ + public class Observable : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + protected virtual void NotifyProperty([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/ProtocolMasterWPF/Model/PublishedFileStore.cs b/ProtocolMasterWPF/Model/PublishedFileStore.cs new file mode 100644 index 0000000..b2a4355 --- /dev/null +++ b/ProtocolMasterWPF/Model/PublishedFileStore.cs @@ -0,0 +1,66 @@ +using ProtocolMasterCore.Utility; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text; +using System.IO; +using System.Threading.Tasks; + +namespace ProtocolMasterWPF.Model +{ + internal class PublishedFileStore + { + public static PublishedFileStore Instance { get => instance; } + private static PublishedFileStore instance = new PublishedFileStore(); + static PublishedFileStore() { } + public ObservableCollection PublishedFiles { get; private set; } + public string Directory { get; private set; } + private PublishedFileStore() + { + AppEnvironment.TryAddLocationAppData("Published", "Published", out string outDir); + Directory = outDir; + string filepath = Path.Combine(Directory, "PubStore.json"); + FileInfo file = new FileInfo(filepath); + if (file.Exists) + { + try + { + FileStream filestream = new FileInfo(filepath).Open(FileMode.Open); + Task> serialTask = JsonSerializer.DeserializeAsync>(filestream).AsTask(); + serialTask.Wait(); + filestream.Close(); + PublishedFiles = serialTask.Result; + } + catch (JsonException e) + { + Log.Error($"Could not read published file store, exception: {e}"); + PublishedFiles = new ObservableCollection(); + } + catch (IOException e) + { + Log.Error($"Could not open published file store {filepath}, exception: {e}"); + PublishedFiles = new ObservableCollection(); + } + } + else PublishedFiles = new ObservableCollection(); + } + public void Add(string name, string url) + { + PublishedFiles.Add(new PublishedFileStreamer() { Name=name, URL=url }); + string filepath = Path.Combine(Directory, "PubStore.json"); + FileStream filestream = new FileInfo(filepath).Open(FileMode.Create); + JsonSerializer.SerializeAsync(filestream, PublishedFiles).Wait(); + filestream.Close(); + } + public void Remove(PublishedFileStreamer target) + { + PublishedFiles.Remove(target); + string filepath = Path.Combine(Directory, "PubStore.json"); + FileStream filestream = new FileInfo(filepath).Open(FileMode.Create); + JsonSerializer.SerializeAsync(filestream, PublishedFiles).Wait(); + filestream.Close(); + } + } +} diff --git a/ProtocolMasterWPF/Model/PublishedFileStreamer.cs b/ProtocolMasterWPF/Model/PublishedFileStreamer.cs new file mode 100644 index 0000000..6f98cf0 --- /dev/null +++ b/ProtocolMasterWPF/Model/PublishedFileStreamer.cs @@ -0,0 +1,37 @@ +using ProtocolMasterCore.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; + +namespace ProtocolMasterWPF.Model +{ + internal class PublishedFileStreamer : IStreamStarter + { + public string Name { get; set; } + public string URL { get; set; } + public Stream StartStream() + { + WebClient webClient = new WebClient(); + Stream result; + try + { + string filepath = Path.Combine(PublishedFileStore.Instance.Directory, "PubCache.file"); + webClient.DownloadFile(URL, filepath); + FileInfo file = new FileInfo(filepath); + result = file.Open(FileMode.Open); + } + catch(Exception e) + { + result = null; + Log.Error($"Could not open Web File {Name}, Exception: {e}"); + } + return result; + } + public override string ToString() + { + return Name; + } + } +} diff --git a/ProtocolMasterWPF/Model/Session.cs b/ProtocolMasterWPF/Model/Session.cs new file mode 100644 index 0000000..3e26241 --- /dev/null +++ b/ProtocolMasterWPF/Model/Session.cs @@ -0,0 +1,247 @@ +using ProtocolMasterCore.Protocol; +using ProtocolMasterCore.Utility; +using ProtocolMasterWPF.Helpers; +using ProtocolMasterWPF.Properties; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Windows.Devices.Enumeration; + +namespace ProtocolMasterWPF.Model +{ + internal enum SessionState + { + NotReady, + Selecting, + Ready, + Running, + Viewing, + } + public delegate void SessionActionCallback(); + internal class Session : Observable + { + public InterpretAndDriveProtocol Protocol { get; private set; } + private string extensionDir; + Task SessionTask { get; set; } + CancellationTokenSource CancelSource { get; set; } + CancellationToken CancelToken { get; set; } + public List InterpreterOptions { get => _interpreterOptions; private set { _interpreterOptions = value; NotifyProperty(); } } + private List _interpreterOptions; + public IExtensionMeta SelectedInterpreter + { + get => Protocol.InterpreterManager.Selected; + set + { + if (value == null) return; + Protocol.InterpreterManager.Selected = value; + NotifyProperty(); + if (Settings.Default.InterpreterMeta != value) + { + Settings.Default.InterpreterMeta = new ExtensionMetaSetting(value); + Settings.Default.Save(); + } + } + } + public List DriverOptions { get => _driverOptions; private set { _driverOptions = value; NotifyProperty(); } } + private List _driverOptions; + public IExtensionMeta SelectedDriver + { + get => Protocol.DriverManager.Selected; + set + { + if (value == null) return; + Protocol.DriverManager.Selected = value; + NotifyProperty(); + if (Settings.Default.DriverMeta != value) + { + Settings.Default.DriverMeta = new ExtensionMetaSetting(value); + Settings.Default.Save(); + } + } + } + public ClockAnimator Animator { get; private set; } + public CameraContainer Cam { get; private set; } + SessionState State { get => _state; set { _state = value; NotifyStateProperties(); } } + SessionState _state = SessionState.NotReady; + public IStreamStarter Selection + { + get => _selection; + private set + { + _selection = value; + // Check validity and set state! + if (_selection.GetType() != typeof(NoFileSelection)) State = SessionState.Ready; + else State = SessionState.NotReady; + NotifyProperty(); + NotifyProperty("SelectionObject"); + } + } + public IStreamStarter _selection; + public object SelectionObject { get { if (Selection != null) return (object)Selection; else return "No Protocol Selected"; } } + public bool CanStart { get => State == SessionState.Ready; } + public bool CanStop { get => State == SessionState.Running; } + public bool CanPreview { get => State == SessionState.Ready; } + public bool CanReset { get => State == SessionState.Viewing; } + public bool CanSelect { get => State == SessionState.Ready || State == SessionState.NotReady; } + public bool IsSelecting { get => State == SessionState.Selecting; } + public SessionActionCallback OnStart, OnStop, OnReset, OnPreview, OnRun; + public Session() + { + AppEnvironment.TryAddLocationAssembly("Extensions", "Extensions", out extensionDir); + Protocol = new InterpretAndDriveProtocol(extensionDir); + Protocol.InterpreterManager.OnOptionsLoaded += LoadInterpreterOptions; + Protocol.DriverManager.OnOptionsLoaded += LoadDriverOptions; + Animator = new ClockAnimator(); + Protocol.InterpreterManager.OnEventsLoaded += Animator.FindMaxTime; + Protocol.DriverManager.OnProtocolStart += Animator.StartAnimatorNow; + Protocol.DriverManager.OnProtocolEnd += Animator.StopAnimator; + + InitDefaultExtensions(); + + + Cam = new CameraContainer(); + OnStart += Cam.StartRecord; + OnStop += Cam.StopRecord; + } + public void InitDefaultExtensions() + { + Protocol.LoadExtensions(); + try + { + SelectedDriver = Settings.Default.DriverMeta; + } + catch (ArgumentException e) + { + Log.Error($"Default Driver could not be seleted, exception: {e}"); + SelectedDriver = Protocol.DriverManager.Selected; + } + catch (NullReferenceException) + { + SelectedDriver = Protocol.DriverManager.Selected; + } + try + { + SelectedInterpreter = Settings.Default.InterpreterMeta; + } + catch (ArgumentException e) + { + Log.Error($"Default Interpreter could not be seleted, exception: {e}"); + SelectedInterpreter = Protocol.InterpreterManager.Selected; + } + catch (NullReferenceException) + { + SelectedDriver = Protocol.DriverManager.Selected; + } + } + private void LoadInterpreterOptions(List options) => InterpreterOptions = options; + private void LoadDriverOptions(List options) => DriverOptions = options; + private void NotifyStateProperties() + { + NotifyProperty("CanStart"); + NotifyProperty("CanStop"); + NotifyProperty("CanPreview"); + NotifyProperty("CanReset"); + NotifyProperty("CanSelect"); + NotifyProperty("IsSelecting"); + } + public void Start(bool overrideCheck = false) + { + if (CanStart || overrideCheck) + { + OnStart?.Invoke(); + State = SessionState.Running; + CancelSource = new CancellationTokenSource(); + CancelToken = CancelSource.Token; + SessionTask = new Task(() => + { + CancelToken.Register(() => + { + Protocol.Cancel(); + }); + Protocol.Interpret(Selection.StartStream()); + if (!CancelToken.IsCancellationRequested) + { + OnRun?.Invoke(); + Protocol.Run(); + } + if (!CancelToken.IsCancellationRequested) + Stop(); + }, CancelSource.Token); + SessionTask.Start(); + } + else throw new Exception($"Cannot start in state {State.ToString()}"); + } + public void Stop(bool overrideCheck = false) + { + if (CanStop || overrideCheck) + { + CancelSource.Cancel(); + OnStop?.Invoke(); + State = SessionState.Viewing; + } + else throw new Exception($"Cannot stop in state {State.ToString()}"); + } + public void Reset(bool overrideCheck = false) + { + if (CanReset || overrideCheck) + { + Protocol.Reset(); + OnReset?.Invoke(); + if (Selection != null) + State = SessionState.Ready; + else + State = SessionState.NotReady; + } + else throw new Exception($"Cannot reset in state {State.ToString()}"); + } + public void Preview(bool overrideCheck = false) + { + if (CanPreview || overrideCheck) + { + CancelSource = new CancellationTokenSource(); + CancelToken = CancelSource.Token; + SessionTask = new Task(() => + { + CancelToken.Register(() => + { + Protocol.Cancel(); + }); + Protocol.Interpret(Selection.StartStream()); + }, CancelSource.Token); + SessionTask.Start(); + OnPreview?.Invoke(); + State = SessionState.Viewing; + } + else throw new Exception($"Cannot preview in state {State.ToString()}"); + } + public void OpenSelection(bool overrideCheck = false) + { + if (CanSelect || overrideCheck) + { + State = SessionState.Selecting; + } + else throw new Exception($"Cannot open selection in state {State.ToString()}"); + } + public void MakeSelection(object select) + { + if (typeof(IStreamStarter).IsAssignableFrom(select.GetType())) + { + Selection = (IStreamStarter)select; + Log.Error($"Making selection: {Selection}"); + Reset(true); + } + else + { + Log.Error($"Object {Selection} not of type {typeof(IStreamStarter)}"); + CancelSelection(); + } + } + public void CancelSelection() + { + Log.Error($"Cancelling selection"); + } + } +} diff --git a/ProtocolMasterWPF/Properties/Settings.Designer.cs b/ProtocolMasterWPF/Properties/Settings.Designer.cs new file mode 100644 index 0000000..9abb4c8 --- /dev/null +++ b/ProtocolMasterWPF/Properties/Settings.Designer.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ProtocolMasterWPF.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string CameraID { + get { + return ((string)(this["CameraID"])); + } + set { + this["CameraID"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string MicrophoneID { + get { + return ((string)(this["MicrophoneID"])); + } + set { + this["MicrophoneID"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::ProtocolMasterWPF.Model.ExtensionMetaSetting DriverMeta { + get { + return ((global::ProtocolMasterWPF.Model.ExtensionMetaSetting)(this["DriverMeta"])); + } + set { + this["DriverMeta"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::ProtocolMasterWPF.Model.ExtensionMetaSetting InterpreterMeta { + get { + return ((global::ProtocolMasterWPF.Model.ExtensionMetaSetting)(this["InterpreterMeta"])); + } + set { + this["InterpreterMeta"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ExperimentDefaultTab { + get { + return ((string)(this["ExperimentDefaultTab"])); + } + set { + this["ExperimentDefaultTab"] = value; + } + } + } +} diff --git a/ProtocolMasterWPF/Properties/Settings.settings b/ProtocolMasterWPF/Properties/Settings.settings new file mode 100644 index 0000000..6a6e335 --- /dev/null +++ b/ProtocolMasterWPF/Properties/Settings.settings @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ProtocolMasterWPF/ProtocolMasterWPF.csproj b/ProtocolMasterWPF/ProtocolMasterWPF.csproj new file mode 100644 index 0000000..7cea15d --- /dev/null +++ b/ProtocolMasterWPF/ProtocolMasterWPF.csproj @@ -0,0 +1,82 @@ + + + + WinExe + netcoreapp3.1 + true + Assets\Logo\LogoSquareIcon.ico + app.manifest + 2021.1 + false + false + + + + x64 + 4 + + + + x64 + false + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + diff --git a/ProtocolMasterWPF/Settings.cs b/ProtocolMasterWPF/Settings.cs new file mode 100644 index 0000000..aac3792 --- /dev/null +++ b/ProtocolMasterWPF/Settings.cs @@ -0,0 +1,28 @@ +namespace ProtocolMasterWPF.Properties { + + + // This class allows you to handle specific events on the settings class: + // The SettingChanging event is raised before a setting's value is changed. + // The PropertyChanged event is raised after a setting's value is changed. + // The SettingsLoaded event is raised after the setting values are loaded. + // The SettingsSaving event is raised before the setting values are saved. + internal sealed partial class Settings { + + public Settings() { + // // To add event handlers for saving and changing settings, uncomment the lines below: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Add code to handle the SettingChangingEvent event here. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Add code to handle the SettingsSaving event here. + } + } +} diff --git a/ProtocolMasterWPF/Theme/Buttons.xaml b/ProtocolMasterWPF/Theme/Buttons.xaml new file mode 100644 index 0000000..90f095e --- /dev/null +++ b/ProtocolMasterWPF/Theme/Buttons.xaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/ProtocolMasterWPF/Theme/Colors.xaml b/ProtocolMasterWPF/Theme/Colors.xaml new file mode 100644 index 0000000..9f51499 --- /dev/null +++ b/ProtocolMasterWPF/Theme/Colors.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ProtocolMasterWPF/View/CameraView.xaml b/ProtocolMasterWPF/View/CameraView.xaml new file mode 100644 index 0000000..5243a80 --- /dev/null +++ b/ProtocolMasterWPF/View/CameraView.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/View/CameraView.xaml.cs b/ProtocolMasterWPF/View/CameraView.xaml.cs new file mode 100644 index 0000000..b6c9107 --- /dev/null +++ b/ProtocolMasterWPF/View/CameraView.xaml.cs @@ -0,0 +1,16 @@ +using ProtocolMasterWPF.ViewModel; +using System.Windows.Controls; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for CameraView.xaml + /// + public partial class CameraView : UserControl + { + public CameraView() + { + InitializeComponent(); + } + } +} diff --git a/ProtocolMasterWPF/View/DriveSelectView.xaml b/ProtocolMasterWPF/View/DriveSelectView.xaml new file mode 100644 index 0000000..d31a1a1 --- /dev/null +++ b/ProtocolMasterWPF/View/DriveSelectView.xaml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/ProtocolMasterWPF/View/DriveSelectView.xaml.cs b/ProtocolMasterWPF/View/DriveSelectView.xaml.cs new file mode 100644 index 0000000..018c5c8 --- /dev/null +++ b/ProtocolMasterWPF/View/DriveSelectView.xaml.cs @@ -0,0 +1,19 @@ +using ProtocolMasterWPF.Model.Google; +using ProtocolMasterWPF.ViewModel; +using System.Windows.Controls; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for DriveSelectView.xaml + /// + public partial class DriveSelectView : UserControl, ISelectView + { + public ListBox SelectList { get => SelectListBox; } + public DriveSelectView() + { + InitializeComponent(); + } + private void SignIn_Click(object sender, System.Windows.RoutedEventArgs e) => ((App)App.Current).GoogleAuthenticate(); + } +} diff --git a/ProtocolMasterWPF/View/DropdownDialog.xaml b/ProtocolMasterWPF/View/DropdownDialog.xaml new file mode 100644 index 0000000..475394a --- /dev/null +++ b/ProtocolMasterWPF/View/DropdownDialog.xaml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/View/DropdownDialog.xaml.cs b/ProtocolMasterWPF/View/DropdownDialog.xaml.cs new file mode 100644 index 0000000..a46fa17 --- /dev/null +++ b/ProtocolMasterWPF/View/DropdownDialog.xaml.cs @@ -0,0 +1,37 @@ +using MaterialDesignThemes.Wpf; +using System; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for DropdownDialog.xaml + /// + public partial class DropdownDialog : UserControl, ISelectView + { + public DropdownDialog(string[] options) + { + InitializeComponent(); + SelectList.ItemsSource = options; + } + public static string DropdownUserSelect(string[] options) + { + Task dialogTask = Application.Current.Dispatcher.Invoke>(new Func>(() => DropdownDialog.DropdownHandler(options))); + dialogTask.Wait(); + + return dialogTask.Result as string; + } + private static Task DropdownHandler(string[] options) + { + DropdownDialog dialog = new DropdownDialog(options); + return DialogHost.Show(dialog, "PromptDialog"); + } + public ListBox SelectList => SelectListBox; + private void SelectButton_Click(object sender, RoutedEventArgs e) + { + DialogHost.Close("PromptDialog", SelectList.SelectedItem); + } + } +} diff --git a/ProtocolMasterWPF/View/ISelectView.cs b/ProtocolMasterWPF/View/ISelectView.cs new file mode 100644 index 0000000..3bf0e66 --- /dev/null +++ b/ProtocolMasterWPF/View/ISelectView.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Controls; + +namespace ProtocolMasterWPF.View +{ + public interface ISelectView + { + public ListBox SelectList { get; } + } +} diff --git a/ProtocolMasterWPF/View/LocalSelectView.xaml b/ProtocolMasterWPF/View/LocalSelectView.xaml new file mode 100644 index 0000000..b41776c --- /dev/null +++ b/ProtocolMasterWPF/View/LocalSelectView.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/View/LocalSelectView.xaml.cs b/ProtocolMasterWPF/View/LocalSelectView.xaml.cs new file mode 100644 index 0000000..7a39350 --- /dev/null +++ b/ProtocolMasterWPF/View/LocalSelectView.xaml.cs @@ -0,0 +1,32 @@ +using ProtocolMasterCore.Utility; +using ProtocolMasterWPF.Model; +using ProtocolMasterWPF.ViewModel; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for LocalSelectView.xaml + /// + public partial class LocalSelectView : UserControl, ISelectView + { + public ListBox SelectList { get => SelectListBox; } + public LocalSelectView() + { + InitializeComponent(); + } + private void RefreshButton_Click(object sender, RoutedEventArgs e) => LocalFileStore.Instance.RefreshFiles(); + private void OpenFolderButton_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, AppEnvironment.GetLocation("Protocols")); + } +} diff --git a/ProtocolMasterWPF/View/LogView.xaml b/ProtocolMasterWPF/View/LogView.xaml new file mode 100644 index 0000000..8188ccc --- /dev/null +++ b/ProtocolMasterWPF/View/LogView.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/View/LogView.xaml.cs b/ProtocolMasterWPF/View/LogView.xaml.cs new file mode 100644 index 0000000..96f789e --- /dev/null +++ b/ProtocolMasterWPF/View/LogView.xaml.cs @@ -0,0 +1,16 @@ +using System.Windows.Controls; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for LogView.xaml + /// + public partial class LogView : UserControl + { + public LogView() + { + InitializeComponent(); + DataContext = ((App)App.Current).LogVM; + } + } +} diff --git a/ProtocolMasterWPF/View/ProtocolSelectView.xaml b/ProtocolMasterWPF/View/ProtocolSelectView.xaml new file mode 100644 index 0000000..ad899c2 --- /dev/null +++ b/ProtocolMasterWPF/View/ProtocolSelectView.xaml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/View/ProtocolSelectView.xaml.cs b/ProtocolMasterWPF/View/ProtocolSelectView.xaml.cs new file mode 100644 index 0000000..2881fb1 --- /dev/null +++ b/ProtocolMasterWPF/View/ProtocolSelectView.xaml.cs @@ -0,0 +1,57 @@ +using ProtocolMasterWPF.Properties; +using ProtocolMasterWPF.ViewModel; +using System.Windows; +using System.Windows.Controls; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for ProtocolSelectView.xaml + /// + public partial class ProtocolSelectView : UserControl + { + public static readonly DependencyProperty CurrentSelectorProperty = + DependencyProperty.Register("CurrentSelector", typeof(ISelectView), typeof(ProtocolSelectView)); + public ISelectView CurrentSelector + { + get { return (ISelectView)GetValue(CurrentSelectorProperty); } + set { SetValue(CurrentSelectorProperty, value); } + } + private RadioButton LastTab { get; set; } + public ProtocolSelectView() + { + string openTab = Settings.Default.ExperimentDefaultTab; + InitializeComponent(); + if (DriveTab.Name == openTab) DriveTab.IsChecked = true; + else if (PublishedTab.Name == openTab) PublishedTab.IsChecked = true; + else if (LocalTab.Name == openTab) LocalTab.IsChecked = true; + } + private void DriveTab_Checked(object sender, RoutedEventArgs e) + { + CurrentSelector = DriveSelect; + LastTab = sender as RadioButton; + } + private void PublishedTab_Checked(object sender, RoutedEventArgs e) + { + CurrentSelector = PublishedSelect; + LastTab = sender as RadioButton; + } + private void LocalTab_Checked(object sender, RoutedEventArgs e) + { + CurrentSelector = LocalSelect; + LastTab = sender as RadioButton; + } + private void SelectButton_Click(object sender, RoutedEventArgs e) + { + Settings.Default.ExperimentDefaultTab = LastTab.Name; + Settings.Default.Save(); + MaterialDesignThemes.Wpf.DialogHost.Close("SessionDialog", CurrentSelector.SelectList.SelectedItem); + } + private void CancelButton_Click(object sender, RoutedEventArgs e) + { + Settings.Default.ExperimentDefaultTab = LastTab.Name; + Settings.Default.Save(); + MaterialDesignThemes.Wpf.DialogHost.Close("SessionDialog"); + } + } +} diff --git a/ProtocolMasterWPF/View/PublishedSelectView.xaml b/ProtocolMasterWPF/View/PublishedSelectView.xaml new file mode 100644 index 0000000..2e8274a --- /dev/null +++ b/ProtocolMasterWPF/View/PublishedSelectView.xaml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/View/PublishedSelectView.xaml.cs b/ProtocolMasterWPF/View/PublishedSelectView.xaml.cs new file mode 100644 index 0000000..4f3ffcd --- /dev/null +++ b/ProtocolMasterWPF/View/PublishedSelectView.xaml.cs @@ -0,0 +1,66 @@ +using ProtocolMasterWPF.Model; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for PublishedSelectView.xaml + /// + public partial class PublishedSelectView : UserControl, ISelectView + { + public static readonly DependencyProperty NewNameProperty = + DependencyProperty.Register("NewName", typeof(string), typeof(PublishedSelectView)); + public string NewName + { + get { return (string)GetValue(NewNameProperty); } + set { SetValue(NewNameProperty, value); } + } + public static readonly DependencyProperty NewURLProperty = + DependencyProperty.Register("NewURL", typeof(string), typeof(PublishedSelectView)); + public string NewURL + { + get { return (string)GetValue(NewURLProperty); } + set { SetValue(NewURLProperty, value); } + } + + private int _numberOfValidationErrors = 0; + public bool HasNoValidationErrors => _numberOfValidationErrors == 0; + + private void HandleValidationError(object sender, ValidationErrorEventArgs e) + { + if (e.Action == ValidationErrorEventAction.Added) + _numberOfValidationErrors++; + else + _numberOfValidationErrors--; + AddButton.IsEnabled = HasNoValidationErrors; + } + public PublishedSelectView() + { + InitializeComponent(); + } + + public ListBox SelectList => SelectListBox; + + + private void DeleteButton_Click(object sender, RoutedEventArgs e) => PublishedFileStore.Instance.Remove(SelectListBox.SelectedValue as PublishedFileStreamer); + private void AddButton_Click(object sender, RoutedEventArgs e) + { + PublishedFileStore.Instance.Add(NewName, NewURL); + NewName = ""; + NewURL = ""; + AddPopup.IsPopupOpen = false; + } + } +} diff --git a/ProtocolMasterWPF/View/SessionControlBarView.xaml b/ProtocolMasterWPF/View/SessionControlBarView.xaml new file mode 100644 index 0000000..288e3d6 --- /dev/null +++ b/ProtocolMasterWPF/View/SessionControlBarView.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/View/SessionControlBarView.xaml.cs b/ProtocolMasterWPF/View/SessionControlBarView.xaml.cs new file mode 100644 index 0000000..f5407f9 --- /dev/null +++ b/ProtocolMasterWPF/View/SessionControlBarView.xaml.cs @@ -0,0 +1,49 @@ +using MaterialDesignThemes.Wpf; +using ProtocolMasterCore.Protocol; +using ProtocolMasterWPF.Model; +using ProtocolMasterWPF.ViewModel; +using System; +using System.Windows; +using System.Windows.Controls; +using Windows.Devices.Enumeration; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for SessionControlBarView.xaml + /// + public partial class SessionControlBarView : UserControl + { + internal Session SessionControl { get => _sessionControl; set { _sessionControl = value; DataContext = _sessionControl;} } + private Session _sessionControl; + public SessionControlBarView() + { + InitializeComponent(); + ResetTimeLocal(); + } + private void PreviewButton_Click(object sender, RoutedEventArgs e) => SessionControl.Preview(); + private void StartButton_Click(object sender, RoutedEventArgs e) => SessionControl.Start(); + private void StopButton_Click(object sender, RoutedEventArgs e) => SessionControl.Stop(); + private void ResetButton_Click(object sender, RoutedEventArgs e) => SessionControl.Reset(); + private void SelectDialog_OnDialogClosing(object sender, DialogClosingEventArgs eventArgs) + { + if (eventArgs.Parameter == null) SessionControl.CancelSelection(); + else SessionControl.MakeSelection(eventArgs.Parameter); + } + private void SelectButton_Click(object sender, RoutedEventArgs e) => DialogHost.Show(new ProtocolSelectView(), "SessionDialog", SelectDialog_OnDialogClosing); + public void UpdateTime(double elapsed, double duration) => App.Current.Dispatcher.Invoke(() => UpdateTimeLocal(elapsed, duration)); + private void UpdateTimeLocal(double elapsed, double duration) + { + ElapsedLabel.Text = DateTime.FromOADate(elapsed).ToString("HH:mm:ss"); + DurationLabel.Text = DateTime.FromOADate(duration).ToString("HH:mm:ss"); + TimeProgressBar.Value = 100f * elapsed / duration; + } + public void ResetTime() => App.Current.Dispatcher.Invoke(() => ResetTimeLocal()); + private void ResetTimeLocal() + { + ElapsedLabel.Text = DateTime.FromOADate(0f).ToString("HH:mm:ss"); + DurationLabel.Text = DateTime.FromOADate(0f).ToString("HH:mm:ss"); + TimeProgressBar.Value = 0f; + } + } +} diff --git a/ProtocolMasterWPF/View/SessionView.xaml b/ProtocolMasterWPF/View/SessionView.xaml new file mode 100644 index 0000000..fb4bf03 --- /dev/null +++ b/ProtocolMasterWPF/View/SessionView.xaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/View/SessionView.xaml.cs b/ProtocolMasterWPF/View/SessionView.xaml.cs new file mode 100644 index 0000000..3447d40 --- /dev/null +++ b/ProtocolMasterWPF/View/SessionView.xaml.cs @@ -0,0 +1,38 @@ +using ProtocolMasterCore.Prompt; +using ProtocolMasterWPF.Model; +using System.Windows.Controls; +using ProtocolMasterWPF; +using ProtocolMasterWPF.ViewModel; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for SessionView.xaml + /// + public partial class SessionView : UserControl + { + internal Session SessionControl { get => _sessionControl; set { _sessionControl = value; DataContext = _sessionControl; } } + private Session _sessionControl; + public SessionView() + { + SessionControl = new Session(); + + PromptTargetStore promptTargets = new PromptTargetStore(); + promptTargets.UserSelect = DropdownDialog.DropdownUserSelect; + SessionControl.Protocol.DriverManager.PromptTargets = promptTargets; + SessionControl.Protocol.InterpreterManager.PromptTargets = promptTargets; + + InitializeComponent(); + ControlBar.SessionControl = SessionControl; + CamView.DataContext = new CameraViewModel(SessionControl.Cam); + SessionControl.Protocol.InterpreterManager.OnEventsLoaded += Timeline.LoadPlotDataInUIThread; + SessionControl.OnStart += Timeline.StartTime; + SessionControl.OnStop += Timeline.StopTime; + SessionControl.OnReset += Timeline.ResetPlot; + SessionControl.OnReset += ControlBar.ResetTime; + SessionControl.Animator.OnUpdate += Timeline.UpdateTime; + SessionControl.Animator.OnUpdate += ControlBar.UpdateTime; + + } + } +} diff --git a/ProtocolMasterWPF/View/TimelineView.xaml b/ProtocolMasterWPF/View/TimelineView.xaml new file mode 100644 index 0000000..0a1ee69 --- /dev/null +++ b/ProtocolMasterWPF/View/TimelineView.xaml @@ -0,0 +1,11 @@ + + + diff --git a/ProtocolMasterWPF/View/TimelineView.xaml.cs b/ProtocolMasterWPF/View/TimelineView.xaml.cs new file mode 100644 index 0000000..221165b --- /dev/null +++ b/ProtocolMasterWPF/View/TimelineView.xaml.cs @@ -0,0 +1,187 @@ +using OxyPlot; +using OxyPlot.Annotations; +using OxyPlot.Axes; +using OxyPlot.Series; +using ProtocolMasterCore.Protocol; +using ProtocolMasterWPF.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Controls; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for TimelineView.xaml + /// + public partial class TimelineView : UserControl + { + public TimelineView() + { + InitializeComponent(); + SetUpPlot(); + } + // Plot properties + LineAnnotation Line { get; set; } + CategoryAxis VerticalCategoryAxis { get; set; } + DateTimeAxis HorizontalTimeAxis { get; set; } + + private void SetUpPlot() + { + // Generate Model + PlotModel model = StyledPlotModel(); + // Generate Axes + HorizontalTimeAxis = StyledDateTimeAxis(); + VerticalCategoryAxis = StyledCategoryAxis(); + // Add Axes + model.Axes.Add(HorizontalTimeAxis); + model.Axes.Add(VerticalCategoryAxis); + // Generate Time Annotation + Line = StyledLineAnnotation(); + // Add Time Annotation + model.Annotations.Add(Line); + Plot.Model = model; + ResetPlot(); + } + public void ResetPlot() + { + Line.X = 0; + Plot.Model.Series.Clear(); + VerticalCategoryAxis.Labels.Clear(); + HorizontalTimeAxis.AbsoluteMinimum = -0.00001; + HorizontalTimeAxis.Minimum = HorizontalTimeAxis.AbsoluteMinimum; + HorizontalTimeAxis.AbsoluteMaximum = 0.99; + + VerticalCategoryAxis.AbsoluteMinimum = -0.6; + VerticalCategoryAxis.AbsoluteMaximum = 0.6; + VerticalCategoryAxis.MaximumRange = VerticalCategoryAxis.AbsoluteMaximum - VerticalCategoryAxis.AbsoluteMinimum; + VerticalCategoryAxis.MinimumRange = VerticalCategoryAxis.AbsoluteMaximum - VerticalCategoryAxis.AbsoluteMinimum; + Plot.Model.InvalidatePlot(true); + } + public void LoadPlotDataInUIThread(List eventList)=>App.Current.Dispatcher.Invoke(() => LoadPlotData(eventList)); + + public void LoadPlotData(List eventList) + { + Plot.Model.Series.Clear(); + + List nodes = CategoryNode.BuildTrees(eventList); + if (nodes != null) + { + List allSeries; + List labels; + List gridLines; + CategoryNode.GeneratePlotData(nodes, out allSeries, out labels, out gridLines); + + // GENERATE LABELS, GRIDLINES, ETC. FROM TREE! + VerticalCategoryAxis.Labels.Clear(); + VerticalCategoryAxis.Labels.AddRange(labels); + VerticalCategoryAxis.ExtraGridlines = gridLines.ToArray(); + + foreach (IntervalBarSeries series in allSeries) + Plot.Model.Series.Add(series); + } + Plot.ResetAllAxes(); + HorizontalTimeAxis.Minimum = HorizontalTimeAxis.AbsoluteMinimum; + VerticalCategoryAxis.AbsoluteMaximum = VerticalCategoryAxis.ExtraGridlines.Max() + 0.6f; + VerticalCategoryAxis.MaximumRange = VerticalCategoryAxis.AbsoluteMaximum - VerticalCategoryAxis.AbsoluteMinimum; + VerticalCategoryAxis.MinimumRange = VerticalCategoryAxis.AbsoluteMaximum - VerticalCategoryAxis.AbsoluteMinimum; + Plot.Model.InvalidatePlot(true); + } + private PlotModel StyledPlotModel() + { + PlotModel styledModel = new PlotModel + { + IsLegendVisible = false + }; + // Set Model Colors + styledModel.DefaultColors = new List + { + OxyColors.DarkRed, + OxyColors.DodgerBlue, + OxyColors.Green, + OxyColors.Yellow + }; + styledModel.TextColor = OxyColors.White; + styledModel.PlotAreaBorderColor = OxyColors.Transparent; + styledModel.Background = OxyColors.Transparent; + // Change Mouse Bindings + Plot.ActualController.UnbindMouseDown(OxyMouseButton.Left); + Plot.ActualController.BindMouseDown(OxyMouseButton.Left, PlotCommands.PanAt); + Plot.ActualController.UnbindMouseDown(OxyMouseButton.Right); + Plot.ActualController.BindMouseDown(OxyMouseButton.Right, PlotCommands.ResetAt); + return styledModel; + } + private DateTimeAxis StyledDateTimeAxis() + { + return new DateTimeAxis() + { + Position = AxisPosition.Bottom, + AxislineThickness = 1.5, + ExtraGridlineThickness = 1.5, + MajorGridlineThickness = 1.5, + MinorGridlineThickness = 1.5, + TicklineColor = OxyColors.Gray, + AxislineColor = OxyColors.Gray, + MinorTicklineColor = OxyColors.Gray, + TextColor = OxyColors.WhiteSmoke, + TitleColor = OxyColors.WhiteSmoke, + ExtraGridlineColor = OxyColors.Gray, + MinorGridlineColor = OxyColors.Gray, + MajorGridlineColor = OxyColors.Gray, + StartPosition = 0, + }; + } + private CategoryAxis StyledCategoryAxis() + { + return new CategoryAxis() + { + Position = AxisPosition.Left, + AxislineThickness = 1.5, + ExtraGridlineThickness = 1.5, + MajorGridlineThickness = 1.5, + MinorGridlineThickness = 1.5, + TicklineColor = OxyColors.Transparent, + AxislineColor = OxyColors.Gray, + MinorTicklineColor = OxyColors.Transparent, + TextColor = OxyColors.WhiteSmoke, + TitleColor = OxyColors.WhiteSmoke, + ExtraGridlineColor = OxyColors.Gray, + MinorGridlineColor = OxyColors.Gray, + MajorGridlineColor = OxyColors.Gray, + GapWidth = 0.0f, + ExtraGridlines = new double[32], + }; + } + private LineAnnotation StyledLineAnnotation() + { + return new LineAnnotation() + { + StrokeThickness = 1.5, + Color = OxyColors.Green, + Type = LineAnnotationType.Vertical, + LineStyle = LineStyle.Solid, + X = 0, + Y = 0 + }; + } + public void StartTime() => App.Current.Dispatcher.Invoke(() => StartTimeLocal()); + private void StartTimeLocal() + { + Line.Color = OxyColors.Red; + } + public void StopTime() => App.Current.Dispatcher.Invoke(() => StopTimeLocal()); + private void StopTimeLocal() + { + Line.Color = OxyColors.Green; + Plot.InvalidatePlot(); + } + public void UpdateTime(double elapsed, double duration) => App.Current.Dispatcher.Invoke(() => UpdateTimeLocal(elapsed, duration)); + private void UpdateTimeLocal(double elapsed, double duration) + { + Line.X = elapsed; + Plot.InvalidatePlot(); + } + } +} diff --git a/ProtocolMasterWPF/View/TitleBarView.xaml b/ProtocolMasterWPF/View/TitleBarView.xaml new file mode 100644 index 0000000..9d8d47b --- /dev/null +++ b/ProtocolMasterWPF/View/TitleBarView.xaml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/View/TitleBarView.xaml.cs b/ProtocolMasterWPF/View/TitleBarView.xaml.cs new file mode 100644 index 0000000..4a1489e --- /dev/null +++ b/ProtocolMasterWPF/View/TitleBarView.xaml.cs @@ -0,0 +1,58 @@ +using ProtocolMasterCore.Utility; +using ProtocolMasterWPF.ViewModel; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; + +namespace ProtocolMasterWPF.View +{ + /// + /// Interaction logic for TitleBarView.xaml + /// + public partial class TitleBarView : UserControl + { + public TitleBarView() + { + InitializeComponent(); + } + private void Minimize_Click(object sender, RoutedEventArgs e) + { + Window.GetWindow(this).WindowState = WindowState.Minimized; + } + private void Maximize_Click(object sender, RoutedEventArgs e) + { + Window window = Window.GetWindow(this); + if (window.WindowState == WindowState.Normal) window.WindowState = WindowState.Maximized; + else window.WindowState = WindowState.Normal; + } + private void Close_Click(object sender, RoutedEventArgs e) + { + Window.GetWindow(this).Close(); + } + private void LogTest_Click(object sender, RoutedEventArgs e) + { + ProtocolMasterCore.Utility.Log.Out($"Testing output log"); + ProtocolMasterCore.Utility.Log.Error($"Testing error log"); + } + private void GoogleAuthButton_Click(object sender, RoutedEventArgs e) + { + ((App)App.Current).GoogleAuthenticate(); + } + private void GoogleDeauthButton_Click(object sender, RoutedEventArgs e) + { + ((App)App.Current).GoogleDeauthenticate(); + } + private void OpenWebsiteHome_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, "https://protocolmaster.philipm.net/home", "https://sites.google.com/view/protocolmaster/home?authuser=0"); + private void OpenGithub_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, "https://github.com/Philip-S-Martin/ProtocolMaster"); + private void OpenReleaseNotes_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, "https://github.com/Philip-S-Martin/ProtocolMaster/releases"); + private void OpenLogFolder_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, AppEnvironment.GetLocation("Log")); + private void OpenVideoFolder_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, AppEnvironment.GetLocation("Video")); + private void OpenExtensionFolder_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, AppEnvironment.GetLocation("Extensions")); + private void OpenWebsiteGettingStarted_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, "https://protocolmaster.philipm.net/docs/getting-started", "https://sites.google.com/view/protocolmaster/docs/getting-started"); + private void OpenWebsiteUserGuide_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, "https://protocolmaster.philipm.net/docs/user-guide", "https://sites.google.com/view/protocolmaster/docs/user-guide"); + private void OpenWiki_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, "https://github.com/Philip-S-Martin/ProtocolMaster/wiki"); + private void OpenProtocolsFolder_Click(object sender, RoutedEventArgs e) => App.TryOpenURI(sender, AppEnvironment.GetLocation("Protocols")); + } +} diff --git a/ProtocolMasterWPF/ViewModel/CameraViewModel.cs b/ProtocolMasterWPF/ViewModel/CameraViewModel.cs new file mode 100644 index 0000000..9ddfbed --- /dev/null +++ b/ProtocolMasterWPF/ViewModel/CameraViewModel.cs @@ -0,0 +1,61 @@ +using Microsoft.Toolkit.Wpf.UI.XamlHost; +using ProtocolMasterCore.Utility; +using ProtocolMasterWPF.Model; +using System; +using System.ComponentModel; +using System.Threading.Tasks; +using Windows.Media.Capture; +using Windows.Media.MediaProperties; +using Windows.Storage; +using Windows.UI.Xaml.Controls; + +namespace ProtocolMasterWPF.ViewModel +{ + internal class CameraViewModel : ViewModelBase + { + CameraContainer CamContainer { get; set; } + internal CameraViewModel(CameraContainer cam) + { + CamContainer = cam; + CamContainer.PropertyChanged += CameraChangedEvent; + GetUwpCaptureElement(); + } + public CaptureElement CapElement { get; set; } + public WindowsXamlHost XamlHostCaptureElement { get; set; } + private void GetUwpCaptureElement() + { + XamlHostCaptureElement = new WindowsXamlHost + { + InitialTypeName = "Windows.UI.Xaml.Controls.CaptureElement" + }; + XamlHostCaptureElement.ChildChanged += XamlHost_ChildChangedAsync; + } + private async void XamlHost_ChildChangedAsync(object sender, EventArgs e) + { + var windowsXamlHost = (WindowsXamlHost)sender; + + var captureElement = (CaptureElement)windowsXamlHost.Child; + if (captureElement != null) + { + CapElement = captureElement; + CapElement.Stretch = Windows.UI.Xaml.Media.Stretch.Uniform; + CapElement.Source = CamContainer.Cam.MediaCap; + CamContainer.Cam.StartPreview(); + } + } + private async void CameraChangedEvent(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "Cam") + { + var cameraContainer = (CameraContainer)sender; + if (CapElement != null && cameraContainer != null) + { + if (CapElement.Source != null) await CapElement.Source.StopPreviewAsync(); + CapElement.Source = CamContainer.Cam.MediaCap; + CamContainer.Cam.StartPreview(); + } + } + } + } +} + diff --git a/ProtocolMasterWPF/ViewModel/LogViewModel.cs b/ProtocolMasterWPF/ViewModel/LogViewModel.cs new file mode 100644 index 0000000..478d1eb --- /dev/null +++ b/ProtocolMasterWPF/ViewModel/LogViewModel.cs @@ -0,0 +1,13 @@ +using System.Collections.ObjectModel; + +namespace ProtocolMasterWPF.ViewModel +{ + public class LogViewModel + { + public ObservableCollection LogText { get; private set; } + public LogViewModel() + { + LogText = new ObservableCollection(); + } + } +} diff --git a/ProtocolMasterWPF/ViewModel/ViewModelBase.cs b/ProtocolMasterWPF/ViewModel/ViewModelBase.cs new file mode 100644 index 0000000..5bd3029 --- /dev/null +++ b/ProtocolMasterWPF/ViewModel/ViewModelBase.cs @@ -0,0 +1,14 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace ProtocolMasterWPF.ViewModel +{ + public class ViewModelBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/ProtocolMasterWPF/app.manifest b/ProtocolMasterWPF/app.manifest new file mode 100644 index 0000000..f125f8d --- /dev/null +++ b/ProtocolMasterWPF/app.manifest @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Schedulino/Properties/AssemblyInfo.cs b/Schedulino/Properties/AssemblyInfo.cs deleted file mode 100644 index 3f7dfac..0000000 --- a/Schedulino/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("SchedulinoDriver")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("SchedulinoDriver")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("edba6b70-6719-4118-977d-f10ba534b394")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Schedulino/Schedulino.csproj b/Schedulino/Schedulino.csproj deleted file mode 100644 index c3c24d7..0000000 --- a/Schedulino/Schedulino.csproj +++ /dev/null @@ -1,70 +0,0 @@ - - - - - Debug - AnyCPU - {EDBA6B70-6719-4118-977D-F10BA534B394} - Library - Properties - SchedulinoDriver - SchedulinoDriver - v4.8 - 512 - true - - - - true - full - false - ..\ProtocolMaster\bin\Debug\net48\Extensions\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - ..\ProtocolMaster\bin\Release\net48\Extensions\ - TRACE - prompt - 4 - - - - ..\ProtocolMaster\bin\Debug\net48\ExcelDataReader.dll - - - - ..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.ComponentModel.Composition.dll - - - - - - - - - - - - - - - - - - - - - - - - {ce4d7148-d741-4ed7-95a1-dc1bd0532e1b} - ProtocolMaster - False - - - - \ No newline at end of file