-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathIOdevice.vb
2341 lines (1613 loc) · 79.8 KB
/
IOdevice.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
'Class IODevice: multithreaded GPIB/Visa/serial communication
'(C)P.Wzietek 2016
'exported classes:
'IODevice 'abstract (MustInherit) class from which real devices are derived
'IOQuery 'class passed to callback functions
'all definitions contained in "IODevices" namespace (in VB do not define a "default" namespace for this project)
' exported IODevice class methods:
'shared methods:
'Shared Function GetDeviceList
'Shared Function DeviceByName
'ShowDevices
' Shared Sub ParseGpibAddr used by constructor
'instance methods:
'1st version: call callback function when data ready
' 'signature of callback functions:
' Public Delegate Sub IOCallback(ByVal q As IOQuery)
'standard:
'Public Function QueryAsync(ByVal cmd As String, _
' ByVal callback As IOCallback, ByVal retry As Boolean) As Integer
'complete:
'Public Function QueryAsync(ByVal cmd As String, _
' ByVal callback As IOCallback, ByVal retry As Boolean, ByVal cbwait As Boolean, _
' ByVal tag As Integer) As Integer
'2nd version : update textbox with data string
'Public Function QueryAsync(ByVal cmd As String, ByVal text As TextBox, _
' ByVal retry As Boolean) As Integer
'Public Function QueryAsync(ByVal cmd As String, ByVal text As TextBox, _
' ByVal retry As Boolean, ByVal tag As Integer) As Integer
'send command, no response:
'default behavior :
'Public Function SendAsync(ByVal cmd As String, ByVal retry As Boolean)
'complete (with callback)
'Public Function SendAsync(ByVal cmd As String, _
' ByVal callback As IOCallback, ByVal retry As Boolean, _
' ByVal cbwait As Boolean, ByVal tag As Integer) As Integer
'blocking versions ********************
'send command and wait for response
'Public Function QueryBlocking(ByVal cmd As String, ByRef q As IOQuery, ByVal retry As Boolean) As Integer
'Public Function QueryBlocking(ByVal cmd As String, ByRef resp As String, ByVal retry As Boolean) As Integer
'Public Function QueryBlocking(ByVal cmd As String, ByRef resparr As Byte(), ByVal retry As Boolean) As Integer
'send command, no response
'Public Function SendBlocking(ByVal cmd As String, ByVal retry As Boolean) As Integer
'other functions
' Public Function IsBlocking() As Boolean
' true when blocking call in progress
' Public Function PendingTasks() As Integer
' return number of queries in the queue
' Public Function PendingTasks(ByVal cmd As String) As Integer
' 'same for a specific command: number of copies of specific command in the queue
' Public Sub WaitAsync()
' waits until queries queued before the call are done (not until the queue is empty - this may never happen if queries called by timers)
' Public Sub AbortAllTasks()
' Public Sub AddToList() 'register device in "devicelist", should be called by child class constructors (is not called in the base class constructor to avoid registering ill-defined objects when error)
'version 2: adds possibility to use asynchronous notifying:
' Protected Sub WakeUp() interrupts waiting for next reading or poll trial
Imports System.Runtime.InteropServices
Imports System.Drawing
Imports IODeviceForms
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Data
Imports System.Diagnostics
Imports System.Threading
Imports System.Reflection
Imports System.Windows.Forms
'Imports System.Runtime.ExceptionServices 'tentative patch for accessviolation exception (blackdog tests)
Namespace IODevices
Public Class IOQuery
Public cmd As String
'query identifier
Public tag As Integer
Public ReadOnly Property ResponseAsString() As String
'response as string or byte arr depending on query type
Get
Return task.parent.ByteArrayToString(resparr)
End Get
End Property
Public ReadOnly Property ResponseAsByteArray() As Byte()
Get
Return resparr
End Get
End Property
Public status As Integer
'0:ok, otherwise combination:
'bit 1:timeout, bit 2 on send(0)/recv(1), bit 3 : other error (see errcode), , bit 4: aborted, bit 5: poll error, bit 8 callback error
'so if not aborted: status=1 tmo on send; =3 tmo on rcv, =4 other err on send, =6 other err on rcv,
' if aborted add 8 , if poll timeout add 16,
'interface error code (if status>0)
Public errcode As Integer
'error message
Public errmsg As String
'when function called
Public timecall As DateTime
'when device unlocked and operation started
Public timestart As DateTime
Public timeend As DateTime
'query type : 1: without response (cmd only) '2: with response
Public type As Integer
Public ReadOnly Property device() As IODevice
Get
If task IsNot Nothing Then
Return task.parent
Else
Return Nothing
End If
End Get
End Property
'abort this task(async or blocking)
Public Sub AbortRetry()
task.abort = True
End Sub
'abort all queued async commands and active blocking command
Public Sub AbortAll()
task.parent.AbortAllTasks()
End Sub
'private fields
Protected Friend resparr As Byte()
'used to access task fields (abort etc)
Friend task As IODevice.IOTask
'constructor
Friend Sub New(ByVal qtype As Integer, ByVal command As String, ByVal qtag As Integer)
cmd = command
resparr = Nothing
type = qtype
errmsg = ""
timestart = DateTime.MinValue
timeend = DateTime.MinValue
timecall = DateTime.MinValue
task = Nothing
End Sub
End Class
'end of IOQuery
'************ class IODevice *******************************
'*****************************************************************************
Public MustInherit Class IODevice
Implements IDisposable
Public Delegate Sub IOCallback(ByVal q As IOQuery)
'signature of callback functions
'************class IODevice public variables
'optional message (status etc.) to display in devices form (eg used by constructors during init)
Public Shared statusmsg As String
'-------------------device instance variables
'used to limit the number of threads
Public maxtasks As Integer
'device name
Public devname As String
Public devaddr As String
'delay to wait between operations (ms)
Public delayop As Integer
'default delay between cmd and read :
Public delayread As Integer
' delay before poll/read, especially useful to avoid blocking gpib bus by slow devices when polling is not available
'default delay before retrying read after timeout
Public delayrereadontimeout As Integer
' or delay between polls if polling used
'delayread may be overwritten on per task basis
'cumulative timeout for read (to replace effect of delayread)
Public readtimeout As Integer
'delay before retry on timeout
Public delayretry As Integer
'if true repeat read if EOI not detected (eg. buffer too small)
Public checkEOI As Boolean
'if true serial poll before reading to not to block bus
Public enablepoll As Boolean
Public MAVmask As Byte = 16 'for GPIB, USBTMC-USB488, VXI-11: standard (488.2) mask for MAV status (bit 5 of the status byte), change it for devices not compliant with 488.2
Public MAVmaskGBIP232CTA As Byte = 128 'for GPIB, USBTMC-USB488, VXI-11: standard (488.2) mask for MAV status (bit 5 of the status byte), change it for devices not compliant with 488.2
'remove crlf in ByteArrayToString function
Public stripcrlf As Boolean
'for blocking commands during delays and retry loop
Public eventsallowed As Boolean
Public showmessages As Boolean ' error window enabled
Public Const showdevicesonstartup As Boolean = True
Public lastasyncquery As IOQuery ' volatile
'set to false when debugging a new interface
Public catchinterfaceexceptions As Boolean
Public catchcallbackexceptions As Boolean
'if callback on each retry
Public callbackonretry As Boolean
'to override when available
Public Overridable Property EnableNotify() As Boolean
Get
Return False
End Get
Set(ByVal value As Boolean)
If value Then
Throw New NotImplementedException("Notify not implemented for interface '" + interfacename + "'")
End If
End Set
End Property
'***************** private IODevice variables *********************************************************
'
'shared variables and functions
'store refs of all created devices
Protected Shared devlist As List(Of IODevice)
'shared objects to lock a common bus during read/write, index=interfacelockid
Private Shared lockbus As Object()
'will be set to main form
Private Shared frm As Form
Friend Shared devform As New DevicesForm()
'to signal when DevicesForm has to update
Friend Shared updated As Boolean = False
'private instance variables:
'task queue private variables:
'to trigger wakeup of async thread
Protected queue_event As EventWaitHandle = New AutoResetEvent(False)
Private asyncthread As Thread
'lock queue during operations on tasks
Private ReadOnly lockqueue As New Object()
Protected tasks As Queue(Of IOTask)
'other private
'set to to true when disposing to terminate properly (so that timer calls cannot fill queue)
Private disposing As Boolean
'set to true when DisposeDevice() called, so it can be called in main class finalizer
Private devicedisposed As Boolean = False
'index in lockbus: should be set by derived classes to distinguish between interfaces that can be used concurently
Protected interfacelockid As Integer
Protected interfacename As String = ""
'last error message for errors not related to query (queue full, blocking call in progress) to display in devices form
Protected Friend liberrmsg As String
'to calculate delay
Private lastoptime As DateTime
'to lock device during cmd-resp sequence
Private ReadOnly lockdev As New Object()
'per device error message form
Private msgfrm As IODeviceForms.IOmsgForm
'currently existing async task or nothing if finished (volatile)
Private currentasynctask As IOTask
'currently existing blocking task or nothing if finished
Private currentblockingtask As IOTask
' task that currently has lock on device (may switch between async/blocking during retry loops)
Protected currentactivetask As IOTask
'currentactivequery: to use in implementation eg if need to know the command to format the response accordingly
Protected ReadOnly Property currentactivequery() As IOQuery
Get
Dim ca As IOTask = currentactivetask 'make a copy of volatile objects
If ca Is Nothing Then
Return Nothing
Else
Return ca.query
End If
End Get
End Property
'event used to wait (sleep) in async routines so that notify can break waiting
Private notify_event As EventWaitHandle = New AutoResetEvent(False)
'equivalent boolean flag used in waiting loops in blocking calls
Private notify_flag As Boolean
' ********** public methods
'static constructor
Shared Sub New()
devlist = New List(Of IODevice)()
Dim i As Integer = 0
lockbus = New Object(100) {}
For i = 0 To lockbus.Length - 1
lockbus(i) = New Object()
Next
frm = Application.OpenForms(0)
'set to Main Form
' (null ref ok if callbacks can be executed on a not-GUI thread, without invoke, and if messages are disabled)
If frm Is Nothing OrElse (frm.IsDisposed Or frm.Disposing) Then
Throw New Exception("error creating IODevice class: main form null or disposed")
End If
'other init: send ifc etc: in derived classes
'
updated = False
If showdevicesonstartup Then ShowDevices()
End Sub
'------------------- IODevice public shared methods
'Public Shared Sub ShowDevices()
'If devform Is Nothing OrElse devform.IsDisposed OrElse devform.Disposing Then
' devform = New DevicesForm()
'End If
''if disposing
'Try
' devform.Show()
' devform.WindowState = FormWindowState.Normal
' devform.BringToFront()
'Catch
'End Try
'End Sub
'Public Shared Sub DisposeAll()
' devform.Close()
''faster if signal abort for all before shutting down one by one
'For Each device As IODevice In devlist
' device.AbortAllTasks()
'Next
'While devlist.Count > 0
' devlist(0).Dispose()
' Application.DoEvents()
'End While
'End Sub
' Position the IODevices pop-up immediately to the right of the main app form.
' There is a big hack in here because on first run the pop-up was positioned fine, but on subsequent pop-ups it set itself x2 in both x and y direction.
' It seems to pop-up appeared twice, the main app under btncreate sub effectively calls the pop-up twice
' Declare the Windows API function to get window rectangle
<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function GetWindowRect(ByVal hWnd As IntPtr, ByRef lpRect As RECT) As Boolean
End Function
' RECT structure to store window's position and size
Private Structure RECT
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
' Store the instance of the form
'Friend Shared devform As DevicesForm
' Static counter to track the number of times ShowDevices has been called
Private Shared showCount As Integer = 0
' Shared method to show the devices form
Public Shared Sub ShowDevices()
' Increment the call counter
showCount += 1
' Instantiate the DevicesForm if it doesn't exist or has been disposed
If devform Is Nothing OrElse devform.IsDisposed OrElse devform.Disposing Then
updated = False
devform = New DevicesForm()
End If
' Get the current process (main application)
Dim mainApp As Process = Process.GetCurrentProcess()
' Get the main window handle
Dim mainWindowHandle As IntPtr = mainApp.MainWindowHandle
' Get the position of the main window
Dim mainWindowRect As New RECT()
If GetWindowRect(mainWindowHandle, mainWindowRect) Then
' Base offsets
Dim baseXOffset As Integer = 530
Dim baseYOffset As Integer = 0
' Adjust offsets based on how many times the form has been shown.....this is a bit of a hack due to the pop-up effectively being called twice by the main app.
Dim xOffset As Integer = 0
Dim yOffset As Integer = 0
If showCount < 2 Then ' first two times the pop-up appears the offset is zero so it positions correctly
xOffset = 0
yOffset = 0
Exit Sub ' Update: this added to abort, it seems that the pop-up appears twice when FIRST run after starting the app and this abort fixes that. Need to get to bottom of why this double pop-up happens on first run of WinGPIB
End If
If showCount >= 2 Then
xOffset = baseXOffset * 2 ' more than two times the position needs adjusted
yOffset = baseYOffset * 2
End If
Dim x As Integer = mainWindowRect.Left + xOffset
Dim y As Integer = mainWindowRect.Top + yOffset
' only set position of pop-up if Dock mode enabled
If My.Settings.dataDock = True Then
' Set the location of the form
devform.StartPosition = FormStartPosition.Manual
devform.Location = New Point(x, y)
End If
' Ensure form is shown and brought to the front
devform.Show()
devform.BringToFront()
End If
Try
devform.WindowState = FormWindowState.Normal
Catch ex As Exception
' Handle any exceptions here if needed
Debug.WriteLine($"Error: {ex.Message}")
End Try
End Sub
' Method to dispose all resources
Public Shared Sub DisposeAll()
' Check if the form is not null and is still open before closing
If devform IsNot Nothing AndAlso Not devform.IsDisposed AndAlso devform.Visible Then
devform.Close()
End If
' Faster if signal abort for all before shutting down one by one
For Each device As IODevice In devlist
device.AbortAllTasks()
Next
' Dispose of all devices
While devlist.Count > 0
devlist(0).Dispose()
'devlist.RemoveAt(0) ' Ensure you remove the disposed item from the list
Application.DoEvents() ' Allow UI thread to process other events
End While
End Sub
'used by DevicesForm
Public Shared Function GetDeviceList(ByVal details As Boolean) As String()
Dim sl As String() = Nothing
Dim n = devlist.Count
If n = 0 Then
Return Nothing
End If
Dim i As Short = 0
Dim s As String = Nothing
Dim sc As String = ""
Dim st As String = ""
Dim si As String = ""
Dim scmd As String = Nothing
Dim cbt As IOTask = Nothing
Dim cat As IOTask = Nothing
Dim ct As IOTask = Nothing
For i = 0 To n - 1
If devlist(i) Is Nothing Then
Continue For
End If
si = "(" & devlist(i).interfacename & ")"
s = devlist(i).devname & "@"
st = ""
cbt = devlist(i).currentblockingtask
'make a copy of volatile objects!!!
Interlocked.Exchange(cat, devlist(i).currentasynctask)
Interlocked.Exchange(ct, devlist(i).currentactivetask)
sc = Convert.ToString(devlist(i).devaddr)
If ct IsNot Nothing Then
If ct.blocking Then
st += ", blocking: "
Else
st += ", async: "
End If
st += "'" & ct.query.cmd & "' "
If devlist(i).PendingTasks() > 0 Then
st += ", pending:" & devlist(i).PendingTasks().ToString()
End If
If Not String.IsNullOrEmpty(ct.query.errmsg) Then
st += ", error:" & ct.query.errmsg
If (cbt IsNot Nothing AndAlso cbt.retry) Or (cat IsNot Nothing AndAlso cat.retry) Then
st += " (retrying...)"
End If
End If
End If
If Not String.IsNullOrEmpty(devlist(i).liberrmsg) Then
st += ", " & devlist(i).liberrmsg
End If
If sl Is Nothing Then
sl = New String(0) {}
Else
Array.Resize(sl, sl.Length + 1)
End If
sl(sl.Length - 1) = si & " " & s & sc & st
'queue
If details Then
'make sure the task list does not change during loop
SyncLock devlist(i).lockqueue
For Each task As IOTask In devlist(i).tasks
If (task IsNot Nothing) Then
scmd = " " & "'" & task.query.cmd & "'"
Array.Resize(sl, sl.Length + 1)
sl(sl.Length - 1) = scmd
End If
Next
End SyncLock
End If
Next
Return sl
End Function
'find device in list using name
Public Shared Function DeviceByName(ByVal name As String) As IODevice
For Each d As IODevice In devlist
If d.devname = name Then
Return d
End If
Next
Return Nothing
End Function
' helper function to interpret gpib or visa type address (simple version, might be rewritten using regex class)
' address may be just a number eg "9", then board will be set to 0
'or "0:9", "GPIB0::9", "GPIB0:9", "GPIB0::9::INSTR" etc
'will
Public Shared Sub ParseGpibAddr(ByVal address As String, ByRef board As Integer, ByRef gpibaddr As Byte)
'interpret address
Dim sarr As String() = Nothing
sarr = address.ToUpper().Split(":".ToCharArray())
Try
Select Case sarr.Length
Case 0
Throw New Exception("invalid address format: " & address)
Case 1
board = 0
gpibaddr = Byte.Parse(sarr(0))
Exit Select
Case Else
If sarr(0).Contains("GPIB") Then
Dim bs As String = sarr(0).Substring(4)
If bs.Length = 0 Then
board = 0
Else
board = Integer.Parse(sarr(0).Substring(4))
End If
Else
board = Integer.Parse(sarr(0))
End If
Dim idx As Integer = 1
While idx < sarr.Length AndAlso String.IsNullOrEmpty(sarr(idx))
idx += 1
End While
If idx = sarr.Length Then
Throw New Exception("invalid address format: " & address)
End If
gpibaddr = Byte.Parse(sarr(idx))
Exit Select
End Select
Catch
Throw New Exception("invalid address format: " & address)
End Try
If gpibaddr = 0 Then
Throw New Exception("invalid address format: " & address)
End If
End Sub
'************class IODevice public instance methods *******************************
'*******************************************
' constructor
Public Sub New(ByVal name As String, ByVal addr As String)
If frm Is Nothing OrElse (frm.IsDisposed Or frm.Disposing) Then
Throw New Exception("error creating IODevice " & name & ": main form null or disposed")
End If
devname = name
devaddr = addr
'name and addr used only to display by devicesform and in error messages
'set default options
delayop = 1
delayread = 20
delayrereadontimeout = 80
delayretry = 1000
readtimeout = 5000
'cumulative timeout, independent of interface settings
showmessages = True
catchinterfaceexceptions = True
'may be useful to set to false during debugging interface routines
catchcallbackexceptions = True
callbackonretry = True
maxtasks = 50
checkEOI = True
eventsallowed = True
enablepoll = True
stripcrlf = True
'create task queue:
tasks = New Queue(Of IOTask)()
'start async thread:
currentasynctask = Nothing
currentblockingtask = Nothing
currentactivetask = Nothing
asyncthread = New Thread(AddressOf AsyncThreadProc)
asyncthread.IsBackground = True
asyncthread.Start()
lastoptime = DateTime.Now
disposing = False
'add device to global list:
' moved to child class constructors (will not be called if exception occurs)
updated = False
End Sub
' interrupts waiting for next reading or poll trial
' may be called e.g. by "Notify" callback (if defined by the implementation) when data ready
'can be called from any thread
Protected Sub WakeUp()
'we don't know from which thread it will be called therefore signal is sent to both blocking and async tasks (will be rearmed ion next command anyway)
notify_flag = True
'used in waitdelay
Try 'may cause error on disposing
notify_event.Set()
'for async calls
Catch
End Try
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If disposing Then
Return
End If
AbortAllTasks()
disposing = True
'prevent new tasks to be appended, signal async thread to exit
If showmessages Then
Try
msg_end_TS()
Catch
End Try
End If
queue_event.[Set]()
'set event to wake up async thread
EnqueueTask(Nothing)
'signal async thread to exit
'wait 3s for the async thread to finish gracefully
If Not asyncthread.Join(3000) Then
asyncthread.Abort()
End If
queue_event.Close()
notify_event.Close()
devlist.Remove(Me)
If msgfrm IsNot Nothing AndAlso Not msgfrm.IsDisposed Then
msgfrm.Close()
End If
' Release external unmanaged resources
If Not devicedisposed Then
Try
DisposeDevice()
devicedisposed = True
'will be tested in finalizer
GC.SuppressFinalize(Me)
Catch
End Try
End If
End Sub
'finalizer: if not disposed properly make sure the unmanaged
'resources are released (eg notify handler uninstalled) before the object is garbage-collected!
Protected Overrides Sub Finalize()
Try
If Not devicedisposed Then
DisposeDevice()
End If
Finally
devicedisposed = True
MyBase.Finalize()
End Try
End Sub
Public Function PendingTasks() As Integer
Dim retval As Integer = 0
SyncLock lockqueue
retval = tasks.Count
End SyncLock
Return retval
End Function
Public Function PendingTasks(ByVal t As DateTime) As Integer
'version counting only tasks with queries called before or at t
Dim p As Short = 0
SyncLock lockqueue
For Each task As IOTask In tasks
If (task IsNot Nothing) Then
If task.query.timecall <= t Then
p += 1
End If
End If
Next
End SyncLock
Return p
End Function
Public Function PendingTasks(ByVal cmd As String) As Integer
'version counting only tasks with certain commands
'case insensitive
Dim p As Short = 0
'make sure async thread does not remove a task during the loop
SyncLock lockqueue
For Each task As IOTask In tasks
If (task IsNot Nothing) Then
If task.query.cmd.ToUpper() = cmd.ToUpper() Then
p += 1
End If
End If
Next
End SyncLock
Return p
End Function
Public Function PendingTasks(ByVal tag As Integer) As Integer
'version counting only tasks with certain tag values
Dim p As Short = 0
'make sure async thread does not remove a task during the loop
SyncLock lockqueue
For Each task As IOTask In tasks
If (task IsNot Nothing) Then
If task.query.tag = tag Then
p += 1
End If
End If
Next
End SyncLock
Return p
End Function
Public Sub AbortAllTasks()
SyncLock lockqueue
For Each task As IOTask In tasks
If (task IsNot Nothing) Then
task.abort = True
End If
Next
Try
If currentasynctask IsNot Nothing Then
currentasynctask.abort = True
End If
If currentblockingtask IsNot Nothing Then
currentblockingtask.abort = True
End If
If currentactivetask IsNot Nothing Then
currentactivetask.abort = True
End If
Catch
End Try
End SyncLock
End Sub
Public Sub WaitAsync()
WaitAsync(DateTime.Now.AddTicks(2))
'make sure for last command
End Sub
'wait until async queries queued before the call are done (usually set ts to "Now")
Public Sub WaitAsync(ByVal ts As DateTime)
Dim t As IOTask = Nothing
Dim bp As Boolean = False
Dim bt As Boolean = False
Dim p As Integer = 0
Dim pt As Integer = 0
Do
t = currentasynctask
p = PendingTasks(ts)
pt = PendingTasks()
bp = (p = 0)
bt = (t Is Nothing OrElse t.query.timecall > ts)
Application.DoEvents()
Thread.Sleep(1)
Loop While Not ((bp And bt) Or disposing)
End Sub
Public Function IsBlocking() As Boolean
Return currentblockingtask IsNot Nothing
End Function