quarchpy 2.1.24.dev3__py2.py3-none-any.whl → 2.1.24.dev4__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.
@@ -57,8 +57,8 @@
57
57
  <recent name="P:\Software\Python\QuarchPy\Trunk\quarchpy" />
58
58
  </key>
59
59
  </component>
60
- <component name="RunManager" selected="Python.simple_terminal">
61
- <configuration name="HammerRegRead" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
60
+ <component name="RunManager" selected="Python.simple_terminal (1)">
61
+ <configuration name="SystemTest" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
62
62
  <module name="quarchpy" />
63
63
  <option name="INTERPRETER_OPTIONS" value="" />
64
64
  <option name="PARENT_ENVS" value="true" />
@@ -66,11 +66,11 @@
66
66
  <env name="PYTHONUNBUFFERED" value="1" />
67
67
  </envs>
68
68
  <option name="SDK_HOME" value="" />
69
- <option name="WORKING_DIRECTORY" value="$USER_HOME$/Desktop" />
70
- <option name="IS_MODULE_SDK" value="false" />
69
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/debug" />
70
+ <option name="IS_MODULE_SDK" value="true" />
71
71
  <option name="ADD_CONTENT_ROOTS" value="true" />
72
72
  <option name="ADD_SOURCE_ROOTS" value="true" />
73
- <option name="SCRIPT_NAME" value="$USER_HOME$/Desktop/HammerRegRead.py" />
73
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/debug/SystemTest.py" />
74
74
  <option name="PARAMETERS" value="" />
75
75
  <option name="SHOW_COMMAND_LINE" value="false" />
76
76
  <option name="EMULATE_TERMINAL" value="false" />
@@ -79,19 +79,19 @@
79
79
  <option name="INPUT_FILE" value="" />
80
80
  <method v="2" />
81
81
  </configuration>
82
- <configuration name="SystemTest" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
83
- <module name="quarchpy" />
82
+ <configuration name="TestMultiNWA" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
83
+ <module name="Application Notes" />
84
84
  <option name="INTERPRETER_OPTIONS" value="" />
85
85
  <option name="PARENT_ENVS" value="true" />
86
86
  <envs>
87
87
  <env name="PYTHONUNBUFFERED" value="1" />
88
88
  </envs>
89
89
  <option name="SDK_HOME" value="" />
90
- <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/debug" />
90
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/../../../../Application Notes" />
91
91
  <option name="IS_MODULE_SDK" value="true" />
92
92
  <option name="ADD_CONTENT_ROOTS" value="true" />
93
93
  <option name="ADD_SOURCE_ROOTS" value="true" />
94
- <option name="SCRIPT_NAME" value="$PROJECT_DIR$/debug/SystemTest.py" />
94
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/../../../../Application Notes/TestMultiNWA.py" />
95
95
  <option name="PARAMETERS" value="" />
96
96
  <option name="SHOW_COMMAND_LINE" value="false" />
97
97
  <option name="EMULATE_TERMINAL" value="false" />
@@ -100,19 +100,19 @@
100
100
  <option name="INPUT_FILE" value="" />
101
101
  <method v="2" />
102
102
  </configuration>
103
- <configuration name="TestPYQPSQISConnectionsSameModule" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
104
- <module name="Application Notes" />
103
+ <configuration name="scanDevices" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
104
+ <module name="QuarchCalibration" />
105
105
  <option name="INTERPRETER_OPTIONS" value="" />
106
106
  <option name="PARENT_ENVS" value="true" />
107
107
  <envs>
108
108
  <env name="PYTHONUNBUFFERED" value="1" />
109
109
  </envs>
110
110
  <option name="SDK_HOME" value="" />
111
- <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/../../../../Application Notes/AN-006 - Python Control of Quarch Modules/Trunk" />
111
+ <option name="WORKING_DIRECTORY" value="C:/Python311/Lib/site-packages/quarchpy/device" />
112
112
  <option name="IS_MODULE_SDK" value="true" />
113
113
  <option name="ADD_CONTENT_ROOTS" value="true" />
114
114
  <option name="ADD_SOURCE_ROOTS" value="true" />
115
- <option name="SCRIPT_NAME" value="$PROJECT_DIR$/../../../../Application Notes/AN-006 - Python Control of Quarch Modules/Trunk/TestPYQPSQISConnectionsSameModule.py" />
115
+ <option name="SCRIPT_NAME" value="C:/Python311/Lib/site-packages/quarchpy/device/scanDevices.py" />
116
116
  <option name="PARAMETERS" value="" />
117
117
  <option name="SHOW_COMMAND_LINE" value="false" />
118
118
  <option name="EMULATE_TERMINAL" value="false" />
@@ -121,19 +121,19 @@
121
121
  <option name="INPUT_FILE" value="" />
122
122
  <method v="2" />
123
123
  </configuration>
124
- <configuration name="scanDevices" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
125
- <module name="QuarchCalibration" />
124
+ <configuration name="simple_terminal (1)" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
125
+ <module name="quarchpy" />
126
126
  <option name="INTERPRETER_OPTIONS" value="" />
127
127
  <option name="PARENT_ENVS" value="true" />
128
128
  <envs>
129
129
  <env name="PYTHONUNBUFFERED" value="1" />
130
130
  </envs>
131
131
  <option name="SDK_HOME" value="" />
132
- <option name="WORKING_DIRECTORY" value="C:/Python311/Lib/site-packages/quarchpy/device" />
133
- <option name="IS_MODULE_SDK" value="true" />
132
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/.." />
133
+ <option name="IS_MODULE_SDK" value="false" />
134
134
  <option name="ADD_CONTENT_ROOTS" value="true" />
135
135
  <option name="ADD_SOURCE_ROOTS" value="true" />
136
- <option name="SCRIPT_NAME" value="C:/Python311/Lib/site-packages/quarchpy/device/scanDevices.py" />
136
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/../simple_terminal.py" />
137
137
  <option name="PARAMETERS" value="" />
138
138
  <option name="SHOW_COMMAND_LINE" value="false" />
139
139
  <option name="EMULATE_TERMINAL" value="false" />
