quarchpy 2.1.24.dev5__py2.py3-none-any.whl → 2.1.25__py2.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 (124) hide show
  1. quarchpy/__pycache__/__init__.cpython-311.pyc +0 -0
  2. quarchpy/__pycache__/_version.cpython-311.pyc +0 -0
  3. quarchpy/__pycache__/connection.cpython-311.pyc +0 -0
  4. quarchpy/__pycache__/run.cpython-311.pyc +0 -0
  5. quarchpy/_version.py +1 -1
  6. quarchpy/config_files/__pycache__/__init__.cpython-311.pyc +0 -0
  7. quarchpy/config_files/__pycache__/quarch_config_parser.cpython-311.pyc +0 -0
  8. quarchpy/connection_specific/QPS/qis/qis.jar +0 -0
  9. quarchpy/connection_specific/QPS/qis/qis_lib/{CInterface-1.7.9.jar → CInterface-1.8.2.jar} +0 -0
  10. quarchpy/connection_specific/QPS/qis/qis_lib/{CInterface-1.8.0.jar → CInterface-1.9.jar} +0 -0
  11. quarchpy/connection_specific/QPS/{qps_lib/QuarchCommon-0.2.12.jar → qis/qis_lib/QuarchCommon-0.2.13.jar} +0 -0
  12. quarchpy/connection_specific/QPS/qps.jar +0 -0
  13. quarchpy/connection_specific/QPS/{qis/qis_lib/QuarchCommon-0.2.12.jar → qps_lib/QuarchCommon-0.2.13.jar} +0 -0
  14. quarchpy/connection_specific/QPS/qps_lib/{qis-1.41.jar → qis-1.43.jar} +0 -0
  15. quarchpy/connection_specific/QPS/resources/InstallBanner.bmp +0 -0
  16. quarchpy/connection_specific/QPS/resources/QPS.ico +0 -0
  17. quarchpy/connection_specific/QPS/resources/QPS.png +0 -0
  18. quarchpy/connection_specific/QPS/resources/QuarchIcon_128x128.png +0 -0
  19. quarchpy/connection_specific/QPS/resources/QuarchIcon_16x16.png +0 -0
  20. quarchpy/connection_specific/QPS/resources/QuarchIcon_256x256.png +0 -0
  21. quarchpy/connection_specific/QPS/resources/QuarchIcon_32x32.png +0 -0
  22. quarchpy/connection_specific/QPS/resources/QuarchIcon_64x64.png +0 -0
  23. quarchpy/connection_specific/QPS/scriptCommands.txt +17 -2
  24. quarchpy/connection_specific/__pycache__/StreamChannels.cpython-311.pyc +0 -0
  25. quarchpy/connection_specific/__pycache__/__init__.cpython-311.pyc +0 -0
  26. quarchpy/connection_specific/__pycache__/connection_QIS.cpython-311.pyc +0 -0
  27. quarchpy/connection_specific/__pycache__/connection_QPS.cpython-311.pyc +0 -0
  28. quarchpy/connection_specific/__pycache__/connection_ReST.cpython-311.pyc +0 -0
  29. quarchpy/connection_specific/__pycache__/connection_Serial.cpython-311.pyc +0 -0
  30. quarchpy/connection_specific/__pycache__/connection_TCP.cpython-311.pyc +0 -0
  31. quarchpy/connection_specific/__pycache__/connection_Telnet.cpython-311.pyc +0 -0
  32. quarchpy/connection_specific/__pycache__/connection_USB.cpython-311.pyc +0 -0
  33. quarchpy/connection_specific/__pycache__/mDNS.cpython-311.pyc +0 -0
  34. quarchpy/connection_specific/connection_QPS.py +52 -0
  35. quarchpy/connection_specific/mDNS.py +126 -0
  36. quarchpy/connection_specific/serial/__pycache__/__init__.cpython-311.pyc +0 -0
  37. quarchpy/connection_specific/serial/__pycache__/serialutil.cpython-311.pyc +0 -0
  38. quarchpy/connection_specific/serial/__pycache__/serialwin32.cpython-311.pyc +0 -0
  39. quarchpy/connection_specific/serial/__pycache__/win32.cpython-311.pyc +0 -0
  40. quarchpy/connection_specific/serial/tools/__pycache__/__init__.cpython-311.pyc +0 -0
  41. quarchpy/connection_specific/serial/tools/__pycache__/list_ports.cpython-311.pyc +0 -0
  42. quarchpy/connection_specific/serial/tools/__pycache__/list_ports_common.cpython-311.pyc +0 -0
  43. quarchpy/connection_specific/serial/tools/__pycache__/list_ports_windows.cpython-311.pyc +0 -0
  44. quarchpy/debug/__pycache__/SystemTest.cpython-311.pyc +0 -0
  45. quarchpy/debug/__pycache__/__init__.cpython-311.pyc +0 -0
  46. quarchpy/debug/__pycache__/module_debug.cpython-311.pyc +0 -0
  47. quarchpy/debug/__pycache__/simple_terminal.cpython-311.pyc +0 -0
  48. quarchpy/debug/__pycache__/upgrade_quarchpy.cpython-311.pyc +0 -0
  49. quarchpy/debug/__pycache__/versionCompare.cpython-311.pyc +0 -0
  50. quarchpy/device/__pycache__/__init__.cpython-311.pyc +0 -0
  51. quarchpy/device/__pycache__/device.cpython-311.pyc +0 -0
  52. quarchpy/device/__pycache__/quarchArray.cpython-311.pyc +0 -0
  53. quarchpy/device/__pycache__/quarchPPM.cpython-311.pyc +0 -0
  54. quarchpy/device/__pycache__/quarchQPS.cpython-311.pyc +0 -0
  55. quarchpy/device/__pycache__/scanDevices.cpython-311.pyc +0 -0
  56. quarchpy/device/device.py +72 -22
  57. quarchpy/device/scanDevices.py +26 -11
  58. quarchpy/disk_test/__pycache__/AbsDiskFinder.cpython-311.pyc +0 -0
  59. quarchpy/disk_test/__pycache__/DiskTargetSelection.cpython-311.pyc +0 -0
  60. quarchpy/disk_test/__pycache__/__init__.cpython-311.pyc +0 -0
  61. quarchpy/disk_test/__pycache__/iometerDiskFinder.cpython-311.pyc +0 -0
  62. quarchpy/docs/CHANGES.rst +9 -0
  63. quarchpy/docs/_build/doctrees/CHANGES.doctree +0 -0
  64. quarchpy/docs/_build/doctrees/environment.pickle +0 -0
  65. quarchpy/docs/_build/doctrees/source/changelog.doctree +0 -0
  66. quarchpy/docs/_build/doctrees/source/quarchpy.connection_specific.doctree +0 -0
  67. quarchpy/docs/_build/doctrees/source/quarchpy.device.doctree +0 -0
  68. quarchpy/docs/_build/doctrees/source/quarchpy.qps.doctree +0 -0
  69. quarchpy/docs/_build/html/CHANGES.html +132 -119
  70. quarchpy/docs/_build/html/_sources/CHANGES.rst.txt +9 -0
  71. quarchpy/docs/_build/html/genindex.html +4 -0
  72. quarchpy/docs/_build/html/index.html +62 -60
  73. quarchpy/docs/_build/html/objects.inv +0 -0
  74. quarchpy/docs/_build/html/readme.html +6 -7
  75. quarchpy/docs/_build/html/searchindex.js +1 -1
  76. quarchpy/docs/_build/html/source/changelog.html +194 -179
  77. quarchpy/docs/_build/html/source/licenses.html +6 -7
  78. quarchpy/docs/_build/html/source/quarchpy.connection_specific.html +10 -0
  79. quarchpy/docs/_build/html/source/quarchpy.device.html +1 -1
  80. quarchpy/docs/_build/html/source/quarchpy.html +2 -0
  81. quarchpy/docs/_build/html/source/quarchpy.iometer.html +6 -7
  82. quarchpy/docs/_build/html/source/quarchpy.qps.html +2 -2
  83. quarchpy/docs/_build/html/source/quarchpy.utilities.html +6 -7
  84. quarchpy/docs/_build/html/source/readme.html +6 -7
  85. quarchpy/fio/__pycache__/FIO_interface.cpython-311.pyc +0 -0
  86. quarchpy/fio/__pycache__/__init__.cpython-311.pyc +0 -0
  87. quarchpy/fio/__pycache__/fioDiskFinder.cpython-311.pyc +0 -0
  88. quarchpy/iometer/__pycache__/__init__.cpython-311.pyc +0 -0
  89. quarchpy/iometer/__pycache__/gen_iometer_template.cpython-311.pyc +0 -0
  90. quarchpy/iometer/__pycache__/iometerFuncs.cpython-311.pyc +0 -0
  91. quarchpy/qis/__pycache__/StreamHeaderInfo.cpython-311.pyc +0 -0
  92. quarchpy/qis/__pycache__/__init__.cpython-311.pyc +0 -0
  93. quarchpy/qis/__pycache__/qisFuncs.cpython-311.pyc +0 -0
  94. quarchpy/qps/__pycache__/__init__.cpython-311.pyc +0 -0
  95. quarchpy/qps/__pycache__/qpsFuncs.cpython-311.pyc +0 -0
  96. quarchpy/qps/qpsFuncs.py +20 -10
  97. quarchpy/qps/qpsFuncs.py.bak +281 -0
  98. quarchpy/user_interface/__pycache__/__init__.cpython-311.pyc +0 -0
  99. quarchpy/user_interface/__pycache__/user_interface.cpython-311.pyc +0 -0
  100. quarchpy/user_interface/user_interface.py +3 -1
  101. quarchpy/utilities/__pycache__/TestCenter.cpython-311.pyc +0 -0
  102. quarchpy/utilities/__pycache__/TimeValue.cpython-311.pyc +0 -0
  103. quarchpy/utilities/__pycache__/Version.cpython-311.pyc +0 -0
  104. quarchpy/utilities/__pycache__/__init__.cpython-311.pyc +0 -0
  105. {quarchpy-2.1.24.dev5.dist-info → quarchpy-2.1.25.dist-info}/METADATA +10 -1
  106. {quarchpy-2.1.24.dev5.dist-info → quarchpy-2.1.25.dist-info}/RECORD +108 -57
  107. quarchpy/connection_specific/QPS/qis/qis_lib/CInterface-1.7.04.jar +0 -0
  108. quarchpy/connection_specific/QPS/qis/qis_lib/CInterface-1.7.8.jar +0 -0
  109. quarchpy/connection_specific/QPS/qis/qis_lib/CInterface-1.8.1.jar +0 -0
  110. quarchpy/connection_specific/QPS/qis/qis_lib/QuarchCommon-0.2.6.jar +0 -0
  111. quarchpy/connection_specific/QPS/qis/qis_lib/QuarchCommon-0.2.9.jar +0 -0
  112. quarchpy/connection_specific/QPS/qis/qis_lib/commons-csv-1.8.jar +0 -0
  113. quarchpy/connection_specific/QPS/qis/qis_lib/commons-lang3-3.2.1.jar +0 -0
  114. quarchpy/connection_specific/QPS/qis/resources/filters/filters.csv +0 -1004
  115. quarchpy/connection_specific/QPS/qps_lib/QuarchCommon-0.2.6.jar +0 -0
  116. quarchpy/connection_specific/QPS/qps_lib/QuarchCommon-0.2.9.jar +0 -0
  117. quarchpy/connection_specific/QPS/qps_lib/commons-lang3-3.12.0.jar +0 -0
  118. quarchpy/connection_specific/QPS/qps_lib/jfxtras-labs-8.0-r6.jar +0 -0
  119. quarchpy/connection_specific/QPS/qps_lib/opencsv-5.9.jar +0 -0
  120. quarchpy/connection_specific/QPS/qps_lib/qis-1.39.jar +0 -0
  121. quarchpy/connection_specific/QPS/qps_lib/qis-1.40.jar +0 -0
  122. quarchpy/connection_specific/connection_mDNS.py +0 -40
  123. {quarchpy-2.1.24.dev5.dist-info → quarchpy-2.1.25.dist-info}/WHEEL +0 -0
  124. {quarchpy-2.1.24.dev5.dist-info → quarchpy-2.1.25.dist-info}/top_level.txt +0 -0
