Chronos Plugins 5.4.0
This documentation covers the plugin interfaces definitions and an example implementation.
Loading...
Searching...
No Matches
MockDevice.cs
Go to the documentation of this file.
3using System;
4using System.Collections.Generic;
5using System.ComponentModel;
6using System.Diagnostics.CodeAnalysis;
7using System.Drawing.Design;
8using System.IO;
9using System.Threading;
10using System.Threading.Tasks;
11using System.Windows.Forms;
13
14// ReSharper disable LocalizableElement
15#pragma warning disable 169
16
21namespace MockPlugin.Device
22{
23
24
36 [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
37 [SuppressMessage("ReSharper", "UnusedMember.Global")]
38 public class MockDevice :
39 // ReSharper disable once RedundantExtendsListEntry
40 IDevice,
46 INotifyPropertyChanged,
47 IDisposable,
48 IHaveMachineParameters<CoffeMakerParams>,
53 {
54 public const string DeviceTypeName = "ACME Coffee Maker";
55 #region private variables
56
57 private bool mIsConnected;
58
59 #endregion private variables
60
61 #region IDevice
62
63 public MockDevice()
64 {
65 Instances.Add(this);
66 }
67
68 internal static readonly List<MockDevice> Instances = new List<MockDevice>();
69
70 public void Dispose()
71 {
72 Instances.Remove(this);
73 DebugOutput?.Invoke($"MockDevice {Name} disposed");
74 }
75
80
84 public string DeviceTypeDescription => "coffee machine";
85
86 public string Name
87 {
88 get;
89 set;
90 }
91
97 {
100 $"Making a {composition.Volume} mL frappuccino with cream type \"{composition.Cream}\"{(composition.DeCaffeinated ? ", decaffeinated." : "")}.");
101 }
102
106 public void SomeDummyMethod()
107 {
108 DebugOutput?.Invoke($"Dummy method of {Name} was called.");
109 }
110
114 public void Connect()
115 {
117 MessageBox.Show(Helpers.Gui.MainWindow, $"Device {Name} was connected to {Connection}.");
119 mIsConnected = true;
121 }
122
126 public void Disconnect()
127 {
129 MessageBox.Show(Helpers.Gui.MainWindow, $"Device {Name} was disconnected from {Connection}.");
131 mIsConnected = false;
133 }
134
135 public event Action<ConnectionState> ConnectionStateChanged;
136
137 #endregion IDevice
138
139 #region INeedAConnection
140
147 [Editor(typeof(ConnectionEditor), typeof(UITypeEditor))]
148 public string Connection { get; set; } = "COM17";
149
150 #endregion INeedAConnection
151
152 #region Custom methods and properties
153
158 public void ShowTheMessage(string messageText)
159 {
160 WaitIfPaused();
161 SetStatusMessage?.Invoke("The device just did something wonderful.");
162 MessageBox.Show(Helpers.Gui.MainWindow,
163 $"The following message was shown addressing the mock device {Name}:\r\n{messageText}",
164 "Mock Device",
165 MessageBoxButtons.OK);
166 DebugOutput?.Invoke($"Finished showing the message {messageText}");
167 }
168
172 public bool IsConnected
174 get => mIsConnected;
175 set
176 {
177 if (!mIsConnected && value)
178 {
179 Connect();
180 }
181 else if (mIsConnected && !value)
182 {
183 Disconnect();
184 }
185 }
186 }
187
188 #endregion Custom methods and properties
189
190 #region IProvideStatusMessages
191
192 public event Action<string> SetStatusMessage;
193
194 #endregion IProvideStatusMessages
195
196 #region IHaveDebugOutput
197
198 public event Action<string> DebugOutput;
199
200 #endregion IHaveDebugOutput
201
202 #region INotifyPropertyChanged
203
207 private void OnIsConnectedChanged()
208 {
209 var myHandler = PropertyChanged;
210 if (myHandler != null)
211 {
212 Helpers.Gui.GuiTaskFactory.StartNew(() => myHandler(this, new PropertyChangedEventArgs(nameof(IsConnected))));
213 }
214 }
215
216 public event PropertyChangedEventHandler PropertyChanged;
217
218 #endregion INotifyPropertyChanged
219
220 #region IAbortSchedules
221
231 public void TriggerAbort(string reason, bool softStop)
232 {
233 Task.Run(() =>
234 {
235 Thread.Sleep(5000);
236 if (softStop)
237 {
238 StopRun?.Invoke(new StopRunArgs()
239 {
240 How = StopRunArgs.StopMode.NoNewJobs, StopQueue = true, Reason = reason,
241 RestartRemainingJobs = false
242 });
243 }
244 else
245 {
246 AbortSchedule?.Invoke(reason);
247 }
248 });
249}
250
251 public event Action<string> AbortSchedule;
252
253 public void CheckForAbort()
254 {
255 if (mAborted.IsSet)
256 {
257 throw new OperationCanceledException("Aborted");
258 }
259 }
260
261 #endregion IAbortSchedules
262
263 #region Implementation of IHaveMachineParameters<CoffeMakerParams>
264
265 public CoffeMakerParams Parameters { get; set; } = new CoffeMakerParams();
266
267 public Task ApplyParametersAsync(bool waitUntilSetpointIsReached, CancellationToken canToken)
268 {
269
270 SetStatusMessage?.Invoke($"Applying parameters: '{Parameters}' and {(!waitUntilSetpointIsReached ? "not " : "")} waiting for setpoint.");
271 if (waitUntilSetpointIsReached)
272 {
273 return Task.Delay(TimeSpan.FromSeconds(10),canToken);
274 }
275 return Task.CompletedTask;
276 }
277
278 #endregion Implementation of IHaveMachineParameters<CoffeMakerParams>
279
280 public void WaitIfPaused()
281 {
282 WaitHandle.WaitAny(new[] { mPauseEnded.WaitHandle, mAborted.WaitHandle });
284 }
285
286 #region Implementation of ICanInterrupt
287
291 private readonly ManualResetEventSlim mPauseEnded = new ManualResetEventSlim(true);
292
293 private readonly ManualResetEventSlim mAborted = new ManualResetEventSlim(false);
294
295 public bool Aborted
296 {
297 get => mAborted.IsSet;
298 set
299 {
300 if (value) { mAborted.Set(); } else { mAborted.Reset(); }
301 }
302 }
303
304 public bool Paused
305 {
306 get => !mPauseEnded.IsSet;
307 set
308 {
309 if (value)
310 {
311 mPauseEnded.Reset();
312 }
313 else
314 {
315 mPauseEnded.Set();
316 }
317 }
318 }
319
320 #endregion Implementation of ICanInterrupt
321
322 public IEnumerable<string> LogPaths =>
323 MockSampleListWorker.GetFakeLogs($"{GetType().FullName} instance {Name}");
324
325 public Func<StopRunArgs, Task> StopRun { get; set; }
326
327 #region Schedule state events
328
330
332 {
333 set
334 {
335 if (mScheduleEvents != null)
336 {
337 mScheduleEvents.ScheduleStateChanged -= ScheduleStateChangedHandler;
338 }
339
340 mScheduleEvents = value;
341 if (value != null)
342 {
343 mScheduleEvents.ScheduleStateChanged += ScheduleStateChangedHandler;
344 }
345 }
346 }
347
349 {
350 DebugOutput?.Invoke($"Schedule {e.PlanerName} ({e.PlanerID}) state {e.State}, abort reason {(string.IsNullOrEmpty(e.AbortReason) ? "N/A" : e.AbortReason)}");
351 }
352 #endregion
353
354 public void BeginInteraction()
355 {
356 DebugOutput?.Invoke("Starting error handling interaction");
357 }
358
359 public void EndInteraction()
360 {
361 DebugOutput?.Invoke("Ending error handling interaction");
362 }
363
370 public void RaiseError(string errorDescription, ErrorType errType, bool resolved)
371 {
372 var errArgs = new InteractiveErrorHandlingEventArgs() { Error = errorDescription, ErrorType = errType };
373 do
374 {
375 HandleError?.Invoke(this, errArgs);
376 } while (errArgs.RetryLastAction);
377
378 if (!resolved)
379 {
380 throw new IOException($"Coffee machine reported a problem: {errorDescription}");
381 }
382 }
383 public event EventHandler<InteractiveErrorHandlingEventArgs> HandleError;
384 }
385}
Classes and interfaces that are meant for plugins. The classes and interfaces below this namespace ar...
ConnectionState
If your connectivity state changes, you should tell the user about it.
ErrorType
Lets a device implementing IHaveInteractiveErrorHandling specify which kind of error occurred.
A fake device. This namespace contains the fake device driver and auxiliary classes for settings,...
Example task implementations. Since there are lots of things that can be done from a task,...
The classes in this namespace demonstrate how to interact with the Chronos sample list.
To be implemented by the "device driver" part of a Chronos plugin.
For devices that need some kind of user configured connection string to address the hardware.
Implement this interface if you want to keep the user up-to-date about what your device is doing.
Implement this interface if you wish to provide debug log output.
Implement this interface if you need to abort schedules on some error condition.
Everything needed for showing the error dialog / reacting on input.
For devices that allow interactive error handling (like retrying the last action)
For devices that support pausing/aborting independent of a task.
Parameters that are constant for the duration of a schedule.
System.Threading.Tasks.TaskFactory GuiTaskFactory
For your convenience, a default task factory for tasks running on the GUI thread.
Definition: Helpers.cs:26
IWin32Window MainWindow
If you need to set the owner window yourself or want to show message boxes.
Definition: Helpers.cs:37
Static instance for access to utility functions and resources.
Definition: Helpers.cs:78
static IGuiHelper Gui
Utility functions for window handling.
Definition: Helpers.cs:92
Information about the current state change.
Currently starting schedule stage.
Implement this interface if you need to track the state of schedules.
This can be called for a sample list worker or device that writes its own set of log files which shou...
Implement this interface with your device or sample list worker to get fine-grained control about sto...
Options for stopping the schedule/queue.
StopMode
Details how to stop the run.
We have a fancy coffee machine that can regulate the warmer temperature for the pot and has lamps for...
Just a primitive UI Type Editor to demonstrate how you can add an editor of your own for connection s...
A chronos plugin implementation for a fake device. We pretend we are controlling a mixture of coffee ...
Definition: MockDevice.cs:53
PropertyChangedEventHandler PropertyChanged
Definition: MockDevice.cs:216
Action< string > DebugOutput
Definition: MockDevice.cs:198
Action< string > SetStatusMessage
Definition: MockDevice.cs:192
bool Aborted
If set, abort execution as soon as possible. You can throw an OperationCanceledException....
Definition: MockDevice.cs:296
void BrewFrappuccino(BrewFrappuccino.CompositionData composition)
Pretend we are doing some operation on a complex parameter set.
Definition: MockDevice.cs:96
void Connect()
Inform the user of our connect attempt / success. Instead of establishing a real connection,...
Definition: MockDevice.cs:114
readonly ManualResetEventSlim mPauseEnded
Using an event here instead of a simple bool helps us avoid polling while checking for the events.
Definition: MockDevice.cs:291
Action< string > AbortSchedule
Definition: MockDevice.cs:251
void SomeDummyMethod()
Just for testing if methods of this device can be called from some other point in our code.
Definition: MockDevice.cs:106
void OnIsConnectedChanged()
For thread-safe update of the toolbox's GUI elements representing the connection state.
Definition: MockDevice.cs:207
EventHandler< InteractiveErrorHandlingEventArgs > HandleError
Definition: MockDevice.cs:383
void Disconnect()
Pretend we are closing the connection. Actual operation substituted by a message box.
Definition: MockDevice.cs:126
void ScheduleStateChangedHandler(object sender, ScheduleStateEventArgs e)
Definition: MockDevice.cs:348
bool IsConnected
Helper for our toolbox.
Definition: MockDevice.cs:173
Func< StopRunArgs, Task > StopRun
Callback function returning a task that completes once the schedule queue was stopped.
Definition: MockDevice.cs:325
IScheduleEvents mScheduleEvents
Definition: MockDevice.cs:329
void ShowTheMessage(string messageText)
Let our device set a status message and display some message box instead of doing real work.
Definition: MockDevice.cs:158
string Name
User-selected name for the device instance.
Definition: MockDevice.cs:87
Task ApplyParametersAsync(bool waitUntilSetpointIsReached, CancellationToken canToken)
Definition: MockDevice.cs:267
string DisplayedTypeName
Visible to the user on the instruments page of the settings editor.
Definition: MockDevice.cs:79
string DeviceTypeDescription
Device class specification referred to by some messages.
Definition: MockDevice.cs:84
void TriggerAbort(string reason, bool softStop)
This will trigger the AbortSchedule-Event 5 seconds after it was called from a task.
Definition: MockDevice.cs:231
IEnumerable< string > LogPaths
Provide full paths to each of your log files here.
Definition: MockDevice.cs:322
bool Paused
If paused, wait until paused is reset before executing the next command.
Definition: MockDevice.cs:305
void EndInteraction()
Will be called if you can lock the terminal again.
Definition: MockDevice.cs:359
CoffeMakerParams Parameters
Definition: MockDevice.cs:265
string Connection
Connection as set in the Chronos instrument settings.
Definition: MockDevice.cs:148
void BeginInteraction()
Will be called if interaction is started, gives you a chance to unlock a terminal,...
Definition: MockDevice.cs:354
readonly ManualResetEventSlim mAborted
Definition: MockDevice.cs:293
IScheduleEvents ScheduleEvents
Hook up immediately or save this for later use.
Definition: MockDevice.cs:332
Action< ConnectionState > ConnectionStateChanged
Definition: MockDevice.cs:135
void RaiseError(string errorDescription, ErrorType errType, bool resolved)
"Retry" here means that we don't retry some action, but that we raise the error again.
Definition: MockDevice.cs:370
Provides an endless supply of nonsense sample lists.
static IEnumerable< string > GetFakeLogs(string creator)
Creates a few fake log files.
A task working on a complex parameter set.
Let's pretend the composition is really complex and better done with a custom editor.