commit 70fca2d84cc0574c144c454b51a1f654e9bcd88c Author: longfellowJian <779035789@qq.com> Date: Thu Mar 27 16:01:28 2025 +0800 提交:初始化 diff --git a/App.config b/App.config new file mode 100644 index 0000000..69d1086 --- /dev/null +++ b/App.config @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AutomationInterface.SampleApp.sln b/AutomationInterface.SampleApp.sln new file mode 100644 index 0000000..0289750 --- /dev/null +++ b/AutomationInterface.SampleApp.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1022 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp", "SampleApp.csproj", "{153C39DC-DB8A-4E8D-9503-A51F4BE96513}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {153C39DC-DB8A-4E8D-9503-A51F4BE96513}.Debug|x64.ActiveCfg = Debug|x64 + {153C39DC-DB8A-4E8D-9503-A51F4BE96513}.Debug|x64.Build.0 = Debug|x64 + {153C39DC-DB8A-4E8D-9503-A51F4BE96513}.Release|x64.ActiveCfg = Release|x64 + {153C39DC-DB8A-4E8D-9503-A51F4BE96513}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E41B27F9-49B0-48B3-B3DD-9DA06108CE7B} + EndGlobalSection + GlobalSection(TeamFoundationVersionControl) = preSolution + SccNumberOfProjects = 1 + SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} + SccTeamFoundationServer = http://tfsapp01.tecan.net:8080/tfs/at_dragonfly + SccProjectUniqueName0 = SampleApp.csproj + SccProjectName0 = . + SccAuxPath0 = http://tfsapp01.tecan.net:8080/tfs/at_dragonfly + SccLocalPath0 = . + SccProvider0 = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} + EndGlobalSection +EndGlobal diff --git a/AutomationInterfaceAccess.cs b/AutomationInterfaceAccess.cs new file mode 100644 index 0000000..fa363b8 --- /dev/null +++ b/AutomationInterfaceAccess.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Tecan.At.Dragonfly.AutomationInterface; +using Tecan.At.Dragonfly.AutomationInterface.Data; +using Tecan.At.Dragonfly.AutomationInterface.MethodModification; + +namespace Tecan.At.AutomationInterface.SampleApp +{ + public static class AutomationInterfaceAccess + { + public static IReadOnlyCollection GetInstruments() + { + using (var ai = AutomationInterfaceFactory.Build()) + { + return ai.InstrumentManagement.GetInstruments(); + } + } + + public static List GetMethods() + { + using (var ai = AutomationInterfaceFactory.Build()) + { + return ai.Queries.GetMethods().ToList(); + } + } + + public static string GetMethodXml(string methodName) + { + using (var ai = AutomationInterfaceFactory.Build()) + { + return ai.Queries.GetMethodXml(methodName); + } + } + + public static bool CheckMethod(IInstrument selectedInstrument, string methodAsXml, string methodName, out IEnumerable messages) + { + messages = null; + using (var ai = AutomationInterfaceFactory.Build()) + { + return ai.MethodExecution.CheckMethod(selectedInstrument, methodAsXml, methodName, out messages); + } + } + + public static IMethodExecutionResult ExecuteMethod(IInstrument selectedInstrument, string methodAsXml, string methodName, bool isStacker) + { + using (var ai = AutomationInterfaceFactory.Build()) + { + return ai.MethodExecution.ExecuteMethod(selectedInstrument, methodAsXml, methodName, isStacker); + } + } + + public static Task ExecuteMethodAsync(IInstrument selectedInstrument, string methodAsXml, string methodName, bool isStacker) + { + using (var ai = AutomationInterfaceFactory.Build()) + { + return ai.MethodExecution.ExecuteMethodAsync(selectedInstrument, methodAsXml, methodName, isStacker); + } + } + + public static void CancelAsyncMethodExecution(IInstrument selectedInstrument) + { + using (var ai = AutomationInterfaceFactory.Build()) + { + ai.MethodExecution.Cancel(selectedInstrument); + } + } + + public static string ExportData(Guid workspaceId, Guid executionId) + { + using (var ai = AutomationInterfaceFactory.Build()) + { + return ai.MethodExecution.GetResults(workspaceId, executionId); + } + } + + public static string ChangePlateLayout(string methodAsXml, int[] wellsToSelect) + { + using (var plateLayoutModifier = MethodModificationFactory.Build(ref methodAsXml)) + { + var plateInfo = plateLayoutModifier.GetPlateInfo(); + for (uint wellIndex = 0; wellIndex < plateInfo.WellAmount; wellIndex++) + { + plateLayoutModifier.SelectWell(wellIndex, wellsToSelect.Any(x => x == wellIndex)); + } + + methodAsXml = plateLayoutModifier.ApplyChangesToXmlString(); + } + + return methodAsXml; + } + } +} diff --git a/CreateZipFile.ps1 b/CreateZipFile.ps1 new file mode 100644 index 0000000..f477740 --- /dev/null +++ b/CreateZipFile.ps1 @@ -0,0 +1,63 @@ +try +{ + # see https://stackoverflow.com/questions/9948517/how-to-stop-a-powershell-script-on-the-first-error + $ErrorActionPreference = "Stop" + + Add-Type -A System.IO.Compression.FileSystem + + + # Write-Host ('OS Tmp Dir: ' + $env:TEMP ) + + # create tmo dir + $tmpDir = $env:TEMP + [guid]::NewGuid() + Write-Host ('Use tmp-dir: ' + $tmpDir) + + New-Item -ItemType Directory -Force -Path $tmpDir + + + # copy Sample App source code files to tmp dir + Copy-Item $($PSScriptRoot + "\*.cs") -Destination $tmpDir + Copy-Item $($PSScriptRoot + "\*.config") -Destination $tmpDir + Copy-Item $($PSScriptRoot + "\*.csproj") -Destination $tmpDir + Copy-Item $($PSScriptRoot + "\*.sln") -Destination $tmpDir + Copy-Item $($PSScriptRoot + "\*.txt") -Destination $tmpDir + Copy-Item $($PSScriptRoot + "\Properties") -Destination $tmpDir -Recurse + + # copy Automation Interface docu to tmp dir + # -> PDF is currently not included into ZIP file + # $aiDocuFileName = Resolve-Path -Path $($PSScriptRoot + "\..\..\Production\Automation\AutomationInterface\Doc\AutomationInterfaceManual.pdf") + # Write-Host ('aiDocuFileName : ' + $aiDocuFileName ) + # Copy-Item $aiDocuFileName -Destination $tmpDir + + + # create zip file + $zipFileName = $PSScriptRoot + "\SampleApp.zip" + Write-Host ('zipFileName: ' + $zipFileName ) + + if (Test-Path $zipFileName) + { + Write-Host("Delete already available .zip file...") + Remove-Item -Recurse -Force $zipFileName + } + + # ...and create it + [IO.Compression.ZipFile]::CreateFromDirectory($tmpDir, $zipFileName) + + # delete temp directory + Write-Host ('Del tmp folder : ' + $tmpDir ) + Remove-Item -Recurse -Force $tmpDir + + + Write-Host('') + Write-Host('') + Write-Host('##############################################################################') + Write-Host('Successfully created Sample Application .zip file: ' + $zipFileName) + Write-Host('##############################################################################') + + exit $LASTEXITCODE +} +catch +{ + Write-Error('ERROR: ' + $_.Exception) + exit $LASTEXITCODE +} diff --git a/DemoAttribute.cs b/DemoAttribute.cs new file mode 100644 index 0000000..69149dd --- /dev/null +++ b/DemoAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Tecan.At.AutomationInterface.SampleApp +{ + [AttributeUsage(AttributeTargets.Method)] + public class DemoAttribute : Attribute + { + public string Name { get; private set; } + public char Key { get; private set; } + + public DemoAttribute(string name, char key) + { + Name = name; + Key = key; + } + } +} \ No newline at end of file diff --git a/ExportDemo.cs b/ExportDemo.cs new file mode 100644 index 0000000..cafda77 --- /dev/null +++ b/ExportDemo.cs @@ -0,0 +1,11 @@ +namespace Tecan.At.AutomationInterface.SampleApp +{ + internal class ExportDemo + { + [Demo("exportData", 'a')] + public static void Export(SampleApp sampleApp, SessionData sessionData) + { + sampleApp.ExportData(sessionData); + } + } +} \ No newline at end of file diff --git a/InputData.cs b/InputData.cs new file mode 100644 index 0000000..c24b760 --- /dev/null +++ b/InputData.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; + +namespace Tecan.At.AutomationInterface.SampleApp +{ + public enum AutomationInterfaceActions + { + Undefined, + GetInstrument, + GetAllMethods, + GetMethod, + CheckMethod, + ExecuteMethod, + PlateOut, + PlateIn, + ExportData, + ClearSession, + StartWorkflow + } + + public class InputData + { + public AutomationInterfaceActions Action { get; private set; } + public List Parameter { get; } = new List(); + + public bool Parse(string value) + { + if (string.IsNullOrWhiteSpace(value)) + return false; + + string[] parts = value.Split(new[] {" "}, StringSplitOptions.RemoveEmptyEntries); + + if (!GetAction(parts[0])) + { + return false; + } + + for (int i = 1; i < parts.Length; i++) + { + Parameter.Add(parts[i]); + } + + return true; + } + + private bool GetAction(string part) + { + if (!part.StartsWith("-")) + { + return false; + } + + switch (part.ToLower()) + { + case "-getinstrument": + Action = AutomationInterfaceActions.GetInstrument; + break; + case "-getallmethods": + Action = AutomationInterfaceActions.GetAllMethods; + break; + case "-getmethod": + Action = AutomationInterfaceActions.GetMethod; + break; + case "-checkmethod": + Action = AutomationInterfaceActions.CheckMethod; + break; + case "-executemethod": + Action = AutomationInterfaceActions.ExecuteMethod; + break; + case "-plateout": + Action = AutomationInterfaceActions.PlateOut; + break; + case "-platein": + Action = AutomationInterfaceActions.PlateIn; + break; + case "-exportdata": + Action = AutomationInterfaceActions.ExportData; + break; + case "-clearsession": + Action = AutomationInterfaceActions.ClearSession; + break; + case "-startworkflow": + Action = AutomationInterfaceActions.StartWorkflow; + break; + default: + return false; + } + + return true; + } + } +} diff --git a/InstrumentDemo.cs b/InstrumentDemo.cs new file mode 100644 index 0000000..82f1d04 --- /dev/null +++ b/InstrumentDemo.cs @@ -0,0 +1,60 @@ +using System; +using Tecan.At.Dragonfly.AutomationInterface; + +namespace Tecan.At.AutomationInterface.SampleApp +{ + internal class InstrumentDemo + { + [Demo("getInstrument ", '1')] + public static void GetInstrumentByAlias(SampleApp sampleApp, SessionData sessionData) + { + Console.Write("Instrument alias? "); + var aliasPart = Console.ReadLine(); + + sampleApp.GetInstrument(sessionData, aliasPart); + + if (sessionData?.SelectedInstrument != null) + { + Console.WriteLine($"Selected instrument: {sessionData.SelectedInstrument.Alias ?? ""}"); + } + else + { + Console.WriteLine($"No instrument available"); + } + + } + + [Demo("plateOut", '8')] + public static void PlateOut(SampleApp sampleApp, SessionData sessionData) + { + sessionData.EnforceSetInstrument(); + + using (AutomationInterfaceFactory.Build()) + { + sessionData.SelectedInstrument.PlateOut(); + } + } + + [Demo("plateIn", '9')] + public static void PlateIn(SampleApp sampleApp, SessionData sessionData) + { + sessionData.EnforceSetInstrument(); + + using (AutomationInterfaceFactory.Build()) + { + sessionData.SelectedInstrument.PlateIn(); + } + } + + [Demo("getInstrumentState", 'f')] + public static void GetState(SampleApp sampleApp, SessionData sessionData) + { + sessionData.EnforceSetInstrument(); + + using (AutomationInterfaceFactory.Build()) + { + Console.WriteLine($"Instrument state is '{ sessionData.SelectedInstrument.State}'."); + } + } + } +} \ No newline at end of file diff --git a/MethodExecutionDemo.cs b/MethodExecutionDemo.cs new file mode 100644 index 0000000..54e8f4b --- /dev/null +++ b/MethodExecutionDemo.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace Tecan.At.AutomationInterface.SampleApp +{ + internal class MethodExecutionDemo + { + [Demo("checkMethod ", '4')] + public static void CheckMethod(SampleApp sampleApp, SessionData sessionData) + { + Console.Write("methodFile? "); + var methodFile = Console.ReadLine(); + + IEnumerable messages; + sampleApp.CheckMethod(sessionData, methodFile, out messages); + + Console.WriteLine(sessionData.MethodIsValid ? "valid" : "invalid"); + + foreach (var message in messages) + { + Console.WriteLine(message); + } + } + + [Demo("executeMethod ", '5')] + public static void ExecuteMethod(SampleApp sampleApp, SessionData sessionData) + { + Console.Write("methodFile? "); + var methodFile = Console.ReadLine(); + + sampleApp.ExecuteMethod(sessionData, methodFile, "AutomationInterfaceMethod", new int[] { }); + } + + [Demo("executeMethodAsync ", '6')] + public static void ExecuteMethodAsync(SampleApp sampleApp, SessionData sessionData) + { + Console.Write("methodFile? "); + var methodFile = Console.ReadLine(); + + sampleApp.ExecuteMethodAsync(sessionData, methodFile, "AutomationInterfaceMethod", new int[] { }); + } + + [Demo("executeMethodAsyncAndCancel ", '7')] + public static void ExecuteMethodAsyncWithCancellation(SampleApp sampleApp, SessionData sessionData) + { + Console.Write("methodFile? "); + var methodFile = Console.ReadLine(); + + sampleApp.ExecuteMethodAsyncAndCancel(sessionData, methodFile, "AutomationInterfaceMethod", new int[] { }); + } + + [Demo("executeMethod (with stacker) ", 'e')] + public static void ExecuteMethodWithStacker(SampleApp sampleApp, SessionData sessionData) + { + Console.Write("methodFile? "); + var methodFile = Console.ReadLine(); + + sampleApp.ExecuteMethod(sessionData, methodFile, "AutomationInterfaceMethod", new int[] { }, true); + } + } +} \ No newline at end of file diff --git a/MiscDemo.cs b/MiscDemo.cs new file mode 100644 index 0000000..b746895 --- /dev/null +++ b/MiscDemo.cs @@ -0,0 +1,15 @@ +namespace Tecan.At.AutomationInterface.SampleApp +{ + internal class MiscDemo + { + [Demo("No Automation Interface scope available", 'd')] + public static void Export(SampleApp sampleApp, SessionData sessionData) + { + // inside method a scope is spanned + sampleApp.GetInstrument(sessionData, string.Empty); + + // no scope here + sessionData.SelectedInstrument.PlateIn(); + } + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..e395629 --- /dev/null +++ b/Program.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Tecan.At.Dragonfly.AutomationInterface; + +namespace Tecan.At.AutomationInterface.SampleApp +{ + class Program + { + static void Main(string[] args) + { + var commands = BuildCommands(); + + try + { + // Needed in your code + AutomationInterfaceFactory.Start(); + + SessionData sessionData = null; + var automationInterfaceAccess = new SampleApp(); + + var quitCommands = new[] { "q", "Q" }; + + while (true) + { + RenderMenu(commands); + + var consoleInput = Console.ReadKey(); + Console.WriteLine(); + if (quitCommands.Any(x => x == consoleInput.KeyChar.ToString())) + { + break; + } + + var command = '0'; + if (!char.TryParse(consoleInput.KeyChar.ToString(), out command)) + { + Console.WriteLine($"'{consoleInput}' isn't a valid command key."); + continue; + } + else if (commands.ContainsKey(command)) + { + if (sessionData == null) + { + sessionData = new SessionData(); + } + + try + { + var theCommand = commands[command]; + + Console.WriteLine($"--- Execute command {theCommand.Item1}..."); + + theCommand.Item2.Invoke(automationInterfaceAccess, sessionData); + + Console.WriteLine("--- done"); + } + catch (TargetInvocationException e) + { + Console.WriteLine($"### Error: {e.InnerException.Message} ###"); + Console.WriteLine(); + Console.WriteLine(e.InnerException.ToString()); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + } + else + { + Console.WriteLine($"Unknown command '{command}'."); + } + } + } + finally + { + // Needed in your code + AutomationInterfaceFactory.Stop(); + } + } + + private static void RenderMenu(Dictionary>> commands) + { + Console.WriteLine(); + commands.Keys.OrderBy(x => x).ToList().ForEach(c => + { + var actCmd = commands[c]; + Console.WriteLine($"{c}. {actCmd.Item1}"); + }); + Console.WriteLine(); + Console.WriteLine($"Press q to exit."); + Console.WriteLine(); + Console.Write("> "); + } + + private static Dictionary>> BuildCommands() + { + var methods = typeof(Program).Assembly.GetTypes() + .SelectMany(t => t.GetMethods()) + .Where(m => m.GetCustomAttributes(typeof(DemoAttribute), false).Length > 0) + .Select(x => new { Attribute = (DemoAttribute)x.GetCustomAttributes(typeof(DemoAttribute), false).Single(), Delegate = x }) + .ToArray(); + + var commands = new Dictionary>>(methods.Length); + + foreach (var actDemo in methods) + { + commands.Add(actDemo.Attribute.Key, Tuple.Create>(actDemo.Attribute.Name, (aia, sd) => { actDemo.Delegate.Invoke(null, new object[] { aia, sd }); })); + } + + Console.WriteLine($"Count: " + methods.Length); + + return commands; + } + + + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d4e3c01 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("AutomationInterface.IntegrationsTestsConsole")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AutomationInterface.IntegrationsTestsConsole")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[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("153c39dc-db8a-4e8d-9503-a51f4be96513")] + +// 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/QueryDemo.cs b/QueryDemo.cs new file mode 100644 index 0000000..38620d3 --- /dev/null +++ b/QueryDemo.cs @@ -0,0 +1,41 @@ +using System; + +namespace Tecan.At.AutomationInterface.SampleApp +{ + internal class QueryDemo + { + [Demo("getMethods", '2')] + public static void GetAllMethods(SampleApp sampleApp, SessionData sessionData) + { + sampleApp.GetMethods(sessionData); + + foreach (var methodName in sessionData.AvailableMethods) + { + Console.WriteLine(methodName); + } + } + + [Demo("getMethodXml ", '3')] + public static void GetMethodByNameAndTarget(SampleApp sampleApp, SessionData sessionData) + { + Console.Write("method name? "); + var methodName = Console.ReadLine(); + + var xmlString = sampleApp.GetMethodXml(methodName); + + if (String.IsNullOrWhiteSpace(xmlString)) + { + Console.WriteLine($"Method '{methodName}' not available."); + return; + } + + Console.WriteLine($"Loaded method '{methodName}'. Length: '{xmlString.Length}'."); + + Console.Write("target file name? "); + var targetFileName = Console.ReadLine(); + + // Console.WriteLine(xmlString); + System.IO.File.WriteAllText(targetFileName, xmlString); + } + } +} diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..20e5d7f --- /dev/null +++ b/README.txt @@ -0,0 +1,21 @@ +Automation Interface Sample Application +----------------------------------------- + +To get the Sample Application running follow these steps: + +1. Add a reference to your project to the file 'Tecan.At.Dragonfly.AutomationInterface.dll'. + This file is located in the Clients folder usually under C:\Program Files\Tecan\SparkControl. + +2. In Visual Studio project post build event add call to a powershell script. + The script is called 'CopyAdditionalAutomationInterfaceFiles.ps1' and is located in the same folder as the 'AutomationInterface.dll'. + + Example: + powershell.exe \CopyAdditionalAutomationInterfaceFiles.ps1 $(TargetDir) + + +Remark: +It might be necessary that you add following call in your pre-build step: + +powershell.exe Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted -Force; + +Try it first without this call! \ No newline at end of file diff --git a/SampleApp.cs b/SampleApp.cs new file mode 100644 index 0000000..49cf977 --- /dev/null +++ b/SampleApp.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Tecan.At.AutomationInterface.SampleApp +{ + public class SampleApp + { + public void GetInstrument(SessionData sessionData, string aliasPart) + { + try + { + var availableInstruments = AutomationInterfaceAccess.GetInstruments(); + sessionData.SelectedInstrument = availableInstruments.FirstOrDefault(x => string.IsNullOrEmpty(aliasPart) || x.Alias.Contains(aliasPart)); + + if (sessionData.SelectedInstrument == null) + { + sessionData.SelectedInstrument = availableInstruments.FirstOrDefault(x => string.IsNullOrEmpty(aliasPart) || x.SerialNumber.Contains(aliasPart)); + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + public void GetMethods(SessionData sessionData) + { + try + { + sessionData.AvailableMethods = AutomationInterfaceAccess.GetMethods(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + public string GetMethodXml(string methodName) + { + try + { + return AutomationInterfaceAccess.GetMethodXml(methodName); + } + catch (Exception e) + { + Console.WriteLine(e); + + return null; + } + } + + public void CheckMethod(SessionData sessionData, string methodFile, out IEnumerable messages) + { + sessionData.EnforceSetInstrument(); + + messages = null; + try + { + string methodAsXml = System.IO.File.ReadAllText(methodFile); + sessionData.MethodIsValid = AutomationInterfaceAccess.CheckMethod(sessionData.SelectedInstrument, methodAsXml, "MethodToCheck", out messages); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + public void ExecuteMethod(SessionData sessionData, string methodFile, string methodName, int[] wellsToSelect, bool isStacker = false) + { + sessionData.EnforceSetInstrument(); + + try + { + string methodAsXml = System.IO.File.ReadAllText(methodFile); + + if (wellsToSelect.Any()) + { + methodAsXml = AutomationInterfaceAccess.ChangePlateLayout(methodAsXml, wellsToSelect); + } + + var result = AutomationInterfaceAccess.ExecuteMethod(sessionData.SelectedInstrument, methodAsXml, methodName, isStacker); + if (result != null) + { + sessionData.WorkspaceId = result.WorkspaceId; + sessionData.ExecutionId = result.ExecutionId; + } + else + { + Console.WriteLine("Error executing method (checkMethod failed)."); + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + public void ExecuteMethodAsync(SessionData sessionData, string methodFile, string methodName, int[] wellsToSelect, bool isStacker = false) + { + sessionData.EnforceSetInstrument(); + + try + { + string methodAsXml = System.IO.File.ReadAllText(methodFile); + + if (wellsToSelect.Any()) + { + methodAsXml = AutomationInterfaceAccess.ChangePlateLayout(methodAsXml, wellsToSelect); + } + + var task = AutomationInterfaceAccess.ExecuteMethodAsync(sessionData.SelectedInstrument, methodAsXml, methodName, isStacker); + + Console.WriteLine($"Started method '{methodName}'..."); + while (task.Status == TaskStatus.Running) + { + Thread.Sleep(1000); + Console.Write("."); + } + + task.Wait(); + + if (task.Result != null) + { + sessionData.WorkspaceId = task.Result.WorkspaceId; + sessionData.ExecutionId = task.Result.ExecutionId; + + Console.WriteLine(); + Console.WriteLine($"Executed method '{methodName}'. WorkspaceId: '{sessionData.WorkspaceId}'."); + } + else + { + Console.WriteLine("Error executing method (checkMethod failed)."); + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + public void ExecuteMethodAsyncAndCancel(SessionData sessionData, string methodFile, string methodName, int[] wellsToSelect, bool isStacker = false) + { + sessionData.EnforceSetInstrument(); + + try + { + string methodAsXml = System.IO.File.ReadAllText(methodFile); + + if (wellsToSelect.Any()) + { + methodAsXml = AutomationInterfaceAccess.ChangePlateLayout(methodAsXml, wellsToSelect); + } + + var task = AutomationInterfaceAccess.ExecuteMethodAsync(sessionData.SelectedInstrument, methodAsXml, methodName, isStacker); + Console.WriteLine($"Started method '{methodName}'..."); + + // method should be a rather complex method that it takes longer to execute as this thread is sleeping + Thread.Sleep(5000); + Console.WriteLine($"Cancel method execution."); + + AutomationInterfaceAccess.CancelAsyncMethodExecution(sessionData.SelectedInstrument); + Console.WriteLine($"Cancellation finished."); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + public void ExportData(SessionData sessionData) + { + try + { + sessionData.ExportedDataFile = AutomationInterfaceAccess.ExportData(sessionData.WorkspaceId, sessionData.ExecutionId); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + } +} diff --git a/SampleApp.csproj b/SampleApp.csproj new file mode 100644 index 0000000..b6cb894 --- /dev/null +++ b/SampleApp.csproj @@ -0,0 +1,78 @@ + + + + + Debug + AnyCPU + {153C39DC-DB8A-4E8D-9503-A51F4BE96513} + Exe + Tecan.At.AutomationInterface.SampleApp + Tecan.At.AutomationInterface.SampleApp + v4.6.2 + 512 + true + true + SAK + SAK + SAK + SAK + + + + + true + bin\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + false + + + bin\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + powershell.exe Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted -Force; + + + powershell.exe <your path>\CopyAdditionalAutomationInterfaceFiles.ps1 $(TargetDir) + + \ No newline at end of file diff --git a/SessionData.cs b/SessionData.cs new file mode 100644 index 0000000..199040c --- /dev/null +++ b/SessionData.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using Tecan.At.Dragonfly.AutomationInterface.Data; + +namespace Tecan.At.AutomationInterface.SampleApp +{ + public class SessionData + { + public List AvailableMethods { get; set; } + public IInstrument SelectedInstrument { get; set; } + public bool MethodIsValid { get; set; } + public Guid WorkspaceId { get; set; } + public Guid ExecutionId { get; set; } + public string ExportedDataFile { get; set; } + + public void EnforceSetInstrument() + { + if (SelectedInstrument == null) + { + throw new InvalidOperationException("Instrument is not set."); + } + } + } +} \ No newline at end of file diff --git a/WorkflowDemo.cs b/WorkflowDemo.cs new file mode 100644 index 0000000..7f86520 --- /dev/null +++ b/WorkflowDemo.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace Tecan.At.AutomationInterface.SampleApp +{ + internal class WorkflowDemo + { + [Demo("startWorkflow ", 'b')] + public static void Export(SampleApp sampleApp, SessionData sessionData) + { + Console.Write("instrument alias? "); + var instrumentAlias = Console.ReadLine(); + + Console.WriteLine("methodFile? "); + var methodFile = Console.ReadLine(); + + sessionData = new SessionData(); + sampleApp.GetInstrument(sessionData, instrumentAlias); + sampleApp.ExecuteMethod(sessionData, methodFile, "AutomationInterfaceMethod", new int[] { }); + + sampleApp.ExportData(sessionData); + + Console.WriteLine("Export: " + sessionData.ExportedDataFile); + } + + [Demo("select wells and execute ", 'c')] + public static void SelectWellsAndExport(SampleApp sampleApp, SessionData sessionData) + { + Console.Write("instrument alias? "); + var instrumentAlias = Console.ReadLine(); + + Console.WriteLine("methodFile? "); + var methodFile = Console.ReadLine(); + + Console.WriteLine("wells to select? Bsp: 2, 5, 32, ..."); + var wellsToSelectString = Console.ReadLine(); + + List wellsToSelect = new List(); + + if (!string.IsNullOrWhiteSpace(wellsToSelectString)) + { + string[] intParts = wellsToSelectString.Split(new char[] { ',' }); + foreach (var intPart in intParts) + { + if (int.TryParse(intPart.Trim(), out int wellIndex)) + { + wellsToSelect.Add(wellIndex); + } + } + } + + sessionData = new SessionData(); + sampleApp.GetInstrument(sessionData, instrumentAlias); + sampleApp.ExecuteMethod(sessionData, methodFile, "AutomationInterfaceMethod", wellsToSelect.ToArray()); + + sampleApp.ExportData(sessionData); + + Console.WriteLine("Export: " + sessionData.ExportedDataFile); + } + } +} \ No newline at end of file