Binary file
quarchpy/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.1.24.dev5"
1
+ __version__ = "2.1.25"
Binary file
@@ -1,7 +1,7 @@
1
1
  QPS Command Summary
2
2
  Copyright (c) Quarch Ltd
3
- QPS v1.39
4
- Generated: 18-Mar-2024
3
+ QPS v1.39.13
4
+ Generated: 13-May-2024
5
5
 
6
6
 
7
7
  *** Commands ***
@@ -345,6 +345,19 @@ Default value: NONE
345
345
  Examples:
346
346
  $open recording qpsFile="C:\quarch\recordings\AC\AC_Gen_test\AC_Gen_Test.qps"
347
347
 
348
+ Command: $progress check
349
+ Description: Checks the progress of a specific QPS task
350
+ Version: 2
351
+ Arguments
352
+ Argument: task
353
+ Description: The task to be checked
354
+ Index: 0
355
+ Type: STRING
356
+ Default value: NONE
357
+ Acceptable values: [open recording]
358
+ Examples:
359
+ $progress check task="open recording"
360
+
348
361
  Command: $qis launch
349
362
  Description: Launches new QIS instance
350
363
  Version: 2
@@ -992,6 +1005,8 @@ $module scan ~ Scans for available devices
992
1005
 
993
1006
  $open recording ~ Opens a recording