@@ -165,11 +165,11 @@
165
165
  </configuration>
166
166
  <recent_temporary>
167
167
  <list>
168
+ <item itemvalue="Python.simple_terminal (1)" />
169
+ <item itemvalue="Python.TestMultiNWA" />
168
170
  <item itemvalue="Python.simple_terminal" />
169
171
  <item itemvalue="Python.scanDevices" />
170
172
  <item itemvalue="Python.SystemTest" />
171
- <item itemvalue="Python.HammerRegRead" />
172
- <item itemvalue="Python.TestPYQPSQISConnectionsSameModule" />
173
173
  </list>
174
174
  </recent_temporary>
175
175
  </component>
@@ -314,17 +314,12 @@
314
314
  </line-breakpoint>
315
315
  <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
316
316
  <url>file://C:/Python311/Lib/site-packages/quarchpy/device/scanDevices.py</url>
317
- <line>350</line>
318
- <option name="timeStamp" value="109" />
319
- </line-breakpoint>
320
- <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
321
- <url>file://C:/Python311/Lib/site-packages/quarchpy/device/scanDevices.py</url>
322
- <line>341</line>
317
+ <line>343</line>
323
318
  <option name="timeStamp" value="110" />
324
319
  </line-breakpoint>
325
320
  <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
326
321
  <url>file://C:/Python311/Lib/site-packages/quarchpy/device/scanDevices.py</url>
327
- <line>326</line>
322
+ <line>328</line>
328
323
  <option name="timeStamp" value="112" />
329
324
  </line-breakpoint>
330
325
  <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
@@ -332,6 +327,26 @@
332
327
  <line>186</line>
333
328
  <option name="timeStamp" value="113" />
334
329
  </line-breakpoint>
330
+ <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
331
+ <url>file://$PROJECT_DIR$/../simple_terminal.py</url>
332
+ <line>21</line>
333
+ <option name="timeStamp" value="114" />
334
+ </line-breakpoint>
335
+ <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
336
+ <url>file://$PROJECT_DIR$/../simple_terminal.py</url>
337
+ <line>13</line>
338
+ <option name="timeStamp" value="115" />
339
+ </line-breakpoint>
340
+ <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
341
+ <url>file://C:/Projects/Software/Python/QuarchPy/Trunk/quarchpy/device/scanDevices.py</url>
342
+ <line>272</line>
343
+ <option name="timeStamp" value="116" />
344
+ </line-breakpoint>
345
+ <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
346
+ <url>file://$PROJECT_DIR$/../../../../Application Notes/TestMultiNWA.py</url>
347
+ <line>30</line>
348
+ <option name="timeStamp" value="117" />
349
+ </line-breakpoint>
335
350
  </breakpoints>
336
351
  <default-breakpoints>
337
352
  <breakpoint type="python-exception">
