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