994
1007
 
1008
+ $progress check ~ Checks the progress of a specific QPS task
1009
+
995
1010
  $qis launch ~ Launches new QIS instance
996
1011
 
997
1012
  $qis shutdown ~ Shuts down current QIS instance
@@ -140,6 +140,17 @@ class QpsInterface:
140
140
  time.sleep(
141
141
  sleep) # Time must be allowed for QPS to Scan. If another scan request is sent it will time out and throw an error.
142
142
 
143
+ def get_list_details(self, sock=None):
144
+ if sock == None:
145
+ sock = self.sock
146
+ devString = self.sendCmdVerbose("$module list details")
147
+ #devString = self.sendAndReceiveText(sock, '$list details')
148
+ devString = devString.replace('>', '')
149
+ devString = devString.replace(r'\d+\) ', '')
150
+ devString = devString.split('\r\n')
151
+ devString = [x for x in devString if x] # remove empty elements
152
+ return devString
153
+
143
154
  def getDeviceList(self, scan = True, ipAddress = None):
144
155
  deviceList = []
145
156
  scanWait = 2
@@ -182,4 +193,45 @@ class QpsInterface:
182
193
  #return list of devices
183
194
  return deviceList
184
195
 
196
+
197
+ def open_recording(self, file_path, cmdTimeout=5, pollInterval=3, startOpenTimout=5):
198
+ """
199
+
200
+ """
201
+ #print("Open recording at file : \""+str(file_path)+"\"")
202
+ self.sendCmdVerbose("$open recording qpsFile=\""+str(file_path)+"\"",timeout=cmdTimeout)
203
+ loadingStarted=False
204
+ message=""
205
+ startTime=time.time()
206
+ notLoadingMessageStartTime=None
207
+ while(1):
208
+ update=self.sendCmdVerbose("$progress check \"open recording\"",timeout=cmdTimeout)
209
+ #print(update)
210
+ m = re.search('\d+(\.\d+)?%', update)
211
+ if m: # A percentage was found
212
+ loadingStarted=True
213
+ found = float(m.group(0)[:-1])
214
+ user_interface.progressBar(found,100)
215
+ if found > 99.9: # This will catch the case we have 99.9999% or 100% loaded. recording with less that 1mill records auto return 100%
216
+ message = "Passed, Recording opened, loading detected and complete."
217
+ break
218
+ elif("Chart window is open but no loading is in progress." in update):
219
+ if loadingStarted ==True:
220
+ # Loading started and has now ended, so we can exit the loop.
221
+ message="Passed, Recording opened, loading detected and complete."
222
+ break
223
+ else: # QPS has not started loading a recording.
224
+ if notLoadingMessageStartTime == None:
225
+ # Start a timer from now so that if loading doesn't take place between now and a timeout value,
226
+ # we exit, stating that no loading started within the desired time.
227
+ notLoadingMessageStartTime = time.time()
228
+ elif time.time() - notLoadingMessageStartTime> startOpenTimout:
229
+ message = "No detection that QPS started loading the recording within "+str(startOpenTimout)+"s."
230
+ break
231
+
232
+ time.sleep(pollInterval) #Sleep pollInterval time, so we are not hammering QPS for updates while its busy loading.
233
+ time.sleep(1) #sleep outside the loop as there is a
234
+ return message
235
+
236
+
185
237
 