quarchpy/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.1.24.dev3"
1
+ __version__ = "2.1.24.dev4"
@@ -0,0 +1 @@
1
+ __version__ = "2.1.24.dev3"
@@ -183,7 +183,7 @@ def list_network(target_conn="all", debugPring=False, lanTimeout=1, ipAddressLoo
183
183
  # Broadcast the message.
184
184
  logging.debug("Broadcast LAN discovery message for UDP scan to all network interfaces")
185
185
  ipList = socket.gethostbyname_ex(socket.gethostname())
186
- temp1= socket.gethostname()
186
+ ipList[2].append("")
187
187
  logging.debug(os.path.basename(__file__) + ": Discovered the following interfaces: " + str(ipList))
188
188
 
189
189
 
@@ -0,0 +1,655 @@
1
+ import time
2
+ import socket
3
+ import sys
4
+ import operator
5
+ import logging
6
+ import os
7
+
8
+ from sys import platform
9
+
10
+ from quarchpy.config_files.quarch_config_parser import return_module_type_list
11
+ from quarchpy.user_interface import*
12
+ from quarchpy.user_interface import User_interface
13
+ try:
14
+ from quarchpy.connection_specific.connection_USB import importUSB # , USBConn
15
+ except:
16
+ printText("System Compatibility issue - Is your Python architecture consistent with the Operating System?")
17
+ pass
18
+ from quarchpy.device import quarchDevice, quarchArray
19
+ from quarchpy.connection_specific.connection_Serial import serialList, serial
20
+ from quarchpy.device.quarchArray import isThisAnArrayController
21
+ from quarchpy.connection_specific.connection_USB import TQuarchUSB_IF
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
25
+
26
+
27
+ '''
28
+ Merge two dictionaries and return the result
29
+ '''
30
+ def mergeDict(x, y):
31
+ if (y is None):
32
+ return x
33
+ else:
34
+ merged = x.copy()
35
+ merged.update(y)
36
+ return merged
37
+
38
+
39
+ '''
40
+ Scan for Quarch modules across all available COM ports
41
+ '''
42
+ def list_serial(debuPrint=False):
43
+ serial_ports = serialList.comports()
44
+ serial_modules = dict()
45
+
46
+ for i in serial_ports:
47
+ logging.debug("Scanning for Quarch devices on: " + str(i))
48
+ try:
49
+ ser = serial.Serial(i[0], 19200, timeout=0.5, write_timeout=0.5)
50
+ ser.write(b'*serial?\r\n')
51
+ s = ser.read(size=64)
52
+ serial_module = s.splitlines()[1]
53
+
54
+ serial_module = str(serial_module).replace("'", "").replace("b", "")
55
+
56
+ if "QTL" not in serial_module:
57
+ serial_module = "QTL" + serial_module
58
+
59
+ module = str(i[0]), str(serial_module)
60
+
61
+ if serial_module[7] == "-" and serial_module[10] == "-":
62
+ serial_modules["SERIAL:" + str(i[0])] = serial_module
63
+ logging.debug("Located quarch module: " + serial_module)
64
+
65
+ ser.close()
66
+ logging.debug("Finished scanning for Quarch devices on: " + str(i))
67
+ except Exception as err:
68
+ logging.debug("Exception during serial scan: " + str(err))
69
+ pass
70
+ return serial_modules
71
+
72
+
73
+ '''
74
+ Scan for all Quarch devices available over USB
75
+ '''
76
+ def list_USB(debuPrint=False):
77
+ QUARCH_VENDOR_ID = 0x16d0
78
+ QUARCH_PRODUCT_ID1 = 0x0449
79
+
80
+ usb1 = importUSB()
81
+
82
+ context = usb1.USBContext()
83
+ usb_list = context.getDeviceList()
84
+
85
+ if (debuPrint): printText(usb_list)
86
+
87
+ usb_modules = dict()
88
+ hdList = []
89
+
90
+ usb_permission_error = False
91
+
92
+ for i in usb_list:
93
+ if hex(i.device_descriptor.idVendor) == hex(QUARCH_VENDOR_ID) and hex(i.device_descriptor.idProduct) == hex(
94
+ QUARCH_PRODUCT_ID1):
95
+ try:
96
+ logging.debug("Opening USB handle to quarch module: " + str(i))
97
+ i_handle = i.open()
98
+ except Exception as err:
99
+ logging.debug("USB port open exception: " + str(err))
100
+ if "LIBUSB_ERROR_ACCESS [-3]" in str(err):
101
+ logging.debug("Unable to communicate with Quarch module over USB, device may be in use already.")
102
+ if not platform == "win32":
103
+ if not os.path.isfile("/etc/udev/rules.d/20-quarchmodules.rules"):
104
+ usb_permission_error = True
105
+ usb_modules["USB:???"] = "LOCKED MODULE"
106
+
107
+
108
+ try:
109
+ module_sn = i_handle.getASCIIStringDescriptor(3)
110
+ if "1944" in module_sn or "2098" in module_sn : #use enclosure number instead of serial number
111
+ hdList.append(i)
112
+ except Exception as err:
113
+ logging.debug("USB exception on reading serial number: " + str(err))
114
+ usb_modules["USB:???"] = "LOCKED MODULE"
115
+ continue
116
+
117
+ try:
118
+ if (debuPrint): printText(i_handle.getASCIIStringDescriptor(3) + " " + i_handle.getASCIIStringDescriptor(
119
+ 2) + " " + i_handle.getASCIIStringDescriptor(1))
120
+ except Exception as err:
121
+ logging.error("USB exception on reading descriptor strings: " + str(err))
122
+ usb_modules["USB:???"] = "LOCKED MODULE"
123
+ continue
124
+
125
+ if "QTL" not in module_sn:
126
+ module_sn = "QTL" + module_sn.strip()
127
+ else:
128
+ module_sn = module_sn.strip()
129
+
130
+ if (debuPrint): printText(module_sn)
131
+
132
+ usb_modules["USB:" + module_sn] = module_sn
133
+ logging.debug("Located USB module: " + module_sn)
134
+
135
+ try:
136
+ logging.debug("Closing USB handle to quarch module: " + str(i))
137
+ i_handle.close()
138
+ except Exception as err:
139
+ logging.error("Exception on closing USB port: " + str(err))
140
+ continue
141
+
142
+ # before returning the list of usb modules scan through the list for a 1944 create a quarch device and use sendCommand("*enclosure?")
143
+
144
+ for module in hdList:
145
+
146
+ QquarchDevice = None
147
+ quarchDevice = None
148
+ quarchDevice = module
149
+ QquarchDevice = TQuarchUSB_IF(context)
150
+ QquarchDevice.connection = quarchDevice
151
+ QquarchDevice.OpenPort()
152
+ time.sleep(0.02) # sleep sometimes needed before sending comand directly after opening device
153
+ QquarchDevice.SetTimeout(2000)
154
+ serialNo = (QquarchDevice.RunCommand("*serial?")).replace("\r\n", "")
155
+ enclNo = (QquarchDevice.RunCommand("*enclosure?")).replace("\r\n", "")
156
+
157
+ keyToFind = "USB:QTL" + serialNo
158
+
159
+ if keyToFind in usb_modules:
160
+ del usb_modules[keyToFind]
161
+ usb_modules["USB:QTL" + enclNo] = "QTL" + enclNo
162
+ logging.debug("Located USB module: QTL" + enclNo)
163
+
164
+ QquarchDevice.ClosePort()
165
+ QquarchDevice.deviceHandle = None
166
+
167
+
168
+ if usb_permission_error:
169
+ logging.warning("Potential permission error accessing Quarch module(s) via USB.")
170
+ logging.warning("If unknown, run the command 'sudo python3 -m quarchpy.run debug --fixusb' to add a new usb rule.")
171
+
172
+ return usb_modules
173
+
174
+
175
+ '''
176
+ List all Quarch devices found over LAN, using a UDP broadcast scan
177
+ '''
178
+ def list_network(target_conn="all", debugPring=False, lanTimeout=1, ipAddressLookup=None):
179
+
180
+ retVal={}
181
+ lan_modules = dict()
182
+ specifiedDevice = None
183
+ # Broadcast the message.
184
+ logging.debug("Broadcast LAN discovery message for UDP scan to all network interfaces")
185
+ ipList = socket.gethostbyname_ex(socket.gethostname())
186
+ temp1= socket.gethostname()
187
+ logging.debug(os.path.basename(__file__) + ": Discovered the following interfaces: " + str(ipList))
188
+
189
+
190
+
191
+ for ip in ipList[2]:
192
+ logging.debug(os.path.basename(__file__) + ": Broadcasting on : " + ip)
193
+ try:
194
+ mySocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
195
+ mySocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
196
+ mySocket.settimeout(lanTimeout)
197
+ mySocket.bind((ip,56732))
198
+ except Exception as err:
199
+ logging.debug("Error while trying to bind to network interfaces: "+" Error: "+str(err))
200
+
201
+ if ipAddressLookup is not None:
202
+ # Attempts to find the device through UDP then REST
203
+ specifiedDevice = lookupDevice(str(ipAddressLookup).strip(), mySocket, lan_modules)
204
+
205
+
206
+ mySocket.sendto(b'Discovery: Who is out there?\0\n', ('255.255.255.255', 30303))
207
+ counter = 0
208
+ # Receive messages until timeout.
209
+ while True:
210
+ network_modules = {}
211
+ counter += 1
212
+ # Receive raw message until timeout, then break.
213
+ try:
214
+ msg_received = mySocket.recvfrom(256)
215
+ except Exception as e:
216
+ # check if any a device was targeted directly and allow parse
217
+ if specifiedDevice is not None:
218
+ msg_received = specifiedDevice
219
+ specifiedDevice = None
220
+ else:
221
+ break
222
+ cont = 0
223
+
224
+ # Used split \r\n since values of 13 or 10 were looked at as /r and /n when using splitlines
225
+ # This fixes for all cases except if 13 is followed by 10.
226
+ splits = msg_received[0].split(b"\r\n")
227
+ del splits[-1]
228
+ for lines in splits:
229
+ if cont <= 1:
230
+ index = cont
231
+ data = repr(lines).replace("'", "").replace("b", "")
232
+ cont += 1
233
+ else:
234
+ index = repr(lines[0]).replace("'", "")
235
+ data = repr(lines[1:]).replace("'", "").replace("b", "")
236
+ network_modules[index] = data
237
+ module_name = get_user_level_serial_number(network_modules)
238
+ logging.debug("Found UDP response: " + module_name)
239
+ ip_module = msg_received[1][0].strip()
240
+ try:
241
+ # Add a QTL before modules without it.
242
+ if "QTL" not in module_name.decode("utf-8"):
243
+ module_name = "QTL" + module_name.decode("utf-8")
244
+ except:
245
+ # Add a QTL before modules without it.
246
+ if "QTL" not in module_name:
247
+ module_name = "QTL" + module_name
248
+
249
+ # Checks if there's a value in the TELNET key.
250
+ if (target_conn.lower() == "all" or target_conn.lower() == "telnet"):
251
+ if network_modules.get("\\x8a") or network_modules.get("138"):
252
+ # Append the information to the list.
253
+ lan_modules["TELNET:" + ip_module] = module_name
254
+ logging.debug("Found Telnet module: " + module_name)
255
+
256
+ # Checks if there's a value in the REST key.
257
+ if (target_conn.lower() == "all" or target_conn.lower() == "rest"):
258
+ if network_modules.get("\\x84") or network_modules.get("132"):
259
+ # Append the information to the list.
260
+ lan_modules["REST:" + ip_module] = module_name
261
+ logging.debug("Found REST module: " + module_name)
262
+
263
+ # Checks if there's a value in the TCP key.
264
+ if (target_conn.lower() == "all" or target_conn.lower() == "tcp"):
265
+ if network_modules.get("\\x85") or network_modules.get("133"):
266
+ # Append the information to the list.
267
+ lan_modules["TCP:" + ip_module] = module_name
268
+ logging.debug("Found TCP module: " + module_name)
269
+ mySocket.close()
270
+
271
+ logging.debug("Finished UDP scan")
272
+ retVal.update(lan_modules)
273
+ return retVal
274
+
275
+
276
+
277
+ def get_user_level_serial_number(network_modules):
278
+ '''
279
+
280
+ :param network_modules:
281
+ :return:
282
+ '''
283
+ list_of_multi_module_units = ["1995"] # List of modules that require enclosure number + Port to be displayed.
284
+
285
+ # Filter the raw message to get the module and ip address.
286
+ if "134" in network_modules.keys():
287
+ module_name = network_modules.get("134").strip() # enclosure number only
288
+ for module in list_of_multi_module_units:
289
+ if module in module_name:
290
+ module_name += "-" + network_modules.get("135").strip() # enclosure number with port
291
+ break
292
+ elif "\\x86" in network_modules.keys():
293
+ module_name = network_modules.get("\\x86").strip() # enclosure number only
294
+ for module in list_of_multi_module_units:
295
+ if module in module_name:
296
+ module_name += "-" + network_modules.get("\\x87").strip() # enclosure number with port
297
+ break
298
+ else:
299
+ if "131" in network_modules.keys():
300
+ module_name = module_name = network_modules.get("131").strip() # serial number
301
+ elif "\\x83" in network_modules.keys():
302
+ module_name = module_name = network_modules.get("\\x83").strip() # serial number
303
+
304
+ return module_name
305
+
306
+
307
+ ''''''
308
+ def lookupDevice(ipAddressLookup, mySocket, lan_modules):
309
+ try:
310
+ specifiedDevice =None
311
+ # For future reference, 0 is the C terminator for a string
312
+ timeout = mySocket.gettimeout()
313
+ # mySocket.settimeout(20)
314
+ # timeout2 = mySocket.gettimeout()
315
+ logging.debug("Ipaddress lookup =" + ipAddressLookup+ " timeout = "+ str(timeout))
316
+
317
+ mySocket.sendto(b'Discovery: Who is out there?\0\n', (str(ipAddressLookup).strip(), 30303))
318
+ specifiedDevice = mySocket.recvfrom(256)
319
+ # Check to see if the response contains the connection protocol
320
+ return specifiedDevice
321
+ except Exception as e:
322
+ logging.warning("Error during UDP lookup of IP address "+ str(ipAddressLookup) +" Error: " + str(e))
323
+ logging.warning("No Quarch module found at this address. Please check the IP address and that you can ping it.\r\n")
324
+
325
+ #return None # Commented out, attempt a REST connection with IP. This is what other quarch applications do.
326
+ if specifiedDevice is None: #Only True if TCP not found or errored
327
+ try:
328
+ restCon = connection_ReST.ReSTConn(str(ipAddressLookup).replace("\r\n", ""))
329
+ restDevice = restCon.sendCommand("*enclosure?") #Try use enclosure for PPMs
330
+ if "fail" in restDevice.lower(): # This will fail nicely for other modules
331
+ restDevice = restCon.sendCommand("*serial?") # and serial number will be used in place.
332
+ if not str(restDevice).startswith("QTL"):
333
+ restDevice = "QTL" + restDevice
334
+ # Exit as device was found correctly
335
+ # Add the item to list
336
+ lan_modules["REST:" + str(ipAddressLookup).replace("\r\n", "")] = restDevice
337
+ specifiedDevice=None # Don't return rest connection, to bypass tcp parsing.
338
+ except Exception as e:
339
+ logging.warning("Error During REST scan of IP address " + str(ipAddressLookup) + " Error: " + str(e))
340
+ logging.warning("Please check the IP address and that you can ping it.\r\n")
341
+ try:
342
+ # from threading import Thread
343
+ # t = Thread(target=connection_TCP.TCPConn, args=(str(ipAddressLookup).replace("\r\n", ""),))
344
+ # t.daemon = True
345
+ # t.start()
346
+ # t.join(50)
347
+ # if t.is_alive():
348
+ # raise TimeoutError("Timeout during TCP Connection attempt.")
349
+
350
+ tcpCon= connection_TCP.TCPConn(str(ipAddressLookup).replace("\r\n", ""))
351
+ tcpDevice = tcpCon.sendCommand("*enclosure?") #Try use enclosure for PPMs
352
+ if "fail" in tcpDevice.lower(): # This will fail nicely for other modules
353
+ tcpDevice = tcpCon.sendCommand("*serial?") # and serial number will be used in place.
354
+ if not str(tcpDevice).startswith("QTL"):
355
+ tcpDevice = "QTL" + tcpDevice
356
+ # Exit as device was found correctly
357
+ # Add the item to list
358
+ lan_modules["TCP:" + str(ipAddressLookup).replace("\r\n", "")] = tcpDevice
359
+ specifiedDevice=None # Don't return tcp connection, to bypass tcp parsing.
360
+ except Exception as e:
361
+ logging.warning("Error During TCP scan of IP address " + str(ipAddressLookup) + " Error: " + str(e))
362
+ logging.warning("Please check the IP address and that you can ping it.\r\n")
363
+ # Needs to return None so previous method will not attempt another lookup.
364
+ return specifiedDevice
365
+
366
+
367
+
368
+ def getSerialNumberFromConnectionTarget(connectionTarget):
369
+ """
370
+ Takes in the connection target and returns the serial number of a module found on the standard scan.
371
+
372
+ Parameters
373
+ ----------
374
+ connectionTarget= : str
375
+ The connection target of the module you would like to know the serial number of.
376
+
377
+ Returns
378
+ -------
379
+ ret_val : str
380
+ The Serial number of the supplied device.
381
+
382
+ """
383
+ myDict = scanDevices(favouriteOnly=False)
384
+ for k,v in myDict.items():
385
+ if k == connectionTarget:
386
+ return v
387
+ return None
388
+
389
+
390
+ def get_connection_target(module_string ,scan_dictionary=None, connection_preference= None, include_conn_type = True):
391
+ """
392
+ Takes in the connection type and serial number of a module and returns the connection target.
393
+
394
+ Parameters
395
+ ----------
396
+ module_string= : str
397
+ The connection type and serial number combination eg. TCP:QTL1999-05-005.
398
+
399
+ scan_dictionary= :dict, optional
400
+ A scan dictionary can be passed so that a scan does not need to take place on every call.
401
+ This would be advised if calling this for every item in a list of serial numbers.
402
+
403
+ connection_preference= : list str, optional
404
+ The preference of which connection type to prioratise if none it given.
405
+ Defaults to "USB", "TCP", "SERIAL", "REST", "TELNET" in that order.
406
+
407
+ include_conn_type = : boolean, optional
408
+ Decided whether the connection type will appear in the return value eg. TCP:192.168.1.1 vs 192.168.1.1
409
+
410
+ Returns
411
+ -------
412
+ ret_val : str
413
+ The Connection target of the supplied device.
414
+ """
415
+ logging.debug("Getting connection target for : "+ str(module_string))
416
+ if connection_preference == None:
417
+ connection_preference = ["USB", "TCP", "SERIAL", "REST", "TELNET"]
418
+ module_string.replace("::", ":") #QIS/QPS format to QuarchPy format
419
+ delimeter_pos = module_string.find(":")
420
+ if delimeter_pos == -1:
421
+ con_type = None
422
+ serial_number = module_string.lower()
423
+ else:
424
+ con_type = module_string[:delimeter_pos]
425
+ serial_number = module_string[delimeter_pos + 1:].lower()
426
+ if serial_number.find("qtl") !=-1:
427
+ serial_number=serial_number.replace("qtl","")
428
+ if scan_dictionary is None:
429
+ logging.debug("Scanning for devices...")
430
+ scan_dictionary = scanDevices(favouriteOnly=False,filterStr=[serial_number])
431
+
432
+ ret_val="Fail Module Not Found"
433
+
434
+ if con_type is None:
435
+ connection_found = False
436
+ for con_type in connection_preference:
437
+ if connection_found is False:
438
+ for k, v in scan_dictionary.items():
439
+ if k.__contains__(con_type):
440
+ ret_val = k
441
+ connection_found = True
442
+ else:
443
+ for k, v in scan_dictionary.items():
444
+ if k.lower().__contains__(con_type.lower()):
445
+ ret_val=k
446
+
447
+ if not include_conn_type and not ret_val.__contains__("Fail"):
448
+ delimeter_pos = ret_val.find(":")
449
+ ret_val = ret_val[delimeter_pos + 1:]
450
+
451
+ return ret_val
452
+
453
+
454
+ '''
455
+ Scans for Quarch modules across the given interface(s). Returns a dictionary of module addresses and serial numbers
456
+ '''
457
+ def filter_module_type(module_type_filter, found_devices):
458
+ """
459
+ Used in scandevices to filter modules by their type.
460
+ Uses config files.
461
+
462
+ :param module_type_filter: Acceptable values are 'Cable', 'Card', 'Drive', 'Power', 'Switch'
463
+ :param found_devices: List of found devices passed from scan_devices
464
+ :return: Returns all devices in found devices that are of 'module filter type'
465
+ """
466
+ accepted_qtl_numbers = return_module_type_list(module_type_filter)
467
+ accepted_qtl_numbers = [x.lower() for x in accepted_qtl_numbers]
468
+ filtered_devices = {}
469
+ if not accepted_qtl_numbers:
470
+ return {}
471
+ for key, value in found_devices.items():
472
+ if "qtl" in str(value).lower():
473
+ qtl_num = str(value[str(value.lower()).index("qtl"):str(value).index("-")]).lower()
474
+ if any(qtl_num in x for x in accepted_qtl_numbers):
475
+ filtered_devices.update({key: value})
476
+ return filtered_devices
477
+
478
+ def scan_mDNS(mdnsListener):
479
+ from zeroconf import ServiceBrowser, Zeroconf
480
+ zeroconf = Zeroconf()
481
+ listener = mdnsListener
482
+ browser = ServiceBrowser(zeroconf, "_http._tcp.local.", listener)
483
+
484
+
485
+ '''
486
+ Scans for Quarch modules across the given interface(s). Returns a dictionary of module addresses and serial numbers
487
+ '''
488
+ def scanDevices(target_conn="all", lanTimeout=1, scanInArray=True, favouriteOnly=True,filterStr=None,
489
+ module_type_filter=None, ipAddressLookup=None):
490
+ foundDevices = dict()
491
+ scannedArrays = list()
492
+ mdnsListener = MyListener()
493
+ scan_mDNS(mdnsListener)
494
+
495
+ if target_conn.lower() == "all":
496
+ foundDevices = list_USB()
497
+ foundDevices = mergeDict(foundDevices, list_serial())
498
+ try:
499
+ #This will fail if the test machine is not connected to a network
500
+ 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))
505
+ except Exception as e:
506
+ logging.error(e)
507
+ logging.warning("Network scan failed, check network connection")
508
+
509
+ if target_conn.lower() == "serial":
510
+ foundDevices = list_serial()
511
+
512
+ if target_conn.lower() == "usb":
513
+ foundDevices = list_USB()
514
+
515
+ if target_conn.lower() == "tcp" or target_conn.lower() == "rest" or target_conn.lower() == "telnet":
516
+ foundDevices = list_network(target_conn, ipAddressLookup=ipAddressLookup, lanTimeout=lanTimeout)
517
+
518
+ if (scanInArray):
519
+ for k, v in foundDevices.items(): # k=Connection target, v=serial number
520
+ if (k not in scannedArrays):
521
+ scannedArrays.append(k)
522
+ if (isThisAnArrayController(v)):
523
+ try:
524
+ myQuarchDevice = quarchDevice(k)
525
+ myArrayControler = quarchArray(myQuarchDevice)
526
+ scanDevices = myArrayControler.scanSubModules()
527
+ foundDevices = mergeDict(foundDevices, scanDevices)
528
+ myArrayControler.closeConnection()
529
+ except Exception as e:
530
+ logging.debug(e, exc_info=True)
531
+ logging.debug("Cannot get serial number. Quarch device may be in use by another program.")
532
+ foundDevices[k] = "DEVICE IN USE"
533
+
534
+ if (favouriteOnly):
535
+
536
+ # Sort list in order of connection type preference. Can be changed by changing position in conPref list. This must be done so that it is in the correct format for picking the favourite connections.
537
+ index = 0
538
+ sortedFoundDevices = {}
539
+ conPref = ["USB", "TCP", "SERIAL", "REST", "TELNET"]
540
+ while len(sortedFoundDevices) != len(foundDevices):
541
+ for k, v in foundDevices.items():
542
+ if conPref[index] in k:
543
+ sortedFoundDevices[k] = v
544
+ index += 1
545
+ foundDevices = sortedFoundDevices
546
+
547
+ # new dictionary only containing one favourite connection to each device.
548
+ favConFoundDevices = {}
549
+ index = 0
550
+ for k, v in sortedFoundDevices.items():
551
+ if (favConFoundDevices == {} or not v in favConFoundDevices.values()):
552
+ favConFoundDevices[k] = v
553
+ foundDevices = favConFoundDevices
554
+
555
+ # Sort by alphabetic order of key
556
+ sortedFoundDevices = {}
557
+ sortedFoundDevices = sorted(foundDevices.items(), key=operator.itemgetter(1))
558
+ foundDevices = dict(sortedFoundDevices)
559
+ if filterStr != None:
560
+ filteredDevices = {}
561
+ for k, v in foundDevices.items():
562
+ for j in filterStr:
563
+ if (j in v or "LOCKED MODULE" in v): #show locked modules too incase the module you are looking for is on the system but is locked
564
+ filteredDevices[k] = v
565
+ foundDevices = filteredDevices
566
+
567
+ # used to filter module via type ( Power / Drive / ...)
568
+ if module_type_filter:
569
+ foundDevices = filter_module_type(module_type_filter, foundDevices)
570
+
571
+ return foundDevices
572
+
573
+
574
+ '''
575
+ Prints out a list of Quarch devices nicely onto the terminal, numbering each unit
576
+ '''
577
+ def listDevices(scanDictionary):
578
+ if not scanDictionary:
579
+ printText("No quarch devices found to display")
580
+ else:
581
+ x = 1
582
+ for k, v in scanDictionary.items():
583
+ printText('{0:>3}'.format(str(x)) + " - " + '{0:<18}'.format(
584
+ v) + "\t" + k) # add padding to keep responses in line
585
+ x += 1
586
+
587
+
588
+ '''
589
+ Requests the user to select one of the devices in the given list
590
+ '''
591
+ def userSelectDevice(scanDictionary=None, scanFilterStr=None,favouriteOnly=True, message=None, title=None, nice=False, additionalOptions = None, target_conn="all"):
592
+ if User_interface.instance != None and User_interface.instance.selectedInterface == "testcenter":
593
+ nice = False
594
+ if message is None: message = "Please select a quarch device"
595
+ if title is None: title = "Select a Device"
596
+ ip_address = None
597
+ while (True):
598
+ # Scan first, if no list is supplied
599
+ if (scanDictionary is None):
600
+ printText("Scanning for devices...")
601
+ if ip_address == None:
602
+ scanDictionary = scanDevices(filterStr=scanFilterStr, favouriteOnly=favouriteOnly, target_conn=target_conn)
603
+ else:
604
+ scanDictionary = scanDevices(filterStr=scanFilterStr, favouriteOnly=favouriteOnly, target_conn=target_conn, ipAddressLookup=ip_address)
605
+
606
+ if len(scanDictionary)<1:
607
+ scanDictionary["***No Devices Found***"]="***No Devices Found***"
608
+
609
+
610
+ if nice: #Prepair the data for niceListSelection using displayTable().
611
+ if additionalOptions is None: additionalOptions = ["Specify IP Address","Rescan","Quit"]
612
+ tempList = []
613
+ tempEl = []
614
+ for k, v in scanDictionary.items():
615
+ tempEl = []
616
+ tempEl.append(v)
617
+ charPos = k.find(":")
618
+ tempEl.append(k)
619
+ tempList.append(tempEl)
620
+ adOp =[]
621
+ for option in additionalOptions:
622
+ adOp.append([option]*2) # Put option in all columns
623
+ #adOp.append([option,"-"])
624
+ userStr = listSelection(title, message, tempList, additionalOptions=adOp, indexReq=True, nice=nice, tableHeaders=["Selection", "Description"])
625
+ userStr = userStr[2] #With the data formatted in this way the ConnTarget will always be in userStr[2]
626
+
627
+ else: # Prepare data for old style selection or testCenter
628
+ devicesString = []
629
+ for k, v in scanDictionary.items():
630
+ charPos = k.find(":")
631
+ devicesString.append(k + '=' + v + ": " + k[:charPos])
632
+ devicesString = ','.join(devicesString)
633
+ if additionalOptions is None :
634
+ additionalOptions = "Specify IP Address=IP Scan,Rescan=Rescan,Quit=Quit"
635
+ userStr = listSelection(title=title,message=message,selectionList=devicesString, additionalOptions=additionalOptions)
636
+
637
+ # Process the user response
638
+ if (userStr.lower() in 'quit'):
639
+ return "quit"
640
+ elif (userStr.lower() in 'rescan'):
641
+ ip_address = None
642
+ scanDictionary = None
643
+ favouriteOnly = True
644
+ elif (userStr.lower() in 'all conn types'):
645
+ ip_address = None
646
+ scanDictionary = None
647
+ favouriteOnly = False
648
+ elif(userStr.lower() in 'specify ip address'):
649
+ ip_address = requestDialog("Please input IP Address of the module you would like to connect to: ")
650
+ scanDictionary = None
651
+ favouriteOnly = False
652
+ else:
653
+ # Return the address string of the selected module
654
+ return userStr
655
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quarchpy
3
- Version: 2.1.24.dev3
3
+ Version: 2.1.24.dev4
4
4
  Summary: This packpage offers Python support for Quarch Technology modules.
