From 9eeb8ba843ea31a573b92921fce012f596d41466 Mon Sep 17 00:00:00 2001 From: avcarr2 <64652734+avcarr2@users.noreply.github.com> Date: Thu, 22 Sep 2022 17:49:28 -0500 Subject: [PATCH 1/6] Added implementations of many IInstrument methods --- Client/InstrumentInterfaces/IInstrument.cs | 4 +- .../InterfaceImplementations/ThermoTribrid.cs | 123 ++++++++++++------ 2 files changed, 83 insertions(+), 44 deletions(-) diff --git a/Client/InstrumentInterfaces/IInstrument.cs b/Client/InstrumentInterfaces/IInstrument.cs index d583988..859b97f 100644 --- a/Client/InstrumentInterfaces/IInstrument.cs +++ b/Client/InstrumentInterfaces/IInstrument.cs @@ -19,11 +19,11 @@ public interface IInstrument void SendScanAction(SingleScanDataObject ssdo); void OpenInstrumentConnection(); void CloseInstrumentConnection(); - void GetSystemState(); + string GetSystemState(int stateOrMode); void CancelAcquisition(); void PauseAcquisition(); void ResumeAcquisition(); - void StartAcquisition(); + void StartAcquisition(string rawFileName); void StartMethodAcquistion(string methodFilePath, string methodName, string outputFileName, string sampleName, double timeInMinutes); void InstrumentOn(); diff --git a/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs b/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs index 696d006..3a94226 100644 --- a/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs +++ b/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs @@ -1,6 +1,7 @@ using System; using System.Runtime; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using ClientServerCommLibrary; using Thermo.Interfaces.FusionAccess_V1; @@ -10,12 +11,15 @@ using Thermo.Interfaces.InstrumentAccess_V1.MsScanContainer; using Thermo.TNG.Factory; using System.Linq; +using System.Text; using Data; using System.Threading.Tasks; +using System.Timers; using Newtonsoft.Json; using Thermo.Interfaces.InstrumentAccess_V1.Control.Acquisition.Modes; using Thermo.Interfaces.InstrumentAccess_V1.Control.Acquisition.Workflow; using Thermo.Interfaces.InstrumentAccess_V1.Control.Scans; + using ScanInstructions = ClientServerCommunication.ScanInstructions; using SingleScanDataObject = Data.SingleScanDataObject; @@ -75,7 +79,14 @@ private void GetInstAccess() InstAccessContainer.MessagesArrived += (o, s) => { }; InstAcq.AcquisitionStreamClosing += (o, s) => { }; InstAcq.AcquisitionStreamOpening += (o, s) => { }; - InstAcq.StateChanged += (o, s) => { }; + InstAcq.StateChanged += (o, s) => + { + EventHandler handler = SystemStateChanged; + if (handler != null) + { + handler(this, s); + } + }; // instacq systemstate also contains an enum, where each value corresponds to the acquisition state // of the system. Could potentially use this as a read-back for the client. // InstAcq.State.SystemState @@ -85,35 +96,92 @@ private void GetInstAccess() #endregion #region - public void GetSystemState() + public string GetSystemState(int stateOrMode) { - + if (stateOrMode > 2 || stateOrMode < 0) + throw new ArgumentException("Integer selection is outside of bounds."); + + switch (stateOrMode) + { + case 0: + return Enum.GetName(typeof(InstrumentState), InstAcq.State.SystemState); + case 1: + return Enum.GetName(typeof(SystemMode), InstAcq.State.SystemMode); + case 2: + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(Enum.GetName(typeof(InstrumentState), InstAcq.State.SystemState)); + sb.AppendLine(Enum.GetName(typeof(SystemMode), InstAcq.State.SystemMode)); + return sb.ToString(); + }; + } + + throw new ArgumentException("Error: State and Mode unable to be found."); } public void CancelAcquisition() { - throw new NotImplementedException(); + InstAcq.CancelAcquisition(); } public void PauseAcquisition() { - throw new NotImplementedException(); + if (InstAcq.CanPause) + { + InstAcq.Pause(); + } + // todo: add handling for the case where InstAcq.CanPause is false. } public void ResumeAcquisition() { - throw new NotImplementedException(); + if (InstAcq.CanResume) + { + InstAcq.Resume(); + } + // todo: add handling for case where CanResume == false. } - public void StartAcquisition() + public void StartAcquisition(string rawFileName) { - throw new NotImplementedException(); + var acquisition = CreateTuneAcquisition(rawFileName); + InstAcq.StartAcquisition(acquisition); + } + + private IAcquisitionWorkflow CreateTuneAcquisition(string rawFileName) + { + var acquisition = InstAcq.CreatePermanentAcquisition(); + if (rawFileName != null) acquisition.RawFileName = rawFileName; + return acquisition; } public void StartMethodAcquistion(string methodFilePath, string methodName, string outputFileName, string sampleName, double timeInMinutes) { - throw new NotImplementedException(); + var method = CreateMethodAcquisition(methodFilePath, 5, AcquisitionContinuation.Standby, + waitForContactClosure: true, methodName, outputFileName, sampleName, timeInMinutes); + InstAcq.StartAcquisition(method); + } + private IAcquisitionMethodRun CreateMethodAcquisition(string methodFilePath, + int singleProcessingDelay, AcquisitionContinuation continuation, + bool waitForContactClosure, string methodName, + string rawFileName, string sampleName, + double timeInMinutes) + { + var methodAcquisition = InstAcq.CreateMethodAcquisition(methodFilePath); + // note: you can set the single processing delay. The instrument will wait + // the number of milliseconds you set before it starts the next scan. + // this needs to be set in the implementation of the interface because I'm not sure if + // anything besides Thermo will have this setting. + methodAcquisition.SingleProcessingDelay = singleProcessingDelay; + // set the default behavior of inter-acquisition time to put the instrument on standby. + methodAcquisition.Continuation = continuation; + methodAcquisition.WaitForContactClosure = waitForContactClosure; + methodAcquisition.MethodName = methodName; + methodAcquisition.RawFileName = rawFileName; + methodAcquisition.SampleName = sampleName; + methodAcquisition.Duration = TimeSpan.FromMinutes(timeInMinutes); + return methodAcquisition; } #endregion @@ -124,7 +192,7 @@ public void CloseInstrumentConnection() InstAccessContainer.Dispose(); } #endregion - // enter main loop is obsolete because of the client pipe restructuring. + #region SendScanAction #endregion @@ -154,38 +222,9 @@ public void InstrumentStandby() public event EventHandler InstrumentDisconnected; public event EventHandler ScanReceived; public event EventHandler ReadyToReceiveScan; - - public void StartMethodAcquisition(string methodFilePath, string methodName, - string outputFileName, string sampleName, double timeInMinutes) - { - var method = CreateMethodAcquisition(methodFilePath, 5, AcquisitionContinuation.Standby, - waitForContactClosure: true, methodName, outputFileName, sampleName, timeInMinutes); - InstAcq.StartAcquisition(method); - } - - private IAcquisitionMethodRun CreateMethodAcquisition(string methodFilePath, - int singleProcessingDelay, AcquisitionContinuation continuation, - bool waitForContactClosure, string methodName, - string rawFileName, string sampleName, - double timeInMinutes) - { - var methodAcquisition = InstAcq.CreateMethodAcquisition(methodFilePath); - // note: you can set the single processing delay. The instrument will wait - // the number of milliseconds you set before it starts the next scan. - // this needs to be set in the implementation of the interface because I'm not sure if - // anything besides Thermo will have this setting. - methodAcquisition.SingleProcessingDelay = singleProcessingDelay; - // set the default behavior of inter-acquisition time to put the instrument on standby. - methodAcquisition.Continuation = continuation; - methodAcquisition.WaitForContactClosure = waitForContactClosure; - methodAcquisition.MethodName = methodName; - methodAcquisition.RawFileName = rawFileName; - methodAcquisition.SampleName = sampleName; - methodAcquisition.Duration = TimeSpan.FromMinutes(timeInMinutes); - return methodAcquisition; - } - - + // TODO: Change from StateChangedEventArgs to a Custom Class that doesn't use a + // thermo-based class. + public event EventHandler SystemStateChanged; } } From 8c4ee8e03ad03f2c8028714ccfe96c07dcfc3753 Mon Sep 17 00:00:00 2001 From: avcarr2 <64652734+avcarr2@users.noreply.github.com> Date: Thu, 29 Sep 2022 12:14:23 -0500 Subject: [PATCH 2/6] * Filled out instrument facing methods. * Added ScanInstructions and requisite properties for Thermo Tribrid * Added ScanInstructions property to SingleScanDataObject * Added method skeleton to validate ScanInstructions for a particular instrument --- ApplicationServer/AppServerPipe.cs | 6 +- ApplicationServer/Program.cs | 3 +- Client/IScanTranslator.cs | 14 ++ Client/InstrumentClient.csproj | 8 +- Client/InstrumentClientPipe.cs | 23 +++- Client/InstrumentInterfaces/IInstrument.cs | 17 ++- .../DataHandling/InstrumentSettingEnums.cs | 75 ++++++++++ .../Thermo/DataHandling/MsScanExtensions.cs | 2 +- .../DataHandling/ThermoTribridSsdoMapping.cs | 39 ++++++ .../InterfaceImplementations/ThermoTribrid.cs | 129 ++++++++++++++---- .../ThermoTribridScanTranslator.cs | 27 ++++ .../ClientServerCommLibrary.csproj | 4 - ClientServerCommunication/ScanInstructions.cs | 40 +++++- .../SingleScanDataObject.cs | 99 ++++++++++++++ InstrumentControl.sln | 6 - 15 files changed, 436 insertions(+), 56 deletions(-) create mode 100644 Client/IScanTranslator.cs create mode 100644 Client/InstrumentInterfaces/Thermo/DataHandling/InstrumentSettingEnums.cs create mode 100644 Client/InstrumentInterfaces/Thermo/DataHandling/ThermoTribridSsdoMapping.cs create mode 100644 Client/InstrumentInterfaces/Thermo/ThermoScanTranslator/ThermoTribridScanTranslator.cs diff --git a/ApplicationServer/AppServerPipe.cs b/ApplicationServer/AppServerPipe.cs index a9ec779..561c689 100644 --- a/ApplicationServer/AppServerPipe.cs +++ b/ApplicationServer/AppServerPipe.cs @@ -73,7 +73,6 @@ public void StartServer() byte[] length = BitConverter.GetBytes(buffer.Length); byte[] finalBuffer = length.Concat(buffer).ToArray(); PipeServer.Write(finalBuffer, 0, finalBuffer.Length); - PipeServer.WaitForPipeDrain(); } }; @@ -111,11 +110,13 @@ private void HandleDataReceived(object? obj, PipeEventArgs eventArgs) SingleScanDataObject ssdo = eventArgs.ToSingleScanDataObject(); if (ssdo == null) throw new ArgumentException("single scan data object is null"); + // TODO: refactor and make handling more robust downstream. if (ssdo.ScanOrder == 1) { ScanQueueMS1.Enqueue(ssdo); if (ScanQueueMS1.Count == Ms1ScanQueueThreshold) { + // method raises event OnMs1QueueThresholdReached(ScanQueueMS1); } } @@ -124,9 +125,10 @@ private void HandleDataReceived(object? obj, PipeEventArgs eventArgs) ScanQueueMS2.Enqueue(ssdo); if (ScanQueueMS2.Count == Ms2ScanQueueThreshold) { + // method raises handler OnMs2QueueThresholdReached(ScanQueueMS2); } - } + } // TODO: Handle scan order > 2. Console.WriteLine("\n"); } private void OnMs1QueueThresholdReached(Queue queue) diff --git a/ApplicationServer/Program.cs b/ApplicationServer/Program.cs index c7b2ba4..7b3d0d7 100644 --- a/ApplicationServer/Program.cs +++ b/ApplicationServer/Program.cs @@ -1,6 +1,5 @@ using System.IO.Pipes; -using ClientServerCommunication; -using Data; +using ClientServerCommLibrary; using InstrumentControl; using Newtonsoft; using Newtonsoft.Json; diff --git a/Client/IScanTranslator.cs b/Client/IScanTranslator.cs new file mode 100644 index 0000000..ac7f10f --- /dev/null +++ b/Client/IScanTranslator.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ClientServerCommLibrary; + +namespace Client +{ + public interface IScanTranslator + { + T Translate(SingleScanDataObject ssdo) where T: new(); + } +} diff --git a/Client/InstrumentClient.csproj b/Client/InstrumentClient.csproj index e9ec032..8c9289a 100644 --- a/Client/InstrumentClient.csproj +++ b/Client/InstrumentClient.csproj @@ -73,9 +73,13 @@ + + + + @@ -90,10 +94,6 @@ {0e9b5801-5167-44cc-90d3-c8caf1df8f6a} ClientServerCommLibrary - - {6b34249b-a513-4a4a-9ed5-cb633bcab646} - Data - diff --git a/Client/InstrumentClientPipe.cs b/Client/InstrumentClientPipe.cs index b5fcc99..778ceb4 100644 --- a/Client/InstrumentClientPipe.cs +++ b/Client/InstrumentClientPipe.cs @@ -6,6 +6,7 @@ using ClientServerCommLibrary; using System.IO.Pipes; using Microsoft.Win32; +using Newtonsoft.Json; namespace InstrumentClient @@ -103,17 +104,27 @@ public void BeginInstrumentConnection(IInstrument instr) instr.OpenInstrumentConnection(); instr.InstrumentConnected += (obj, sender) => { InstrumentConnectedBool = true; }; instr.InstrumentDisconnected += (obj, sender) => { InstrumentConnectedBool = false; }; - instr.ReadyToReceiveScan += (obj, sender) => { instrReadyToReceiveScan = true; }; + instr.InstrumentReadyToReceiveScan += (obj, sender) => + { + // check if queue contains anything and send scan action if it does. + if (ScanInstructionsQueue.Count > 0) + { + instr.SendScanAction(ScanInstructionsQueue.Dequeue()); + } + }; instr.ScanReceived += (obj, sender) => { - // send scan to the server. + // convert to byte[] and send to WorkflowServer. + byte[] buffer = SerializeAndCreateBuffer(sender.Ssdo); + PipeClient.WriteAsync(buffer, 0, buffer.Length); }; + // don't think I need this event after all. ScanQueueThresholdReached += (obj, sender) => { // send the scan to the instrument - }; + }; // enter instrument main routine: while (InstrumentConnectedBool) { @@ -121,5 +132,11 @@ public void BeginInstrumentConnection(IInstrument instr) } } #endregion + + private byte[] SerializeAndCreateBuffer(T obj) + { + string jsonString = JsonConvert.SerializeObject(obj); + return Encoding.UTF8.GetBytes(jsonString); + } } } diff --git a/Client/InstrumentInterfaces/IInstrument.cs b/Client/InstrumentInterfaces/IInstrument.cs index 859b97f..2d96dc5 100644 --- a/Client/InstrumentInterfaces/IInstrument.cs +++ b/Client/InstrumentInterfaces/IInstrument.cs @@ -1,8 +1,5 @@ -using ClientServerCommunication; +using ClientServerCommLibrary; using System; -using Data; -using Thermo.Interfaces.InstrumentAccess_V1.MsScanContainer; -using ScanInstructions = Data.ScanInstructions; namespace InstrumentClient { @@ -15,6 +12,11 @@ public interface IInstrument // send action (Ms1 scan, SIM scan, boxcar scan, etc.) to instrument // open instrument connection // close instrument connection + + /// + /// Used for explicitly gaining access to the last received instrument scan without requiring an event. + /// + /// Returns SingleScanDataObject. SingleScanDataObject GetLastScan(); void SendScanAction(SingleScanDataObject ssdo); void OpenInstrumentConnection(); @@ -28,11 +30,12 @@ void StartMethodAcquistion(string methodFilePath, string methodName, string outputFileName, string sampleName, double timeInMinutes); void InstrumentOn(); void InstrumentOff(); - void InstrumentStandby(); + void InstrumentStandby(); + void GetScanPossibleParameters(); event EventHandler InstrumentConnected; event EventHandler InstrumentDisconnected; - event EventHandler ScanReceived; - event EventHandler ReadyToReceiveScan; + event EventHandler ScanReceived; + event EventHandler InstrumentReadyToReceiveScan; } } \ No newline at end of file diff --git a/Client/InstrumentInterfaces/Thermo/DataHandling/InstrumentSettingEnums.cs b/Client/InstrumentInterfaces/Thermo/DataHandling/InstrumentSettingEnums.cs new file mode 100644 index 0000000..01104e2 --- /dev/null +++ b/Client/InstrumentInterfaces/Thermo/DataHandling/InstrumentSettingEnums.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Client +{ + internal class InstrumentSettingEnums + { + public enum InstrumentSettings + { + // common + FirstMass, + LastMass, + Analyzer, + ScanType, + SourceCIDEnergy, + SrcRFLens, + Polarity, + DataType, + IsolationMode, + AGCTarget, + MaxIT, + Microscans, + + // detector resolutions + OrbitrapResolution, + ScanRate, + + // ms1 is only defined by the common settings + + // ms2 only + CollisionEnergy, + IsolationWidth, + ActivationType, + ChargeStates, + ActivationQ, + PrecursorMass, + ReactionTime, + ReagentMaxIT, + ReagentAGCTarget + } + + + public enum Analyzer + { + IonTrap, + Orbitrap + } + public enum ScanType + { + Full, + SIM, + MSn + } + public enum Polarity + { + Postive, + Negative + } + public enum DataType + { + Centroid, + Profile + } + public enum IsolationMode + { + None, + Quadrupole, + IonTrap + } + + } +} diff --git a/Client/InstrumentInterfaces/Thermo/DataHandling/MsScanExtensions.cs b/Client/InstrumentInterfaces/Thermo/DataHandling/MsScanExtensions.cs index 9f733d9..8ded991 100644 --- a/Client/InstrumentInterfaces/Thermo/DataHandling/MsScanExtensions.cs +++ b/Client/InstrumentInterfaces/Thermo/DataHandling/MsScanExtensions.cs @@ -2,7 +2,7 @@ using Thermo.Interfaces.InstrumentAccess_V1.MsScanContainer; using System.Collections.Generic; using System.Linq; -using Data; +using ClientServerCommLibrary; namespace InstrumentClient { public static class MsScanExtensions diff --git a/Client/InstrumentInterfaces/Thermo/DataHandling/ThermoTribridSsdoMapping.cs b/Client/InstrumentInterfaces/Thermo/DataHandling/ThermoTribridSsdoMapping.cs new file mode 100644 index 0000000..7722e2d --- /dev/null +++ b/Client/InstrumentInterfaces/Thermo/DataHandling/ThermoTribridSsdoMapping.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Client.InstrumentInterfaces.Thermo.DataHandling +{ + public static class ThermoTribridSsdoMapping + { + public static readonly Dictionary TrbridToSsdoMapping = new Dictionary + { + // key = ssdo; value = Tribrid + {"MinX", "FirstMass"}, + {"MaxX", "LastMass"}, + {"AnalyzerType", "Analyzer"}, + {"ScanType", "ScanType"}, + {"SourceEnergy", "SourceCIDEnergy"}, + {"SrcRFLens", "SrcRFLens"}, + {"Polarity", "Polarity"}, + {"DataType", "DataType"}, + {"IsolationType", "IsolationMode"}, + {"AgcTarget", "AGCTarget"}, + {"MaxIt", "MaxIT"}, + {"Microscans", "Microscans"}, + {"OrbitrapResolution", "OrbitrapResolution"}, + {"IonTrapScanRate", "ScanRate"}, + {"CollisionEnergy", "CollisionEnergy"}, + {"IsolationWidth", "IsolationWidth"}, + {"ActivationType", "ActivationType"}, + {"ChargeStates", "ChargeStates"}, + {"ActivationQ", "ActivationQ"}, + {"PrecursorMass", "PrecursorMass"}, + {"ReactionTime", "ReactionTime"}, + {"ReagentMaxIT", "ReagentMaxIT"}, + {"ReagentAGCTarget", "ReagentAGCTarget"} + }; + } +} diff --git a/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs b/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs index 3a94226..265551a 100644 --- a/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs +++ b/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs @@ -2,6 +2,7 @@ using System.Runtime; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Threading; using ClientServerCommLibrary; using Thermo.Interfaces.FusionAccess_V1; @@ -12,50 +13,90 @@ using Thermo.TNG.Factory; using System.Linq; using System.Text; -using Data; -using System.Threading.Tasks; -using System.Timers; -using Newtonsoft.Json; using Thermo.Interfaces.InstrumentAccess_V1.Control.Acquisition.Modes; using Thermo.Interfaces.InstrumentAccess_V1.Control.Acquisition.Workflow; using Thermo.Interfaces.InstrumentAccess_V1.Control.Scans; -using ScanInstructions = ClientServerCommunication.ScanInstructions; -using SingleScanDataObject = Data.SingleScanDataObject; - namespace InstrumentClient { public class ThermoTribrid : IInstrument - { - public ClientPipe PipeClient { get; set; } - public static IScans MScan { get; set; } + { + // IScans contains CanAcceptNextCustomScan and PossibleParametersChanged. public string InstrumentId { get; private set; } public string InstrumentName { get; private set; } + // InstAccessContainer contains MessagesArrived, ServiceConnectionChanged. public static IFusionInstrumentAccessContainer InstAccessContainer { get; private set; } + // InstAccess contains ContactClosureChanged, AcquisitionErrorsArrived, + // ConnectionChanged. public static IFusionInstrumentAccess InstAccess { get; private set; } + // MsScanContainer contains events MsScanArrived public static IFusionMsScanContainer MsScanContainer { get; private set; } + // InstAcq contains events AcquisitionStreamClosing, AcquisitionStreamOpening, + // StateChanged public static IAcquisition InstAcq { get; private set; } public static IControl InstControl { get; private set; } // Private Properties private int SystemState { get; set; } + private static IScans MScan { get; set; } // Constructors public ThermoTribrid() { - InstAccessContainer = Factory.Create(); + InstAccessContainer = Factory.Create(); } #region OpenInstrumentConnection public SingleScanDataObject GetLastScan() { - throw new NotImplementedException(); + IMsScan scan = MsScanContainer.GetLastMsScan(); + return scan.ConvertToSingleScanDataObject(); } public void SendScanAction(SingleScanDataObject ssdo) { - throw new NotImplementedException(); + if (ssdo.ScanType == null) + throw new ArgumentException("ScanAction type is invalid! (property was null)"); + + switch (ssdo.ScanType) + { + case (ScanType.CustomScan): + SendRepeatingScan(); + break; + case (ScanType.RepeatingScan): + SendCustomScan(); + break; + } } + private IDictionary SsdoToDictionary(SingleScanDataObject ssdo) + { + Dictionary valuesDict = new Dictionary(); + + // create an ssdo property to Thermo instrument value name. + + + return valuesDict; + } + private void CreateRepeatingScan(SingleScanDataObject ssdo) + { + IRepeatingScan rscan = MScan.CreateRepeatingScan(); + + + } + private void SendRepeatingScan() + { + + } + private void CreateCustomScan(SingleScanDataObject ssdo) + { + + } + + private void SendCustomScan() + { + + } + public void OpenInstrumentConnection() { InstAccessContainer.StartOnlineAccess(); @@ -74,6 +115,7 @@ private void GetInstAccess() InstrumentId = InstAccess.InstrumentId.ToString(); InstrumentName = InstAccess.InstrumentName; MsScanContainer = InstAccess.GetMsScanContainer(0); + MScan = InstControl.GetScans(false); InstAccessContainer.ServiceConnectionChanged += (o, s) => { }; InstAccessContainer.MessagesArrived += (o, s) => { }; @@ -87,11 +129,29 @@ private void GetInstAccess() handler(this, s); } }; - // instacq systemstate also contains an enum, where each value corresponds to the acquisition state + + // instacq.systemstate also contains an enum, where each value corresponds to the acquisition state // of the system. Could potentially use this as a read-back for the client. // InstAcq.State.SystemState - MsScanContainer.MsScanArrived += (o, s) => { }; - + MsScanContainer.MsScanArrived += (o, s) => + { + // convert to single scan data object and raise this.ScanReceived + var ssdo = s.GetScan().ConvertToSingleScanDataObject(); + EventHandler handler = ScanReceived; + if (handler != null) + { + handler(this, new ScanReceivedEventArgs(ssdo)); + } + }; + + MScan.CanAcceptNextCustomScan += (o, s) => + { + EventHandler handler = InstrumentReadyToReceiveScan; + if (handler != null) + { + handler(this, EventArgs.Empty); + } + }; } #endregion #region @@ -115,7 +175,6 @@ public string GetSystemState(int stateOrMode) return sb.ToString(); }; } - throw new ArgumentException("Error: State and Mode unable to be found."); } @@ -192,14 +251,7 @@ public void CloseInstrumentConnection() InstAccessContainer.Dispose(); } #endregion - - #region SendScanAction - #endregion - - #region GetLastScan - - #endregion public void InstrumentOn() { @@ -220,11 +272,36 @@ public void InstrumentStandby() public event EventHandler InstrumentConnected; public event EventHandler InstrumentDisconnected; - public event EventHandler ScanReceived; - public event EventHandler ReadyToReceiveScan; + public event EventHandler ScanReceived; + public event EventHandler InstrumentReadyToReceiveScan; // TODO: Change from StateChangedEventArgs to a Custom Class that doesn't use a // thermo-based class. public event EventHandler SystemStateChanged; } + + public class ScanReceivedEventArgs : EventArgs + { + public SingleScanDataObject Ssdo { get; set; } + public ScanReceivedEventArgs(SingleScanDataObject ssdo) + { + Ssdo = ssdo; + } + } + + public enum ConnectionState + { + Connected = 1, + Disconnected = 0 + } + public class InstrumentConnectionStateEventArgs : EventArgs + { + + public ConnectionState ConnectionStatus { get; set; } + + public InstrumentConnectionStateEventArgs(ConnectionState connection) + { + ConnectionStatus = connection; + } + } } diff --git a/Client/InstrumentInterfaces/Thermo/ThermoScanTranslator/ThermoTribridScanTranslator.cs b/Client/InstrumentInterfaces/Thermo/ThermoScanTranslator/ThermoTribridScanTranslator.cs new file mode 100644 index 0000000..95b7175 --- /dev/null +++ b/Client/InstrumentInterfaces/Thermo/ThermoScanTranslator/ThermoTribridScanTranslator.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ClientServerCommLibrary; + +namespace Client +{ + public class ThermoTribridScanTranslator : IScanTranslator + { + public T Translate(SingleScanDataObject ssdo) where T : new() + { + if (typeof(T) != typeof(Dictionary)) + { + throw new ArgumentException("Invalid return object type for Thermo Tribrid instrument class!"); + } + + + } + + private Dictionary TranslateSsdo(SingleScanDataObject ssdo) + { + + } + } +} diff --git a/ClientServerCommunication/ClientServerCommLibrary.csproj b/ClientServerCommunication/ClientServerCommLibrary.csproj index ea0d257..e8ad1a1 100644 --- a/ClientServerCommunication/ClientServerCommLibrary.csproj +++ b/ClientServerCommunication/ClientServerCommLibrary.csproj @@ -8,8 +8,4 @@ - - - - diff --git a/ClientServerCommunication/ScanInstructions.cs b/ClientServerCommunication/ScanInstructions.cs index 263adea..c20f129 100644 --- a/ClientServerCommunication/ScanInstructions.cs +++ b/ClientServerCommunication/ScanInstructions.cs @@ -1,12 +1,50 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Permissions; using System.Text; using System.Threading.Tasks; -namespace ClientServerCommunication + +namespace ClientServerCommLibrary { + // TODO: Turn into a partial class for improved organization + [Serializable] public class ScanInstructions { + #region ThermoTribrid Settings + public double? FirstMass { get; set; } + public double? LastMass { get; set; } + public AnalyzerType? AnalyzerType { get; set; } + public ScanType? ScanType { get; set; } + public double? SourceEnergy { get; set; } + public double? SrcRFLens { get; set; } + public Polarity? Polarity { get; set; } + public DataType? DataType { get; set; } + public IsolationType? IsolationType { get; set; } + public double? AgcTarget { get; set; } + public double? MaxIt { get; set; } + public int? Microscans { get; set; } + public OrbitrapResolution? OrbitrapResolution { get; set; } + public IonTrapScanRate? IonTrapScanRate { get; set; } + public double? CollisionEnergy { get; set; } + public double? IsolationWidth { get; set; } + public ActivationType? ActivationType { get; set; } + public string ChargeStates { get; private set; } + public double? ActivationQ { get; set; } + public double? PrecursorMass { get; set; } + public double? ReactionTime { get; set; } + public double? ReagentMaxIT { get; set; } + public double? ReagentAGCTarget { get; set; } + public void SetChargeStates(int[] chargeStates) + { + ChargeStates = string.Join(",", chargeStates); + } + #endregion + + } + } diff --git a/ClientServerCommunication/SingleScanDataObject.cs b/ClientServerCommunication/SingleScanDataObject.cs index fd857d2..f454b0c 100644 --- a/ClientServerCommunication/SingleScanDataObject.cs +++ b/ClientServerCommunication/SingleScanDataObject.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Permissions; using System.Text; using System.Threading.Tasks; namespace ClientServerCommLibrary { + // TODO: Write validation methods for SingleScanDataObject [Serializable] public class SingleScanDataObject { @@ -15,6 +17,10 @@ public class SingleScanDataObject public double? MzPrecursor { get; set; } public double[] XArray { get; set; } public double[] YArray { get; set; } + public double TotalIonCurrent { get; set; } + public double MinX { get; set; } + public double MaxX { get; set; } + public ScanInstructions ScanInstructions { get; private set; } public SingleScanDataObject() { @@ -38,5 +44,98 @@ public SingleScanDataObject(int scanOrder, int scanNumber, PrecursorScanNumber = precursorScanNumber; MzPrecursor = mzPrecursor; } + public void SetScanInstructions(ScanInstructions si, string instrument) + { + if (!ValidateScanInstructionsForInstrument(si, instrument)) + { + throw new ArgumentException( + "Invalid scan instructions for instrument type: {0}", + instrument); + } + SetScanInstructions(si); + } + private void SetScanInstructions(ScanInstructions si) + { + ScanInstructions = si; + } + // TODO: Fill out the method. + private bool ValidateScanInstructionsForInstrument(ScanInstructions si, string instrument) + { + return true; + } + } + + public enum IsolationType + { + Quadropole, + IonTrap + } + + public enum AnalyzerType + { + Quadropole, + TOF, + Orbitrap, + IonTrap + } + + public enum CustomScanType + { + RepeatingScan, + CustomScan + } + + public enum InstrumentType + { + ThermoTribrid, + ThermoQE, + WatersTOF, + BrukerTOF + } + + public enum ScanType + { + Full, + SIM, + MSn + } + + public enum Polarity + { + Positive, + Negative + } + + public enum DataType + { + Profile, + Centroid + } + public enum OrbitrapResolution : int + { + X_7500 = 7500, + X_15000 = 15000, + X_30000 = 30000, + X_50000 = 50000, + X_60000 = 60000, + X_120000 = 120000, + X_240000 = 240000, + X_500000 = 500000 + } + public enum IonTrapScanRate + { + Normal, + Enhanced, + Zoom, + Rapid, + Turbo + } + public enum ActivationType + { + CID, + HCD, + ETD, + EThcD, + ETciD } } diff --git a/InstrumentControl.sln b/InstrumentControl.sln index 1f79597..0d0e7c7 100644 --- a/InstrumentControl.sln +++ b/InstrumentControl.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32516.85 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Data", "Data\Data.csproj", "{6B34249B-A513-4A4A-9ED5-CB633BCAB646}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowServer", "ApplicationServer\WorkflowServer.csproj", "{7989657B-6BE1-42AC-A77A-EBE537AAB710}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClientServerCommLibrary", "ClientServerCommunication\ClientServerCommLibrary.csproj", "{0E9B5801-5167-44CC-90D3-C8CAF1DF8F6A}" @@ -19,10 +17,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6B34249B-A513-4A4A-9ED5-CB633BCAB646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6B34249B-A513-4A4A-9ED5-CB633BCAB646}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6B34249B-A513-4A4A-9ED5-CB633BCAB646}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6B34249B-A513-4A4A-9ED5-CB633BCAB646}.Release|Any CPU.Build.0 = Release|Any CPU {7989657B-6BE1-42AC-A77A-EBE537AAB710}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7989657B-6BE1-42AC-A77A-EBE537AAB710}.Debug|Any CPU.Build.0 = Debug|Any CPU {7989657B-6BE1-42AC-A77A-EBE537AAB710}.Release|Any CPU.ActiveCfg = Release|Any CPU From 6814c788a1294eff183b1ba050527571985273f9 Mon Sep 17 00:00:00 2001 From: avcarr2 <64652734+avcarr2@users.noreply.github.com> Date: Thu, 29 Sep 2022 13:56:51 -0500 Subject: [PATCH 3/6] * added method to get enum values generically * working on method to get Properties from ScanInstructions to pass translate method --- ApplicationServer/ScanQueue/ScanQueue.cs | 3 +- .../ScanQueue/ThresholdReachedEventArgs.cs | 2 +- ApplicationServer/WorkflowServer.csproj | 1 + Client/IScanTranslator.cs | 4 +- Client/InstrumentClient.csproj | 12 +++ .../Thermo/DataHandling/MsScanExtensions.cs | 3 +- .../DataHandling/ThermoTribridSsdoMapping.cs | 6 +- .../InterfaceImplementations/ThermoQE.cs | 69 ++++++++++++++- .../InterfaceImplementations/ThermoTribrid.cs | 25 +++--- .../ThermoTribridScanTranslator.cs | 49 +++++++++-- Client/MsScanReadyToSendEventArgs.cs | 2 +- Client/Program.cs | 3 +- Client/ScanInstructionEventArgs.cs | 4 +- Client/packages.config | 1 + .../ClientServerCommLibrary.csproj | 1 + .../SingleScanDataObject.cs | 4 +- .../SingleScanDataObjectExtensions.cs | 1 - .../InstrumentClientTests.csproj | 24 +++++ InstrumentClientTests/UnitTest1.cs | 87 +++++++++++++++++++ InstrumentClientTests/Usings.cs | 1 + InstrumentControl.sln | 6 ++ ProcessStarter/ProcessStarter.csproj | 4 + 22 files changed, 275 insertions(+), 37 deletions(-) create mode 100644 InstrumentClientTests/InstrumentClientTests.csproj create mode 100644 InstrumentClientTests/UnitTest1.cs create mode 100644 InstrumentClientTests/Usings.cs diff --git a/ApplicationServer/ScanQueue/ScanQueue.cs b/ApplicationServer/ScanQueue/ScanQueue.cs index 3c887ab..23fc130 100644 --- a/ApplicationServer/ScanQueue/ScanQueue.cs +++ b/ApplicationServer/ScanQueue/ScanQueue.cs @@ -1,4 +1,5 @@ -using Data; +using ClientServerCommLibrary; + namespace ApplicationServer { diff --git a/ApplicationServer/ScanQueue/ThresholdReachedEventArgs.cs b/ApplicationServer/ScanQueue/ThresholdReachedEventArgs.cs index 658e006..76d4b7f 100644 --- a/ApplicationServer/ScanQueue/ThresholdReachedEventArgs.cs +++ b/ApplicationServer/ScanQueue/ThresholdReachedEventArgs.cs @@ -1,4 +1,4 @@ -using Data; +using ClientServerCommLibrary; namespace ApplicationServer { diff --git a/ApplicationServer/WorkflowServer.csproj b/ApplicationServer/WorkflowServer.csproj index 65865a5..e205882 100644 --- a/ApplicationServer/WorkflowServer.csproj +++ b/ApplicationServer/WorkflowServer.csproj @@ -9,6 +9,7 @@ + diff --git a/Client/IScanTranslator.cs b/Client/IScanTranslator.cs index ac7f10f..f793add 100644 --- a/Client/IScanTranslator.cs +++ b/Client/IScanTranslator.cs @@ -7,8 +7,8 @@ namespace Client { - public interface IScanTranslator + public abstract class ScanTranslator { - T Translate(SingleScanDataObject ssdo) where T: new(); + public abstract void Translate(SingleScanDataObject ssdo); } } diff --git a/Client/InstrumentClient.csproj b/Client/InstrumentClient.csproj index 8c9289a..affd4ff 100644 --- a/Client/InstrumentClient.csproj +++ b/Client/InstrumentClient.csproj @@ -1,5 +1,6 @@  + Debug @@ -12,6 +13,8 @@ 512 true true + + x64 @@ -47,6 +50,9 @@ ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + ..\packages\NUnit.3.13.3\lib\net45\nunit.framework.dll + False ..\..\..\..\..\..\Thermo\Instruments\TNG\OrbitrapFusionLumos\3.3\System\Programs\Spectrum-1.0.dll @@ -97,4 +103,10 @@ + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/Client/InstrumentInterfaces/Thermo/DataHandling/MsScanExtensions.cs b/Client/InstrumentInterfaces/Thermo/DataHandling/MsScanExtensions.cs index 8ded991..968763d 100644 --- a/Client/InstrumentInterfaces/Thermo/DataHandling/MsScanExtensions.cs +++ b/Client/InstrumentInterfaces/Thermo/DataHandling/MsScanExtensions.cs @@ -79,8 +79,7 @@ public static SingleScanDataObject ConvertToSingleScanDataObject(this IMsScan sc YArray = scan.Centroids.Select(c => c.Intensity).ToArray(), TotalIonCurrent = 1E6, MinX = scan.GetValueFromHeaderDict("FirstMass"), - MaxX = scan.GetValueFromHeaderDict("LastMass"), - Resolution = scan.GetValueFromHeaderDict("") + MaxX = scan.GetValueFromHeaderDict("LastMass") }; return sso; } diff --git a/Client/InstrumentInterfaces/Thermo/DataHandling/ThermoTribridSsdoMapping.cs b/Client/InstrumentInterfaces/Thermo/DataHandling/ThermoTribridSsdoMapping.cs index 7722e2d..e3849af 100644 --- a/Client/InstrumentInterfaces/Thermo/DataHandling/ThermoTribridSsdoMapping.cs +++ b/Client/InstrumentInterfaces/Thermo/DataHandling/ThermoTribridSsdoMapping.cs @@ -4,15 +4,15 @@ using System.Text; using System.Threading.Tasks; -namespace Client.InstrumentInterfaces.Thermo.DataHandling +namespace Client { public static class ThermoTribridSsdoMapping { public static readonly Dictionary TrbridToSsdoMapping = new Dictionary { // key = ssdo; value = Tribrid - {"MinX", "FirstMass"}, - {"MaxX", "LastMass"}, + {"FirstMass", "FirstMass"}, + {"LastMass", "LastMass"}, {"AnalyzerType", "Analyzer"}, {"ScanType", "ScanType"}, {"SourceEnergy", "SourceCIDEnergy"}, diff --git a/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoQE.cs b/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoQE.cs index 3ea819a..4ac51a9 100644 --- a/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoQE.cs +++ b/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoQE.cs @@ -3,7 +3,8 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using ClientServerCommunication; +using ClientServerCommLibrary; + namespace InstrumentClient { @@ -11,6 +12,16 @@ internal class ThermoQE : IInstrument { public ClientPipe PipeClient { get; set; } + public SingleScanDataObject GetLastScan() + { + throw new NotImplementedException(); + } + + public void SendScanAction(SingleScanDataObject ssdo) + { + throw new NotImplementedException(); + } + public void OpenInstrumentConnection() { throw new System.NotSupportedException(); @@ -21,6 +32,62 @@ public void CloseInstrumentConnection() throw new NotSupportedException(); } + public string GetSystemState(int stateOrMode) + { + throw new NotImplementedException(); + } + + public void CancelAcquisition() + { + throw new NotImplementedException(); + } + + public void PauseAcquisition() + { + throw new NotImplementedException(); + } + + public void ResumeAcquisition() + { + throw new NotImplementedException(); + } + + public void StartAcquisition(string rawFileName) + { + throw new NotImplementedException(); + } + + public void StartMethodAcquistion(string methodFilePath, string methodName, string outputFileName, string sampleName, + double timeInMinutes) + { + throw new NotImplementedException(); + } + + public void InstrumentOn() + { + throw new NotImplementedException(); + } + + public void InstrumentOff() + { + throw new NotImplementedException(); + } + + public void InstrumentStandby() + { + throw new NotImplementedException(); + } + + public void GetScanPossibleParameters() + { + throw new NotImplementedException(); + } + + public event EventHandler InstrumentConnected; + public event EventHandler InstrumentDisconnected; + public event EventHandler ScanReceived; + public event EventHandler InstrumentReadyToReceiveScan; + public void MsScanReadyToSend(MsScanReadyToSendEventArgs scanEventArgs) { throw new NotImplementedException(); diff --git a/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs b/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs index 265551a..c4b6d15 100644 --- a/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs +++ b/Client/InstrumentInterfaces/Thermo/InterfaceImplementations/ThermoTribrid.cs @@ -54,18 +54,18 @@ public SingleScanDataObject GetLastScan() public void SendScanAction(SingleScanDataObject ssdo) { - if (ssdo.ScanType == null) + if (ssdo.ScanInstructions.ScanType == null) throw new ArgumentException("ScanAction type is invalid! (property was null)"); - switch (ssdo.ScanType) - { - case (ScanType.CustomScan): - SendRepeatingScan(); - break; - case (ScanType.RepeatingScan): - SendCustomScan(); - break; - } + //switch (ssdo.ScanInstructions.ScanType) + ////{ + //// case (ScanInstructions.CustomScan): + //// SendRepeatingScan(); + //// break; + //// case (ScanType.RepeatingScan): + //// SendCustomScan(); + //// break; + //} } private IDictionary SsdoToDictionary(SingleScanDataObject ssdo) @@ -270,6 +270,11 @@ public void InstrumentStandby() InstAcq.SetMode(sbMode); } + public void GetScanPossibleParameters() + { + throw new NotImplementedException(); + } + public event EventHandler InstrumentConnected; public event EventHandler InstrumentDisconnected; public event EventHandler ScanReceived; diff --git a/Client/InstrumentInterfaces/Thermo/ThermoScanTranslator/ThermoTribridScanTranslator.cs b/Client/InstrumentInterfaces/Thermo/ThermoScanTranslator/ThermoTribridScanTranslator.cs index 95b7175..6b5fe54 100644 --- a/Client/InstrumentInterfaces/Thermo/ThermoScanTranslator/ThermoTribridScanTranslator.cs +++ b/Client/InstrumentInterfaces/Thermo/ThermoScanTranslator/ThermoTribridScanTranslator.cs @@ -1,27 +1,58 @@ using System; +using System.CodeDom; using System.Collections.Generic; using System.Linq; +using System.Reflection; +using System.Reflection.Emit; using System.Text; using System.Threading.Tasks; using ClientServerCommLibrary; namespace Client { - public class ThermoTribridScanTranslator : IScanTranslator + public class ThermoTribridScanTranslator : ScanTranslator { - public T Translate(SingleScanDataObject ssdo) where T : new() + public Dictionary InstrumentCompatibleScanInstructions { get; private set; } + public override void Translate(SingleScanDataObject ssdo) { - if (typeof(T) != typeof(Dictionary)) - { - throw new ArgumentException("Invalid return object type for Thermo Tribrid instrument class!"); - } - - + throw new NotImplementedException(); } - private Dictionary TranslateSsdo(SingleScanDataObject ssdo) { + var result = new Dictionary(); + PropertyInfo[] properties = typeof(ScanInstructions).GetProperties(); + + foreach (PropertyInfo property in properties) + { + // set result key by matching the property name to the constant dictionary key + // and returning the value. + string newKey = ThermoTribridSsdoMapping.TrbridToSsdoMapping[property.Name]; + var propType = property.GetType(); + string valConvertToString = String.Empty; + + switch (propType.Name) + { + case nameof(Double): + break; + case nameof(Int32): + break; + case nameof(Enum): + break; + } + } + + return result; + } + private static string GetEnumString(T enumOfType) where T : Enum + { + var enumType = typeof(T); + if (enumType == typeof(OrbitrapResolution)) + { + var temp = (int)Enum.Parse(typeof(OrbitrapResolution), enumOfType.ToString()); + return temp.ToString(); + } + return Enum.Parse(typeof(T), enumOfType.ToString()).ToString(); } } } diff --git a/Client/MsScanReadyToSendEventArgs.cs b/Client/MsScanReadyToSendEventArgs.cs index 38024a3..c61ead5 100644 --- a/Client/MsScanReadyToSendEventArgs.cs +++ b/Client/MsScanReadyToSendEventArgs.cs @@ -3,8 +3,8 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Data; using System.Runtime; +using ClientServerCommLibrary; namespace InstrumentClient { diff --git a/Client/Program.cs b/Client/Program.cs index e27bd5e..296f60e 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -6,8 +6,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using ClientServerCommunication; -using Data; +using ClientServerCommLibrary; namespace InstrumentClient { diff --git a/Client/ScanInstructionEventArgs.cs b/Client/ScanInstructionEventArgs.cs index ba2cf72..da51bcd 100644 --- a/Client/ScanInstructionEventArgs.cs +++ b/Client/ScanInstructionEventArgs.cs @@ -3,13 +3,13 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Data; +using ClientServerCommLibrary; namespace InstrumentClient { public class ScanInstructionsEventArgs : EventArgs { - public Data.ScanInstructions ScanInstructions { get; set; } + public ScanInstructions ScanInstructions { get; set; } public ScanInstructionsEventArgs(ScanInstructions scanInstructions) { ScanInstructions = scanInstructions; diff --git a/Client/packages.config b/Client/packages.config index bc2a97e..d3acfa9 100644 --- a/Client/packages.config +++ b/Client/packages.config @@ -1,4 +1,5 @@  + \ No newline at end of file diff --git a/ClientServerCommunication/ClientServerCommLibrary.csproj b/ClientServerCommunication/ClientServerCommLibrary.csproj index e8ad1a1..ac29bec 100644 --- a/ClientServerCommunication/ClientServerCommLibrary.csproj +++ b/ClientServerCommunication/ClientServerCommLibrary.csproj @@ -6,6 +6,7 @@ + diff --git a/ClientServerCommunication/SingleScanDataObject.cs b/ClientServerCommunication/SingleScanDataObject.cs index f454b0c..1a4e2cd 100644 --- a/ClientServerCommunication/SingleScanDataObject.cs +++ b/ClientServerCommunication/SingleScanDataObject.cs @@ -20,7 +20,7 @@ public class SingleScanDataObject public double TotalIonCurrent { get; set; } public double MinX { get; set; } public double MaxX { get; set; } - public ScanInstructions ScanInstructions { get; private set; } + public ScanInstructions ScanInstructions { get; set; } public SingleScanDataObject() { @@ -58,7 +58,7 @@ private void SetScanInstructions(ScanInstructions si) { ScanInstructions = si; } - // TODO: Fill out the method. + // TODO: Check that the required fields are correctly set. Will probably require a static dictionary to check against. private bool ValidateScanInstructionsForInstrument(ScanInstructions si, string instrument) { return true; diff --git a/ClientServerCommunication/SingleScanDataObjectExtensions.cs b/ClientServerCommunication/SingleScanDataObjectExtensions.cs index 1209b49..6651347 100644 --- a/ClientServerCommunication/SingleScanDataObjectExtensions.cs +++ b/ClientServerCommunication/SingleScanDataObjectExtensions.cs @@ -1,5 +1,4 @@ using System.Text; -using Data; using Newtonsoft.Json; diff --git a/InstrumentClientTests/InstrumentClientTests.csproj b/InstrumentClientTests/InstrumentClientTests.csproj new file mode 100644 index 0000000..aea379a --- /dev/null +++ b/InstrumentClientTests/InstrumentClientTests.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + enable + enable + + false + + + + + + + + + + + + + + + + diff --git a/InstrumentClientTests/UnitTest1.cs b/InstrumentClientTests/UnitTest1.cs new file mode 100644 index 0000000..a38b612 --- /dev/null +++ b/InstrumentClientTests/UnitTest1.cs @@ -0,0 +1,87 @@ +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using InstrumentClient; +using ClientServerCommLibrary; +namespace InstrumentClientTests +{ + public class Tests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void TestGetEnum() + { + var orbiResTest = OrbitrapResolution.X_120000; + var expected = 120000; + string result = String.Empty; + var enumType = typeof(OrbitrapResolution); + if (enumType == typeof(OrbitrapResolution)) + { + var temp = (int)Enum.Parse(typeof(OrbitrapResolution), orbiResTest.ToString()); + result = temp.ToString(); + } + + Assert.That(expected.ToString(), Is.EqualTo(result)); + } + + [Test] + [TestCase(AnalyzerType.IonTrap, "IonTrap")] + [TestCase(ScanType.Full, "Full")] + [TestCase(Polarity.Negative, "Negative")] + [TestCase(DataType.Centroid, "Centroid")] + [TestCase(IsolationType.IonTrap, "IonTrap")] + [TestCase(OrbitrapResolution.X_30000, "X_30000")] + [TestCase(IonTrapScanRate.Enhanced, "Enhanced")] + [TestCase(ActivationType.ETD, "ETD")] + public void TestGetEnumPart2(T enumTest, object expectedVal) + { + var result = Enum.Parse(typeof(T), enumTest.ToString()).ToString(); + Assert.That(result, Is.EqualTo(expectedVal)); + + } + + [Test] + [TestCase(AnalyzerType.IonTrap, "IonTrap")] + [TestCase(ScanType.Full, "Full")] + [TestCase(Polarity.Negative, "Negative")] + [TestCase(DataType.Centroid, "Centroid")] + [TestCase(IsolationType.IonTrap, "IonTrap")] + [TestCase(OrbitrapResolution.X_30000, "30000")] + [TestCase(IonTrapScanRate.Enhanced, "Enhanced")] + [TestCase(ActivationType.ETD, "ETD")] + public void TestGetEnumString(T enumTest, object expectedVal) where T: Enum + { + var result = GetEnumString(enumTest); + Assert.That(result, Is.EqualTo(expectedVal)); + } + + private static string GetEnumString(T enumOfType) where T : Enum + { + var enumType = typeof(T); + if (enumType == typeof(OrbitrapResolution)) + { + var temp = (int)Enum.Parse(typeof(OrbitrapResolution), enumOfType.ToString()); + return temp.ToString(); + } + return Enum.Parse(typeof(T), enumOfType.ToString()).ToString(); + } + + [Test] + public void TestTranslateSsdo() + { + SingleScanDataObject ssdo = new SingleScanDataObject(); + ScanInstructions si = new ScanInstructions(); + ssdo.ScanInstructions = si; + ssdo.ScanInstructions.ScanType = ScanType.Full; + + PropertyInfo[] properties = typeof(ScanInstructions).GetProperties(); + foreach(PropertyInfo property in properties) + { + var result = property.GetValue(ssdo.ScanInstructions); + } + } + } +} \ No newline at end of file diff --git a/InstrumentClientTests/Usings.cs b/InstrumentClientTests/Usings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/InstrumentClientTests/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/InstrumentControl.sln b/InstrumentControl.sln index 0d0e7c7..2a4a484 100644 --- a/InstrumentControl.sln +++ b/InstrumentControl.sln @@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProcessStarter", "ProcessSt EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InstrumentClient", "Client\InstrumentClient.csproj", "{2920D894-D01A-44AE-B2B7-EB7626782147}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InstrumentClientTests", "InstrumentClientTests\InstrumentClientTests.csproj", "{94F6C9E6-402B-4E6A-98FC-4AB817EE0453}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,10 @@ Global {2920D894-D01A-44AE-B2B7-EB7626782147}.Debug|Any CPU.Build.0 = Debug|Any CPU {2920D894-D01A-44AE-B2B7-EB7626782147}.Release|Any CPU.ActiveCfg = Release|Any CPU {2920D894-D01A-44AE-B2B7-EB7626782147}.Release|Any CPU.Build.0 = Release|Any CPU + {94F6C9E6-402B-4E6A-98FC-4AB817EE0453}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94F6C9E6-402B-4E6A-98FC-4AB817EE0453}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94F6C9E6-402B-4E6A-98FC-4AB817EE0453}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94F6C9E6-402B-4E6A-98FC-4AB817EE0453}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ProcessStarter/ProcessStarter.csproj b/ProcessStarter/ProcessStarter.csproj index 74abf5c..1aa4277 100644 --- a/ProcessStarter/ProcessStarter.csproj +++ b/ProcessStarter/ProcessStarter.csproj @@ -7,4 +7,8 @@ enable + + + + From 9b74e98b58aa660f5193aa2c6057e5d76d296623 Mon Sep 17 00:00:00 2001 From: Nic Bollis Date: Mon, 10 Oct 2022 10:58:25 -0500 Subject: [PATCH 4/6] Changes occuring during live testing --- ApplicationServer/AppServerPipe.cs | 75 ++++++++----------- ApplicationServer/Program.cs | 31 ++++++++ .../Properties/launchSettings.json | 3 +- Client/InstrumentClientPipe.cs | 26 ++++--- Client/Program.cs | 9 ++- ClientServerCommunication/SsdoExtensions.cs | 31 ++++++++ ProcessStarter/Program.cs | 3 +- 7 files changed, 117 insertions(+), 61 deletions(-) create mode 100644 ClientServerCommunication/SsdoExtensions.cs diff --git a/ApplicationServer/AppServerPipe.cs b/ApplicationServer/AppServerPipe.cs index 561c689..492cdf9 100644 --- a/ApplicationServer/AppServerPipe.cs +++ b/ApplicationServer/AppServerPipe.cs @@ -23,8 +23,6 @@ internal class AppServerPipe private ProcessMs2ScansDelegate? Ms2Delegate { get; set; } // static class with processing workflows? public AppServerPipe(NamedPipeServerStream pipeServer, - StreamWriter sw, - StreamReader sr, int ms1ScanQueueThreshold, int ms2ScanQueueThreshold, ProcessMs1ScansDelegate ms1Delegate = null, @@ -46,63 +44,55 @@ public void StartServer() Console.WriteLine("Pipe client connected. Sent from event."); }; // delegate for processing needs to be used as a function. - ProcessMs1ScansDelegate ms1Del = (o, scans) => - { - // select highest m/z from the scans and send a singlescandataobject back to client - List mzPrecursors = new(); - foreach (var sc in scans.ListSsdo) - { - double max = sc.YArray.Max(); - int posX = Array.IndexOf(sc.YArray, max); - mzPrecursors.Add(sc.XArray[posX]); - } + //ProcessMs1ScansDelegate ms1Del = (o, scans) => + //{ + // // select highest m/z from the scans and send a singlescandataobject back to client + // List mzPrecursors = new(); + // foreach (var sc in scans.ListSsdo) + // { + // double max = sc.YArray.Max(); + // int posX = Array.IndexOf(sc.YArray, max); + // mzPrecursors.Add(sc.XArray[posX]); + // } - foreach (var mz in mzPrecursors) - { - SingleScanDataObject ssdoTemp = new() - { - ScanOrder = 2, - ScanNumber = 10, - PrecursorScanNumber = 3, - MzPrecursor = 15, - XArray = new double[] { 0, 0 }, - YArray = new double[] { 0, 0 } - }; - string temp = JsonConvert.SerializeObject(ssdoTemp); - byte[] buffer = Encoding.UTF8.GetBytes(temp); - byte[] length = BitConverter.GetBytes(buffer.Length); - byte[] finalBuffer = length.Concat(buffer).ToArray(); - PipeServer.Write(finalBuffer, 0, finalBuffer.Length); - } + // foreach (var mz in mzPrecursors) + // { + // SingleScanDataObject ssdoTemp = new() + // { + // ScanOrder = 2, + // ScanNumber = 10, + // PrecursorScanNumber = 3, + // MzPrecursor = 15, + // XArray = new double[] { 0, 0 }, + // YArray = new double[] { 0, 0 } + // }; + // string temp = JsonConvert.SerializeObject(ssdoTemp); + // byte[] buffer = Encoding.UTF8.GetBytes(temp); + // byte[] length = BitConverter.GetBytes(buffer.Length); + // byte[] finalBuffer = length.Concat(buffer).ToArray(); + // PipeServer.Write(finalBuffer, 0, finalBuffer.Length); + // } - }; + //}; PipeDataReceived += HandleDataReceived; - Ms1ScanQueueThresholdReached += ms1Del.Invoke; + Ms1ScanQueueThresholdReached += Ms1Delegate.Invoke; Ms2ScanQueueThresholdReached += Ms2Delegate.Invoke; Ms1ProcessingCompleted += (object? obj, ProcessingCompletedEventArgs sender) => { - + Console.WriteLine("Ms1 Processing Completed"); }; Ms2ProcessingCompleted += (object? obj, ProcessingCompletedEventArgs sender) => { - byte[] buffer = Encoding.UTF8.GetBytes("15"); - var taskResult = new ValueTask(); - taskResult = PipeServer.WriteAsync(buffer); - buffer = null; + Console.WriteLine("Ms2 Processing Completed."); }; var connectionResult = PipeServer.BeginWaitForConnection(Connected, null); // wait for the connection to occur before proceeding. connectionResult.AsyncWaitHandle.WaitOne(); connectionResult.AsyncWaitHandle.Close(); - var serializer = new JsonSerializer(); StartReaderAsync(); - while (PipeServer.IsConnected) - { - - } } private void HandleDataReceived(object? obj, PipeEventArgs eventArgs) { @@ -146,12 +136,10 @@ private void Connected(IAsyncResult ar) OnConnection(); PipeServer.EndWaitForConnection(ar); } - private void OnConnection() { PipeConnected?.Invoke(this, EventArgs.Empty); } - private void StartByteReaderAsync(Action packetReceived) { byte[] byteDataLength = new byte[sizeof(int)]; @@ -171,7 +159,6 @@ private void StartByteReaderAsync(Action packetReceived) }); }); } - public void StartReaderAsync() { StartByteReaderAsync((b) => diff --git a/ApplicationServer/Program.cs b/ApplicationServer/Program.cs index 7b3d0d7..5d871fc 100644 --- a/ApplicationServer/Program.cs +++ b/ApplicationServer/Program.cs @@ -3,6 +3,7 @@ using InstrumentControl; using Newtonsoft; using Newtonsoft.Json; +using WorkflowServer; namespace ApplicationServer { @@ -10,7 +11,37 @@ public class Program { public static void Main(string[] args) { + NamedPipeServerStream pipe = new NamedPipeServerStream("test", PipeDirection.InOut,5, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + ProcessMs1ScansDelegate ms1Del = (obj, ev) => + { + Console.WriteLine(ev.ListSsdo.First().MaxX); + }; + ProcessMs2ScansDelegate ms2Del = (obj, ev) => + { + Console.WriteLine(ev.ListSsdo.First().PrecursorScanNumber); + }; + AppServerPipe appPipe = new(pipe, 1, 1, ms1Del, ms2Del); + + bool connectedBool = false; + appPipe.PipeConnected += (obj, ev) => + { + connectedBool = true; + }; + appPipe.PipeDataReceived += (obj, ev) => + { + var scan = ev.ToSingleScanDataObject(); + Console.WriteLine(scan.MaxX); + }; + appPipe.StartServer(); + while (connectedBool) + { + appPipe.PipeDataReceived += (obj, ev) => + { + var scan = ev.ToSingleScanDataObject(); + Console.WriteLine(scan.MaxX.ToString()); + }; + } } } } \ No newline at end of file diff --git a/ApplicationServer/Properties/launchSettings.json b/ApplicationServer/Properties/launchSettings.json index 5570a64..9c95913 100644 --- a/ApplicationServer/Properties/launchSettings.json +++ b/ApplicationServer/Properties/launchSettings.json @@ -1,8 +1,7 @@ { "profiles": { "ApplicationServer": { - "commandName": "Project", - "commandLineArgs": "simple Server" + "commandName": "Project" } } } \ No newline at end of file diff --git a/Client/InstrumentClientPipe.cs b/Client/InstrumentClientPipe.cs index 778ceb4..5f10e9f 100644 --- a/Client/InstrumentClientPipe.cs +++ b/Client/InstrumentClientPipe.cs @@ -6,7 +6,7 @@ using ClientServerCommLibrary; using System.IO.Pipes; using Microsoft.Win32; -using Newtonsoft.Json; + namespace InstrumentClient @@ -102,6 +102,7 @@ public void BeginInstrumentConnection(IInstrument instr) { bool instrReadyToReceiveScan = false; instr.OpenInstrumentConnection(); + instr.InstrumentConnected += (obj, sender) => { InstrumentConnectedBool = true; }; instr.InstrumentDisconnected += (obj, sender) => { InstrumentConnectedBool = false; }; instr.InstrumentReadyToReceiveScan += (obj, sender) => @@ -115,28 +116,29 @@ public void BeginInstrumentConnection(IInstrument instr) instr.ScanReceived += (obj, sender) => { // convert to byte[] and send to WorkflowServer. - byte[] buffer = SerializeAndCreateBuffer(sender.Ssdo); + Console.WriteLine("Scan received event activated"); + // need to create the correct buffer format, which is length is the first n bytes, + // followed by the actual data. + byte[] buffer = sender.Ssdo.CreateSerializedSingleScanDataObject(); PipeClient.WriteAsync(buffer, 0, buffer.Length); + PipeClient.WaitForPipeDrain(); }; - + // don't think I need this event after all. ScanQueueThresholdReached += (obj, sender) => { // send the scan to the instrument }; - // enter instrument main routine: - while (InstrumentConnectedBool) - { + //// enter instrument main routine: + + - } } #endregion - private byte[] SerializeAndCreateBuffer(T obj) - { - string jsonString = JsonConvert.SerializeObject(obj); - return Encoding.UTF8.GetBytes(jsonString); - } + + + } } diff --git a/Client/Program.cs b/Client/Program.cs index 296f60e..a5b1f0c 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -21,7 +21,7 @@ public static void Main(string[] args) ClientPipe clientPipe = new ClientPipe(pipeClient); - string instrumentType = args[0]; + string instrumentType = "tribrid"; IInstrumentFactory factory = null; switch (instrumentType) @@ -40,6 +40,10 @@ public static void Main(string[] args) { clientPipe.ConnectClientToServer(); clientPipe.BeginInstrumentConnection(instrumentApi); + while (true) + { + + } } catch (Exception e) { @@ -48,8 +52,9 @@ public static void Main(string[] args) } finally { - pipeClient.Dispose(); + } + pipeClient.Close(); } diff --git a/ClientServerCommunication/SsdoExtensions.cs b/ClientServerCommunication/SsdoExtensions.cs new file mode 100644 index 0000000..3428ecd --- /dev/null +++ b/ClientServerCommunication/SsdoExtensions.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace ClientServerCommLibrary +{ + public static class SsdoExtensions + { + public static byte[] CreateSerializedSingleScanDataObject(this SingleScanDataObject ssdo) + { + byte[] buffer = SerializeAndCreateBuffer(ssdo); + int buffLength = buffer.Length; + byte[] lenghtInByteFormat = BitConverter.GetBytes(buffLength); + + // combine the two arrays + byte[] resultBuffer = new byte[lenghtInByteFormat.Length + buffLength]; + Buffer.BlockCopy(lenghtInByteFormat, 0, resultBuffer, 0, lenghtInByteFormat.Length); + Buffer.BlockCopy(buffer, 0, resultBuffer, lenghtInByteFormat.Length, buffLength); + return resultBuffer; + } + private static byte[] SerializeAndCreateBuffer(T obj) + { + string jsonString = JsonConvert.SerializeObject(obj); + return Encoding.UTF8.GetBytes(jsonString); + } + } + +} diff --git a/ProcessStarter/Program.cs b/ProcessStarter/Program.cs index 1688821..f55ef0a 100644 --- a/ProcessStarter/Program.cs +++ b/ProcessStarter/Program.cs @@ -10,7 +10,7 @@ static void Main(string[] args) //string clientPath = args[1]; string serverCommands = "Server"; - string clientCommands = ". Server tribrid"; + string clientCommands = @". Server tribrid"; ProcessStartInfo serverProcessStartInfo = new( @"C:\Users\Orbitrap Lumos\source\repos\InstrumentControl\ApplicationServer\bin\Debug\net6.0\ApplicationServer.exe") { @@ -25,6 +25,7 @@ static void Main(string[] args) Process.Start(serverProcessStartInfo); Process.Start(clientProcessStartInfo); Console.ReadLine(); + } } } \ No newline at end of file From f84711085bc3d68d1ace6bf3c83b8afb188b7c1f Mon Sep 17 00:00:00 2001 From: Nic Bollis Date: Mon, 10 Oct 2022 11:49:05 -0500 Subject: [PATCH 5/6] Some more fixes and testing --- ApplicationServer/Program.cs | 17 +++++++++-------- Client/Program.cs | 30 ++++++++++++++++++++++++++++-- ProcessStarter/Program.cs | 6 +++--- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/ApplicationServer/Program.cs b/ApplicationServer/Program.cs index 5d871fc..e94f88c 100644 --- a/ApplicationServer/Program.cs +++ b/ApplicationServer/Program.cs @@ -11,15 +11,16 @@ public class Program { public static void Main(string[] args) { - NamedPipeServerStream pipe = new NamedPipeServerStream("test", PipeDirection.InOut,5, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + NamedPipeServerStream pipe = new NamedPipeServerStream("test", PipeDirection.InOut,5, + PipeTransmissionMode.Byte, PipeOptions.Asynchronous); ProcessMs1ScansDelegate ms1Del = (obj, ev) => { - Console.WriteLine(ev.ListSsdo.First().MaxX); + //Console.WriteLine(ev.ListSsdo.First().MaxX); }; ProcessMs2ScansDelegate ms2Del = (obj, ev) => { - Console.WriteLine(ev.ListSsdo.First().PrecursorScanNumber); + //Console.WriteLine(ev.ListSsdo.First().PrecursorScanNumber); }; AppServerPipe appPipe = new(pipe, 1, 1, ms1Del, ms2Del); @@ -36,11 +37,11 @@ public static void Main(string[] args) appPipe.StartServer(); while (connectedBool) { - appPipe.PipeDataReceived += (obj, ev) => - { - var scan = ev.ToSingleScanDataObject(); - Console.WriteLine(scan.MaxX.ToString()); - }; + //appPipe.PipeDataReceived += (obj, ev) => + //{ + // var scan = ev.ToSingleScanDataObject(); + // Console.WriteLine(scan.MaxX.ToString()); + //}; } } } diff --git a/Client/Program.cs b/Client/Program.cs index a5b1f0c..8b07136 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -38,11 +38,37 @@ public static void Main(string[] args) try { + System.Timers.Timer timer = new System.Timers.Timer(); + int i = 0; + clientPipe.ConnectClientToServer(); clientPipe.BeginInstrumentConnection(instrumentApi); - while (true) - { + Console.WriteLine(instrumentApi.GetSystemState(1)); + while (i < 6) + { + switch (i) + { + case 0: + instrumentApi.StartAcquisition("test.raw"); + break; + case 1: + instrumentApi.PauseAcquisition(); + break; + case 2: + instrumentApi.CancelAcquisition(); + break; + case 3: + instrumentApi.InstrumentStandby(); + break; + case 4: + break; + default: + Console.WriteLine("Test completed"); + break; + } + Console.WriteLine(instrumentApi.GetSystemState(1) + "\n"); + i++; } } catch (Exception e) diff --git a/ProcessStarter/Program.cs b/ProcessStarter/Program.cs index f55ef0a..b0736b1 100644 --- a/ProcessStarter/Program.cs +++ b/ProcessStarter/Program.cs @@ -9,10 +9,10 @@ static void Main(string[] args) //string serverPath = args[0]; //string clientPath = args[1]; - string serverCommands = "Server"; - string clientCommands = @". Server tribrid"; + string serverCommands = @""; + string clientCommands = @"tribrid"; ProcessStartInfo serverProcessStartInfo = new( - @"C:\Users\Orbitrap Lumos\source\repos\InstrumentControl\ApplicationServer\bin\Debug\net6.0\ApplicationServer.exe") + @"C:\Users\Orbitrap Lumos\source\repos\InstrumentControl\ApplicationServer\bin\Debug\net6.0\WorkflowServer.exe") { Arguments = serverCommands }; From fe0b4b0ad59ec8a151813568e7e3ec0c563ae129 Mon Sep 17 00:00:00 2001 From: avcarr2 <64652734+avcarr2@users.noreply.github.com> Date: Mon, 24 Oct 2022 15:23:39 -0500 Subject: [PATCH 6/6] Clean up some methods. Added documentation to some methods --- ApplicationServer/AppServerPipe.cs | 76 +++++++++++-------- Client/InstrumentClientPipe.cs | 35 ++------- Client/Program.cs | 72 +----------------- ClientServerCommunication/PipeEventArgs.cs | 3 + .../ProcessingCompletedEventArgs.cs | 6 +- .../ScanQueueThresholdReachedEventArgs.cs | 3 + 6 files changed, 64 insertions(+), 131 deletions(-) diff --git a/ApplicationServer/AppServerPipe.cs b/ApplicationServer/AppServerPipe.cs index 492cdf9..f03f639 100644 --- a/ApplicationServer/AppServerPipe.cs +++ b/ApplicationServer/AppServerPipe.cs @@ -6,6 +6,9 @@ namespace WorkflowServer { + /// + /// Class is used to communicate and run processing workflows on data received from the client. + /// internal class AppServerPipe { public event EventHandler PipeConnected; @@ -37,44 +40,16 @@ public AppServerPipe(NamedPipeServerStream pipeServer, Ms2Delegate = ms2Delegate; } + /// + /// Begins the server connection; starts the async buffer reader; connects event handler methods to events. + /// public void StartServer() { PipeConnected += (obj, sender) => { Console.WriteLine("Pipe client connected. Sent from event."); }; - // delegate for processing needs to be used as a function. - //ProcessMs1ScansDelegate ms1Del = (o, scans) => - //{ - // // select highest m/z from the scans and send a singlescandataobject back to client - // List mzPrecursors = new(); - // foreach (var sc in scans.ListSsdo) - // { - // double max = sc.YArray.Max(); - // int posX = Array.IndexOf(sc.YArray, max); - // mzPrecursors.Add(sc.XArray[posX]); - // } - - // foreach (var mz in mzPrecursors) - // { - // SingleScanDataObject ssdoTemp = new() - // { - // ScanOrder = 2, - // ScanNumber = 10, - // PrecursorScanNumber = 3, - // MzPrecursor = 15, - // XArray = new double[] { 0, 0 }, - // YArray = new double[] { 0, 0 } - // }; - // string temp = JsonConvert.SerializeObject(ssdoTemp); - // byte[] buffer = Encoding.UTF8.GetBytes(temp); - // byte[] length = BitConverter.GetBytes(buffer.Length); - // byte[] finalBuffer = length.Concat(buffer).ToArray(); - // PipeServer.Write(finalBuffer, 0, finalBuffer.Length); - // } - - //}; - + PipeDataReceived += HandleDataReceived; Ms1ScanQueueThresholdReached += Ms1Delegate.Invoke; Ms2ScanQueueThresholdReached += Ms2Delegate.Invoke; @@ -85,6 +60,7 @@ public void StartServer() }; Ms2ProcessingCompleted += (object? obj, ProcessingCompletedEventArgs sender) => { + Console.WriteLine("Ms2 Processing Completed."); }; @@ -94,6 +70,12 @@ public void StartServer() connectionResult.AsyncWaitHandle.Close(); StartReaderAsync(); } + /// + /// Used to determine which queue scan that was received from server should go into. + /// + /// + /// + /// private void HandleDataReceived(object? obj, PipeEventArgs eventArgs) { // convert pipeeventargs to single scan data object @@ -121,16 +103,28 @@ private void HandleDataReceived(object? obj, PipeEventArgs eventArgs) } // TODO: Handle scan order > 2. Console.WriteLine("\n"); } + /// + /// Triggers event that begins processing of Ms1 queue. + /// + /// private void OnMs1QueueThresholdReached(Queue queue) { Ms1ScanQueueThresholdReached?.Invoke(this, new ScanQueueThresholdReachedEventArgs(queue.DequeueChunk(Ms1ScanQueueThreshold))); } + /// + /// Triggers event that begins processing of Ms2 queue. + /// + /// private void OnMs2QueueThresholdReached(Queue queue) { Ms2ScanQueueThresholdReached?.Invoke(this, new ScanQueueThresholdReachedEventArgs(queue.DequeueChunk(Ms2ScanQueueThreshold))); } + /// + /// Callback method used to wait for client pipe connection. + /// + /// private void Connected(IAsyncResult ar) { OnConnection(); @@ -140,6 +134,14 @@ private void OnConnection() { PipeConnected?.Invoke(this, EventArgs.Empty); } + /// + /// Begins asynchronous buffer reading. Converts the first four bytes into the integer size of the object contained in the rest of the buffer. + /// Then creates the buffer of that size and reads the data into the buffer. + /// + /// DO NOT MODIFY OR THINK ABOUT + /// MODIFYING THIS METHOD UNLESS YOU 100% KNOW WHAT IT DOES AND WHAT YOU ARE CHANGING. + /// + /// The Action delegate determines what is done with the byte buffer that is received. private void StartByteReaderAsync(Action packetReceived) { byte[] byteDataLength = new byte[sizeof(int)]; @@ -159,10 +161,20 @@ private void StartByteReaderAsync(Action packetReceived) }); }); } + /// + /// Starts the reader asynchronously and triggers the PipeDataReceived event when data is received from the pipe. + /// public void StartReaderAsync() { StartByteReaderAsync((b) => PipeDataReceived?.Invoke(this, new PipeEventArgs(b))); } + + private void SendScanToClient(object obj, ProcessingCompletedEventArgs sender) + { + byte[] buffer = sender.Ssdo.CreateSerializedSingleScanDataObject(); + PipeServer.WriteAsync(buffer, 0, buffer.Length); + PipeServer.WaitForPipeDrain(); + } } } diff --git a/Client/InstrumentClientPipe.cs b/Client/InstrumentClientPipe.cs index 5f10e9f..a6bda36 100644 --- a/Client/InstrumentClientPipe.cs +++ b/Client/InstrumentClientPipe.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using ClientServerCommLibrary; using System.IO.Pipes; +using System.Runtime.Remoting.Channels; using Microsoft.Win32; @@ -100,11 +101,7 @@ private void StartByteReaderAsync(Action packetReceived) #region Client to Instrument Methods public void BeginInstrumentConnection(IInstrument instr) { - bool instrReadyToReceiveScan = false; instr.OpenInstrumentConnection(); - - instr.InstrumentConnected += (obj, sender) => { InstrumentConnectedBool = true; }; - instr.InstrumentDisconnected += (obj, sender) => { InstrumentConnectedBool = false; }; instr.InstrumentReadyToReceiveScan += (obj, sender) => { // check if queue contains anything and send scan action if it does. @@ -113,32 +110,16 @@ public void BeginInstrumentConnection(IInstrument instr) instr.SendScanAction(ScanInstructionsQueue.Dequeue()); } }; - instr.ScanReceived += (obj, sender) => - { - // convert to byte[] and send to WorkflowServer. - Console.WriteLine("Scan received event activated"); - // need to create the correct buffer format, which is length is the first n bytes, - // followed by the actual data. - byte[] buffer = sender.Ssdo.CreateSerializedSingleScanDataObject(); - PipeClient.WriteAsync(buffer, 0, buffer.Length); - PipeClient.WaitForPipeDrain(); - }; - - // don't think I need this event after all. - ScanQueueThresholdReached += (obj, sender) => - { - // send the scan to the instrument - - }; - //// enter instrument main routine: - - + instr.ScanReceived += SendScanToServer; } #endregion - - - + private void SendScanToServer(object obj, ScanReceivedEventArgs sender) + { + byte[] buffer = sender.Ssdo.CreateSerializedSingleScanDataObject(); + PipeClient.WriteAsync(buffer, 0, buffer.Length); + PipeClient.WaitForPipeDrain(); + } } } diff --git a/Client/Program.cs b/Client/Program.cs index 8b07136..40f7816 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -38,87 +38,17 @@ public static void Main(string[] args) try { - System.Timers.Timer timer = new System.Timers.Timer(); - int i = 0; - clientPipe.ConnectClientToServer(); clientPipe.BeginInstrumentConnection(instrumentApi); - Console.WriteLine(instrumentApi.GetSystemState(1)); - while (i < 6) - { - switch (i) - { - case 0: - instrumentApi.StartAcquisition("test.raw"); - break; - case 1: - instrumentApi.PauseAcquisition(); - break; - case 2: - instrumentApi.CancelAcquisition(); - break; - case 3: - instrumentApi.InstrumentStandby(); - break; - case 4: - break; - default: - Console.WriteLine("Test completed"); - break; - } - Console.WriteLine(instrumentApi.GetSystemState(1) + "\n"); - i++; - } } catch (Exception e) { Console.WriteLine(e); throw; - } - finally - { - } pipeClient.Close(); } - - - //static void Main(string[] args) - //{ - // // fire up the pipe client - // string serverPipeName = args[0]; - // string pipeName = args[1]; - // ClientPipe clientPipe = new ClientPipe(serverPipeName, pipeName, - // p => p.StartByteReaderAsync()); - - // string instrumentType = args[2]; - // IInstrumentFactory factory = null; - // switch (instrumentType) - // { - // case "qe": - // factory = new ThermoQEFactory(); - // break; - // case "tribrid": - // factory = new ThermoTribridFactory(); - // break; - // } - - // try - // { - // IInstrument instrumentApi = factory?.CreateInstrumentApi(); - // instrumentApi.OpenInstrumentConnection(); - // instrumentApi.PipeClient = clientPipe; - // instrumentApi?.EnterMainLoop(); - // } - // catch (Exception e) - // { - // Console.WriteLine(e); - // } - // Console.ReadLine(); - //} - //// TODO: Write method to query what type of instrument is attached. - - + } } diff --git a/ClientServerCommunication/PipeEventArgs.cs b/ClientServerCommunication/PipeEventArgs.cs index 9c76d82..9648c8e 100644 --- a/ClientServerCommunication/PipeEventArgs.cs +++ b/ClientServerCommunication/PipeEventArgs.cs @@ -7,6 +7,9 @@ namespace ClientServerCommLibrary { + /// + /// Class used to pass buffer sent from client to server or server to client. + /// public class PipeEventArgs : EventArgs { public byte[] Buffer; diff --git a/ClientServerCommunication/ProcessingCompletedEventArgs.cs b/ClientServerCommunication/ProcessingCompletedEventArgs.cs index dcfa035..dd72b05 100644 --- a/ClientServerCommunication/ProcessingCompletedEventArgs.cs +++ b/ClientServerCommunication/ProcessingCompletedEventArgs.cs @@ -6,8 +6,12 @@ namespace ClientServerCommLibrary { + /// + /// Used to pass a single scan data object from the workflow to the + /// method used to communicate with the client. + /// public class ProcessingCompletedEventArgs : EventArgs { - + public SingleScanDataObject Ssdo { get; set; } } } diff --git a/ClientServerCommunication/ScanQueueThresholdReachedEventArgs.cs b/ClientServerCommunication/ScanQueueThresholdReachedEventArgs.cs index ed434c4..8e98064 100644 --- a/ClientServerCommunication/ScanQueueThresholdReachedEventArgs.cs +++ b/ClientServerCommunication/ScanQueueThresholdReachedEventArgs.cs @@ -6,6 +6,9 @@ namespace ClientServerCommLibrary { + /// + /// Class used to pass scans when the scan queue threshold is triggered. + /// public class ScanQueueThresholdReachedEventArgs : EventArgs { public IEnumerable ListSsdo { get; set; }