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.
- hardwarelibrary/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- hardwarelibrary/.idea/vcs.xml +6 -0
- hardwarelibrary/.idea/workspace.xml +39 -0
- hardwarelibrary/__init__.py +19 -0
- hardwarelibrary/__main__.py +76 -0
- hardwarelibrary/cameras/__init__.py +2 -0
- hardwarelibrary/cameras/camera.py +181 -0
- hardwarelibrary/communication/.idea/.gitignore +3 -0
- hardwarelibrary/communication/.idea/communication.iml +12 -0
- hardwarelibrary/communication/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- hardwarelibrary/communication/.idea/misc.xml +4 -0
- hardwarelibrary/communication/.idea/modules.xml +8 -0
- hardwarelibrary/communication/.idea/vcs.xml +6 -0
- hardwarelibrary/communication/__init__.py +53 -0
- hardwarelibrary/communication/commands.py +183 -0
- hardwarelibrary/communication/communicationport.py +131 -0
- hardwarelibrary/communication/debugport.py +98 -0
- hardwarelibrary/communication/diagnostics.py +427 -0
- hardwarelibrary/communication/echoport.py +10 -0
- hardwarelibrary/communication/libusb/Darwin/libusb-1.0.0.dylib +0 -0
- hardwarelibrary/communication/libusb/MS32/libusb-1.0.dll +0 -0
- hardwarelibrary/communication/libusb/MS64/libusb-1.0.dll +0 -0
- hardwarelibrary/communication/serialport.py +211 -0
- hardwarelibrary/communication/usbport.py +168 -0
- hardwarelibrary/daq/__init__.py +4 -0
- hardwarelibrary/daq/daqdevice.py +58 -0
- hardwarelibrary/daq/labjackdevice.py +42 -0
- hardwarelibrary/devicemanager.py +320 -0
- hardwarelibrary/echodevice.py +43 -0
- hardwarelibrary/manuals/Cobolt-owners-manual-0401-D0105-A.pdf +0 -0
- hardwarelibrary/manuals/HR2000+-OEM-Data-Sheet.pdf +0 -0
- hardwarelibrary/manuals/MPC-325_OpMan.pdf +0 -0
- hardwarelibrary/manuals/Mightex CCD Line Camera Din8 Connector Description.pdf +0 -0
- hardwarelibrary/manuals/Mightex CCD Linear Camera User Manual.pdf +0 -0
- TDS 2000B Manual.pdf +0 -0
- hardwarelibrary/manuals/Tektronix TDS-1002 Programming manual.pdf +0 -0
- hardwarelibrary/manuals/USB Primer.pdf +0 -0
- hardwarelibrary/manuals/USB2000+-OEM-Data-Sheet.pdf +0 -0
- hardwarelibrary/manuals/USB2000-OEM-Data-Sheet.pdf +0 -0
- hardwarelibrary/manuals/User_Manual_Integra_V3.0.pdf +0 -0
- hardwarelibrary/manuals/usb4000-oem-data-sheet.pdf +0 -0
- hardwarelibrary/motion/__init__.py +7 -0
- hardwarelibrary/motion/intellidrivedevice.py +119 -0
- hardwarelibrary/motion/linearmotiondevice.py +126 -0
- hardwarelibrary/motion/rotationdevice.py +66 -0
- hardwarelibrary/motion/sutterdevice.py +173 -0
- hardwarelibrary/notificationcenter.py +103 -0
- hardwarelibrary/oscilloscope/__init__.py +3 -0
- hardwarelibrary/oscilloscope/oscilloscopedevice.py +185 -0
- hardwarelibrary/physicaldevice.py +214 -0
- hardwarelibrary/powermeters/__init__.py +3 -0
- hardwarelibrary/powermeters/integradevice.py +50 -0
- hardwarelibrary/powermeters/powermeterdevice.py +38 -0
- hardwarelibrary/sources/__init__.py +2 -0
- hardwarelibrary/sources/cobolt.py +287 -0
- hardwarelibrary/sources/lasersourcedevice.py +20 -0
- hardwarelibrary/spectrometers/__init__.py +41 -0
- hardwarelibrary/spectrometers/base.py +299 -0
- hardwarelibrary/spectrometers/darkbulb.png +0 -0
- hardwarelibrary/spectrometers/intelhexreader.py +130 -0
- hardwarelibrary/spectrometers/lightbulb.png +0 -0
- hardwarelibrary/spectrometers/oceaninsight.py +961 -0
- hardwarelibrary/spectrometers/stellarnet.zip +0 -0
- hardwarelibrary/spectrometers/viewer.py +288 -0
- hardwarelibrary/tests/deprecated/cobolt.json +38 -0
- hardwarelibrary/tests/deprecated/socketclient.py +35 -0
- hardwarelibrary/tests/deprecated/socketmain.py +39 -0
- hardwarelibrary/tests/deprecated/testCobolt.py +116 -0
- hardwarelibrary/tests/deprecated/testCoboltDevice.py +111 -0
- hardwarelibrary/tests/deprecated/testCoboltSerial.py +102 -0
- hardwarelibrary/tests/deprecated/testIntegraSerialPort.py +35 -0
- hardwarelibrary/tests/deprecated/testJSON.py +14 -0
- hardwarelibrary/tests/deprecated/usbFinder.py +15 -0
- hardwarelibrary/tests/env.py +17 -0
- hardwarelibrary/tests/testCommunicationPorts.py +354 -0
- hardwarelibrary/tests/testConnectSutter.py +62 -0
- hardwarelibrary/tests/testDeviceManager.py +259 -0
- hardwarelibrary/tests/testIntegraDevice.py +149 -0
- hardwarelibrary/tests/testIntellidrive.py +173 -0
- hardwarelibrary/tests/testLabjackU3.py +108 -0
- hardwarelibrary/tests/testLinearMotionDevice.py +181 -0
- hardwarelibrary/tests/testMapPositionsFunction.py +79 -0
- hardwarelibrary/tests/testModulePyFTDI.py +70 -0
- hardwarelibrary/tests/testModulePySerial.py +41 -0
- hardwarelibrary/tests/testNotificationCenter.py +154 -0
- hardwarelibrary/tests/testOptotuneDevice.py +91 -0
- hardwarelibrary/tests/testOscilloscope.py +288 -0
- hardwarelibrary/tests/testPhysicalDevice.py +312 -0
- hardwarelibrary/tests/testPyUSB.py +58 -0
- hardwarelibrary/tests/testSerialHardwareFLow.py +60 -0
- hardwarelibrary/tests/testSutterDevice.py +123 -0
- hardwarelibrary/tests/testSutterSerialPort.py +207 -0
- hardwarelibrary/utils.py +160 -0
- hardwarelibrary-0.0.0.dist-info/METADATA +279 -0
- hardwarelibrary-0.0.0.dist-info/RECORD +97 -0
- hardwarelibrary-0.0.0.dist-info/WHEEL +5 -0
- hardwarelibrary-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -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,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,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,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,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
|