5
5
  Author: Quarch Technology ltd
6
6
  Author-email: support@quarch.com
@@ -2,7 +2,8 @@ quarchpy/LICENSE.rst,sha256=SmYK6o5Xs2xRaUwYT-HqNDRu9eygu6y9QwweXNttiRc,3690
2
2
  quarchpy/QuarchPy Function Listing.xlsx,sha256=NE68cZPn7b9P3wcnCsnQr3H6yBkt6D5S6dH6457X520,31245
3
3
  quarchpy/README.txt,sha256=-LbrJ5rCPGSxoBmvr9CxJVokQUDwZSjoHa43eN8bWck,980
4
4
  quarchpy/__init__.py,sha256=0aEaJ1Aduzm1g_1OvibCDm5j2PcZAh-OPdVWiXZwF_E,3090
5
- quarchpy/_version.py,sha256=LwRNPO0mit7fBI4ePuiG42FK_PW44fibu4bi3333LWA,27
5
+ quarchpy/_version.py,sha256=7JXuR6Iu5TL3ORCOcm0I8fTXgxeTSfK1wjhRA_papEQ,27
6
+ quarchpy/_version.py.bak,sha256=LwRNPO0mit7fBI4ePuiG42FK_PW44fibu4bi3333LWA,27
6
7
  quarchpy/connection.py,sha256=9lPIUP4LCWkFAedc8CWorDY-3ujdAA-cyeNkSBdIv9E,2226