@@ -0,0 +1,126 @@
1
+ import platform # For getting the operating system name
2
+ import subprocess # For executing a shell command
3
+
4
+ from zeroconf import Zeroconf
5
+
6
+
7
+ def ping(host):
8
+ """
9
+ Returns True if host (str) responds to a ping request.
10
+ """
11
+ # Option for the number of packets as a function of
12
+ param = '-n' if platform.system().lower() == 'windows' else '-c'
13
+
14
+ # Building the command. Ex: "ping -c 1 google.com"
15
+ command = ['ping', param, '1', host]
16
+
17
+ # Execute the ping command and capture the output and return code
18
+ result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
19
+
20
+ # Check the return code and output to determine the result
21
+ if result.returncode == 0 and ("destination host unreachable" not in str(result.stdout).lower()):
22
+ return True # Ping successful
23
+ else:
24
+ return False # Ping failed
25
+
26
+
27
+ class MyListener:
28
+ """
29
+ MyListener class to handle service updates, removals, and additions in Zeroconf
30
+ """
31
+
32
+ _instance = None
33
+
34
+ @classmethod
35
+ def get_instance(cls):
36
+ """
37
+ Get an instance of MyListener class. If none exists, create one.
38
+ """
39
+ if cls._instance is None:
40
+ cls._instance = MyListener()
41
+ return cls._instance
42
+
43
+ def __init__(self):
44
+ """
45
+ Initialize MyListener instance.
46
+ """
47
+ self.found_devices = {}
48
+ self.mdns_service_running = False
49
+ self.zeroconf = None
50
+ self.target_conn = None
51
+
52
+ def update_service(self, zc, type_, name):
53
+ """
54
+ Handle service update event.
55
+ """
56
+ info = zc.get_service_info(type_, name)
57
+ if "Quarch:" in str(info):
58
+ # decode the incoming properties from mdns
59
+ decoded_properties = {key.decode('utf-8'): value.decode('utf-8') for key, value in info.properties.items()}
60
+ decoded_ip = ".".join(str(byte) for byte in info.addresses[0])
61
+ self.get_instance().add_device(decoded_properties, decoded_ip)
62
+
63
+ def remove_service(self, zc, type_, name):
64
+ """
65
+ Handle service removal event.
66
+ """
67
+ return None
68
+
69
+ def add_service(self, zc, type_, name):
70
+ """
71
+ Handle service addition event.
72
+ """
73
+ info = zc.get_service_info(type_, name)
74
+ if "Quarch:" in str(info):
75
+ # decode the incoming properties from mdns
76
+ decoded_properties = {key.decode('utf-8'): value.decode('utf-8') for key, value in info.properties.items()}
77
+ decoded_ip = ".".join(str(byte) for byte in info.addresses[0])
78
+ self.get_instance().add_device(decoded_properties, decoded_ip)
79
+
80
+ def add_device(self, properties_dict, ip_address):
81
+ """
82
+ Add a device to the found devices dictionary.
83
+ """
84
+ qtl_num = "QTL" + properties_dict['86'] if '86' in properties_dict else None
85
+ # Check if module contains REST connection
86
+ if '84' in properties_dict:
87
+ # Check the user specified connection type
88
+ if self.get_instance().target_conn == "all" or self.get_instance().target_conn == "rest":
89
+ if properties_dict['84'] == '80':
90
+ # print("Rest connection exists for device: " + qtl_num)
91
+ # Updates the found devices dict
92
+ self.get_instance().update_device_dict(device_dict={"REST:" + ip_address: qtl_num})
93
+ # Check if module contains TCP connection
94
+ if '85' in properties_dict:
95
+ # Check the user specified connection type
96
+ if self.get_instance().target_conn == "all" or self.get_instance().target_conn == "tcp":
97
+ if properties_dict['85'] == "9760":
98
+ # print("TCP connection exists for device: " + qtl_num)
99
+ # Updates the found devices dict
100
+ self.get_instance().update_device_dict(device_dict={"TCP:" + ip_address: qtl_num})
101
+
102
+ def update_device_dict(self, device_dict):
103
+ """
104
+ Update the found devices dictionary.
105
+ """
106
+ self.get_instance().found_devices.update(device_dict)
107
+
108
+ def get_found_devices(self):
109
+ """
110
+ Get the found devices and perform ping check.
111
+ """
112
+ temp_dict = self.get_instance().found_devices
113
+ for key, value in list(temp_dict.items()):
114
+ result = ping(key[key.index(":") + 1:])
115
+ if not result or self.get_instance().target_conn not in key.lower():
116
+ del self.get_instance().found_devices[key]
117
+ print(str(self.get_instance().found_devices))
118
+ return self.get_instance().found_devices
119
+
120
+ def get_zeroconf(self):
121
+ """
122
+ Get the Zeroconf instance. If none exists, create one.
123
+ """
124
+ if self.get_instance().zeroconf is None:
125
+ self.get_instance().zeroconf = Zeroconf()
126
+ return self.get_instance().zeroconf
quarchpy/device/device.py CHANGED
@@ -110,21 +110,16 @@ class quarchDevice:
110
110
  ## QIS
