hardwarelibrary 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. hardwarelibrary/.idea/inspectionProfiles/profiles_settings.xml +6 -0
  2. hardwarelibrary/.idea/vcs.xml +6 -0
  3. hardwarelibrary/.idea/workspace.xml +39 -0
  4. hardwarelibrary/__init__.py +19 -0
  5. hardwarelibrary/__main__.py +76 -0
  6. hardwarelibrary/cameras/__init__.py +2 -0
  7. hardwarelibrary/cameras/camera.py +181 -0
  8. hardwarelibrary/communication/.idea/.gitignore +3 -0
  9. hardwarelibrary/communication/.idea/communication.iml +12 -0
  10. hardwarelibrary/communication/.idea/inspectionProfiles/profiles_settings.xml +6 -0
  11. hardwarelibrary/communication/.idea/misc.xml +4 -0
  12. hardwarelibrary/communication/.idea/modules.xml +8 -0
  13. hardwarelibrary/communication/.idea/vcs.xml +6 -0
  14. hardwarelibrary/communication/__init__.py +53 -0
  15. hardwarelibrary/communication/commands.py +183 -0
  16. hardwarelibrary/communication/communicationport.py +131 -0
  17. hardwarelibrary/communication/debugport.py +98 -0
  18. hardwarelibrary/communication/diagnostics.py +427 -0
  19. hardwarelibrary/communication/echoport.py +10 -0
  20. hardwarelibrary/communication/libusb/Darwin/libusb-1.0.0.dylib +0 -0
  21. hardwarelibrary/communication/libusb/MS32/libusb-1.0.dll +0 -0
  22. hardwarelibrary/communication/libusb/MS64/libusb-1.0.dll +0 -0
  23. hardwarelibrary/communication/serialport.py +211 -0
  24. hardwarelibrary/communication/usbport.py +168 -0
  25. hardwarelibrary/daq/__init__.py +4 -0
  26. hardwarelibrary/daq/daqdevice.py +58 -0
  27. hardwarelibrary/daq/labjackdevice.py +42 -0
  28. hardwarelibrary/devicemanager.py +320 -0
  29. hardwarelibrary/echodevice.py +43 -0
  30. hardwarelibrary/manuals/Cobolt-owners-manual-0401-D0105-A.pdf +0 -0
  31. hardwarelibrary/manuals/HR2000+-OEM-Data-Sheet.pdf +0 -0
  32. hardwarelibrary/manuals/MPC-325_OpMan.pdf +0 -0
  33. hardwarelibrary/manuals/Mightex CCD Line Camera Din8 Connector Description.pdf +0 -0
  34. hardwarelibrary/manuals/Mightex CCD Linear Camera User Manual.pdf +0 -0
  35. TDS 2000B Manual.pdf +0 -0
  36. hardwarelibrary/manuals/Tektronix TDS-1002 Programming manual.pdf +0 -0
  37. hardwarelibrary/manuals/USB Primer.pdf +0 -0
  38. hardwarelibrary/manuals/USB2000+-OEM-Data-Sheet.pdf +0 -0
  39. hardwarelibrary/manuals/USB2000-OEM-Data-Sheet.pdf +0 -0
  40. hardwarelibrary/manuals/User_Manual_Integra_V3.0.pdf +0 -0
  41. hardwarelibrary/manuals/usb4000-oem-data-sheet.pdf +0 -0
  42. hardwarelibrary/motion/__init__.py +7 -0
  43. hardwarelibrary/motion/intellidrivedevice.py +119 -0
  44. hardwarelibrary/motion/linearmotiondevice.py +126 -0
  45. hardwarelibrary/motion/rotationdevice.py +66 -0
  46. hardwarelibrary/motion/sutterdevice.py +173 -0
  47. hardwarelibrary/notificationcenter.py +103 -0
  48. hardwarelibrary/oscilloscope/__init__.py +3 -0
  49. hardwarelibrary/oscilloscope/oscilloscopedevice.py +185 -0
  50. hardwarelibrary/physicaldevice.py +214 -0
  51. hardwarelibrary/powermeters/__init__.py +3 -0
  52. hardwarelibrary/powermeters/integradevice.py +50 -0
  53. hardwarelibrary/powermeters/powermeterdevice.py +38 -0
  54. hardwarelibrary/sources/__init__.py +2 -0
  55. hardwarelibrary/sources/cobolt.py +287 -0
  56. hardwarelibrary/sources/lasersourcedevice.py +20 -0
  57. hardwarelibrary/spectrometers/__init__.py +41 -0
  58. hardwarelibrary/spectrometers/base.py +299 -0
  59. hardwarelibrary/spectrometers/darkbulb.png +0 -0
  60. hardwarelibrary/spectrometers/intelhexreader.py +130 -0
  61. hardwarelibrary/spectrometers/lightbulb.png +0 -0
  62. hardwarelibrary/spectrometers/oceaninsight.py +961 -0
  63. hardwarelibrary/spectrometers/stellarnet.zip +0 -0
  64. hardwarelibrary/spectrometers/viewer.py +288 -0
  65. hardwarelibrary/tests/deprecated/cobolt.json +38 -0
  66. hardwarelibrary/tests/deprecated/socketclient.py +35 -0
  67. hardwarelibrary/tests/deprecated/socketmain.py +39 -0
  68. hardwarelibrary/tests/deprecated/testCobolt.py +116 -0
  69. hardwarelibrary/tests/deprecated/testCoboltDevice.py +111 -0
  70. hardwarelibrary/tests/deprecated/testCoboltSerial.py +102 -0
  71. hardwarelibrary/tests/deprecated/testIntegraSerialPort.py +35 -0
  72. hardwarelibrary/tests/deprecated/testJSON.py +14 -0
  73. hardwarelibrary/tests/deprecated/usbFinder.py +15 -0
  74. hardwarelibrary/tests/env.py +17 -0
  75. hardwarelibrary/tests/testCommunicationPorts.py +354 -0
  76. hardwarelibrary/tests/testConnectSutter.py +62 -0
  77. hardwarelibrary/tests/testDeviceManager.py +259 -0
  78. hardwarelibrary/tests/testIntegraDevice.py +149 -0
  79. hardwarelibrary/tests/testIntellidrive.py +173 -0
  80. hardwarelibrary/tests/testLabjackU3.py +108 -0
  81. hardwarelibrary/tests/testLinearMotionDevice.py +181 -0
  82. hardwarelibrary/tests/testMapPositionsFunction.py +79 -0
  83. hardwarelibrary/tests/testModulePyFTDI.py +70 -0
  84. hardwarelibrary/tests/testModulePySerial.py +41 -0
  85. hardwarelibrary/tests/testNotificationCenter.py +154 -0
  86. hardwarelibrary/tests/testOptotuneDevice.py +91 -0
  87. hardwarelibrary/tests/testOscilloscope.py +288 -0
  88. hardwarelibrary/tests/testPhysicalDevice.py +312 -0
  89. hardwarelibrary/tests/testPyUSB.py +58 -0
  90. hardwarelibrary/tests/testSerialHardwareFLow.py +60 -0
  91. hardwarelibrary/tests/testSutterDevice.py +123 -0
  92. hardwarelibrary/tests/testSutterSerialPort.py +207 -0
  93. hardwarelibrary/utils.py +160 -0
  94. hardwarelibrary-0.0.0.dist-info/METADATA +279 -0
  95. hardwarelibrary-0.0.0.dist-info/RECORD +97 -0
  96. hardwarelibrary-0.0.0.dist-info/WHEEL +5 -0
  97. hardwarelibrary-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,6 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,39 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ChangeListManager">