7
8
  quarchpy/launchQPSJ21.bat,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
9
  quarchpy/run.py,sha256=mRp9IGPs-wdGisKK1FG_9DciFx_V0LePH5mfkTPhsd4,9681
@@ -11,10 +12,10 @@ quarchpy/.idea/.name,sha256=Z0w79DYY3o5FZDJpHMs2Rb_ooPlznKnemSWw-jFRCXk,14
11
12
  quarchpy/.idea/misc.xml,sha256=njbmweQkO94aRIK_Z8dZDifybOXO9VrC_K3UDiYEiTE,189
12
13
  quarchpy/.idea/modules.xml,sha256=pIQEC9RvqPyHKJcSkJ4i1fHWx6zcC-D8tNGREb6Au1Y,649
13
14
  quarchpy/.idea/quarchpy.iml,sha256=YYqwuBAzDwOyZwSwAQ8jDY5X-cmbNp-VAITW-h1uLdQ,688
14
- quarchpy/.idea/workspace.xml,sha256=WX97vV-5Nfi5qYRDNytuxhxaYCPTEJmNtZ4nEDrn1M8,18098
15
+ quarchpy/.idea/workspace.xml,sha256=GS0y52z-EHiEpNwssZ47qFW6zbQWFcH0X2h56wpO3X0,18734
15
16
  quarchpy/.idea/inspectionProfiles/profiles_settings.xml,sha256=YXLFmX7rPNGcnKK1uX1uKYPN0fpgskYNe7t0BV7cqkY,174