111
111
  # ConType may be QIS only or QIS:ip:port [:3] checks if the first 3 letters are QIS.
112
112
  elif self.ConType[:3].upper() == "QIS":
113
- # If host and port are specified.
114
- try:
115
- # Extract QIS, host and port.
116
- QIS, host, port = self.ConType.split(':')
117
- # QIS port should be an int.
118
- port = int(port)
119
- # If host and port are not specified.
120
- except:
113
+ try: # If host and port are specified.
114
+ QIS, host, port = self.ConType.split(':') # Extract QIS, host and port.
115
+ port = int(port) # QIS port should be an int.
116
+ except: # If host and port are not specified.
121
117
  host = '127.0.0.1'
122
118
  port = 9722
123
119
 
124
120
  numb_colons = self.ConString.count(":")
125
121
  if numb_colons == 1:
126
122
  self.ConString = self.ConString.replace(':', '::')
127
-
128
123
  # Creates the connection object.
129
124
  self.connectionObj = QISConnection(self.ConString, host, port)
130
125
 
@@ -132,9 +127,8 @@ class quarchDevice:
132
127
  list_str = "".join(list).lower()
133
128
 
134
129
  timeout = time.time() + int(timeout) # now + n seconds
135
-
136
130
  # check for device in list, has a timeout
