quarchpy 2.2.4__py2.py3-none-any.whl → 2.2.5__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.
- quarchpy/.idea/.gitignore +3 -0
- quarchpy/.idea/inspectionProfiles/Project_Default.xml +1 -39
- quarchpy/.idea/misc.xml +0 -3
- quarchpy/.idea/modules.xml +1 -0
- quarchpy/.idea/quarchpy.iml +1 -0
- quarchpy/.idea/workspace.xml +71 -113
- quarchpy/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/__pycache__/_version.cpython-311.pyc +0 -0
- quarchpy/__pycache__/connection.cpython-311.pyc +0 -0
- quarchpy/__pycache__/run.cpython-311.pyc +0 -0
- quarchpy/_version.py +1 -1
- quarchpy/_version.py.bak +1 -0
- quarchpy/config_files/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/config_files/__pycache__/quarch_config_parser.cpython-311.pyc +0 -0
- quarchpy/connection_specific/QPS/win-amd64/qps_lib/JFXUtilities-1.0.jar +0 -0
- quarchpy/connection_specific/QPS/win-amd64/resources/profiles/3_PHASE_PAM_AC_DEFAULT.rcf +2 -2
- quarchpy/connection_specific/QPS/win-amd64/resources/profiles/3_PHASE_PAM_AC_FULL.rcf +5 -56
- quarchpy/connection_specific/QPS/win-amd64/resources/profiles/PAM_EXAMPLE_CONFIG.rcf +1 -1
- quarchpy/connection_specific/connection_QPS.py +4 -4
- quarchpy/debug/__pycache__/SystemTest.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/module_debug.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/simple_terminal.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/upgrade_quarchpy.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/versionCompare.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/device.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/quarchArray.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/quarchPPM.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/quarchQPS.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/scanDevices.cpython-311.pyc +0 -0
- quarchpy/device/quarchQPS.py.bak +398 -0
- quarchpy/disk_test/__pycache__/AbsDiskFinder.cpython-311.pyc +0 -0
- quarchpy/disk_test/__pycache__/DiskTargetSelection.cpython-311.pyc +0 -0
- quarchpy/disk_test/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/disk_test/__pycache__/iometerDiskFinder.cpython-311.pyc +0 -0
- quarchpy/docs/CHANGES.rst +0 -3
- quarchpy/docs/CHANGES.rst.bak +430 -0
- quarchpy/fio/__pycache__/FIO_interface.cpython-311.pyc +0 -0
- quarchpy/fio/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/fio/__pycache__/fioDiskFinder.cpython-311.pyc +0 -0
- quarchpy/iometer/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/iometer/__pycache__/gen_iometer_template.cpython-311.pyc +0 -0
- quarchpy/iometer/__pycache__/iometerFuncs.cpython-311.pyc +0 -0
- quarchpy/qis/__pycache__/StreamHeaderInfo.cpython-311.pyc +0 -0
- quarchpy/qis/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/qis/__pycache__/qisFuncs.cpython-311.pyc +0 -0
- quarchpy/qps/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/qps/__pycache__/qpsFuncs.cpython-311.pyc +0 -0
- quarchpy/user_interface/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/user_interface/__pycache__/user_interface.cpython-311.pyc +0 -0
- quarchpy/utilities/__pycache__/TestCenter.cpython-311.pyc +0 -0
- quarchpy/utilities/__pycache__/TimeValue.cpython-311.pyc +0 -0
- quarchpy/utilities/__pycache__/Version.cpython-311.pyc +0 -0
- quarchpy/utilities/__pycache__/__init__.cpython-311.pyc +0 -0
- {quarchpy-2.2.4.dist-info → quarchpy-2.2.5.dist-info}/METADATA +2 -5
- {quarchpy-2.2.4.dist-info → quarchpy-2.2.5.dist-info}/RECORD +59 -138
- {quarchpy-2.2.4.dist-info → quarchpy-2.2.5.dist-info}/WHEEL +1 -1
- quarchpy/.idea/.name +0 -1
- quarchpy/.idea/vcs.xml +0 -6
- quarchpy/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/__pycache__/_version.cpython-312.pyc +0 -0
- quarchpy/__pycache__/connection.cpython-312.pyc +0 -0
- quarchpy/__pycache__/run.cpython-312.pyc +0 -0
- quarchpy/config_files/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/config_files/__pycache__/quarch_config_parser.cpython-312.pyc +0 -0
- quarchpy/connection_specific/QPS/win-amd64/InstallType.dat +0 -1
- quarchpy/connection_specific/QPS/win-amd64/qps_lib/qis-1.45.jar +0 -0
- quarchpy/connection_specific/QPS/win-amd64/qps_lib/qis-1.46.jar +0 -0
- quarchpy/connection_specific/__pycache__/StreamChannels.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/StreamChannels.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_QIS.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_QIS.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_QPS.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_QPS.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_ReST.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_ReST.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_Serial.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_Serial.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_TCP.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_TCP.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_USB.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_USB.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/mDNS.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/mDNS.cpython-312.pyc +0 -0
- quarchpy/connection_specific/connection_QIS.py.bak +0 -1749
- quarchpy/connection_specific/jdk_j21_jres/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/connection_specific/jdk_j21_jres/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/connection_specific/jdk_j21_jres/__pycache__/fix_permissions.cpython-311.pyc +0 -0
- quarchpy/connection_specific/jdk_j21_jres/__pycache__/fix_permissions.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/serialutil.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/serialutil.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/serialwin32.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/serialwin32.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/win32.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/win32.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports_common.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports_common.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports_windows.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports_windows.cpython-312.pyc +0 -0
- quarchpy/connection_specific/usb_libs/__pycache__/libusb1.cpython-312.pyc +0 -0
- quarchpy/connection_specific/usb_libs/__pycache__/usb1.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/SystemTest.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/module_debug.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/simple_terminal.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/upgrade_quarchpy.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/versionCompare.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/device.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/quarchArray.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/quarchPPM.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/quarchQPS.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/scanDevices.cpython-312.pyc +0 -0
- quarchpy/device/quarchPPM.py.bak +0 -67
- quarchpy/disk_test/__pycache__/AbsDiskFinder.cpython-312.pyc +0 -0
- quarchpy/disk_test/__pycache__/DiskTargetSelection.cpython-312.pyc +0 -0
- quarchpy/disk_test/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/disk_test/__pycache__/iometerDiskFinder.cpython-312.pyc +0 -0
- quarchpy/fio/__pycache__/FIO_interface.cpython-312.pyc +0 -0
- quarchpy/fio/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/iometer/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/iometer/__pycache__/gen_iometer_template.cpython-312.pyc +0 -0
- quarchpy/iometer/__pycache__/iometerFuncs.cpython-312.pyc +0 -0
- quarchpy/qis/__pycache__/StreamHeaderInfo.cpython-312.pyc +0 -0
- quarchpy/qis/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/qis/__pycache__/qisFuncs.cpython-312.pyc +0 -0
- quarchpy/qps/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/qps/__pycache__/qpsFuncs.cpython-312.pyc +0 -0
- quarchpy/user_interface/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/user_interface/__pycache__/user_interface.cpython-312.pyc +0 -0
- quarchpy/utilities/__pycache__/TestCenter.cpython-312.pyc +0 -0
- quarchpy/utilities/__pycache__/TimeValue.cpython-312.pyc +0 -0
- quarchpy/utilities/__pycache__/Version.cpython-312.pyc +0 -0
- quarchpy/utilities/__pycache__/__init__.cpython-312.pyc +0 -0
- {quarchpy-2.2.4.dist-info → quarchpy-2.2.5.dist-info}/top_level.txt +0 -0
@@ -1,1749 +0,0 @@
|
|
1
|
-
import csv
|
2
|
-
import socket
|
3
|
-
import re
|
4
|
-
import time
|
5
|
-
import sys
|
6
|
-
import os
|
7
|
-
import datetime
|
8
|
-
import select
|
9
|
-
import threading
|
10
|
-
import math
|
11
|
-
import logging
|
12
|
-
import struct
|
13
|
-
from io import StringIO
|
14
|
-
from quarchpy.user_interface import *
|
15
|
-
import xml.etree.ElementTree as ET
|
16
|
-
from connection_specific.StreamChannels import StreamGroups
|
17
|
-
|
18
|
-
|
19
|
-
# QisInterface provides a way of connecting to a Quarch backend running at the specified ip address and port, defaults to localhost and 9722
|
20
|
-
class QisInterface:
|
21
|
-
def __init__(self, host='127.0.0.1', port=9722, connectionMessage=True):
|
22
|
-
self.host = host
|
23
|
-
self.port = port
|
24
|
-
self.maxRxBytes = 4096
|
25
|
-
self.sock = None
|
26
|
-
self.StreamRunSentSemaphore = threading.Semaphore()
|
27
|
-
self.sockSemaphore = threading.Semaphore()
|
28
|
-
self.stopFlagList = []
|
29
|
-
self.listSemaphore = threading.Semaphore()
|
30
|
-
self.deviceList = []
|
31
|
-
self.deviceDict = {}
|
32
|
-
self.dictSemaphore = threading.Semaphore()
|
33
|
-
self.connect(connectionMessage = connectionMessage)
|
34
|
-
self.stripesEvent = threading.Event()
|
35
|
-
|
36
|
-
self.qps_stream_header = None
|
37
|
-
self.qps_record_dir_path = None
|
38
|
-
self.qps_record_start_time = None
|
39
|
-
self.qps_stream_folder_name = None
|
40
|
-
|
41
|
-
self.module_xml_header = None
|
42
|
-
self.streamGroups = None
|
43
|
-
self.has_digitals = False
|
44
|
-
self.is_multirate = False
|
45
|
-
|
46
|
-
self.streamSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
47
|
-
self.streamSock.settimeout(5)
|
48
|
-
self.streamSock.connect((self.host, self.port))
|
49
|
-
self.pythonVersion = sys.version[0]
|
50
|
-
self.cursor = '>'
|
51
|
-
#clear packets
|
52
|
-
welcomeString = self.streamSock.recv(self.maxRxBytes).rstrip()
|
53
|
-
|
54
|
-
|
55
|
-
def connect(self, connectionMessage = True):
|
56
|
-
'''
|
57
|
-
Connect() tries to open a socket on the host and port specified in the objects variables
|
58
|
-
If successful it returns the backends welcome string. If it fails it returns a string saying unable to connect
|
59
|
-
The backend should be running and host and port set before running this function. Normally it should be called at the beggining
|
60
|
-
of talking to the backend and left open until finished talking when the disconnect() function should be ran
|
61
|
-
|
62
|
-
Param:
|
63
|
-
connectionMessage: boolean, optional
|
64
|
-
Set to False if you don't want a warning message to appear when an instance is already running on that port. Useful when using isQisRunning() from qisFuncs
|
65
|
-
'''
|
66
|
-
try:
|
67
|
-
self.deviceDictSetup('QIS')
|
68
|
-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
69
|
-
self.sock.settimeout(5)
|
70
|
-
self.sock.connect((self.host, self.port))
|
71
|
-
|
72
|
-
#clear packets
|
73
|
-
try:
|
74
|
-
welcomeString = self.sock.recv(self.maxRxBytes).rstrip()
|
75
|
-
welcomeString = 'Connected@' + str(self.host) + ':' + str(self.port) + ' ' + '\n ' + str(welcomeString)
|
76
|
-
self.deviceDict['QIS'][0:3] = [False, 'Connected', welcomeString]
|
77
|
-
return welcomeString
|
78
|
-
except Exception as e:
|
79
|
-
logging.error('No welcome received. Unable to connect to Quarch backend on specified host and port (' + self.host + ':' + str(self.port) + ')')
|
80
|
-
logging.error('Is backend running and host accessible?')
|
81
|
-
self.deviceDict['QIS'][0:3] = [True, 'Disconnected', 'Unable to connect to QIS']
|
82
|
-
raise e
|
83
|
-
except Exception as e:
|
84
|
-
self.deviceDictSetup('QIS')
|
85
|
-
if connectionMessage:
|
86
|
-
logging.error('Unable to connect to Quarch backend on specified host and port (' + self.host + ':' + str(self.port) + ').')
|
87
|
-
logging.error('Is backend running and host accessible?')
|
88
|
-
self.deviceDict['QIS'][0:3] = [True, 'Disconnected', 'Unable to connect to QIS']
|
89
|
-
raise e
|
90
|
-
|
91
|
-
# Tries to close the socket to specified host and port.
|
92
|
-
def disconnect(self):
|
93
|
-
res = 'Disconnecting from backend'
|
94
|
-
try:
|
95
|
-
self.sock.shutdown(socket.SHUT_RDWR)
|
96
|
-
self.sock.close()
|
97
|
-
self.deviceDict['QIS'][0:3] = [False, "Disconnected", 'Successfully disconnected from QIS']
|
98
|
-
except Exception as e:
|
99
|
-
exc_type, exc_obj, exc_tb = sys.exc_info()
|
100
|
-
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
101
|
-
message = 'Unable to end connection. ' + self.host + ':' + str(self.port) + ' \r\n' + str(exc_type) + ' ' + str(fname) + ' ' + str(exc_tb.tb_lineno)
|
102
|
-
self.deviceDict['QIS'][0:3] = [True, "Connected", message]
|
103
|
-
raise e
|
104
|
-
return res
|
105
|
-
|
106
|
-
def closeConnection(self, sock=None, conString=None):
|
107
|
-
if sock == None:
|
108
|
-
sock = self.sock
|
109
|
-
if conString is None:
|
110
|
-
cmd="close"
|
111
|
-
else:
|
112
|
-
cmd =conString+" close"
|
113
|
-
|
114
|
-
response = self.sendAndReceiveText(sock, cmd)
|
115
|
-
return response
|
116
|
-
|
117
|
-
def startStream(self, module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator, streamDuration = None, inMemoryData = None):
|
118
|
-
self.StreamRunSentSemaphore.acquire()
|
119
|
-
self.deviceDictSetup('QIS')
|
120
|
-
i = self.deviceMulti(module)
|
121
|
-
self.stopFlagList[i] = True
|
122
|
-
self.stripesEvent.set()
|
123
|
-
self.module_xml_header = None
|
124
|
-
|
125
|
-
# Create the thread
|
126
|
-
t1 = threading.Thread(target=self.startStreamThread, name=module,
|
127
|
-
args=(module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator, streamDuration, inMemoryData))
|
128
|
-
# Start the thread
|
129
|
-
t1.start()
|
130
|
-
|
131
|
-
# count = 0
|
132
|
-
while (self.stripesEvent.is_set()):
|
133
|
-
# count += 1 --debugging to show delay
|
134
|
-
pass
|
135
|
-
# just wait until event is cleared
|
136
|
-
|
137
|
-
def startStreamQPS(self, module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator):
|
138
|
-
self.StreamRunSentSemaphore.acquire()
|
139
|
-
self.deviceDictSetup('QIS')
|
140
|
-
i = self.deviceMulti(module)
|
141
|
-
self.stopFlagList[i] = True
|
142
|
-
self.stripesEvent.set()
|
143
|
-
self.module_xml_header = None
|
144
|
-
|
145
|
-
# Create the thread
|
146
|
-
t1 = threading.Thread(target=self.startStreamThreadQPS, name=module,
|
147
|
-
args=(module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator))
|
148
|
-
# Start the thread
|
149
|
-
t1.start()
|
150
|
-
|
151
|
-
# count = 0
|
152
|
-
while (self.stripesEvent.is_set()):
|
153
|
-
# count += 1 --debugging to show delay
|
154
|
-
pass
|
155
|
-
# just wait until event is cleared
|
156
|
-
|
157
|
-
def stopStream(self, module, blocking = True):
|
158
|
-
moduleName=module.ConString
|
159
|
-
i = self.deviceMulti(moduleName)
|
160
|
-
self.stopFlagList[i] = False
|
161
|
-
# Wait until the stream thread is finished before returning to user.
|
162
|
-
# This means this function will block until the QIS buffer is emptied by the second while
|
163
|
-
# loop in startStreanThread. This may take some time, especially at low averaging but
|
164
|
-
# should gurantee the data won't be lost and QIS buffer is emptied.
|
165
|
-
if blocking:
|
166
|
-
running = True
|
167
|
-
while running:
|
168
|
-
threadNameList = []
|
169
|
-
for t1 in threading.enumerate():
|
170
|
-
threadNameList.append(t1.name)
|
171
|
-
moduleStreaming= module.sendCommand("rec stream?").lower() #checking if module thinks its streaming.
|
172
|
-
moduleStreaming2= module.sendCommand("stream?").lower() #checking if the module has told qis it has stopped streaming.
|
173
|
-
|
174
|
-
if (moduleName in threadNameList or "running" in moduleStreaming or "running" in moduleStreaming2):
|
175
|
-
time.sleep(0.1)
|
176
|
-
|
177
|
-
else:
|
178
|
-
running = False
|
179
|
-
time.sleep(0.1)
|
180
|
-
|
181
|
-
def startStreamThread(self, module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator, streamDuration = None, inMemoryData = None):
|
182
|
-
# This is the function that is run when t1 is created. It is run in a seperate thread from
|
183
|
-
# the main application so streaming can happen without blocking the main application from
|
184
|
-
# doing other things. Within this function/thread you have to be very careful not to try
|
185
|
-
# and 'communicate' with anything from other threads. If you do, you MUST use a thread safe
|
186
|
-
# way of communicating. The thread creates it's own socket and should use that, NOT the objects socket
|
187
|
-
# (which some of the comms with module functions will use by default).
|
188
|
-
|
189
|
-
f = None
|
190
|
-
#Start module streaming and then read stream data
|
191
|
-
if inMemoryData is not None:
|
192
|
-
if not isinstance(inMemoryData, StringIO):
|
193
|
-
raise Exception ("Error! The parameter 'inMemoryData' is NOT of type StringIO")
|
194
|
-
f = inMemoryData
|
195
|
-
else:
|
196
|
-
f = open(fileName, 'w')
|
197
|
-
|
198
|
-
stripes = ['Empty Header']
|
199
|
-
#Send stream command so module starts streaming data into the backends buffer
|
200
|
-
streamRes = self.sendAndReceiveCmd(self.streamSock, 'rec stream', device=module, betweenCommandDelay = 0)
|
201
|
-
#printText(streamRes)
|
202
|
-
if ('rec stream : OK' in streamRes):
|
203
|
-
if (releaseOnData == False):
|
204
|
-
self.StreamRunSentSemaphore.release()
|
205
|
-
self.stripesEvent.clear()
|
206
|
-
self.deviceDict[module][0:3] = [False, 'Running', 'Stream Running']
|
207
|
-
else:
|
208
|
-
self.StreamRunSentSemaphore.release()
|
209
|
-
self.stripesEvent.clear()
|
210
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', module + " couldn't start because " + streamRes]
|
211
|
-
return
|
212
|
-
|
213
|
-
baseSamplePeriod = self.streamHeaderAverage(device=module, sock=self.streamSock)
|
214
|
-
count=0
|
215
|
-
maxTries=10
|
216
|
-
while 'Header Not Available' in baseSamplePeriod:
|
217
|
-
baseSamplePeriod = self.streamHeaderAverage(device=module, sock=self.streamSock)
|
218
|
-
time.sleep(0.1)
|
219
|
-
count += 1
|
220
|
-
if count > maxTries:
|
221
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', 'Header not available']
|
222
|
-
exit()
|
223
|
-
version = self.streamHeaderVersion(device=module, sock=self.streamSock)
|
224
|
-
timeStampHeader = datetime.datetime.now().strftime("%H:%M:%S:%f %d/%m/%y")
|
225
|
-
formatHeader = self.streamHeaderFormat(device=module, sock=self.streamSock)
|
226
|
-
formatHeader = formatHeader.replace(", ", separator)
|
227
|
-
f.write(formatHeader + '\n')
|
228
|
-
|
229
|
-
numStripesPerRead = 4096
|
230
|
-
maxFileExceeded = False
|
231
|
-
openAttempts = 0
|
232
|
-
leftover = 0
|
233
|
-
remainingStripes = []
|
234
|
-
streamOverrun = False
|
235
|
-
streamComplete = False
|
236
|
-
|
237
|
-
# baseSamplePeriod is a string in the form [int][unit]
|
238
|
-
# where the unit can be S,mS,uS,nS
|
239
|
-
# we need to convert it to a float number of seconds
|
240
|
-
# and we also derive the baseSampleUnits in string and numeric form
|
241
|
-
if 'ns' in baseSamplePeriod.lower():
|
242
|
-
baseSampleUnitText = 'ns'
|
243
|
-
baseSampleUnitExponent = -9
|
244
|
-
elif 'us' in baseSamplePeriod.lower():
|
245
|
-
baseSampleUnitText = 'us'
|
246
|
-
baseSampleUnitExponent = -6
|
247
|
-
elif 'ms' in baseSamplePeriod.lower():
|
248
|
-
baseSampleUnitText = 'ms'
|
249
|
-
baseSampleUnitExponent = -3
|
250
|
-
elif 'S' in baseSamplePeriod.lower():
|
251
|
-
baseSampleUnitText = 's'
|
252
|
-
baseSampleUnitExponent = 0
|
253
|
-
else:
|
254
|
-
raise ValueError("couldn't decode samplePeriod")
|
255
|
-
|
256
|
-
baseSamplePeriodS = int(re.search(r'^\d*\.?\d*', baseSamplePeriod).group())*(10**baseSampleUnitExponent)
|
257
|
-
|
258
|
-
# # TODO: MD Thinks this implements software averaging, is unused and now performed in QIS
|
259
|
-
# if streamAverage != None:
|
260
|
-
# #Matt converting streamAveraging into number
|
261
|
-
# streamAverage = self.convertStreamAverage(streamAverage)
|
262
|
-
# stripesPerAverage = float(streamAverage) / (float(baseSamplePeriodS) * 4e-6)
|
263
|
-
if inMemoryData is None:
|
264
|
-
f = open(fileName, 'a', newline='') #changed from ab to a as all data should be in string format now regardless of py2 or py3
|
265
|
-
isRun = True
|
266
|
-
while isRun:
|
267
|
-
try:
|
268
|
-
# Until the event threadRunEvent is set externally to this thread,
|
269
|
-
# loop and read from the stream
|
270
|
-
i = self.deviceMulti(module)
|
271
|
-
while self.stopFlagList[i] and (not streamOverrun) and (not streamComplete):
|
272
|
-
#now = time.time()
|
273
|
-
streamOverrun, removeChar, newStripes = self.streamGetStripesText(self.streamSock, module, numStripesPerRead)
|
274
|
-
newStripes = newStripes.replace(' ', separator)
|
275
|
-
if streamOverrun:
|
276
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', 'Device buffer overrun']
|
277
|
-
# TODO: MD Why don't we return isEmpty in the tuple, instead of having this confusing test?
|
278
|
-
if (removeChar == -6 and len(newStripes) == 6):
|
279
|
-
isEmpty = True
|
280
|
-
else:
|
281
|
-
isEmpty = False
|
282
|
-
if isEmpty == False:
|
283
|
-
#Writes in file if not too big else stops streaming
|
284
|
-
if fileName is not None:
|
285
|
-
statInfo = os.stat(fileName)
|
286
|
-
fileMB = statInfo.st_size / 1048576
|
287
|
-
else:
|
288
|
-
fileMB = 1
|
289
|
-
try:
|
290
|
-
int(fileMaxMB)
|
291
|
-
except:
|
292
|
-
continue
|
293
|
-
if int(fileMB) < int(fileMaxMB):
|
294
|
-
if (releaseOnData == True):
|
295
|
-
self.StreamRunSentSemaphore.release()
|
296
|
-
self.stripesEvent.clear()
|
297
|
-
releaseOnData = False
|
298
|
-
# TODO: MD Thinks this implements software averaging, is unused and now performed in QIS where required
|
299
|
-
if(streamAverage != None):
|
300
|
-
leftover, remainingStripes = self.averageStripes(leftover, stripesPerAverage, newStripes[:removeChar], f, remainingStripes)
|
301
|
-
else:
|
302
|
-
# if we have a fixed streamDuration
|
303
|
-
if streamDuration != None:
|
304
|
-
# Get the last data line in the file
|
305
|
-
lastLine = newStripes.splitlines()[-3] # the last data line is followed by 'eof' and '>'
|
306
|
-
lastTime = lastLine.split(separator)[0] # get the first (time) entry
|
307
|
-
|
308
|
-
# if the last entry is still within the required stream length, write the whole lot
|
309
|
-
if int(lastTime) < int(streamDuration/(10**baseSampleUnitExponent)): # < rather than <= because we start at 0
|
310
|
-
newStripes = newStripes.replace(' ', separator)
|
311
|
-
f.write(newStripes[:removeChar])
|
312
|
-
|
313
|
-
# else write each line individually until we have reached the desired endpoint
|
314
|
-
else:
|
315
|
-
for thisLine in newStripes.splitlines()[:-2]:
|
316
|
-
lastTime = thisLine.split(separator)[0]
|
317
|
-
if int(lastTime) < int(streamDuration/(10**baseSampleUnitExponent)):
|
318
|
-
f.write(thisLine + '\r\n') # Put the CR back on the end
|
319
|
-
else:
|
320
|
-
streamComplete = True
|
321
|
-
break
|
322
|
-
else:
|
323
|
-
newStripes = newStripes.replace(' ', separator)
|
324
|
-
f.write(newStripes[:removeChar])
|
325
|
-
|
326
|
-
else:
|
327
|
-
maxFileExceeded = True
|
328
|
-
#printText('QisInterface file size exceeded in loop 1- breaking')
|
329
|
-
maxFileStatus = self.streamBufferStatus(device=module, sock=self.streamSock)
|
330
|
-
f.write('Warning: Max file size exceeded before end of stream.\n')
|
331
|
-
f.write('Unrecorded stripes in buffer when file full: ' + maxFileStatus + '.')
|
332
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', 'User defined max filesize reached']
|
333
|
-
break
|
334
|
-
else:
|
335
|
-
# there's no stripes in the buffer - it's not filling up fast -
|
336
|
-
# sleeps so we don't spam qis with requests (seems to make QIS crash)
|
337
|
-
# it might be clever to change the sleep time accoring to the situation
|
338
|
-
# e.g. wait longer with higher averaging or lots of no stripes in a row
|
339
|
-
time.sleep(0.1)
|
340
|
-
streamStatus = self.streamRunningStatus(device=module, sock=self.streamSock)
|
341
|
-
if streamOverrun:
|
342
|
-
#printText('QisInterface overrun - breaking')
|
343
|
-
break
|
344
|
-
elif "Stopped" in streamStatus:
|
345
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', 'User halted stream']
|
346
|
-
break
|
347
|
-
#printText('Left while 1')
|
348
|
-
self.sendAndReceiveCmd(self.streamSock, 'rec stop', device=module, betweenCommandDelay = 0)
|
349
|
-
streamState = self.sendAndReceiveCmd(self.streamSock, 'stream?', device=module, betweenCommandDelay=0) # use "stream?" rather than "rec stream?" as it checks both QIS AND the device.
|
350
|
-
while "stopped" not in streamState.lower():
|
351
|
-
logging.debug("waiting for stream? to return stopped")
|
352
|
-
time.sleep(0.1)
|
353
|
-
streamState = self.sendAndReceiveCmd(self.streamSock, 'stream?', device=module, betweenCommandDelay=0) # use "stream?" rather than "rec stream?" as it checks both QIS AND the device.
|
354
|
-
|
355
|
-
if (not streamOverrun) and (not maxFileExceeded):
|
356
|
-
self.deviceDict[module][0:3] = [False, 'Stopped', 'Stream stopped - emptying buffer']
|
357
|
-
# print self.streamBufferStatus(device=module, sock=self.streamSock)
|
358
|
-
if (not maxFileExceeded):
|
359
|
-
#If the backend buffer still has data then keep reading it out
|
360
|
-
#printText('Streaming stopped. Emptying data left in QIS buffer to file (' + self.streamBufferStatus(device=module, sock=self.streamSock) + ')')
|
361
|
-
streamOverrun, removeChar, newStripes = self.streamGetStripesText(self.streamSock, module, numStripesPerRead)
|
362
|
-
# TODO: Why don't we return isEmpty in the tuple, instead of having this confusing test?
|
363
|
-
if (removeChar == -6 and len(newStripes) == 6):
|
364
|
-
isEmpty = True
|
365
|
-
else:
|
366
|
-
isEmpty = False
|
367
|
-
while isEmpty == False: # if newStripes has length 6 then it only contains 'eof\r\n'
|
368
|
-
if inMemoryData is None:
|
369
|
-
statInfo = os.stat(fileName)
|
370
|
-
fileMB = statInfo.st_size / 1048576
|
371
|
-
else:
|
372
|
-
fileMB = 1
|
373
|
-
try:
|
374
|
-
int(fileMaxMB)
|
375
|
-
except:
|
376
|
-
continue
|
377
|
-
if int(fileMB) < int(fileMaxMB):
|
378
|
-
if streamComplete != True:
|
379
|
-
if(streamAverage != None):
|
380
|
-
leftover, remainingStripes = self.averageStripes(leftover, stripesPerAverage, newStripes[:removeChar], f, remainingStripes)
|
381
|
-
else:
|
382
|
-
newStripes = newStripes.replace(' ',separator)
|
383
|
-
f.write(newStripes[:removeChar])
|
384
|
-
else:
|
385
|
-
if not maxFileExceeded:
|
386
|
-
maxFileStatus = self.streamBufferStatus(device=module, sock=self.streamSock)
|
387
|
-
maxFileExceeded = True
|
388
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', 'User defined max filesize reached']
|
389
|
-
break
|
390
|
-
#time.sleep(0.01) #reduce speed of loop to stop spamming qis
|
391
|
-
streamOverrun, removeChar, newStripes = self.streamGetStripesText(self.streamSock, module, numStripesPerRead, skipStatusCheck=True)
|
392
|
-
if removeChar == -6:
|
393
|
-
if len(newStripes) == 6:
|
394
|
-
isEmpty = True
|
395
|
-
if maxFileExceeded:
|
396
|
-
f.write(b'Warning: Max file size exceeded before end of stream.\n')
|
397
|
-
f.write(b'Unrecorded stripes in buffer when file full: ' + maxFileStatus + '.')
|
398
|
-
logging.warning('Max file size exceeded. Some data has not been saved to file: ' + maxFileStatus + '.')
|
399
|
-
|
400
|
-
#printText('Stripes in buffer now: ' + self.streamBufferStatus(device=module, sock=self.streamSock))
|
401
|
-
|
402
|
-
if streamOverrun:
|
403
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', 'Device buffer overrun - QIS buffer empty']
|
404
|
-
elif not maxFileExceeded:
|
405
|
-
self.deviceDict[module][0:3] = [False, 'Stopped', 'Stream stopped']
|
406
|
-
time.sleep(0.2)
|
407
|
-
isRun = False
|
408
|
-
except IOError as err:
|
409
|
-
#printText('\n\n!!!!!!!!!!!!!!!!!!!! IO Error in QisInterface !!!!!!!!!!!!!!!!!!!!\n\n')
|
410
|
-
if inMemoryData is None:
|
411
|
-
f.close()
|
412
|
-
time.sleep(0.5)
|
413
|
-
openAttempts += 1
|
414
|
-
if openAttempts > 4:
|
415
|
-
logging.error('\n\n!!!!!!!!!!!!!!!!!!!! Too many IO Errors in QisInterface !!!!!!!!!!!!!!!!!!!!\n\n')
|
416
|
-
raise err
|
417
|
-
finally:
|
418
|
-
if inMemoryData is None:
|
419
|
-
f.close()
|
420
|
-
else:
|
421
|
-
inMemoryData = f
|
422
|
-
|
423
|
-
# This is the function that is ran when t1 is created. It is ran in a seperate thread from
|
424
|
-
# the main application so streaming can happen without blocking the main application from
|
425
|
-
# doing other things. Within this function/thread you have to be very careful not to try
|
426
|
-
# and 'communicate' with anything from other threads. If you do, you MUST use a thread safe
|
427
|
-
# way of communicating. The thread creates it's own socket and should use that NOT the objects socket
|
428
|
-
# (which some of the comms with module functions will use by default).
|
429
|
-
|
430
|
-
def startStreamThreadQPS(self, module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator):
|
431
|
-
|
432
|
-
# Start module streaming and then read stream data
|
433
|
-
# self.sendAndReceiveCmd(self.streamSock, 'stream mode resample 10mS', device=module, betweenCommandDelay=0)
|
434
|
-
self.sendAndReceiveCmd(self.streamSock, 'stream mode header v3', device=module, betweenCommandDelay=0)
|
435
|
-
self.sendAndReceiveCmd(self.streamSock, 'stream mode power enable', device=module, betweenCommandDelay=0)
|
436
|
-
self.sendAndReceiveCmd(self.streamSock, 'stream mode power total enable', device=module, betweenCommandDelay=0)
|
437
|
-
|
438
|
-
self.qps_record_start_time = time.time() * 1000
|
439
|
-
|
440
|
-
stripes = ['Empty Header']
|
441
|
-
# Send stream command so module starts streaming data into the backends buffer
|
442
|
-
streamRes = self.sendAndReceiveCmd(self.streamSock, 'rec stream', device=module, betweenCommandDelay=0)
|
443
|
-
# printText(streamRes)
|
444
|
-
if ('rec stream : OK' in streamRes):
|
445
|
-
if (releaseOnData == False):
|
446
|
-
self.StreamRunSentSemaphore.release()
|
447
|
-
self.stripesEvent.clear()
|
448
|
-
self.deviceDict[module][0:3] = [False, 'Running', 'Stream Running']
|
449
|
-
else:
|
450
|
-
self.StreamRunSentSemaphore.release()
|
451
|
-
self.stripesEvent.clear()
|
452
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', module + " couldn't start because " + streamRes]
|
453
|
-
return
|
454
|
-
|
455
|
-
# If recording to file then get header for file
|
456
|
-
if (fileName is not None):
|
457
|
-
|
458
|
-
baseSamplePeriod = self.streamHeaderAverage(device=module, sock=self.streamSock)
|
459
|
-
count = 0
|
460
|
-
maxTries = 10
|
461
|
-
while 'Header Not Available' in baseSamplePeriod:
|
462
|
-
baseSamplePeriod = self.streamHeaderAverage(device=module, sock=self.streamSock)
|
463
|
-
time.sleep(0.1)
|
464
|
-
count += 1
|
465
|
-
if count > maxTries:
|
466
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', 'Header not available']
|
467
|
-
exit()
|
468
|
-
version = self.streamHeaderVersion(device=module, sock=self.streamSock)
|
469
|
-
|
470
|
-
numStripesPerRead = 4096
|
471
|
-
maxFileExceeded = False
|
472
|
-
openAttempts = 0
|
473
|
-
leftover = 0
|
474
|
-
remainingStripes = []
|
475
|
-
streamOverrun = False
|
476
|
-
# if streamAverage != None:
|
477
|
-
# # Matt converting streamAveraging into number
|
478
|
-
# streamAverage = self.convertStreamAverage(streamAverage)
|
479
|
-
# stripesPerAverage = float(streamAverage) / (float(baseSamplePeriodS) * 4e-6)
|
480
|
-
|
481
|
-
isRun = True
|
482
|
-
|
483
|
-
self.create_dir_structure(module, fileName)
|
484
|
-
|
485
|
-
while isRun:
|
486
|
-
try:
|
487
|
-
# with open(fileName, 'ab') as f:
|
488
|
-
# Until the event threadRunEvent is set externally to this thread,
|
489
|
-
# loop and read from the stream
|
490
|
-
i = self.deviceMulti(module)
|
491
|
-
while self.stopFlagList[i] and (not streamOverrun):
|
492
|
-
# now = time.time()
|
493
|
-
streamOverrun, removeChar, newStripes = self.streamGetStripesText(self.streamSock, module,
|
494
|
-
numStripesPerRead)
|
495
|
-
newStripes = newStripes.replace(' ',separator)
|
496
|
-
|
497
|
-
if streamOverrun:
|
498
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', 'Device buffer overrun']
|
499
|
-
if (removeChar == -6 and len(newStripes) == 6):
|
500
|
-
isEmpty = True
|
501
|
-
else:
|
502
|
-
isEmpty = False
|
503
|
-
if isEmpty == False:
|
504
|
-
# Writes in file if not too big else stops streaming
|
505
|
-
# Writing multiple stripes
|
506
|
-
if "\r\n" in y:
|
507
|
-
y = y.split("\r\n")
|
508
|
-
|
509
|
-
if self.has_digitals:
|
510
|
-
# Write qps files for PAM
|
511
|
-
for stripes in y:
|
512
|
-
if stripes:
|
513
|
-
stripe = stripes.split(",")
|
514
|
-
self.write_stripe_to_files_PAM(stripe)
|
515
|
-
else:
|
516
|
-
# Write qps files for PPM
|
517
|
-
for stripes in y:
|
518
|
-
if stripes:
|
519
|
-
stripe = stripes.split(",")
|
520
|
-
self.write_stripe_to_files_HD(stripe)
|
521
|
-
|
522
|
-
else:
|
523
|
-
if self.has_digitals:
|
524
|
-
# Write qps files for PAM
|
525
|
-
for stripes in y:
|
526
|
-
if stripes:
|
527
|
-
stripe = stripes.split(",")
|
528
|
-
self.write_stripe_to_files_PAM(stripe)
|
529
|
-
else:
|
530
|
-
# Write qps files for PPM
|
531
|
-
for stripes in y:
|
532
|
-
if stripes:
|
533
|
-
stripe = stripes.split(",")
|
534
|
-
self.write_stripe_to_files_HD(stripe)
|
535
|
-
|
536
|
-
|
537
|
-
else:
|
538
|
-
# there's no stripes in the buffer - it's not filling up fast -
|
539
|
-
# sleeps so we don't spam qis with requests (seems to make QIS crash)
|
540
|
-
# it might be clever to change the sleep time accoring to the situation
|
541
|
-
# e.g. wait longer with higher averaging or lots of no stripes in a row
|
542
|
-
time.sleep(0.1)
|
543
|
-
streamStatus = self.streamRunningStatus(device=module, sock=self.streamSock)
|
544
|
-
if streamOverrun:
|
545
|
-
# printText('QisInterface overrun - breaking')
|
546
|
-
break
|
547
|
-
elif "Stopped" in streamStatus:
|
548
|
-
self.deviceDict[module][0:3] = [True, 'Stopped', 'User halted stream']
|
549
|
-
break
|
550
|
-
|
551
|
-
# printText('Left while 1')
|
552
|
-
self.sendAndReceiveCmd(self.streamSock, 'rec stop', device=module, betweenCommandDelay=0)
|
553
|
-
# streamState = self.sendAndReceiveCmd(self.streamSock, 'stream?', device=module, betweenCommandDelay=0) # use "stream?" rather than "rec stream?" as it checks both QIS AND the device.
|
554
|
-
# while "stopped" not in streamState.lower():
|
555
|
-
# logging.debug("waiting for stream? to contained stopped")
|
556
|
-
# time.sleep(0.1)
|
557
|
-
# streamState = self.sendAndReceiveCmd(self.streamSock, 'stream?', device=module,betweenCommandDelay=0) # use "stream?" rather than "rec stream?" as it checks both QIS AND the device.
|
558
|
-
|
559
|
-
isRun = False
|
560
|
-
except IOError as err:
|
561
|
-
# printText('\n\n!!!!!!!!!!!!!!!!!!!! IO Error in QisInterface !!!!!!!!!!!!!!!!!!!!\n\n')
|
562
|
-
time.sleep(0.5)
|
563
|
-
openAttempts += 1
|
564
|
-
if openAttempts > 4:
|
565
|
-
logging.error(
|
566
|
-
'\n\n!!!!!!!!!!!!!!!!!!!! Too many IO Errors in QisInterface !!!!!!!!!!!!!!!!!!!!\n\n')
|
567
|
-
raise err
|
568
|
-
|
569
|
-
self.create_index_file()
|
570
|
-
if self.has_digitals:
|
571
|
-
self.create_index_file_digitals()
|
572
|
-
|
573
|
-
self.create_qps_file(module)
|
574
|
-
|
575
|
-
def write_stripe_to_files_HD(self, stripe):
|
576
|
-
# Cycle through items in stripe
|
577
|
-
for index, item in enumerate(stripe):
|
578
|
-
if index == 0:
|
579
|
-
continue
|
580
|
-
with open(os.path.join(self.qps_record_dir_path, "data000",
|
581
|
-
"data000_00" + index - 1 + "_000000000"),
|
582
|
-
"a") as file1:#changed from ab to a as all data should be in string format now regardless of py2 or py3
|
583
|
-
|
584
|
-
x = struct.pack(">d", int(item))
|
585
|
-
# logging.debug(item, x)
|
586
|
-
file1.write(x)
|
587
|
-
|
588
|
-
def write_stripe_to_files_PAM(self, stripe):
|
589
|
-
# Note to reader - List should be ordered 1>x on analogue and digitals
|
590
|
-
counter = 0
|
591
|
-
for group in self.streamGroups.groups:
|
592
|
-
for i, channel in enumerate(group.channels):
|
593
|
-
# incrementing here so we skip stripe[0] which is time
|
594
|
-
counter += 1
|
595
|
-
|
596
|
-
x = i
|
597
|
-
while len(str(x)) < 3:
|
598
|
-
x = "0" + str(x)
|
599
|
-
|
600
|
-
# Write all in group 0 to analogue
|
601
|
-
if group.group_id == 0:
|
602
|
-
|
603
|
-
with open(os.path.join(self.qps_record_dir_path, "data000",
|
604
|
-
"data000_"+x+"_000000000"),
|
605
|
-
"a") as file1:#changed from ab to a as all data should be in string format now regardless of py2 or py3
|
606
|
-
x = struct.pack(">d", int(stripe[counter]))
|
607
|
-
# logging.debug(item, x)
|
608
|
-
file1.write(x)
|
609
|
-
else:
|
610
|
-
# Write all in group 1 to digital
|
611
|
-
with open(os.path.join(self.qps_record_dir_path, "data101",
|
612
|
-
"data101_"+x+"_000000000"),
|
613
|
-
"a") as file1:#changed from ab to a as all data should be in string format now regardless of py2 or py3
|
614
|
-
x = struct.pack(">d", int(stripe[counter]))
|
615
|
-
# logging.debug(item, x)
|
616
|
-
file1.write(x)
|
617
|
-
|
618
|
-
# Query the backend for a list of connected modules. A $scan command is sent to refresh the list of devices,
|
619
|
-
# Then a wait occurs while the backend discovers devices (network ones can take a while) and then a list of device name strings is returned
|
620
|
-
# The objects connection needs to be opened (connect()) before this is used
|
621
|
-
def getDeviceList(self, sock=None):
|
622
|
-
|
623
|
-
if sock == None:
|
624
|
-
sock = self.sock
|
625
|
-
devString = self.sendAndReceiveText(sock, '$list')
|
626
|
-
devString = devString.replace('>', '')
|
627
|
-
devString = devString.replace(r'\d+\) ', '')
|
628
|
-
devString = devString.split('\r\n')
|
629
|
-
devString = filter(None, devString) #remove empty elements
|
630
|
-
return devString
|
631
|
-
|
632
|
-
def get_list_details(self, sock=None):
|
633
|
-
if sock == None:
|
634
|
-
sock = self.sock
|
635
|
-
|
636
|
-
devString = self.sendAndReceiveText(sock, '$list details')
|
637
|
-
devString = devString.replace('>', '')
|
638
|
-
devString = devString.replace(r'\d+\) ', '')
|
639
|
-
devString = devString.split('\r\n')
|
640
|
-
devString = [x for x in devString if x] # remove empty elements
|
641
|
-
return devString
|
642
|
-
|
643
|
-
def scanIP(QisConnection, ipAddress):
|
644
|
-
"""
|
645
|
-
Triggers QIS to look at a specific IP address for a quarch module
|
646
|
-
|
647
|
-
Parameters
|
648
|
-
----------
|
649
|
-
QisConnection : QpsInterface
|
650
|
-
The interface to the instance of QPS you would like to use for the scan.
|
651
|
-
ipAddress : str
|
652
|
-
The IP address of the module you are looking for eg '192.168.123.123'
|
653
|
-
sleep : int, optional
|
654
|
-
This optional variable sleeps to allow the network to scan for the module before allowing new commands to be sent to QIS.
|
655
|
-
"""
|
656
|
-
|
657
|
-
logging.debug("Starting QIS IP Address Lookup at " + ipAddress)
|
658
|
-
if not ipAddress.lower().__contains__("tcp::"):
|
659
|
-
ipAddress = "TCP::" + ipAddress
|
660
|
-
response = "No response from QIS Scan"
|
661
|
-
try:
|
662
|
-
response = QisConnection.sendCmd(cmd="$scan " + ipAddress, expectedResponse=True)
|
663
|
-
# valid response is "Located device: 192.168.1.2"
|
664
|
-
if "located" in response.lower():
|
665
|
-
logging.debug(response)
|
666
|
-
# return the valid response
|
667
|
-
return response
|
668
|
-
else:
|
669
|
-
if "startup" not in response.lower():
|
670
|
-
logging.warning("No module found at " + ipAddress)
|
671
|
-
logging.warning(response)
|
672
|
-
return response
|
673
|
-
|
674
|
-
except Exception as e:
|
675
|
-
logging.warning(e)
|
676
|
-
if "startup" not in response.lower():
|
677
|
-
logging.warning("No module found at " + ipAddress)
|
678
|
-
|
679
|
-
def GetQisModuleSelection(self, favouriteOnly=True , additionalOptions=['rescan', 'all con types', 'ip scan'], scan=True):
|
680
|
-
'''
|
681
|
-
Fuction used to list the available deviced to QIS and present them to the user for selection.
|
682
|
-
|
683
|
-
Returns myDeviceID - Str the connection string used to connect to the selected device.
|
684
|
-
'''
|
685
|
-
tableHeaders =["Modules"]
|
686
|
-
ip_address = None
|
687
|
-
favourite = favouriteOnly
|
688
|
-
while True:
|
689
|
-
printText("Scanning for modules...")
|
690
|
-
if scan and ip_address is None:
|
691
|
-
foundDevices = self.qis_scan_devices(scan=scan, favouriteOnly=favourite)
|
692
|
-
elif scan and ip_address is not None:
|
693
|
-
foundDevices = self.qis_scan_devices(scan=scan, favouriteOnly=favourite, ipAddress=ip_address)
|
694
|
-
|
695
|
-
myDeviceID = listSelection(title="Select a module",message="Select a module",selectionList=foundDevices,
|
696
|
-
additionalOptions= additionalOptions, nice=True, tableHeaders=tableHeaders,
|
697
|
-
indexReq=True)
|
698
|
-
if myDeviceID.lower() == 'rescan':
|
699
|
-
favourite = True
|
700
|
-
ip_address = None
|
701
|
-
continue
|
702
|
-
elif myDeviceID.lower() == 'all con types':
|
703
|
-
favourite = False
|
704
|
-
printText("Displaying all connection types...")
|
705
|
-
continue
|
706
|
-
elif myDeviceID.lower() == 'ip scan':
|
707
|
-
ip_address = requestDialog(title="Please input the IP Address you would like to scan")
|
708
|
-
favourite = False
|
709
|
-
continue
|
710
|
-
break
|
711
|
-
|
712
|
-
return myDeviceID
|
713
|
-
|
714
|
-
def qis_scan_devices(self, scan=True, favouriteOnly=True, ipAddress=None):
|
715
|
-
deviceList = []
|
716
|
-
foundDevices = "1"
|
717
|
-
foundDevices2 = "2" # this is used to check if new modules are being discovered or if all have been found.
|
718
|
-
scanWait = 2 # The number of seconds waited between the two scans.
|
719
|
-
|
720
|
-
if scan:
|
721
|
-
if ipAddress == None:
|
722
|
-
devString = self.sendAndReceiveText(self.sock, '$scan')
|
723
|
-
else:
|
724
|
-
devString = self.sendAndReceiveText(self.sock, '$scan TCP::' + ipAddress)
|
725
|
-
time.sleep(scanWait)
|
726
|
-
while foundDevices not in foundDevices2:
|
727
|
-
foundDevices = self.sendAndReceiveText(self.sock, '$list')
|
728
|
-
time.sleep(scanWait)
|
729
|
-
foundDevices2 = self.sendAndReceiveText(self.sock, '$list')
|
730
|
-
else:
|
731
|
-
foundDevices = self.sendAndReceiveText(self.sock, '$list')
|
732
|
-
|
733
|
-
if not "no devices found" in foundDevices.lower():
|
734
|
-
foundDevices = foundDevices.replace('>', '')
|
735
|
-
#foundDevices = foundDevices.replace(r'\d\) ', '')
|
736
|
-
# printText('"' + devString + '"')
|
737
|
-
foundDevices = foundDevices.split('\r\n')
|
738
|
-
#Can't stream over REST! Removing all REST connections.
|
739
|
-
tempList= list()
|
740
|
-
for item in foundDevices:
|
741
|
-
if item is None or "rest" in item.lower() or item == "":
|
742
|
-
pass
|
743
|
-
else:
|
744
|
-
tempList.append(item.split(")")[1].strip())
|
745
|
-
foundDevices = tempList
|
746
|
-
|
747
|
-
#If favourite only is True then only show one connection type for each module connected.
|
748
|
-
#First order the devices in preference type and then pick the first con type found for each module.
|
749
|
-
if (favouriteOnly):
|
750
|
-
foundDevices = self.sortFavourite(foundDevices)
|
751
|
-
else:
|
752
|
-
foundDevices = ["***No Devices Found***"]
|
753
|
-
|
754
|
-
return foundDevices
|
755
|
-
|
756
|
-
def sortFavourite(self, foundDevices):
|
757
|
-
index = 0
|
758
|
-
sortedFoundDevices = []
|
759
|
-
conPref = ["USB", "TCP", "SERIAL", "REST", "TELNET"]
|
760
|
-
while len(sortedFoundDevices) != len(foundDevices):
|
761
|
-
for device in foundDevices:
|
762
|
-
if conPref[index] in device.upper():
|
763
|
-
sortedFoundDevices.append(device)
|
764
|
-
index += 1
|
765
|
-
foundDevices = sortedFoundDevices
|
766
|
-
# new dictionary only containing one favourite connection to each device.
|
767
|
-
favConFoundDevices = []
|
768
|
-
index = 0
|
769
|
-
for device in sortedFoundDevices:
|
770
|
-
if (favConFoundDevices == [] or not device.split("::")[1] in str(favConFoundDevices)):
|
771
|
-
favConFoundDevices.append(device)
|
772
|
-
foundDevices = favConFoundDevices
|
773
|
-
return foundDevices
|
774
|
-
|
775
|
-
# Query stream status for a device attached to backend
|
776
|
-
# The objects connection needs to be opened (connect()) before this is used
|
777
|
-
def streamRunningStatus(self, device, sock=None):
|
778
|
-
if sock == None:
|
779
|
-
sock = self.sock
|
780
|
-
index = 0 # index of relevant line in split string
|
781
|
-
streamStatus = self.sendAndReceiveText(sock, 'stream?', device)
|
782
|
-
streamStatus = streamStatus.split('\r\n')
|
783
|
-
streamStatus[index] = re.sub(r':', '', streamStatus[index]) #remove :
|
784
|
-
return streamStatus[index]
|
785
|
-
|
786
|
-
# Query stream buffer status for a device attached to backend
|
787
|
-
# The objects connection needs to be opened (connect()) before this is used
|
788
|
-
def streamBufferStatus(self, device, sock=None):
|
789
|
-
if sock == None:
|
790
|
-
sock = self.sock
|
791
|
-
index = 1 # index of relevant line in split string
|
792
|
-
streamStatus = self.sendAndReceiveText(sock, 'stream?', device)
|
793
|
-
streamStatus = streamStatus.split('\r\n')
|
794
|
-
streamStatus[index] = re.sub(r'^Stripes Buffered: ', '', streamStatus[index])
|
795
|
-
return streamStatus[index]
|
796
|
-
|
797
|
-
# TODO: MD - This function should be replaced with a more generic method of accessing the header
|
798
|
-
# The return of a string with concatenated value and units should be replaced with something easier to parse
|
799
|
-
#
|
800
|
-
# Get the averaging used on the last/current stream
|
801
|
-
# The objects connection needs to be opened (connect()) before this is used
|
802
|
-
def streamHeaderAverage(self, device, sock=None):
|
803
|
-
try:
|
804
|
-
if sock == None:
|
805
|
-
sock = self.sock
|
806
|
-
index = 2 # index of relevant line in split string
|
807
|
-
streamStatus = self.sendAndReceiveText(sock, sentText='stream text header', device=device)
|
808
|
-
|
809
|
-
self.qps_stream_header = streamStatus
|
810
|
-
|
811
|
-
# Check for the header format. If XML, process here
|
812
|
-
if (self.isXmlHeader(streamStatus)):
|
813
|
-
# Get the basic averaging rate (V3 header)
|
814
|
-
xml_root = self.getStreamXmlHeader(device=device, sock=sock)
|
815
|
-
|
816
|
-
# For QPS streaming, stream header v3 command has already been issued before this
|
817
|
-
self.module_xml_header = xml_root
|
818
|
-
|
819
|
-
# Return the time based averaging string
|
820
|
-
device_period = xml_root.find('.//devicePeriod')
|
821
|
-
if device_period == None:
|
822
|
-
device_period = xml_root.find('.//devicePerioduS')
|
823
|
-
if device_period == None:
|
824
|
-
device_period = xml_root.find('.//mainPeriod')
|
825
|
-
averageStr = device_period.text
|
826
|
-
return averageStr
|
827
|
-
# For legacy text headers, process here
|
828
|
-
else:
|
829
|
-
streamStatus = streamStatus.split('\r\n')
|
830
|
-
if('Header Not Available' in streamStatus[0]):
|
831
|
-
dummy = streamStatus[0] + '. Check stream has been run on device.'
|
832
|
-
return dummy
|
833
|
-
streamStatus[index] = re.sub(r'^Average: ', '', streamStatus[index])
|
834
|
-
avg = streamStatus[index]
|
835
|
-
avg = 2 ** int(avg)
|
836
|
-
return '{}'.format(avg)
|
837
|
-
except Exception as e:
|
838
|
-
logging.error(device + ' Unable to get stream average.' + self.host + ':' + str(self.port))
|
839
|
-
raise e
|
840
|
-
|
841
|
-
# Get the version of the stream and convert to string for the specified device
|
842
|
-
# The objects connection needs to be opened (connect()) before this is used
|
843
|
-
def streamHeaderVersion(self, device, sock=None):
|
844
|
-
try:
|
845
|
-
if sock == None:
|
846
|
-
sock = self.sock
|
847
|
-
index = 0 # index of relevant line in split string
|
848
|
-
streamStatus = self.sendAndReceiveText(sock,'stream text header', device)
|
849
|
-
streamStatus = streamStatus.split('\r\n')
|
850
|
-
if 'Header Not Available' in streamStatus[0]:
|
851
|
-
str = streamStatus[0] + '. Check stream has been ran on device.'
|
852
|
-
logging.error(str)
|
853
|
-
return str
|
854
|
-
version = re.sub(r'^Version: ', '', streamStatus[index])
|
855
|
-
if version == '3':
|
856
|
-
version = 'Original PPM'
|
857
|
-
elif version == '4':
|
858
|
-
version = 'XLC PPM'
|
859
|
-
elif version == '5':
|
860
|
-
version = 'HD PPM'
|
861
|
-
else:
|
862
|
-
version = 'Unknown stream version'
|
863
|
-
return version
|
864
|
-
except Exception as e:
|
865
|
-
logging.error(device + ' Unable to get stream version.' + self.host + ':' + str(self.port))
|
866
|
-
raise e
|
867
|
-
|
868
|
-
# Get a header string giving which measurements are returned in the string for the specified device
|
869
|
-
# The objects connection needs to be opened (connect()) before this is used
|
870
|
-
def streamHeaderFormat(self, device, sock=None):
|
871
|
-
try:
|
872
|
-
if sock == None:
|
873
|
-
sock = self.sock
|
874
|
-
index = 1 # index of relevant line in split string STREAM MODE HEADER [?|V1,V2,V3]
|
875
|
-
streamStatus = self.sendAndReceiveText(sock,'stream text header', device)
|
876
|
-
# Check if this is a new XML form header
|
877
|
-
if (self.isXmlHeader (streamStatus)):
|
878
|
-
# Get the basic averaging rate (V3 header)
|
879
|
-
xml_root = self.getStreamXmlHeader (device=device, sock=sock)
|
880
|
-
# Return the time based averaging string
|
881
|
-
device_period = xml_root.find('.//devicePeriod')
|
882
|
-
time_unit = 'uS'
|
883
|
-
if device_period == None:
|
884
|
-
device_period = xml_root.find('.//devicePerioduS')
|
885
|
-
if device_period == None:
|
886
|
-
device_period = xml_root.find('.//mainPeriod')
|
887
|
-
if ('ns' in device_period.text):
|
888
|
-
time_unit = 'nS'
|
889
|
-
averageStr = device_period.text
|
890
|
-
|
891
|
-
# Time column always first
|
892
|
-
formatHeader = 'Time ' + time_unit + ','
|
893
|
-
# Find the channels section of each group and iterate through it to add the channel columns
|
894
|
-
for group in xml_root.iter():
|
895
|
-
if (group.tag == "channels"):
|
896
|
-
for chan in group:
|
897
|
-
# Avoid children that are not named channels
|
898
|
-
if (chan.find('.//name') is not None):
|
899
|
-
nameStr = chan.find('.//name').text
|
900
|
-
unitStr = chan.find('.//units').text
|
901
|
-
formatHeader = formatHeader + nameStr + " " + unitStr + ","
|
902
|
-
return formatHeader
|
903
|
-
# Handle legacy text headers here
|
904
|
-
else:
|
905
|
-
streamStatus = streamStatus.split('\r\n')
|
906
|
-
if 'Header Not Available' in streamStatus[0]:
|
907
|
-
str = streamStatus[0] + '. Check stream has been ran on device.'
|
908
|
-
logging.error(str)
|
909
|
-
return str
|
910
|
-
outputMode = self.sendAndReceiveText(sock,'Config Output Mode?', device)
|
911
|
-
powerMode = self.sendAndReceiveText(sock,'stream mode power?', device)
|
912
|
-
format = int(re.sub(r'^Format: ', '', streamStatus[index]))
|
913
|
-
b0 = 1 #12V_I
|
914
|
-
b1 = 1 << 1 #12V_V
|
915
|
-
b2 = 1 << 2 #5V_I
|
916
|
-
b3 = 1 << 3 #5V_V
|
917
|
-
formatHeader = 'StripeNum, Trig, '
|
918
|
-
if format & b3:
|
919
|
-
if ('3V3' in outputMode):
|
920
|
-
formatHeader = formatHeader + '3V3_V,'
|
921
|
-
else:
|
922
|
-
formatHeader = formatHeader + '5V_V,'
|
923
|
-
if format & b2:
|
924
|
-
if ('3V3' in outputMode):
|
925
|
-
formatHeader = formatHeader + ' 3V3_I,'
|
926
|
-
else:
|
927
|
-
formatHeader = formatHeader + ' 5V_I,'
|
928
|
-
|
929
|
-
if format & b1:
|
930
|
-
formatHeader = formatHeader + ' 12V_V,'
|
931
|
-
if format & b0:
|
932
|
-
formatHeader = formatHeader + ' 12V_I'
|
933
|
-
if 'Enabled' in powerMode:
|
934
|
-
if ('3V3' in outputMode):
|
935
|
-
formatHeader = formatHeader + ' 3V3_P'
|
936
|
-
else:
|
937
|
-
formatHeader = formatHeader + ' 5V_P'
|
938
|
-
if ((format & b1) or (format & b0)):
|
939
|
-
formatHeader = formatHeader + ' 12V_P'
|
940
|
-
return formatHeader
|
941
|
-
except Exception as e:
|
942
|
-
logging.error(device + ' Unable to get stream format.' + self.host + ':' + '{}'.format(self.port))
|
943
|
-
raise e
|
944
|
-
|
945
|
-
# Get stripes out of the backends stream buffer for the specified device using text commands
|
946
|
-
# The objects connection needs to be opened (connect()) before this is used
|
947
|
-
def streamGetStripesText(self, sock, device, numStripes=4096, skipStatusCheck=False):
|
948
|
-
|
949
|
-
bufferStatus = False
|
950
|
-
# Allows the status check to be skipped when emptying the buffer after streaming has stopped (saving time)
|
951
|
-
if (skipStatusCheck == False):
|
952
|
-
streamStatus = self.sendAndReceiveText(sock, 'stream?', device)
|
953
|
-
if ('Overrun' in streamStatus) or ('8388608 of 8388608' in streamStatus):
|
954
|
-
bufferStatus = True
|
955
|
-
stripes = self.sendAndReceiveText(sock, 'stream text all', device, readUntilCursor=True)
|
956
|
-
# time.sleep(0.001)
|
957
|
-
if stripes[-1:] != self.cursor:
|
958
|
-
return "Error no cursor returned."
|
959
|
-
else:
|
960
|
-
genEndOfFile = 'eof\r\n>'
|
961
|
-
if stripes[-6:] == genEndOfFile:
|
962
|
-
removeChar = -6
|
963
|
-
else:
|
964
|
-
removeChar = -1
|
965
|
-
|
966
|
-
# stripes = stripes.split('\r\n')
|
967
|
-
# stripes = filter(None, stripes) #remove empty sting elements
|
968
|
-
#printText(stripes)
|
969
|
-
return bufferStatus, removeChar, stripes
|
970
|
-
|
971
|
-
def avgStringFromPwr(self, avgPwrTwo):
|
972
|
-
if(avgPwrTwo==0):
|
973
|
-
return '0'
|
974
|
-
elif(avgPwrTwo==1):
|
975
|
-
return '2'
|
976
|
-
elif(avgPwrTwo > 1 and avgPwrTwo < 10 ):
|
977
|
-
avg = 2 ** int(avgPwrTwo)
|
978
|
-
return '{}'.format(avg)
|
979
|
-
elif(avgPwrTwo==10):
|
980
|
-
return '1k'
|
981
|
-
elif(avgPwrTwo==11):
|
982
|
-
return '2k'
|
983
|
-
elif(avgPwrTwo==12):
|
984
|
-
return '4k'
|
985
|
-
elif(avgPwrTwo==13):
|
986
|
-
return '8k'
|
987
|
-
elif(avgPwrTwo==14):
|
988
|
-
return '16k'
|
989
|
-
elif(avgPwrTwo==15):
|
990
|
-
return '32k'
|
991
|
-
else:
|
992
|
-
return 'Invalid Average Value'
|
993
|
-
|
994
|
-
# TODO: MD Thinks this implements software averaging, is unused and now performed in QIS
|
995
|
-
# Works out average values of timescales longer than max device averaging
|
996
|
-
def averageStripes(self, leftover, streamAverage, newStripes, f, remainingStripes = []):
|
997
|
-
newString = str(newStripes)
|
998
|
-
newList = []
|
999
|
-
if remainingStripes == []:
|
1000
|
-
newList = newString.split('\r\n')
|
1001
|
-
else:
|
1002
|
-
newList = remainingStripes
|
1003
|
-
newList.extend(newString.split('\r\n'))
|
1004
|
-
numElements = newList[0].count(' ') + 1
|
1005
|
-
streamTotalAverage = leftover + streamAverage
|
1006
|
-
splitList = [] * numElements
|
1007
|
-
if len(newList) < streamTotalAverage:
|
1008
|
-
remainingStripes = newList[:-1]
|
1009
|
-
return leftover, remainingStripes
|
1010
|
-
runningAverage = [0] * (len(newList[0].split(' ')) - 2)
|
1011
|
-
j = 0
|
1012
|
-
z = 1
|
1013
|
-
for i in newList[:-1]:
|
1014
|
-
splitList = i.split(' ')
|
1015
|
-
splitNumbers = [int(x) for x in splitList[2:]]
|
1016
|
-
runningAverage = [sum(x) for x in zip(runningAverage, splitNumbers)]
|
1017
|
-
if z == math.floor(streamTotalAverage):
|
1018
|
-
finalAverage = splitList[0:2] + [str(round(x / streamAverage)) for x in runningAverage]
|
1019
|
-
for counter in xrange(len(finalAverage)-1):
|
1020
|
-
finalAverage[counter] = finalAverage[counter] + ' '
|
1021
|
-
for x in finalAverage:
|
1022
|
-
f.write(x)
|
1023
|
-
f.write('\r\n')
|
1024
|
-
streamTotalAverage += streamAverage
|
1025
|
-
j += 1
|
1026
|
-
z += 1
|
1027
|
-
remainingStripes = newList[int(math.floor(j * streamAverage + leftover)):-1]
|
1028
|
-
leftover = (streamTotalAverage - streamAverage) % 1
|
1029
|
-
return leftover, remainingStripes
|
1030
|
-
|
1031
|
-
def deviceMulti(self, device):
|
1032
|
-
if (device in self.deviceList):
|
1033
|
-
return self.deviceList.index(device)
|
1034
|
-
else:
|
1035
|
-
self.listSemaphore.acquire()
|
1036
|
-
self.deviceList.append(device)
|
1037
|
-
self.stopFlagList.append(True)
|
1038
|
-
self.listSemaphore.release()
|
1039
|
-
return self.deviceList.index(device)
|
1040
|
-
|
1041
|
-
def deviceDictSetup(self, module):
|
1042
|
-
if module in self.deviceDict.keys():
|
1043
|
-
return
|
1044
|
-
elif module == 'QIS':
|
1045
|
-
self.dictSemaphore.acquire()
|
1046
|
-
self.deviceDict[module] = [False, 'Disconnected', "No attempt to connect to QIS yet"]
|
1047
|
-
self.dictSemaphore.release()
|
1048
|
-
else:
|
1049
|
-
self.dictSemaphore.acquire()
|
1050
|
-
self.deviceDict[module] = [False, 'Stopped', "User hasn't started stream"]
|
1051
|
-
self.dictSemaphore.release()
|
1052
|
-
|
1053
|
-
def streamInterrupt(self):
|
1054
|
-
for key in self.deviceDict.keys():
|
1055
|
-
if self.deviceDict[key][0]:
|
1056
|
-
return True
|
1057
|
-
return False
|
1058
|
-
|
1059
|
-
def interruptList(self):
|
1060
|
-
streamIssueList = []
|
1061
|
-
for key in self.deviceDict.keys():
|
1062
|
-
if self.deviceDict[key][0]:
|
1063
|
-
streamIssue = [key]
|
1064
|
-
streamIssue.append(self.deviceDict[key][1])
|
1065
|
-
streamIssue.append(self.deviceDict[key][2])
|
1066
|
-
streamIssueList.append(streamIssue)
|
1067
|
-
return streamIssueList
|
1068
|
-
|
1069
|
-
def waitStop(self):
|
1070
|
-
running = 1
|
1071
|
-
while running != 0:
|
1072
|
-
threadNameList = []
|
1073
|
-
for t1 in threading.enumerate():
|
1074
|
-
threadNameList.append(t1.name)
|
1075
|
-
running = 0
|
1076
|
-
for module in self.deviceList:
|
1077
|
-
if (module in threadNameList):
|
1078
|
-
running += 1
|
1079
|
-
time.sleep(0.5)
|
1080
|
-
time.sleep(1)
|
1081
|
-
|
1082
|
-
def convertStreamAverage (self, streamAveraging):
|
1083
|
-
returnValue = 32000;
|
1084
|
-
if ("k" in streamAveraging):
|
1085
|
-
returnValue = streamAveraging.replace("k", "000")
|
1086
|
-
else:
|
1087
|
-
returnValue = streamAveraging
|
1088
|
-
|
1089
|
-
return returnValue
|
1090
|
-
|
1091
|
-
# Pass in a stream header and we check if it is XML or legacy format
|
1092
|
-
def isXmlHeader (self, headerText):
|
1093
|
-
if('?xml version=' not in headerText):
|
1094
|
-
return False;
|
1095
|
-
else:
|
1096
|
-
return True
|
1097
|
-
|
1098
|
-
# Internal function. Gets the stream header and parses it into useful information
|
1099
|
-
def getStreamXmlHeader (self, device, sock=None):
|
1100
|
-
try:
|
1101
|
-
if sock == None:
|
1102
|
-
sock = self.sock
|
1103
|
-
|
1104
|
-
# Get the raw data
|
1105
|
-
headerData = self.sendAndReceiveText(sock, sentText='stream text header', device=device)
|
1106
|
-
|
1107
|
-
# The XML can contain the cursor on the end! Trap and remove it here TODO: Needs fixed in the command layer above
|
1108
|
-
if ('\r\n>' in headerData):
|
1109
|
-
headerData = headerData[:-1]
|
1110
|
-
|
1111
|
-
# Check for no header (no stream started)
|
1112
|
-
if('Header Not Available' in headerData):
|
1113
|
-
logging.error(device + ' Stream header not available.' + self.host + ':' + str(self.port))
|
1114
|
-
return None;
|
1115
|
-
|
1116
|
-
# Check for XML format
|
1117
|
-
if('?xml version=' not in headerData):
|
1118
|
-
logging.error(device + ' Header not in XML form.' + self.host + ':' + str(self.port))
|
1119
|
-
return None;
|
1120
|
-
|
1121
|
-
# Parse XML into structured format
|
1122
|
-
xml_root = ET.fromstring(headerData)
|
1123
|
-
|
1124
|
-
# Check header format is supported by quarchpy
|
1125
|
-
versionStr = xml_root.find('.//version').text
|
1126
|
-
if ('V3' not in versionStr):
|
1127
|
-
logging.error(device + ' Stream header version not compatible: ' + xml_root['version'].text + '.' + self.host + ':' + str(self.port))
|
1128
|
-
raise Exception ("Stream header version not supported");
|
1129
|
-
|
1130
|
-
# Return the XML structure for the code to use
|
1131
|
-
return xml_root
|
1132
|
-
|
1133
|
-
except Exception as e:
|
1134
|
-
logging.error(device + ' Exception while parsing stream header XML.' + self.host + ':' + str(self.port))
|
1135
|
-
raise e
|
1136
|
-
|
1137
|
-
def create_dir_structure(self, module, directory=None):
|
1138
|
-
"""
|
1139
|
-
Creates the QPS directory structure and (empty) files to be written to
|
1140
|
-
|
1141
|
-
I've put a bunch of try-except just to be sure the directory is correctly created.
|
1142
|
-
( There's probably a better way of doing this than this )
|
1143
|
-
|
1144
|
-
:param: module: String - Module string
|
1145
|
-
:param: directory: String - Name of directory for QPS stream (defaults to default recording location if invalid)
|
1146
|
-
:return: success: Boolean - Was the file structure created successfully?
|
1147
|
-
"""
|
1148
|
-
|
1149
|
-
directory = self.create_qps_directory(directory)
|
1150
|
-
|
1151
|
-
digital_count = 0
|
1152
|
-
non_dig_counter = 0
|
1153
|
-
self.streamGroups = StreamGroups()
|
1154
|
-
for index, i in enumerate(self.module_xml_header.findall('.//channels')):
|
1155
|
-
self.streamGroups.add_group(index)
|
1156
|
-
for item in i.findall('.//channel'):
|
1157
|
-
self.streamGroups.groups[index].add_channel(item.find(".//name"), item.find(".//group"), item.find(".//dataPosition"))
|
1158
|
-
if item.find(".//group").text == "Digital":
|
1159
|
-
digital_count += 1
|
1160
|
-
self.has_digitals = True
|
1161
|
-
else:
|
1162
|
-
non_dig_counter += 1
|
1163
|
-
|
1164
|
-
# Inner folders for analogue and digital signals streaming
|
1165
|
-
in_folder_analogue = "data000"
|
1166
|
-
try:
|
1167
|
-
inner_path_analogues = os.path.join(directory, in_folder_analogue)
|
1168
|
-
os.mkdir(inner_path_analogues)
|
1169
|
-
except:
|
1170
|
-
logging.warning("Failed to make inner directory for analogue signals " + inner_path_analogues)
|
1171
|
-
return False
|
1172
|
-
|
1173
|
-
in_folder_digitals = "data101"
|
1174
|
-
if self.has_digitals:
|
1175
|
-
try:
|
1176
|
-
inner_path_digitals = os.path.join(directory, in_folder_digitals)
|
1177
|
-
os.mkdir(inner_path_digitals)
|
1178
|
-
except:
|
1179
|
-
logging.warning("Failed to make inner directory for digital signals "+ inner_path_digitals)
|
1180
|
-
return False
|
1181
|
-
|
1182
|
-
logging.debug("Steaming to : " + self.qps_record_dir_path)
|
1183
|
-
|
1184
|
-
logging.debug("Creating qps data files")
|
1185
|
-
try:
|
1186
|
-
for i in range(non_dig_counter):
|
1187
|
-
file_name = "data000_00"+i+"_000000000"
|
1188
|
-
f = open(os.path.join(inner_path_analogues, file_name), "w")
|
1189
|
-
f.close()
|
1190
|
-
for i in range(digital_count):
|
1191
|
-
x = i
|
1192
|
-
while len(str(x)) < 3:
|
1193
|
-
x = "0" + str(x)
|
1194
|
-
file_name = "data101_"+x+"_000000000"
|
1195
|
-
f = open(os.path.join(inner_path_digitals, file_name), "w")
|
1196
|
-
f.close()
|
1197
|
-
except:
|
1198
|
-
logging.warning("failed to create qps data files for analogue signals")
|
1199
|
-
return False
|
1200
|
-
|
1201
|
-
logging.debug("Finished creating qps data files")
|
1202
|
-
|
1203
|
-
logging.debug("Creating qps upper level files")
|
1204
|
-
try:
|
1205
|
-
file_names = ["annotations.xml", "notes.txt", "triggers.txt"]
|
1206
|
-
for file_nome in file_names:
|
1207
|
-
f = open(os.path.join(self.qps_record_dir_path, file_nome), "w")
|
1208
|
-
f.close()
|
1209
|
-
except Exception as err:
|
1210
|
-
logging.warning("failed to create qps upper level files, "+err)
|
1211
|
-
return False
|
1212
|
-
|
1213
|
-
try:
|
1214
|
-
# Adding data000.idx separate as it's written in bytes not normal text
|
1215
|
-
f = open(os.path.join(self.qps_record_dir_path, "data000.idx"), "wb")
|
1216
|
-
f.close()
|
1217
|
-
if digital_count > 0:
|
1218
|
-
f = open(os.path.join(self.qps_record_dir_path, "data101.idx"), "wb")
|
1219
|
-
f.close()
|
1220
|
-
except Exception as err:
|
1221
|
-
logging.warning("failed to create data000.idx file, "+err)
|
1222
|
-
return False
|
1223
|
-
|
1224
|
-
logging.debug("Finished creating QPS dir structure")
|
1225
|
-
|
1226
|
-
return True
|
1227
|
-
|
1228
|
-
def create_qps_directory(self, directory):
|
1229
|
-
folder_name = None
|
1230
|
-
# Checking if there was a directory passed; and if it's a valid directory
|
1231
|
-
if not directory:
|
1232
|
-
directory = os.path.join(os.path.expanduser("~"), "AppData", "Local", "Quarch", "QPS", "Recordings")
|
1233
|
-
logging.debug("No directory specified")
|
1234
|
-
elif not os.path.isdir(directory):
|
1235
|
-
new_dir = os.path.join(str(os.path.expanduser("~"), "AppData", "Local", "Quarch", "QPS", "Recordings"))
|
1236
|
-
logging.warning(directory+" was not a valid directory, streaming to default location: \n"+new_dir)
|
1237
|
-
directory = new_dir
|
1238
|
-
else:
|
1239
|
-
# Split the directory into a path of folders
|
1240
|
-
folder_name = str(directory).split(os.sep)
|
1241
|
-
# last folder name is the name we want
|
1242
|
-
folder_name = folder_name[-1]
|
1243
|
-
# Make it known to the entire class that the path we're streaming to is the one sent across by the user
|
1244
|
-
self.qps_record_dir_path = directory
|
1245
|
-
|
1246
|
-
# If no folder name for the stream was passed, then default to 'quarchpy_recording' and a timestamp
|
1247
|
-
if not folder_name:
|
1248
|
-
folder_name = "quarchpy_recording"
|
1249
|
-
folder_name = folder_name + "-" + time.time()
|
1250
|
-
path = os.path.join(directory, self.qps_stream_folder_name)
|
1251
|
-
os.mkdir(path)
|
1252
|
-
self.qps_record_dir_path = path
|
1253
|
-
|
1254
|
-
self.qps_stream_folder_name = folder_name
|
1255
|
-
|
1256
|
-
return directory
|
1257
|
-
|
1258
|
-
def create_index_file(self):
|
1259
|
-
"""
|
1260
|
-
Create the necessary index file for QPS data000.idx
|
1261
|
-
|
1262
|
-
For future revisions, this should be updated if there are file limits on each data file
|
1263
|
-
Current implementation assumes only 1 of each data file are made.
|
1264
|
-
|
1265
|
-
No Return./
|
1266
|
-
"""
|
1267
|
-
|
1268
|
-
stream_header_size = -1
|
1269
|
-
|
1270
|
-
my_byte_array = []
|
1271
|
-
|
1272
|
-
# tree = ET.ElementTree(ET.fromstring(self.module_xml_header[:-1]))
|
1273
|
-
tree = self.module_xml_header
|
1274
|
-
|
1275
|
-
return_b_array = []
|
1276
|
-
outBuffer = []
|
1277
|
-
x = 20
|
1278
|
-
stream_header_size = 20
|
1279
|
-
|
1280
|
-
temp_dict = {"channels": 0}
|
1281
|
-
|
1282
|
-
return_b_array, stream_header_size = self.add_header_to_byte_array(return_b_array, stream_header_size,
|
1283
|
-
temp_dict, tree, is_digital=False)
|
1284
|
-
|
1285
|
-
self.add_header_to_buffer(outBuffer, return_b_array, stream_header_size, temp_dict)
|
1286
|
-
|
1287
|
-
# Attempting to read the size of the first file in data files
|
1288
|
-
file = os.path.join(self.qps_record_dir_path, "data000", "data000_000_000000000")
|
1289
|
-
data = None
|
1290
|
-
with open(file, "rb") as f:
|
1291
|
-
data = f.read() # if you only wanted to read 512 bytes, do .read(512)
|
1292
|
-
|
1293
|
-
if not data:
|
1294
|
-
raise "No data written to file"
|
1295
|
-
|
1296
|
-
num_records = len(data) / 8
|
1297
|
-
logging.debug("num_record = " + num_records)
|
1298
|
-
return_b_array.append(int(num_records).to_bytes(4, byteorder='big'))
|
1299
|
-
|
1300
|
-
start_number = 0
|
1301
|
-
logging.debug("start_record = " + start_number)
|
1302
|
-
return_b_array.append(start_number.to_bytes(8, byteorder='big'))
|
1303
|
-
|
1304
|
-
num_records = num_records - 1
|
1305
|
-
logging.debug("last_Record_number = "+num_records)
|
1306
|
-
return_b_array.append(int(num_records).to_bytes(8, byteorder='big'))
|
1307
|
-
|
1308
|
-
# Add names of every file in data000 dir here.
|
1309
|
-
files = os.listdir(os.path.join(self.qps_record_dir_path, "data000"))
|
1310
|
-
for file3 in files:
|
1311
|
-
# print(file)
|
1312
|
-
item = strToBb(file3, False)
|
1313
|
-
# print(item)
|
1314
|
-
while len(item) < 32:
|
1315
|
-
item.append("\x00")
|
1316
|
-
# print(item)
|
1317
|
-
return_b_array.append(item)
|
1318
|
-
|
1319
|
-
with open(os.path.join(self.qps_record_dir_path, "data000.idx"), "ab") as f:
|
1320
|
-
for item in outBuffer:
|
1321
|
-
# print(item)
|
1322
|
-
# print(type(item))
|
1323
|
-
f.write(bytes(item))
|
1324
|
-
# f.write(outBuffer)
|
1325
|
-
|
1326
|
-
with open(os.path.join(self.qps_record_dir_path, "data000.idx"), "ab") as f:
|
1327
|
-
self.write_b_array_to_idx_file(f, return_b_array)
|
1328
|
-
|
1329
|
-
def create_index_file_digitals(self):
|
1330
|
-
"""
|
1331
|
-
Create the necessary index file for QPS data101.idx
|
1332
|
-
|
1333
|
-
For future revisions, this should be updated if there are file limits on each data file
|
1334
|
-
Current implementation assumes only 1 of each data file are made.
|
1335
|
-
|
1336
|
-
No Return.
|
1337
|
-
"""
|
1338
|
-
|
1339
|
-
stream_header_size = -1
|
1340
|
-
my_byte_array = []
|
1341
|
-
tree = self.module_xml_header
|
1342
|
-
return_b_array = []
|
1343
|
-
outBuffer = []
|
1344
|
-
temp_dict = {}
|
1345
|
-
|
1346
|
-
return_b_array, stream_header_size = self.add_header_to_byte_array(return_b_array, stream_header_size,
|
1347
|
-
temp_dict, tree, is_digital=True)
|
1348
|
-
|
1349
|
-
self.add_header_to_buffer(outBuffer, return_b_array, stream_header_size, temp_dict)
|
1350
|
-
|
1351
|
-
# Attempting to read the size of the first file in data files
|
1352
|
-
file = os.path.join(self.qps_record_dir_path, "data101", "data101_000_000000000")
|
1353
|
-
data = None
|
1354
|
-
with open(file, "rb") as f:
|
1355
|
-
data = f.read() # if you only wanted to read 512 bytes, do .read(512)
|
1356
|
-
|
1357
|
-
if not data:
|
1358
|
-
raise "No data written to file"
|
1359
|
-
|
1360
|
-
num_records = len(data) / 8
|
1361
|
-
logging.debug("num_record = "+ num_records)
|
1362
|
-
return_b_array.append(int(num_records).to_bytes(4, byteorder='big'))
|
1363
|
-
|
1364
|
-
start_number = 0
|
1365
|
-
logging.debug("start_record = "+start_number)
|
1366
|
-
return_b_array.append(start_number.to_bytes(8, byteorder='big'))
|
1367
|
-
|
1368
|
-
num_records = num_records - 1
|
1369
|
-
logging.debug("last_Record_number = "+ num_records)
|
1370
|
-
return_b_array.append(int(num_records).to_bytes(8, byteorder='big'))
|
1371
|
-
|
1372
|
-
# Add names of every file in data000 dir here.
|
1373
|
-
files = os.listdir(os.path.join(self.qps_record_dir_path, "data101"))
|
1374
|
-
for file3 in files:
|
1375
|
-
# print(file)
|
1376
|
-
item = strToBb(file3, False)
|
1377
|
-
# print(item)
|
1378
|
-
while len(item) < 32:
|
1379
|
-
item.append("\x00")
|
1380
|
-
# print(item)
|
1381
|
-
return_b_array.append(item)
|
1382
|
-
|
1383
|
-
with open(os.path.join(self.qps_record_dir_path, "data101.idx"), "ab") as f:
|
1384
|
-
for item in outBuffer:
|
1385
|
-
f.write(bytes(item))
|
1386
|
-
|
1387
|
-
with open(os.path.join(self.qps_record_dir_path, "data101.idx"), "ab") as f:
|
1388
|
-
self.write_b_array_to_idx_file(f, return_b_array)
|
1389
|
-
|
1390
|
-
def add_header_to_byte_array(self, return_b_array, stream_header_size, temp_dict, tree, is_digital=False):
|
1391
|
-
for element in tree:
|
1392
|
-
if "legacyVersion" in element.tag:
|
1393
|
-
intItem = element.text
|
1394
|
-
temp_dict[element.tag] = intItem
|
1395
|
-
# my_byte_array.append(int.to_bytes(intItem, 'big'))
|
1396
|
-
if "legacyAverage" in element.tag:
|
1397
|
-
intItem = element.text
|
1398
|
-
temp_dict[element.tag] = intItem
|
1399
|
-
# my_byte_array.append(int.to_bytes(intItem, 'big'))
|
1400
|
-
if "legacyFormat" in element.tag:
|
1401
|
-
intItem = element.text
|
1402
|
-
temp_dict[element.tag] = intItem
|
1403
|
-
# my_byte_array.append(int.to_bytes(intItem, 'big'))
|
1404
|
-
if "mainPeriod" in element.tag:
|
1405
|
-
intItem = element.text
|
1406
|
-
intItem = intItem[:-2]
|
1407
|
-
temp_dict[element.tag] = intItem
|
1408
|
-
if "channels" in element.tag:
|
1409
|
-
counter = 0
|
1410
|
-
for child in element:
|
1411
|
-
for child2 in child:
|
1412
|
-
if "group" in child2.tag:
|
1413
|
-
if is_digital:
|
1414
|
-
if str(child2.text).lower() == "digital":
|
1415
|
-
counter += 1
|
1416
|
-
else:
|
1417
|
-
if str(child2.text).lower() != "digital":
|
1418
|
-
counter += 1
|
1419
|
-
|
1420
|
-
temp_dict[element.tag] = counter
|
1421
|
-
|
1422
|
-
return_b_array = []
|
1423
|
-
|
1424
|
-
stream_header_size = 20
|
1425
|
-
|
1426
|
-
# Cycle through all the channels.
|
1427
|
-
for child in element:
|
1428
|
-
|
1429
|
-
if child.tag == "groupId":
|
1430
|
-
continue
|
1431
|
-
|
1432
|
-
if is_digital:
|
1433
|
-
# skip channel if we're only looking for digitals
|
1434
|
-
if not str(child.find(".//group").text).lower() == "digital":
|
1435
|
-
continue
|
1436
|
-
else:
|
1437
|
-
# skip if we're looking for analogues
|
1438
|
-
if str(child.find(".//group").text).lower() == "digital":
|
1439
|
-
continue
|
1440
|
-
|
1441
|
-
# my_byte_array.append(int.to_bytes(5, 'big'))
|
1442
|
-
return_b_array.append(int(5).to_bytes(4, byteorder='big'))
|
1443
|
-
stream_header_size += 4
|
1444
|
-
name = None
|
1445
|
-
|
1446
|
-
for child2 in child:
|
1447
|
-
|
1448
|
-
if "group" in child2.tag:
|
1449
|
-
my_byte_array = strToBb(str(child2.text))
|
1450
|
-
return_b_array.append(my_byte_array)
|
1451
|
-
# QPS index file requires name tag come after group tag.
|
1452
|
-
return_b_array.append(name)
|
1453
|
-
stream_header_size += len(my_byte_array)
|
1454
|
-
|
1455
|
-
if "name" in child2.tag:
|
1456
|
-
my_byte_array = strToBb(str(child2.text))
|
1457
|
-
name = my_byte_array
|
1458
|
-
stream_header_size += len(my_byte_array)
|
1459
|
-
|
1460
|
-
if "units" in child2.tag:
|
1461
|
-
my_byte_array = strToBb(str(child2.text))
|
1462
|
-
return_b_array.append(my_byte_array)
|
1463
|
-
stream_header_size += len(my_byte_array)
|
1464
|
-
|
1465
|
-
"""
|
1466
|
-
# Unclear if the only thing here is TRUE
|
1467
|
-
bb = strToBB( Boolean.toString( cdr.isUsePrefixStr() ));
|
1468
|
-
bbList.add(bb);
|
1469
|
-
retVal += bb.capacity();
|
1470
|
-
"""
|
1471
|
-
my_byte_array = strToBb(str("true"))
|
1472
|
-
return_b_array.append(my_byte_array)
|
1473
|
-
stream_header_size += len(my_byte_array)
|
1474
|
-
|
1475
|
-
if "maxTValue" in child2.tag:
|
1476
|
-
my_byte_array = strToBb(str(child2.text))
|
1477
|
-
return_b_array.append(my_byte_array)
|
1478
|
-
stream_header_size += len(my_byte_array)
|
1479
|
-
|
1480
|
-
return return_b_array, stream_header_size
|
1481
|
-
|
1482
|
-
def add_header_to_buffer(self, outBuffer, return_b_array, stream_header_size, temp_dict):
|
1483
|
-
number = 2
|
1484
|
-
outBuffer.append(number.to_bytes(4, byteorder='big'))
|
1485
|
-
logging.debug("indexVersion : "+ number)
|
1486
|
-
|
1487
|
-
number = 1 if self.has_digitals else 0
|
1488
|
-
outBuffer.append(number.to_bytes(4, byteorder='big'))
|
1489
|
-
logging.debug("value0 : "+ number)
|
1490
|
-
number = stream_header_size
|
1491
|
-
outBuffer.append(number.to_bytes(4, byteorder='big'))
|
1492
|
-
logging.debug("header_size : "+number)
|
1493
|
-
logging.debug("legacyVersion : "+ temp_dict['legacyVersion'])
|
1494
|
-
outBuffer.append(int(temp_dict["legacyVersion"]).to_bytes(4, byteorder='big'))
|
1495
|
-
logging.debug("legacyAverage : " + temp_dict['legacyAverage'])
|
1496
|
-
outBuffer.append(int(temp_dict["legacyAverage"]).to_bytes(4, byteorder='big'))
|
1497
|
-
logging.debug("legacyFormat : "+temp_dict['legacyFormat'])
|
1498
|
-
outBuffer.append(int(temp_dict["legacyFormat"]).to_bytes(4, byteorder='big'))
|
1499
|
-
logging.debug("mainPeriod : "+temp_dict['mainPeriod'])
|
1500
|
-
outBuffer.append(int(temp_dict["mainPeriod"]).to_bytes(4, byteorder='big'))
|
1501
|
-
logging.debug("channels : "+temp_dict['channels'])
|
1502
|
-
outBuffer.append(int(temp_dict["channels"]).to_bytes(4, byteorder='big'))
|
1503
|
-
return_b_array.append(int(self.qps_record_start_time).to_bytes(8, byteorder='big'))
|
1504
|
-
index_record_state = True
|
1505
|
-
logging.debug(int(1))
|
1506
|
-
return_b_array.append(int(1).to_bytes(1, byteorder='big'))
|
1507
|
-
record_type = 1
|
1508
|
-
logging.debug("record type : "+int(index_record_state))
|
1509
|
-
return_b_array.append(int(record_type).to_bytes(1, byteorder='big'))
|
1510
|
-
|
1511
|
-
def write_b_array_to_idx_file(self, f, return_b_array):
|
1512
|
-
# print(return_b_array)
|
1513
|
-
for item in return_b_array:
|
1514
|
-
# print(item)
|
1515
|
-
if isinstance(item, int):
|
1516
|
-
# 'f.write(str(item).encode())
|
1517
|
-
# print(item)
|
1518
|
-
f.write(bytes([item]))
|
1519
|
-
continue
|
1520
|
-
if isinstance(item, bytes):
|
1521
|
-
# print(item)
|
1522
|
-
f.write(bytes(item))
|
1523
|
-
continue
|
1524
|
-
if isinstance(item, list):
|
1525
|
-
for character in item:
|
1526
|
-
if isinstance(character, int):
|
1527
|
-
f.write(bytes([character]))
|
1528
|
-
continue
|
1529
|
-
elif isinstance(item, bytes):
|
1530
|
-
f.write(item)
|
1531
|
-
continue
|
1532
|
-
else:
|
1533
|
-
f.write(str(character).encode())
|
1534
|
-
continue
|
1535
|
-
|
1536
|
-
def create_qps_file(self, module):
|
1537
|
-
"""
|
1538
|
-
Creates the end QPS file that is used to open QPS
|
1539
|
-
|
1540
|
-
:param module: Module QTL number that was used for the stream
|
1541
|
-
:return:
|
1542
|
-
"""
|
1543
|
-
|
1544
|
-
with open(os.path.join(self.qps_record_dir_path, self.qps_stream_folder_name + ".qps"), "w") as f:
|
1545
|
-
x = datetime.datetime.fromtimestamp(self.qps_record_start_time / 1000.0)
|
1546
|
-
x = str(x).split(".")
|
1547
|
-
x = x[0]
|
1548
|
-
x = x.replace("-", " ")
|
1549
|
-
f.write("Started: "+x+"\n")
|
1550
|
-
f.write("Device: " + module + "\n")
|
1551
|
-
f.write("Fixture: \n")
|
1552
|
-
|
1553
|
-
x = datetime.datetime.now()
|
1554
|
-
x = str(x).split(".")
|
1555
|
-
x = x[0]
|
1556
|
-
x = x.replace("-", " ")
|
1557
|
-
f.write("Saved: "+x+ "\n")
|
1558
|
-
|
1559
|
-
|
1560
|
-
def sendCommand(self, cmd, device="", timeout=20,sock=None,readUntilCursor=True, betweenCommandDelay=0.0, expectedResponse=True):
|
1561
|
-
'''Send command is used to send a command to QIS and as far as I can see it has no difference than sendAndReceiveCmd'''
|
1562
|
-
if expectedResponse is True:
|
1563
|
-
if sock == None:
|
1564
|
-
sock = self.sock
|
1565
|
-
if not (device == ''):
|
1566
|
-
self.deviceDictSetup(device)
|
1567
|
-
res = self.sendAndReceiveText(sock, cmd, device, readUntilCursor)
|
1568
|
-
if (betweenCommandDelay > 0):
|
1569
|
-
time.sleep(betweenCommandDelay)
|
1570
|
-
# If ends with cursor get rid of it
|
1571
|
-
if res[-1:] == '>':
|
1572
|
-
res = res[:-3] # remove last three chars - hopefully '\r\n>'
|
1573
|
-
return res
|
1574
|
-
|
1575
|
-
else :
|
1576
|
-
self.sendText(sock, cmd, device)
|
1577
|
-
return
|
1578
|
-
|
1579
|
-
# when sending commands to module (as opposed to back end)
|
1580
|
-
# If read until cursor is set to True (which is default) then keep reading response until a cursor is returned as the last character of result string
|
1581
|
-
# After command is sent wait for betweenCommandDelay which defaults to 0 but can be specified to add a delay between commands
|
1582
|
-
# The objects connection needs to be opened (connect()) before this is used
|
1583
|
-
def sendCmd(self, device='', cmd='$help', sock=None, readUntilCursor=True, betweenCommandDelay=0.0, expectedResponse = True):
|
1584
|
-
'''Send command is used to send a command to QIS and as far as I can see it has no difference than sendAndReceiveCmd'''
|
1585
|
-
if expectedResponse is True:
|
1586
|
-
res = self.sendAndReceiveCmd(device=device, cmd=cmd, sock=sock, readUntilCursor=readUntilCursor, betweenCommandDelay=betweenCommandDelay)
|
1587
|
-
#If ends with cursor get rid of it
|
1588
|
-
if res[-1:] == self.cursor:
|
1589
|
-
res = res[:-3] #remove last three chars - hopefully '\r\n>'
|
1590
|
-
return res
|
1591
|
-
else :
|
1592
|
-
self.sendText(sock, cmd, device)
|
1593
|
-
return
|
1594
|
-
|
1595
|
-
def sendAndReceiveCmd(self, sock=None, cmd='$help', device='', readUntilCursor=True, betweenCommandDelay=0.0):
|
1596
|
-
if sock==None:
|
1597
|
-
sock = self.sock
|
1598
|
-
if not (device == ''):
|
1599
|
-
self.deviceDictSetup(device)
|
1600
|
-
res = self.sendAndReceiveText(sock, cmd, device, readUntilCursor)
|
1601
|
-
if (betweenCommandDelay > 0):
|
1602
|
-
time.sleep(betweenCommandDelay)
|
1603
|
-
#If ends with cursor get rid of it
|
1604
|
-
if res[-1:] == '>':
|
1605
|
-
res = res[:-3] #remove last three chars - hopefully '\r\n>'
|
1606
|
-
return cmd + ' : ' + res
|
1607
|
-
|
1608
|
-
def sendAndReceiveText(self, sock, sentText='$help', device='', readUntilCursor=True):
|
1609
|
-
# Send text to QIS
|
1610
|
-
# The objects connection needs to be opened (connect()) before this is used
|
1611
|
-
# If read until cursor is set to True (which is default) then keep reading response until a cursor is returned as the last character of result string
|
1612
|
-
|
1613
|
-
# do sendText
|
1614
|
-
self.sockSemaphore.acquire()
|
1615
|
-
try:
|
1616
|
-
#Send Text
|
1617
|
-
self.sendText(sock, sentText, device)
|
1618
|
-
#Receive Response
|
1619
|
-
res = self.receiveText(sock)
|
1620
|
-
# Error Check / validate response
|
1621
|
-
if len(res) == 0:
|
1622
|
-
#logging.error("Empty response from QIS.")
|
1623
|
-
self.sendText(sock, "stream?", device)
|
1624
|
-
res = self.receiveText(sock)
|
1625
|
-
if len(res) != 0:
|
1626
|
-
self.sendText(sock, sentText, device)
|
1627
|
-
res = self.receiveText(sock)
|
1628
|
-
if len(res) == 0:
|
1629
|
-
raise (Exception("Empty response from QIS. Sent: " + sentText))
|
1630
|
-
else:
|
1631
|
-
raise (Exception("Empty response from QIS. Sent: " + sentText))
|
1632
|
-
|
1633
|
-
if res[0] == self.cursor:
|
1634
|
-
logging.warning('Only returned a cursor from QIS. Sent: ' + sentText)
|
1635
|
-
if 'Create Socket Fail' == res[0]: # If create socked fail (between QIS and tcp/ip module)
|
1636
|
-
logging.warning(res[0])
|
1637
|
-
if 'Connection Timeout' == res[0]:
|
1638
|
-
logging.warning(res[0])
|
1639
|
-
# If reading until a cursor comes back then keep reading until a cursor appears or max tries exceeded
|
1640
|
-
if readUntilCursor:
|
1641
|
-
maxReads = 1000
|
1642
|
-
count = 1
|
1643
|
-
# check for cursor at end of read and if not there read again
|
1644
|
-
while res[-1:] != self.cursor:
|
1645
|
-
res+= self.receiveText(sock) #TODO Confirm this works with multi response CMD Like a $get stats on a large stream with many annos. test with py2 and py3
|
1646
|
-
#res.extend(self.rxBytes(sock))
|
1647
|
-
count += 1
|
1648
|
-
if count >= maxReads:
|
1649
|
-
raise Exception(' Count = Error: max reads exceeded before cursor returned')
|
1650
|
-
return res
|
1651
|
-
|
1652
|
-
except Exception as e:
|
1653
|
-
#something went wrong during send qis cmd
|
1654
|
-
logging.error(e)
|
1655
|
-
raise e
|
1656
|
-
finally:
|
1657
|
-
self.sockSemaphore.release()
|
1658
|
-
|
1659
|
-
|
1660
|
-
def receiveText(self, sock):
|
1661
|
-
if self.pythonVersion == '3':
|
1662
|
-
res = bytearray()
|
1663
|
-
res.extend(self.rxBytes(sock))
|
1664
|
-
res = res.decode()
|
1665
|
-
else:
|
1666
|
-
res = self.rxBytes(sock)
|
1667
|
-
return res
|
1668
|
-
|
1669
|
-
def sendText(self, sock, message='$help', device=''):
|
1670
|
-
# Send text to QIS, don't read it's response
|
1671
|
-
# The objects connection needs to be opened (connect()) before this is used
|
1672
|
-
if device != '':
|
1673
|
-
#specialTimeout = '%500000'
|
1674
|
-
#message = device + ' ' + specialTimeout + ' ' + message
|
1675
|
-
message = device + ' ' + message
|
1676
|
-
#printText('Sending: "' + message + '" ' + self.host + ':' + str(self.port))
|
1677
|
-
|
1678
|
-
if self.pythonVersion == 2:
|
1679
|
-
sock.sendall(message + '\r\n')
|
1680
|
-
else:
|
1681
|
-
convM = message + '\r\n'
|
1682
|
-
sock.sendall(convM.encode('utf-8'))
|
1683
|
-
return 'Sent:' + message
|
1684
|
-
|
1685
|
-
def rxBytes(self,sock):
|
1686
|
-
#sock.setblocking(0) #make socket non-blocking
|
1687
|
-
#printText('rxBytes')
|
1688
|
-
maxExceptions=10
|
1689
|
-
exceptions=0
|
1690
|
-
maxReadRepeats=50
|
1691
|
-
readRepeats=0
|
1692
|
-
timeout_in_seconds = 10
|
1693
|
-
#Keep trying to read bytes until we get some, unless number of read repeads or exceptions is exceeded
|
1694
|
-
while True:
|
1695
|
-
try:
|
1696
|
-
#select.select returns a list of waitable objects which are ready. On windows it has to be sockets.
|
1697
|
-
#The first arguement is a list of objects to wait for reading, second writing, third 'exceptional condition'
|
1698
|
-
#We only use the read list and our socket to check if it is readable. if no timeout is specified then it blocks until it becomes readable.
|
1699
|
-
ready = select.select([sock], [], [], timeout_in_seconds)
|
1700
|
-
#time.sleep(0.1)
|
1701
|
-
#ready = [1,2]
|
1702
|
-
if ready[0]:
|
1703
|
-
ret = sock.recv(self.maxRxBytes)
|
1704
|
-
#time.sleep(0.1)
|
1705
|
-
return ret
|
1706
|
-
else:
|
1707
|
-
#printText('rxBytes - readRepeats + 1')
|
1708
|
-
|
1709
|
-
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
1710
|
-
sock.connect((self.host, self.port))
|
1711
|
-
sock.settimeout(5)
|
1712
|
-
|
1713
|
-
try:
|
1714
|
-
welcomeString = self.sock.recv(self.maxRxBytes).rstrip()
|
1715
|
-
welcomeString = 'Connected@' + self.host + ':' + str(self.port) + ' ' + '\n ' + welcomeString
|
1716
|
-
printText('New Welcome:' + welcomeString)
|
1717
|
-
except Exception as e:
|
1718
|
-
logging.error('tried and failed to get new welcome')
|
1719
|
-
raise e
|
1720
|
-
|
1721
|
-
readRepeats=readRepeats+1
|
1722
|
-
time.sleep(0.5)
|
1723
|
-
|
1724
|
-
except Exception as e:
|
1725
|
-
#printText('rxBytes - exceptions + 1')
|
1726
|
-
exceptions=exceptions+1
|
1727
|
-
time.sleep(0.5)
|
1728
|
-
raise e
|
1729
|
-
|
1730
|
-
#If read repeats has been exceeded we failed to get any data on this read.
|
1731
|
-
# !!! This is likely to break whatever called us !!!
|
1732
|
-
if readRepeats >= maxReadRepeats:
|
1733
|
-
logging.error('Max read repeats exceeded - returning.')
|
1734
|
-
return 'No data received from QIS'
|
1735
|
-
#If number of exceptions exceeded then give up by exiting
|
1736
|
-
if exceptions >= maxExceptions:
|
1737
|
-
logging.error('Max exceptions exceeded - exiting') #exceptions are probably 10035 non-blocking socket could not complete immediatley
|
1738
|
-
exit()
|
1739
|
-
|
1740
|
-
|
1741
|
-
def strToBb(string_in, add_length=True):
|
1742
|
-
length = len(str(string_in))
|
1743
|
-
b_array = []
|
1744
|
-
if add_length:
|
1745
|
-
b_array.append(length)
|
1746
|
-
for character in str(string_in):
|
1747
|
-
b_array.append(character)
|
1748
|
-
|
1749
|
-
return b_array
|