16
17
  quarchpy/__pycache__/__init__.cpython-311.pyc,sha256=hCNane24BOaTOLH7AuH65LSZYyBQqhMG_LRK5tVjq8g,5074
17
- quarchpy/__pycache__/_version.cpython-311.pyc,sha256=xwdjvq5dEkLncIXkXOitJ-Nel71slzb7YwTp4aKRW_I,228
18
+ quarchpy/__pycache__/_version.cpython-311.pyc,sha256=HfUbx31bSFgB-wNMFpVZiMmA3VtV_MV6nlmjfe4DhhE,198
18
19
  quarchpy/__pycache__/connection.cpython-311.pyc,sha256=ho3AhQMhS0nSVxbVoYgFpfEKXD4NYmQ65K5hUbnlvAA,3677
19
20
  quarchpy/config_files/__init__.py,sha256=bP9FM5hjP0r7s8qmWqcsVjAfHM0B4XY7cz5sTpe2HDA,290
20
21
  quarchpy/config_files/quarch_config_parser.py,sha256=mjP-uO58hVlHm25ZAFjX9Zwhn8z0np4vveLNHtx8Bt4,28891
@@ -1249,13 +1250,14 @@ quarchpy/device/device.py,sha256=5H3lBjrwckYuew1x6JXsiXTqGTWg0fGBaYzdTD3fXRM,222
1249
1250
  quarchpy/device/quarchArray.py,sha256=_8z6P1wCtIs2kkkAzNRc1YrT-NmGRvzd3btqa-Yl3Hc,2816