137
- while time.time() < timeout:
131
+ while time.time() < timeout: # look for the connection string in qis $list details
138
132
 
139
133
  # Check if it's a module's QTL number
140
134
  if "qtl" not in self.ConString.lower():
@@ -143,7 +137,6 @@ class quarchDevice:
143
137
  ip_address = re.search(r"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}", self.ConString)
144
138
  if not ip_address:
145
139
  raise ValueError("ConString " + self.ConString + " does not contain a valid QTL number or IP address")
146
-
147
140
  # Attempt to get QTL number from qis "$list details"
148
141
  temp_str = _check_ip_in_qis_list(ip_address.group(), self.connectionObj.qis.get_list_details())
149
142
  if temp_str:
@@ -159,9 +152,8 @@ class quarchDevice:
159
152
  if "located" in str(self.connectionObj.qis.scanIP(self.ConString)).lower():
160
153
  # Note - Qis takes a moment or 2 to add this newly located device to the $list 21/03/23
161
154
  timeout += 20 # Extend the timeout as the drive was located
162
-
163
155
  while time.time() < timeout:
164
- # try find the new device again from ipaddress
156
+ # try find the QTL from ipaddress
165
157
  temp_str = _check_ip_in_qis_list(ip_address.group(), self.connectionObj.qis.get_list_details())
166
158
  if temp_str:
167
159
  # If the item is found, break out of this loop
@@ -181,8 +173,7 @@ class quarchDevice:
181
173
  time.sleep(1)
182
174
  list = self.connectionObj.qis.getDeviceList()
183
175
  list_str = "".join(list).lower()
184
- else:
185
- # If we didn't hit a 'break' condition in the above loop, then it timed out
176
+ else: # If we didn't hit a 'break' condition in the above loop, then it timed out
186
177
  raise timeout_exception("Could not find module " + self.ConString + " from Qis within specified time")
187
178
 
188
179
  self.connectionObj.qis.sendAndReceiveCmd(cmd="$default " + self.ConString)
@@ -190,9 +181,9 @@ class quarchDevice:
190
181
  ## QPS
191
182
  elif self.ConType[:3].upper() == "QPS":
192
183
  try:
193
- # Extract QIS, host and port.
194
- QIS, host, port = self.ConType.split(':')
195
- # QIS port should be an int.
184
+ # Extract QPS, host and port.
185
+ QPS, host, port = self.ConType.split(':')
186
+ # QPS port should be an int.
196
187
  port = int(port)
197
188
  # If host and port are not specified.
198
189
  except:
@@ -204,6 +195,65 @@ class quarchDevice:
204
195
  self.ConString = self.ConString.replace(':', '::')
205
196
 
206
197
  self.connectionObj = QPSConnection(host, port)