4
+ <list default="true" id="90ca4116-5fad-4c16-b2e5-e7165d36af6f" name="Default Changelist" comment="">
5
+ <change beforePath="$PROJECT_DIR$/../.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../.idea/workspace.xml" afterDir="false" />
6
+ <change beforePath="$PROJECT_DIR$/communication/serialport.py" beforeDir="false" afterPath="$PROJECT_DIR$/communication/serialport.py" afterDir="false" />
7
+ </list>
8
+ <option name="SHOW_DIALOG" value="false" />
9
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
10
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
11
+ <option name="LAST_RESOLUTION" value="IGNORE" />
12
+ </component>
13
+ <component name="Git.Settings">
14
+ <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
15
+ </component>
16
+ <component name="ProjectId" id="1uLsLaSQEOdaPgWc7Tr7BFMizJv" />
17
+ <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
18
+ <component name="ProjectViewState">
19
+ <option name="hideEmptyMiddlePackages" value="true" />
20
+ <option name="showLibraryContents" value="true" />
21
+ </component>
22
+ <component name="PropertiesComponent">
23
+ <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
24
+ <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
25
+ <property name="last_opened_file_path" value="$PROJECT_DIR$" />
26
+ <property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
27
+ </component>
28
+ <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
29
+ <component name="TaskManager">
30
+ <task active="true" id="Default" summary="Default task">
31
+ <changelist id="90ca4116-5fad-4c16-b2e5-e7165d36af6f" name="Default Changelist" comment="" />
32
+ <created>1624456151873</created>
33
+ <option name="number" value="Default" />
34
+ <option name="presentableId" value="Default" />
35
+ <updated>1624456151873</updated>
36
+ </task>
37
+ <servers />
38
+ </component>
39
+ </project>
@@ -0,0 +1,19 @@
1
+ __version__ = "1.0.5"
2
+ __author__ = "Daniel Cote <dccote@cervo.ulaval.ca>"
3
+
4
+ __all__ = ["communication","motion","oscilloscope","powermeters","spectrometers"]
5
+
6
+ # We want the modules at the top level
7
+ from .devicemanager import *
8
+
9
+ from hardwarelibrary.physicaldevice import *
10
+ from hardwarelibrary.notificationcenter import *
11
+ from hardwarelibrary.communication import *
12
+
13
+ # We want these modules in their namespace
14
+ import hardwarelibrary.spectrometers
15
+ import hardwarelibrary.oscilloscope
16
+ import hardwarelibrary.motion
17
+ # import hardwarelibrary.cameras
18
+
19
+ #import sources #TODO: Not much to see here yet
@@ -0,0 +1,76 @@
1
+ import os
2
+ import sys
3
+ import argparse
4
+ import re
5
+ import subprocess
6
+ from pathlib import Path
7
+ import platform
8
+ import hardwarelibrary.spectrometers as spectro
9
+ from hardwarelibrary import DeviceManager, connectedUSBDevices
10
+
11
+ import signal
12
+ import sys
13
+
14
+ def signal_handler(sig, frame):
15
+ dm = DeviceManager()
16
+ if dm.isMonitoring and not dm.quitMonitoring:
17
+ print('Quitting nicely')
18
+ dm.stopMonitoring()
19
+
20
+ signal.signal(signal.SIGINT, signal_handler)
21
+
22
+ # We start by figuring out what the user really wants. If they don't know,
23
+ # we offer some help
24
+ ap = argparse.ArgumentParser(prog='python -m hardwarelibrary')
25
+ ap.add_argument("-stellar", "--stellarnet", required=False, action='store_const',
26
+ const=True, help="Decrypt the StellarnNet driver. Contact StellarNet for info.")
27
+ ap.add_argument("-s", "--spectrometer", required=False, action='store_const',
28
+ const=True, help="Display any spectrometer")
29
+ ap.add_argument("-l", "--list", required=False, action='store_const',
30
+ const=True, help="List all USB devices")
31
+ ap.add_argument("-dm", "--devicemanager", required=False, action='store_const',
32
+ const=True, help="Show notifications from DeviceManager")
33
+ ap.add_argument("-d", "--debugusb", required=False, action='store_const',
34
+ const=True, help="Help debugging USB libraries issues")
35
+
36
+ args = vars(ap.parse_args())
37
+ decrypt = args['stellarnet']
38
+ displaySpectrum = args['spectrometer']
39
+ deviceManager = args['devicemanager']
40
+ debugUSB = args['debugusb']
41
+ listAll = args['list']
42
+
43
+ if listAll == True:
44
+ devices = connectedUSBDevices()
45
+ for device in devices:
46
+ print(device)
47
+
48
+ if decrypt == True:
49
+ rootHardwareLibrary = Path(os.path.abspath(__file__)).parents[1]
50
+ spectrometersDirectory = rootHardwareLibrary.joinpath('hardwarelibrary/spectrometers')
51
+ zipFile =spectrometersDirectory.joinpath('stellarnet.zip')
52
+
53
+ if platform.system() == 'Windows':
54
+ os.startfile(zipFile)
55
+ elif platform.system() == 'Darwin':
56
+ completedProcess = subprocess.run(["unzip", zipFile, "-d", spectrometersDirectory])
57
+ if completedProcess.returncode != 0:
58
+ print("There was an error when unzipping and decrypting the stellarnet.zip file")
59
+ else:
60
+ print('Cannot unzip stellarnet file, you must unzip it manually : {0}'.format(zipFile))
61
+
62
+ if displaySpectrum == True:
63
+ spectro.displayAny()
64
+
65
+ if deviceManager == True:
66
+ dm = DeviceManager()
67
+ dm.showNotifications()
68
+ dm.startMonitoring()
69
+
70
+ if debugUSB == True:
71
+ from hardwarelibrary.communication import validateUSBBackend
72
+ backend = validateUSBBackend(verbose=True)
73
+ if backend is not None:
74
+ print("PyUSB found and uses backend from: {0}".format(backend.lib))
75
+ else:
76
+ print("PyUSB has not found a backend to operate.")
@@ -0,0 +1,2 @@
1
+
2
+ from .camera import OpenCVCamera, CameraDeviceNotification
@@ -0,0 +1,181 @@
1
+ import time
2
+ from enum import Enum
3
+ from hardwarelibrary.physicaldevice import PhysicalDevice
4
+ from hardwarelibrary.notificationcenter import NotificationCenter, Notification
5
+ from threading import Thread, RLock
6
+ try:
7
+ import cv2
8
+ except Exception as err:
9
+ print("No support for OpenCVcameras")
10
+ pass
11
+
12
+ import re
13
+
14
+ class CameraDeviceNotification(Enum):
15
+ willStartCapture = "willStartCapture"
16
+ didStartCapture = "didStartCapture"
17
+ willStopCapture = "willStopCapture"
18
+ didStopCapture = "didStopCapture"
19
+ imageCaptured = "imageCaptured"
20
+
21
+ class CameraDevice(PhysicalDevice):
22
+ def __init__(self, serialNumber:str = None, idProduct:int = None, idVendor:int = None):
23
+ super().__init__(serialNumber, idProduct, idVendor)
24
+ self.version = ""
25
+ self.quitLoop = False
26
+ self.lock = RLock()
27
+ self.mainLoop = None
28
+
29
+ def doInitializeDevice(self):
30
+ pass # nothing to do, but incuded by symmetry with doShutdown
31
+
32
+ def doShutdownDevice(self):
33
+ with self.lock:
34
+ if self.isCapturing:
35
+ self.stop()
36
+
37
+ def livePreview(self):
38
+ NotificationCenter().postNotification(notificationName=CameraDeviceNotification.willStartCapture,
39
+ notifyingObject=self)
40
+ self.captureLoopSynchronous()
41
+
42
+ def start(self):
43
+ with self.lock:
44
+ if not self.isCapturing:
45
+ self.quitLoop = False
46
+ self.mainLoop = Thread(target=self.captureLoop, name="Camera-CaptureLoop")
47
+ NotificationCenter().postNotification(notificationName=CameraDeviceNotification.willStartCapture, notifyingObject=self)
48
+ self.mainLoop.start()
49
+ else:
50
+ raise RuntimeError("Capture loop already running")
51
+
52
+ @property
53
+ def isCapturing(self):
54
+ return self.mainLoop is not None
55
+
56
+ def stop(self):
57
+ if self.isCapturing:
58
+ NotificationCenter().postNotification(CameraDeviceNotification.willStopCapture, notifyingObject=self)
59
+ with self.lock:
60
+ self.quitLoop = True
61
+ self.mainLoop.join()
62
+ self.mainLoop = None
63
+ NotificationCenter().postNotification(CameraDeviceNotification.didStopCapture, notifyingObject=self)
64
+ else:
65
+ raise RuntimeError("No monitoring loop running")
66
+
67
+ def captureLoopSynchronous(self):
68
+ frame = None
69
+ NotificationCenter().postNotification(notificationName=CameraDeviceNotification.didStartCapture,
70
+ notifyingObject=self)
71
+ while (True):
72
+ frame = self.doCaptureFrame()
73
+ NotificationCenter().postNotification(CameraDeviceNotification.imageCaptured, self, frame)
74
+
75
+ cv2.imshow('Preview (Q to quit)', frame)
76
+ if cv2.waitKey(1) & 0xFF == ord('q'):
77
+ NotificationCenter().postNotification(CameraDeviceNotification.willStopCapture, notifyingObject=self)
78
+ break
79
+
80
+ cv2.destroyAllWindows()
81
+ NotificationCenter().postNotification(CameraDeviceNotification.didStopCapture, notifyingObject=self)
82
+
83
+ def captureFrames(self, n=1):
84
+ frames = []
85
+ for i in range(n):
86
+ frames.append(self.doCaptureFrame())
87
+ return frames
88
+
89
+ def captureLoopThread(self):
90
+ frame = None
91
+ NotificationCenter().postNotification(notificationName=CameraDeviceNotification.didStartCapture,
92
+ notifyingObject=self)
93
+ while (True):
94
+ frame = self.doCaptureFrame()
95
+ NotificationCenter().postNotification(CameraDeviceNotification.imageCaptured, self, frame)
96
+
97
+ if self.quitLoop:
98
+ return
99
+
100
+ class OpenCVCamera(CameraDevice):
101
+
102
+ def __init__(self, serialNumber:str = None, idProduct:int = None, idVendor:int = None):
103
+ super().__init__(serialNumber, idProduct, idVendor)
104
+ self.version = ""
105
+ self.cameraHandler = None
106
+ self.cvCameraIndex = 0
107
+ if serialNumber is not None:
108
+ self.cvCameraIndex = int(serialNumber)
109
+
110
+ def openCVProperties(self):
111
+ properties = {}
112
+ for property in dir(cv2):
113
+ if re.search('CAP_', property) is not None:
114
+ indexProp = getattr(cv2, property)
115
+ value = cam.get(indexProp)
116
+ if value != 0:
117
+ properties[property] = value
118
+ return properties
119
+
120
+ @classmethod
121
+ def isCompatibleWith(cls, serialNumber, idProduct, idVendor):
122
+ return False
123
+
124
+ try:
125
+ cameraIndex = int(serialNumber)
126
+ except Exception as err:
127
+ cameraIndex = 0
128
+
129
+ cam = cv2.VideoCapture(cameraIndex)
130
+ wasAcquired, frame = cam.read()
131
+ cam.release()
132
+
133
+ return wasAcquired
134
+
135
+ def doInitializeDevice(self):
136
+ super().doInitializeDevice()
137
+ with self.lock:
138
+ # FIXME: Open the first camera we find
139
+ self.cameraHandler = cv2.VideoCapture(self.cvCameraIndex)
140
+
141
+ if self.cameraHandler is None:
142
+ raise Exception("Could not open video device")
143
+
144
+ if not (self.cameraHandler.isOpened()):
145
+ raise Exception("Could not open video device")
146
+
147
+ def doShutdownDevice(self):
148
+ super().doShutdownDevice()
149
+ with self.lock:
150
+ self.cameraHandler.release()
151
+ self.cameraHandler = None
152
+
153
+ def doCaptureFrame(self):
154
+ with self.lock:
155
+ # Capture frame-by-frame
156
+ ret, frame = self.cameraHandler.read()
157
+ return frame
158
+
159
+ @classmethod
160
+ def availableCameras(cls):
161
+ return 0
162
+
163
+ numberOfCameras = 0
164
+ for i in range(10):
165
+ cam = cv2.VideoCapture(i)
166
+ wasAcquired, frame = cam.read()
167
+ cam.release()
168
+
169
+ if not wasAcquired:
170
+ break
171
+
172
+ numberOfCameras += 1
173
+
174
+ return numberOfCameras
175
+
176
+ if __name__ == "__main__":
177
+ print(OpenCVCamera.availableCameras())
178
+ cam = OpenCVCamera()
179
+ cam.initializeDevice()
180
+ cam.livePreview()
181
+ cam.shutdownDevice()
@@ -0,0 +1,3 @@
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$" />
5
+ <orderEntry type="inheritedJdk" />
6
+ <orderEntry type="sourceFolder" forTests="false" />
7
+ </component>
8
+ <component name="PyDocumentationSettings">
9
+ <option name="format" value="PLAIN" />
10
+ <option name="myDocStringFormat" value="Plain" />
11
+ </component>
12
+ </module>
@@ -0,0 +1,6 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9" project-jdk-type="Python SDK" />
4
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/communication.iml" filepath="$PROJECT_DIR$/.idea/communication.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,53 @@
1
+ from .communicationport import *
2
+ from .serialport import SerialPort
3
+ from .usbport import USBPort
4
+ from .diagnostics import USBParameters, DeviceCommand, USBDeviceDescription
5
+ from .debugport import DebugPort
6
+ from .echoport import DebugEchoPort
7
+ import usb.backend.libusb1
8
+ import platform
9
+ from pathlib import *
10
+ import os
11
+
12
+ def validateUSBBackend(verbose=False):
13
+ backend = usb.backend.libusb1.get_backend()
14
+ if backend is not None:
15
+ try:
16
+ usb.core.find(backend=backend)
17
+ return backend
18
+ except:
19
+ if verbose:
20
+ print("The default backend search of PyUSB does not find a libusb backend")
21
+
22
+ libusbPath = None
23
+ candidates = []
24
+ if platform.system() == 'Windows':
25
+ rootHardwareLibrary = PureWindowsPath(os.path.abspath(__file__)).parents[1]
26
+ candidates = [rootHardwareLibrary.joinpath('communication/libusb/MS64/libusb-1.0.dll'),
27
+ rootHardwareLibrary.joinpath('communication/libusb/MS32/libusb-1.0.dll')]
28
+ elif platform.system() == 'Darwin':
29
+ rootHardwareLibrary = PurePosixPath(os.path.abspath(__file__)).parents[1]
30
+ candidates = [rootHardwareLibrary.joinpath('communication/libusb/Darwin/libusb-1.0.0.dylib')]
31
+ else:
32
+ if verbose:
33
+ otherDir = rootHardwareLibrary.joinpath('communication/libusb/other')
34
+ print("""Platform not recognized {1} and default PyUSB backend not found. You should try installing libusb.
35
+ If it does not work out of the box, you can try copying the library in the {0} directory""".format(otherDir, platform.system()))
36
+ candidates = os.listdir(otherDir)
37
+
38
+ for libpath in candidates:
39
+ if os.path.exists(libpath):
40
+ backend = usb.backend.libusb1.get_backend(find_library=lambda x: "{0}".format(libpath))
41
+ if backend is not None:
42
+ try:
43
+ usb.core.find(backend=backend)
44
+ return backend
45
+ except:
46
+ pass
47
+ else:
48
+ if verbose:
49
+ print("Library candidate {0} does not exist".format(libpath))
50
+
51
+ return None
52
+
53
+ validateUSBBackend()
@@ -0,0 +1,183 @@
1
+ import re
2
+
3
+ class Command:
4
+ def __init__(self, name:str, endPoints = (None, None)):
5
+ self.name = name
6
+ self.reply = None
7
+ self.matchGroups = None
8
+ self.endPoints = endPoints
9
+
10
+ self.isSent = False
11
+ self.isSentSuccessfully = False
12
+ self.exceptions = []
13
+
14
+ self.isReplyReceived = False
15
+ self.isReplyReceivedSuccessfully = False
16
+
17
+ @property
18
+ def payload(self):
19
+ return None
20
+
21
+ @property
22
+ def numberOfArguments(self):
23
+ return 0
24
+
25
+ def matchAsFloat(self, index=0):
26
+ if self.matchGroups is not None:
27
+ return float(self.matchGroups[index])
28
+ return None
29
+
30
+ @property
31
+ def hasError(self):
32
+ return len(self.exceptions) != 0
33
+
34
+ def send(self, port) -> bool:
35
+ raise NotImplementedError("Subclasses must implement the send() command")
36
+
37
+ class TextCommand(Command):
38
+ def __init__(self, name, text, replyPattern = None,
39
+ alternatePattern = None,
40
+ endPoints = (None, None)):
41
+ Command.__init__(self, name, endPoints=endPoints)
42
+ self.text : str = text
43
+ self.replyPattern: str = replyPattern
44
+ self.alternatePattern: str = alternatePattern
45
+
46
+ @property
47
+ def payload(self):
48
+ return self.text
49
+
50
+ @property
51
+ def numberOfArguments(self):
52
+ match = re.search(r"\{(.*?)\}", self.text)
53
+ if match is None:
54
+ return 0
55
+ return len(match.groups())
56
+
57
+ def send(self, port, params=None) -> bool:
58
+ try:
59
+ if params is not None:
60
+ textCommand = self.text.format(params)
61
+ else:
62
+ textCommand = self.text
63
+
64
+ if port is None:
65
+ raise RuntimeError("port cannot be None")
66
+
67
+ port.writeString(string=textCommand, endPoint=self.endPoints[0])
68
+ self.isSent = True
69
+
70
+ if self.replyPattern is not None:
71
+ self.reply, self.matchGroups = port.readMatchingGroups(
72
+ replyPattern=self.replyPattern,
73
+ alternatePattern=self.alternatePattern,
74
+ endPoint=self.endPoints[1])
75
+ else:
76
+ pass
77
+
78
+ self.isSentSuccessfully = True
79
+ except Exception as err:
80
+ self.exceptions.append(err)
81
+ self.isSentSuccessfully = False
82
+ return True
83
+
84
+ return False
85
+
86
+
87
+ class MultilineTextCommand(Command):
88
+ def __init__(self, name, text,
89
+ replyPattern=None,
90
+ alternatePattern=None,
91
+ lineCount=1,
92
+ lastLinePattern=None,
93
+ endPoints=(None, None)):
94
+ Command.__init__(self, name=name, endPoints=endPoints)
95
+ self.text: str = text
96
+ self.replyPattern: str = replyPattern
97
+ self.alternatePattern: str = alternatePattern
98
+ self.lineCount = lineCount
99
+ self.lastLinePattern = lastLinePattern
100
+
101
+ @property
102
+ def payload(self):
103
+ return self.text
104
+
105
+ def send(self, port, params=None) -> bool:
106
+ try:
107
+ if params is not None:
108
+ textCommand = self.text.format(params)
109
+ else:
110
+ textCommand = self.text
111
+
112
+ if port is None:
113
+ raise RuntimeError("port cannot be None")
114
+
115
+ port.writeString(string=textCommand, endPoint=self.endPoints[0])
116
+ self.isSent = True
117
+
118
+ if self.lineCount > 1:
119
+ self.reply = []
120
+ self.matchGroups = []
121
+
122
+ for i in range(self.lineCount):
123
+ reply, matchGroups = port.readMatchingGroups(
124
+ replyPattern=self.replyPattern,
125
+ alternatePattern=self.alternatePattern,
126
+ endPoint=self.endPoints[1])
127
+ self.reply.append(reply)
128
+ self.matchGroups.append(matchGroups)
129
+
130
+ elif self.lastLinePattern is not None:
131
+ self.reply = []
132
+ self.matchGroups = []
133
+
134
+ while True:
135
+ reply, matchGroups = port.readMatchingGroups(
136
+ replyPattern=self.replyPattern,
137
+ alternatePattern=self.alternatePattern,
138
+ endPoint=self.endPoints[1])
139
+ self.reply.append(reply)
140
+ self.matchGroups.append(matchGroups)
141
+
142
+ if re.search(self.lastLinePattern, reply) is not None:
143
+ break
144
+ else:
145
+ raise Exception("lineCount and lastLinePattern cannot both be None")
146
+
147
+ self.isSentSuccessfully = True
148
+ except Exception as err:
149
+ self.exceptions.append(err)
150
+ self.isSentSuccessfully = False
151
+ return True
152
+
153
+ return False
154
+
155
+
156
+ class DataCommand(Command):
157
+ def __init__(self, name, data, replyHexRegex = None, replyDataLength = 0, unpackingMask = None, endPoints = (None, None)):
158
+ Command.__init__(self, name, endPoints=endPoints)
159
+ self.data : bytearray = data
160
+ self.replyHexRegex: str = replyHexRegex
161
+ self.replyDataLength: int = replyDataLength
162
+ self.unpackingMask:str = unpackingMask
163
+
164
+ @property
165
+ def payload(self):
166
+ return self.data
167
+
168
+ def send(self, port) -> bool:
169
+ try:
170
+ nBytes = port.writeData(data=self.data, endPoint=self.endPoints[0])
171
+ self.isSent = True
172
+ if self.replyDataLength > 0:
173
+ self.reply = port.readData(length=self.replyDataLength)
174
+ elif self.replyHexRegex is not None:
175
+ raise NotImplementedError("DataCommand reply pattern not implemented")
176
+ # self.reply = port.readData(length=self.replyDataLength)
177
+ self.isSentSuccessfully = True
178
+ except Exception as err:
179
+ self.exceptions.append(err)
180
+ self.isSentSuccessfully = False
181
+ raise(err)
182
+
183
+ return False