1250
1251
  quarchpy/device/quarchPPM.py,sha256=DnqkdxDjyu533RL2ZL0ISpe2nSXTkm4O6jFqAnZZf6g,3271
1251
1252
  quarchpy/device/quarchQPS.py,sha256=D6v0l-MwoABP3Lz5l3bnLJMfIpghAZZ319j4m5RRKvw,18780
1252
- quarchpy/device/scanDevices.py,sha256=2qq9vpaATCd9tH5P4ZIHsQ1prkR8DsW0TiDHVR244Aw,28878
1253
+ quarchpy/device/scanDevices.py,sha256=cXBnBb6_QIaIxS1qhWiMPkbYLqu34LB1v_q-c-fYROE,28871
1254
+ quarchpy/device/scanDevices.py.bak,sha256=2qq9vpaATCd9tH5P4ZIHsQ1prkR8DsW0TiDHVR244Aw,28878
1253
1255
  quarchpy/device/__pycache__/__init__.cpython-311.pyc,sha256=3RVGRWzAz2ikB9pqThm8OPiqpY1zsXx46t25s0eAyPY,830
1254
1256
  quarchpy/device/__pycache__/device.cpython-311.pyc,sha256=KoOysrTzD37BddEY6vBriQQx-YQQQD0M5RD_FI2SscQ,23708