198
+ list = self.connectionObj.qps.sendCmdVerbose("$module list details").replace("\r\n","\n").split("\n")
199
+ list_str = "".join(list).lower()
200
+
201
+ timeout = time.time() + int(timeout) # now + n seconds
202
+ # check for device in list, has a timeout
203
+ while time.time() < timeout: # look for the connection string in QPS $list details
204
+
205
+ # Check if it's a module's QTL number
206
+ if "qtl" not in self.ConString.lower():
207
+
208
+ # If not, check if it contains a valid IP address format
209
+ ip_address = re.search(r"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}", self.ConString)
210
+ if not ip_address:
211
+ raise ValueError("ConString " + self.ConString + " does not contain a valid QTL number or IP address")
212
+
213
+ # Attempt to get QTL number from QPS "$list details"
214
+ temp_str = _check_ip_in_qis_list(ip_address.group(), list)
215
+ if temp_str:
216
+ # If found
217
+ self.ConString = temp_str
218
+ break
219
+
220
+ logging.debug("Did not find ip address in list details, attempt targetted QPS scan")
221
+
222
+ # If it's not present in the list already, then try scanning for it via QPS
223
+ # Scan is purposefully after initial check! 09/03/2023
224
+ # Valid response example "Located device: 192.168.1.3"
225
+
226
+ if "located" in str(self.connectionObj.qps.scanIP(self.ConString)).lower():
227
+ # Note - QPS takes a moment or 2 to add this newly located device to the $list 21/03/23
228
+ timeout += 20 # Extend the timeout as the drive was located
229
+ while time.time() < timeout:
230
+ # try find the QTL from ipaddress
231
+ temp_str = _check_ip_in_qis_list(ip_address.group(), self.connectionObj.qps.get_list_details())
232
+ if temp_str:
233
+ # If the item is found, break out of this loop
234
+ self.ConString = temp_str
235
+ break
236
+ time.sleep(1) # Slow down the poll
237
+ else:
238
+ # if it's not found, continue and allow program to timeout
239
+ continue
240
+ # Break out of both loops
241
+ break
242
+
243
+ elif str(self.ConString).lower() in str(list_str).lower():
244
+ # If we have QTL device, and it's in list, nothing more needs done.
245
+ break
246
+ else:
247
+ time.sleep(1)
248
+ list = self.connectionObj.qps.getDeviceList()
249
+ list_str = "".join(list).lower()
250
+ else: # If we didn't hit a 'break' condition in the above loop, then it timed out
251
+ raise timeout_exception("Could not find module " + self.ConString + " from QPS within specified time")
252
+
253
+
254
+
255
+
256
+
207
257
 
208
258
  ## Neither PY or QIS, connection cannot be created.
209
259
  else:
@@ -238,7 +288,7 @@ class quarchDevice:
238
288
  # send command to log
239
289
  logging.debug(os.path.basename(__file__) + ": "+self.ConType[:3]+" sending command: " + CommandString)
240
290
 
241
- if self.ConType[:3] == "QIS":
291
+ if self.ConType[:3].upper() == "QIS":
242
292
 
243
293
  numb_colons = self.ConString.count(":")
244
294
  if numb_colons == 1:
@@ -249,13 +299,13 @@ class quarchDevice:
249
299
  logging.debug(os.path.basename(__file__) + ": "+self.ConType[:3]+" received: " + response)
250
300
  return response
251
301
 
252
- elif self.ConType == "PY":
302
+ elif self.ConType.upper() == "PY":
253
303
  response = self.connectionObj.connection.sendCommand(CommandString, expectedResponse=expectedResponse)
254
304
  # send response to log
255
305
  logging.debug(os.path.basename(__file__) + ": "+self.ConType[:3]+" received: " + response)
256
306
  return response
257
307
 
258
- elif self.ConType[:3] == "QPS":
308
+ elif self.ConType[:3].upper() == "QPS":
259
309
  # If "$" CMD is for QPS, else its for the specific module. Since QPS can talk to many modules we must added the conString.
260
310
  if CommandString[0] != '$':
261
311
  CommandString = self.ConString + " " + CommandString
@@ -20,8 +20,7 @@ from quarchpy.connection_specific.connection_Serial import serialList, serial
20
20
  from quarchpy.device.quarchArray import isThisAnArrayController
21
21
  from quarchpy.connection_specific.connection_USB import TQuarchUSB_IF
22
22
  from quarchpy.connection_specific import connection_ReST, connection_TCP
23
- from quarchpy.connection_specific.connection_mDNS import MyListener
24
- from quarchpy.utilities import TestCenter
23
+ from quarchpy.connection_specific.mDNS import MyListener
25
24
 
26
25
 
27
26
  '''
@@ -475,11 +474,14 @@ def filter_module_type(module_type_filter, found_devices):
475
474
  filtered_devices.update({key: value})
476
475
  return filtered_devices
477
476
 
478
- def scan_mDNS(mdnsListener):
477
+ def scan_mDNS(mdnsListener, zeroconf=None):
479
478
  from zeroconf import ServiceBrowser, Zeroconf
480
- zeroconf = Zeroconf()
479
+ if zeroconf is None:
480
+ zeroconf = Zeroconf()
481
481
  listener = mdnsListener
482
482
  browser = ServiceBrowser(zeroconf, "_http._tcp.local.", listener)
483
+ return browser
484
+
483
485
 
484
486
 
485
487
  '''
@@ -487,10 +489,23 @@ Scans for Quarch modules across the given interface(s). Returns a dictionary of
487
489
  '''
488
490
  def scanDevices(target_conn="all", lanTimeout=1, scanInArray=True, favouriteOnly=True,filterStr=None,
489
491
  module_type_filter=None, ipAddressLookup=None):
492
+
490
493
  foundDevices = dict()
491
494
  scannedArrays = list()
492
- mdnsListener = MyListener()
493
- scan_mDNS(mdnsListener)
495
+
496
+ # Setup mdns with zeroconf
497
+ # Ensure listener/zeroconf instance stay persistent (ensures only one thread is used for each scan cycle)
498
+ mdns_listener = MyListener().get_instance()
499
+ zeroconf = mdns_listener.get_zeroconf()
500
+ # Setup new mdns discovery service for every scan cycle
501
+ browser = scan_mDNS(mdns_listener, zeroconf)
502
+ # Set target_conn
503
+ mdns_listener.target_conn = target_conn.lower()
504
+ # Setup mdns discovery that stays persistent in the background - (could be removed)
505
+ # if not mdns_listener.mdns_service_running:
506
+ # from zeroconf import ServiceBrowser, Zeroconf
507
+ # scan_mDNS(mdns_listener, None)
508
+ # mdns_listener.mdns_service_running = True
494
509
 
495
510
  if target_conn.lower() == "all":
496
511
  foundDevices = list_USB()
@@ -498,10 +513,7 @@ def scanDevices(target_conn="all", lanTimeout=1, scanInArray=True, favouriteOnly
498
513
  try:
499
514
  #This will fail if the test machine is not connected to a network
500
515
  foundDevices = mergeDict(foundDevices, list_network("all", ipAddressLookup=ipAddressLookup, lanTimeout=lanTimeout))
501
- try:
502
- foundDevices = mergeDict(foundDevices, mdnsListener.found_devices)
503
- except Exception as mdnsExcept:
504
- logging.debug("An error occurred while trying to use the mdns listner to scan\n" +str(mdnsExcept))
516
+ foundDevices = mergeDict(foundDevices, mdns_listener.get_found_devices())
505
517
  except Exception as e:
506
518
  logging.error(e)
507
519
  logging.warning("Network scan failed, check network connection")
@@ -514,6 +526,8 @@ def scanDevices(target_conn="all", lanTimeout=1, scanInArray=True, favouriteOnly
514
526
 
515
527
  if target_conn.lower() == "tcp" or target_conn.lower() == "rest" or target_conn.lower() == "telnet":
516
528
  foundDevices = list_network(target_conn, ipAddressLookup=ipAddressLookup, lanTimeout=lanTimeout)
529
+ foundDevices = mergeDict(foundDevices, mdns_listener.get_found_devices())
530
+
517
531
 
518
532
  if (scanInArray):
519
533
  for k, v in foundDevices.items(): # k=Connection target, v=serial number
@@ -567,7 +581,8 @@ def scanDevices(target_conn="all", lanTimeout=1, scanInArray=True, favouriteOnly
567
581
  # used to filter module via type ( Power / Drive / ...)
568
582
  if module_type_filter:
569
583
  foundDevices = filter_module_type(module_type_filter, foundDevices)
570
-
584
+ # Cancels the mdns discovery service (required to close active thread)
585
+ browser.cancel()
571
586
  return foundDevices
572
587
 
573
588
 
quarchpy/docs/CHANGES.rst CHANGED
@@ -13,6 +13,15 @@ Quarchpy
13
13
  Change Log
14
14
  ----------
15
15
 
16
+ 2.1.25
17
+ ------
18
+ - New QPS 1.40 and Qis 1.43
19
+ - mdns scanning added to quarchpy
20
+
21
+ 2.1.24
22
+ ------
23
+ - Yanked
24
+
16
25
  2.1.23
17
26
  ------
18
27
  - QIS and QPS devices and interfaces can use sendCommand to send comannds to the modules and to the applications uniformly