1255
1257
  quarchpy/device/__pycache__/quarchArray.cpython-311.pyc,sha256=fXYuoZoVIVcq4tGEb-GXxTn79cJQ_IyfgLXlI20ZLgs,3877
1256
1258
  quarchpy/device/__pycache__/quarchPPM.cpython-311.pyc,sha256=NwuFww49zzwIkmFwy5sWK_FvMK7Lgwbw02L6a2aO46A,4866
1257
1259
  quarchpy/device/__pycache__/quarchQPS.cpython-311.pyc,sha256=oSBP_1HVZ6IFCYGvAbwDWSU2BvW6Emr1NKyKYtE9odw,23330
1258
- quarchpy/device/__pycache__/scanDevices.cpython-311.pyc,sha256=kTTN1mhhhYh0IZCN8AlrYSPtlva7djbzr1KUhmioFRw,33002
1260
+ quarchpy/device/__pycache__/scanDevices.cpython-311.pyc,sha256=CMQ5x6AJNw_Df2fQUUiwm3XR6OByOGMA5qn_52yBuuQ,32970
1259
1261
  quarchpy/disk_test/AbsDiskFinder.py,sha256=QhtekaSSKubikGM7aqZ916u7Cc22FLEB7CqGiEvdByc,377
1260
1262
  quarchpy/disk_test/DiskTargetSelection.py,sha256=BY3zc7mg7FPxEp9OB2yRkyYIHRwrAB6U0XrsSWZrKU4,925
1261
1263
  quarchpy/disk_test/__init__.py,sha256=3-LXyQMX5macnPaHc8J3mcJQ7faTInycD8-skQYyFbE,59
@@ -1408,7 +1410,7 @@ quarchpy/utilities/__pycache__/TestCenter.cpython-311.pyc,sha256=80Rr4SEmFdyqNwX
1408
1410
  quarchpy/utilities/__pycache__/TimeValue.cpython-311.pyc,sha256=GWzrEu1fzX7JULgrzVuFl8aVNaMUKXURUY2yAwbhXRo,2668
1409
1411
  quarchpy/utilities/__pycache__/Version.cpython-311.pyc,sha256=C5tM-1GBjmxJS8nlY5AjCMdPxW-oX1NmlJAGi3RRSxQ,2697
1410
1412
  quarchpy/utilities/__pycache__/__init__.cpython-311.pyc,sha256=UVS7BpP9Dg3A0PT2JmHeY988_h_o9XZjaCoJjOnP398,212
1411
- quarchpy-2.1.24.dev3.dist-info/METADATA,sha256=3u8CoY7XES69rgxYwrBX7KoB3h-5wIZyzeUwzrLYyZo,9777
1412
- quarchpy-2.1.24.dev3.dist-info/WHEEL,sha256=a-zpFRIJzOq5QfuhBzbhiA1eHTzNCJn8OdRvhdNX0Rk,110
1413
- quarchpy-2.1.24.dev3.dist-info/top_level.txt,sha256=Vc7qsvkVax7oeBaBy_e7kvJvqb_VAAJcW_MuDMmi5W8,9
1414
- quarchpy-2.1.24.dev3.dist-info/RECORD,,
1413
+ quarchpy-2.1.24.dev4.dist-info/METADATA,sha256=1kmkdQwfHPHMm-HKFguSik1YPniQYSo-ttLtrATKZ4s,9777
1414
+ quarchpy-2.1.24.dev4.dist-info/WHEEL,sha256=a-zpFRIJzOq5QfuhBzbhiA1eHTzNCJn8OdRvhdNX0Rk,110
1415
+ quarchpy-2.1.24.dev4.dist-info/top_level.txt,sha256=Vc7qsvkVax7oeBaBy_e7kvJvqb_VAAJcW_MuDMmi5W8,9
1416
+ quarchpy-2.1.24.dev4.dist-info/RECORD,,