def onReadyRead(self): inputStream = QtCore.QDataStream(self.tcpSocket) while(1): if len(self.rxBuffer) < Messaging.hdrSize: if self.tcpSocket.bytesAvailable() < Messaging.hdrSize: return self.rxBuffer += inputStream.readRawData(Messaging.hdrSize - len(self.rxBuffer)) if len(self.rxBuffer) >= Messaging.hdrSize: hdr = Messaging.hdr(self.rxBuffer) bodyLen = hdr.GetDataLength() if len(self.rxBuffer)+self.tcpSocket.bytesAvailable() < Messaging.hdrSize + bodyLen: return self.rxBuffer += inputStream.readRawData(Messaging.hdrSize + bodyLen - len(self.rxBuffer)) # create a new header object with the appended body hdr = Messaging.hdr(self.rxBuffer) # if we got this far, we have a whole message! So, emit the signal self.messagereceived.emit(hdr) # then clear the buffer, so we start over on the next message self.rxBuffer = bytearray()
def HandleButtonPress(self): # when a button is used to start a log, the text of that button changes to "Stop". # starting any other log will stop the current one (changing it's text back to normal) button = self.sender() if "logtag" in button.options: if button == self.activeLogButton: self.CloseLogFile() button.setText(button.label) self.activeLogButton = None else: if self.activeLogButton != None: self.activeLogButton.setText(self.activeLogButton.label) self.CreateLogFile(button.options["logtag"][0]) button.setText("Stop") self.activeLogButton = button if "send" in button.options: for opts in button.options['send']: msgname = opts.split("[")[0] fields = opts.split("[")[1].replace("]","").split(",") msgClass = Messaging.MsgClassFromName[msgname] msg = msgClass() for field in fields: parts = field.split("=") name = parts[0] value = parts[1] Messaging.set(msg, Messaging.findFieldInfo(msgClass.fields, name), value) self.SendMsg(msg)
def onReadyRead(self): inputStream = QtCore.QDataStream(self.tcpSocket) while (1): if len(self.rxBuffer) < Messaging.hdrSize: if self.tcpSocket.bytesAvailable() < Messaging.hdrSize: return self.rxBuffer += inputStream.readRawData(Messaging.hdrSize - len(self.rxBuffer)) if len(self.rxBuffer) >= Messaging.hdrSize: hdr = Messaging.hdr(self.rxBuffer) bodyLen = hdr.GetDataLength() if len(self.rxBuffer) + self.tcpSocket.bytesAvailable( ) < Messaging.hdrSize + bodyLen: return self.rxBuffer += inputStream.readRawData(Messaging.hdrSize + bodyLen - len(self.rxBuffer)) # create a new header object with the appended body hdr = Messaging.hdr(self.rxBuffer) # if we got this far, we have a whole message! So, emit the signal self.messagereceived.emit(hdr) # then clear the buffer, so we start over on the next message self.rxBuffer = bytearray()
def HandleButtonPress(self): # when a button is used to start a log, the text of that button changes to "Stop". # starting any other log will stop the current one (changing it's text back to normal) button = self.sender() if "logtag" in button.options: if button == self.activeLogButton: self.CloseLogFile() button.setText(button.label) self.activeLogButton = None else: if self.activeLogButton != None: self.activeLogButton.setText(self.activeLogButton.label) self.CreateLogFile(button.options["logtag"][0]) button.setText("Stop") self.activeLogButton = button if "send" in button.options: for opts in button.options['send']: msgname = opts.split("[")[0] fields = opts.split("[")[1].replace("]", "").split(",") msgClass = Messaging.MsgClassFromName[msgname] msg = msgClass() for field in fields: parts = field.split("=") name = parts[0] value = parts[1] Messaging.set( msg, Messaging.findFieldInfo(msgClass.fields, name), value) self.SendMsg(msg)
def data(self, column, role): if column != 2: return super(FieldItem, self).data(column, role) alert = Messaging.getAlert(self.msg, self.fieldInfo) if role == Qt.FontRole: font = QFont() if alert == 1: font.setBold(1) return font if role == Qt.ForegroundRole: brush = QBrush() if alert == 1: brush.setColor(Qt.red) return brush if role == Qt.DisplayRole: value = Messaging.get(self.msg, self.fieldInfo) try: self.overrideWidget valueAsString = Messaging.get(self.msg, self.fieldInfo) if valueAsString in self.comboBoxIndexOfEnum: #self.overrideWidget.setCurrentText(valueAsString) self.overrideWidget.setCurrentIndex(self.comboBoxIndexOfEnum[valueAsString]) else: #self.overrideWidget.setEditText(valueAsString) self.overrideWidget.setCurrentIndex(-1) except AttributeError: pass return str(value) return super(FieldItem, self).data(column, role)
def ProcessMessage(self, msg): self.messageCount += 1 id = msg.hdr.GetMessageID() # if we write CSV to multiple files, we'd probably look up a hash table for this message id, # and open it and write a header if (id in self.outputFiles): outputFile = self.outputFiles[id] else: # create a new file outputFile = open( self.outputName + "/" + msg.MsgName().replace("/", "_") + ".csv", 'w') # store a pointer to it, so we can find it next time (instead of creating it again) self.outputFiles[id] = outputFile # add table header, one column for each message field tableHeader = msgcsv.csvHeader( msg, nameColumn=False, timeColumn=True) + '\n' outputFile.write(tableHeader) try: # \todo Detect time rolling. this only matters when we're processing a log file # with insufficient timestamp size, such that time rolls over from a large number # to a small one, during the log. thisTimestamp = msg.hdr.GetTime() if thisTimestamp < self._lastTimestamp: self._timestampOffset += 1 self._lastTimestamp = thisTimestamp timeSizeInBits = int( round( math.log( int( Messaging.findFieldInfo(msg.hdr.fields, "Time").maxVal), 2))) timestamp = ( self._timestampOffset << timeSizeInBits) + thisTimestamp if Messaging.findFieldInfo(msg.hdr.fields, "Time").units == "ms": timestamp = timestamp / 1000.0 text = str(timestamp) + ", " except AttributeError: text = "unknown, " text += msgcsv.toCsv(msg, nameColumn=False, timeColumn=False) text += '\n' outputFile.write(text) # This is not efficient, but if we don't flush during socket processing # and the user hits Ctrl-C, we'll drop a bunch of data and end up with empty files. # So flush each message as it comes in. if self.connectionType != 'file': outputFile.flush()
def store_message(self, msg): try: timeVal = msg.hdr.GetTime() timeInfo = Messaging.findFieldInfo(msg.hdr.fields, "Time") # if it's not big enough to be an absolute timestamp, give up on using it and just use current time if float(timeInfo.maxVal) <= 2**32: raise AttributeError if timeInfo.units == "ms": timeVal = timeVal / 1000.0 timeVal = datetime.datetime.fromtimestamp(timeVal, datetime.timezone.utc) except AttributeError: timeVal = datetime.datetime.now() dbJson = { "time": str(timeVal), "measurement": msg.MsgName(), 'fields': {}, 'tags': {} } for fieldInfo in msg.hdr.fields: if len(fieldInfo.bitfieldInfo) == 0: if fieldInfo.idbits == 0 and fieldInfo.name != "Time" and fieldInfo.name != "DataLength": dbJson['tags'][fieldInfo.name] = Messaging.get( msg.hdr, fieldInfo) else: for bitInfo in fieldInfo.bitfieldInfo: if bitInfo.idbits == 0 and bitInfo.name != "Time" and bitInfo.name != "DataLength": dbJson['tags'][bitInfo.name] = Messaging.get( msg.hdr, bitInfo) msgClass = type(msg) for fieldInfo in msgClass.fields: if fieldInfo.count == 1: if len(fieldInfo.bitfieldInfo) == 0: dbJson['fields'][ fieldInfo.name] = InfluxDBConnection.GetDBValue( msg, fieldInfo) else: for bitInfo in fieldInfo.bitfieldInfo: dbJson['fields'][ bitInfo.name] = InfluxDBConnection.GetDBValue( msg, bitInfo) # leave out arrays until we figure out how to handle them #else: # arrayList = [] # for i in range(0,fieldInfo.count): # arrayList.append(InfluxDBConnection.GetDBValue(msg, fieldInfo, i)) # dbJson['fields'][fieldInfo.name] = arrayList #print("Create a retention policy") #retention_policy = 'awesome_policy' #client.create_retention_policy(retention_policy, '3d', 3, default=True) self.db.write_points([dbJson]) #, retention_policy=retention_policy)
def sendMsg(self, msgClass): msg = msgClass() if self.timeInfo: hdr = Messaging.hdr(msg.rawBuffer()) t = self.currentTime maxTime = self.timeInfo.maxVal # if the timestamp field is small, assume the user wants relative times since midnight. if maxTime != "DBL_MAX" and (maxTime == 'FLT_MAX' or float(maxTime) <= 2**32): t = (datetime.fromtimestamp(self.currentTime) - datetime.fromtimestamp(self.currentTime).replace(hour=0, minute=0, second=0, microsecond=0)).total_seconds() if self.timeInfo.units == "ms": t = t * 1000.0 if self.timeInfo.type == "int": t = int(t) hdr.SetTime(t) for fieldInfo in msgClass.fields: if fieldInfo.units == 'ASCII': Messaging.set(msg, fieldInfo, 's'+str(self.fieldValue(fieldInfo))) else: if(fieldInfo.count == 1): Messaging.set(msg, fieldInfo, str(self.fieldValue(fieldInfo))) for bitInfo in fieldInfo.bitfieldInfo: Messaging.set(msg, bitInfo, str(self.fieldValue(bitInfo))) else: for i in range(0,fieldInfo.count): Messaging.set(msg, fieldInfo, self.fieldValue(fieldInfo), i) self.SendMsg(msg)
def run(self): try: for i in range (0,3): self.output("waiting for msg1") msg = self.listener.WaitForMsg('Msg1Name') self.output(Messaging.toJson(msg)) self.output("waiting for msg2") msg = self.listener.WaitForMsg('Msg2Name') self.output(Messaging.toJson(msg)) except MsgTimeoutError as e: self.output(">>>> " + str(e))
def main(args=None): msgLib = Messaging(None, 0, "NetworkHeader") if len(sys.argv) > 1 and sys.argv[1] == "server": connection = SynchronousMsgServer(Messaging.hdr) else: connection = SynchronousMsgClient(Messaging.hdr) # say my name connectMsg = msgLib.Messages.Network.Connect() connectMsg.SetName("CLI") connection.send_message(connectMsg) # do default subscription to get *everything* subscribeMsg = msgLib.Messages.Network.MaskedSubscription() connection.send_message(subscribeMsg) _cmd = "" try: while True: cmd = input("") #print("got input cmd [" + cmd + "]") if cmd: if "getmsg" in cmd: msgIDs = [] msgIDNames = cmd.split(",")[1:] for msgname in msgIDNames: try: if int(msgname, 0): msgIDs.append(int(msgname,0)) except ValueError: if msgname in Messaging.MsgIDFromName: msgIDs.append(int(Messaging.MsgIDFromName[msgname], 0)) else: print("invalid msg " + msgname) # this blocks until message received, or timeout occurs timeout = 10.0 # value in seconds hdr = connection.get_message(timeout, msgIDs) if hdr: msg = msgLib.MsgFactory(hdr) # print as JSON for debug purposes json = Messaging.toJson(msg) print(json) else: print("{}") else: # this translates the input command from CSV to a message, and sends it. msg = Messaging.csvToMsg(cmd) if msg: connection.send_message(msg) # I can't get exit on Ctrl-C to work! except KeyboardInterrupt: print('You pressed Ctrl+C!') connection.stop()
def ProcessMessage(self, msg): self.messageCount += 1 # if we write CSV to multiple files, we'd probably look up a hash table for this message id, # and open it and write a header if (id in self.outputFiles): outputFile = self.outputFiles[id] else: # create a new file outputFile = open( self.outputName + "/" + msg.MsgName().replace("/", "_") + ".csv", 'w') # store a pointer to it, so we can find it next time (instead of creating it again) self.outputFiles[id] = outputFile # add table header, one column for each message field tableHeader = "Time (ms), " for fieldInfo in type(msg).fields: tableHeader += fieldInfo.name + ", " for bitInfo in fieldInfo.bitfieldInfo: tableHeader += bitInfo.name + ", " tableHeader += '\n' outputFile.write(tableHeader) try: # \todo Detect time rolling. this only matters when we're processing a log file # with insufficient timestamp size, such that time rolls over from a large number # to a small one, during the log. thisTimestamp = msg.hdr.GetTime() if thisTimestamp < self._lastTimestamp: self._timestampOffset += 1 self._lastTimestamp = thisTimestamp # instead of left shifting by 16, we should use the actual size in bits of the timestamp field! text = str((self._timestampOffset << 16) + thisTimestamp) + ", " except AttributeError: text = "unknown, " for fieldInfo in type(msg).fields: if (fieldInfo.count == 1): columnText = str(Messaging.get(msg, fieldInfo)) + ", " for bitInfo in fieldInfo.bitfieldInfo: columnText += str(Messaging.get(msg, bitInfo)) + ", " else: columnText = "" for i in range(0, fieldInfo.count): columnText += str(Messaging.get(msg, fieldInfo, i)) + ", " text += columnText text += '\n' outputFile.write(text)
def __init__(self, hdr, portName): super(SerialConnection, self).__init__(None) self.hdr = hdr self.settings = QtCore.QSettings("MsgTools", "MessageServer/SerialPlugin") # button to open/close serial port self.openCloseButton = QtWidgets.QPushButton("button") self.openCloseButton.pressed.connect(self.openCloseSwitch) # button select new serial port self.selectPortButton = QtWidgets.QPushButton("Select Port") self.selectPortButton.pressed.connect(self.selectPort) self.statusLabel = QtWidgets.QLabel() self.subscriptions = {} self.subMask = 0 self.subValue = 0 self.isHardwareLink = True # if port not specified, default to last used port if not portName: portName = self.settings.value("portName", None) self.serialPort = QSerialPort(portName) self.serialPort.setBaudRate(QSerialPort.Baud115200) self.serialPort.setFlowControl(QSerialPort.NoFlowControl) self.serialPort.setParity(QSerialPort.NoParity) self.serialPort.setDataBits(QSerialPort.Data8) self.serialPort.setStopBits(QSerialPort.OneStop) self.rxBuffer = bytearray() self.gotHeader = 0 self.rxMsgCount = 0 self.serialPort.readyRead.connect(self.onReadyRead) self.name = "Serial " + self.serialPort.portName() self.hdrTranslator = Messaging.HeaderTranslator(hdr, NetworkHeader) self.serialStartSeqField = Messaging.findFieldInfo( hdr.fields, "StartSequence") if self.serialStartSeqField != None: self.startSequence = int(hdr.GetStartSequence.default) self.startSeqSize = int(hdr.GetStartSequence.size) try: self.hdrCrcRegion = int(hdr.GetHeaderChecksum.offset) except AttributeError: self.hdrCrcRegion = None self.tmpRxHdr = ctypes.create_string_buffer(0)
def PrintAccessors(self, msgClass): msg = msgClass() for fieldInfo in msgClass.fields: txt = "body.%s.%s: " % (msgClass.__name__, fieldInfo.name) if (fieldInfo.count == 1): txt += str(Messaging.get(msg, fieldInfo)) else: for i in range(0, fieldInfo.count): #print("body.",msgClass.__name__, ".", method.__name__, "[",i,"] = ", method(msg,i), " #", method.__doc__, "in", method.units) txt += str(Messaging.get(msg, fieldInfo, i)) if (i < fieldInfo.count - 1): txt += ", " txt += " # " + fieldInfo.description + " in " + fieldInfo.units print(txt)
def __init__(self): self.msgLib = Messaging(None, 0, "NetworkHeader") self.connection = SynchronousMsgClient(Messaging.hdr) # say my name connectMsg = self.msgLib.Messages.Network.Connect() connectMsg.SetName("InfluxDB") self.connection.send_message(connectMsg) # do default subscription to get *everything* subscribeMsg = self.msgLib.Messages.Network.MaskedSubscription() self.connection.send_message(subscribeMsg) self.db = InfluxDBConnection()
def PrintAccessors(self, msgClass): msg = msgClass() for fieldInfo in msgClass.fields: txt = "body.%s.%s: " % (msgClass.__name__, fieldInfo.name) if(fieldInfo.count == 1): txt += str(Messaging.get(msg, fieldInfo)) else: for i in range(0,fieldInfo.count): #print("body.",msgClass.__name__, ".", method.__name__, "[",i,"] = ", method(msg,i), " #", method.__doc__, "in", method.units) txt += str(Messaging.get(msg, fieldInfo, i)) if(i < fieldInfo.count - 1): txt += ", " txt += " # "+fieldInfo.description+" in " + fieldInfo.units print(txt)
def test_accessors(self): msgclass = Messaging.MsgClassFromName["Network.Connect"] sameMsgClass = Messaging.Messages.Network.Connect self.assertEqual(msgclass, sameMsgClass) expected = "Testing" testMsg = msgclass() Messaging.set(testMsg, msgclass.fields[0], expected) observed = testMsg.GetName() self.assertMultiLineEqual(expected, observed) expected = "MoreTesting" testMsg.SetName(expected) observed = Messaging.get(testMsg, msgclass.fields[0]) self.assertMultiLineEqual(expected, observed)
def test_accessors(self): msgclass = Messaging.MsgClassFromName["Network.Connect"] sameMsgClass = Messaging.Messages.Network.Connect self.assertEqual(msgclass, sameMsgClass) expected = "Testing" testMsg = msgclass() Messaging.set(testMsg, msgclass.fields[0], expected) observed = testMsg.GetName() self.assertMultiLineEqual(expected, observed) expected="MoreTesting" testMsg.SetName(expected) observed=Messaging.get(testMsg, msgclass.fields[0]) self.assertMultiLineEqual(expected, observed)
def store_message(self, msg): try: timeVal = msg.hdr.GetTime() timeInfo = Messaging.findFieldInfo(msg.hdr.fields, "Time") # if it's not big enough to be an absolute timestamp, give up on using it and just use current time if float(timeInfo.maxVal) <= 2**32: raise AttributeError if timeInfo.units == "ms": timeVal = timeVal / 1000.0 timeVal = datetime.datetime.fromtimestamp(timeVal, datetime.timezone.utc) except AttributeError: timeVal = datetime.datetime.now() dbJson = { "time": str(timeVal), "measurement": msg.MsgName(), 'fields': {}, 'tags': {} } for fieldInfo in msg.hdr.fields: if len(fieldInfo.bitfieldInfo) == 0: if fieldInfo.idbits == 0 and fieldInfo.name != "Time" and fieldInfo.name != "DataLength": dbJson['tags'][fieldInfo.name] = Messaging.get(msg.hdr, fieldInfo) else: for bitInfo in fieldInfo.bitfieldInfo: if bitInfo.idbits == 0 and bitInfo.name != "Time" and bitInfo.name != "DataLength": dbJson['tags'][bitInfo.name] = Messaging.get(msg.hdr, bitInfo) msgClass = type(msg) for fieldInfo in msgClass.fields: if fieldInfo.count == 1: if len(fieldInfo.bitfieldInfo) == 0: dbJson['fields'][fieldInfo.name] = InfluxDBConnection.GetDBValue(msg, fieldInfo) else: for bitInfo in fieldInfo.bitfieldInfo: dbJson['fields'][bitInfo.name] = InfluxDBConnection.GetDBValue(msg, bitInfo) # leave out arrays until we figure out how to handle them #else: # arrayList = [] # for i in range(0,fieldInfo.count): # arrayList.append(InfluxDBConnection.GetDBValue(msg, fieldInfo, i)) # dbJson['fields'][fieldInfo.name] = arrayList #print("Create a retention policy") #retention_policy = 'awesome_policy' #client.create_retention_policy(retention_policy, '3d', 3, default=True) self.db.write_points([dbJson]) #, retention_policy=retention_policy)
def addData(self, msg): # TODO what to do for things that can't be numerically expressed? just ascii strings, i guess? for line in self.lines: newDataPoint = Messaging.getFloat(msg, line.fieldInfo, line.fieldSubindex) try: newTime = float(msg.hdr.GetTime() / 1000.0) if newTime != 0: self.useHeaderTime = 1 if not self.useHeaderTime: newTime = elapsedSeconds() except AttributeError: # if header has no time, fallback to PC time. newTime = elapsedSeconds() # add data in the array until MAX_LENGTH is reached, then drop data off start of array # such that plot appears to scroll. The array size is limited to MAX_LENGTH. if len(line.dataArray) >= MsgPlot.MAX_LENGTH: line.dataArray[:-1] = line.dataArray[ 1:] # shift data in the array one sample left line.dataArray[-1] = newDataPoint line.timeArray[:-1] = line.timeArray[ 1:] # shift data in the array one sample left line.timeArray[-1] = newTime else: line.dataArray.append(newDataPoint) line.timeArray.append(newTime) if not self.pause: line.curve.setData(line.timeArray, line.dataArray) line.curve.setPos(line.ptr1, 0)
def handle_query(self, msg): msgquery = msg.GetQuery() msgAndFieldName = msgquery.split(",")[0] msgname = msgAndFieldName.split(".")[0] fieldname = msgAndFieldName.split(".")[1] tagQuery = msgquery.split(",")[1] if tagQuery != "": tagQuery += " AND " start = msg.GetStartTime() end = msg.GetEndTime() averagingPeriod = msg.GetAveragingPeriod() if averagingPeriod != 0: dbquery = 'SELECT "' + fieldname + '" FROM "' + msgname + '"' else: dbquery = 'SELECT MEAN("' + fieldname + '") FROM "' + msgname + '"' dbquery += " WHERE " + tagQuery + "time > " + FormattedTime(start) if end != 0: dbquery += " AND time < " + FormattedTime(end) else: end = datetime.datetime.now() if averagingPeriod != 0: # limit averaging period such that we don't get too many data points if avg / (end - start) > MAX_POINTS: avg = MAX_POINTS * (end - start) dbquery += " GROUP BY *,time("+FormattedTime(avg)+")" dbquery += " LIMIT " + str(MAX_POINTS) # send query to database, get result result = self.db.query(dbquery) # output results resultMsg = Messaging.Messages.Network.History.Result() resultMsg.SetCookie(self.cookie) resultCount = f(result) resultMsg.SetResultCount(resultCount) resultMsg.SetQuery(msgquery) self.msgClient.send_message(resultMsg) dataInfo = Messaging.findFieldInfo(Messaging.Messages.Network.History.Data, "Data") maxDataPerMsg = dataInfo.count resultNumber = 0 for something in result: dataMsg = Messaging.Messages.Network.History.Data() dataMsg.SetCookie(self.cookie) dataMsg.SetResultNumber(resultNumber) dataRange = min(maxDataPerMsg, tilEndOfResults) for index in range(0, dataRange,2): dataMsg.SetData(time(something), index) dataMsg.SetData(value(something), index+1) # truncate message buffer to actual amount of data dataMsg.hdr.SetDataLength(dataInfo.offset + dataInfo.size * dataRange) self.msgClient.send_message(dataMsg) resultNumber += 1 # increment cookie so the next query's results are easy to tell apart from out query results # especially useful if two clients query at almost the same time! self.cookie += 1
def __init__(self, name='Client', timeout=10): # keep a reference to Messages, for convenience of cmdline scripts self.Messages = Messaging.Messages # load all messages if not already loaded if Messaging.hdr == None: Messaging.LoadAllMessages() self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._sock.connect(("127.0.0.1", 5678)) self._timeout = timeout if self._timeout == 0.0: self._sock.setblocking(0) else: self._sock.setblocking(1) self._sock.settimeout(self._timeout) # say my name connectMsg = Messaging.Messages.Network.Connect() connectMsg.SetName(name) self.send(connectMsg) # do default subscription to get *everything* subscribeMsg = Messaging.Messages.Network.MaskedSubscription() self.send(subscribeMsg) # keep a dictionary of the latest value of all received messages self.received = {} # also record particular messages the user wants to record, even if # they never called recv on them self._extra_msgs_to_record = {}
def ProcessMessage(self, msg): hdr = msg.hdr # create identifying string for type and source of message msgRoute = Messaging.MsgRoute(msg) msgKey = msg.MsgName() if len(msgRoute) > 0 and not (all("0" == a for a in msgRoute)): msgKey += " (" + "->".join(msgRoute) + ")" if self.allowedMessages: if not msg.MsgName() in self.allowedMessages: return if (not (msgKey in self.msgWidgets)): # create a new tree widget try: keyField = self.keyFields[msg.MsgName()] except KeyError: keyField = None msgWidget = msgtools.lib.gui.MsgTreeWidget(type(msg), keyField) # connect to double-clicked signal to change scrolling globally msgWidget.itemDoubleClicked.connect(self.tableDataDoubleClicked) # add it to the tab widget, so the user can see it self.tabWidget.addTab(msgWidget, msgKey) # store a pointer to it, so we can find it next time (instead of creating it again) self.msgWidgets[msgKey] = msgWidget # give the data to the widget self.msgWidgets[msgKey].addData(msg, self.autoscroll)
def display_message_in_rx_list(self, msg_key, msg): rx_time = datetime.datetime.now() if not msg_key in self.rx_msg_list: widget_name = msg.MsgName() msg_route = Messaging.MsgRoute(msg) if len(msg_route) > 0 and not (all("0" == a for a in msg_route)): widget_name += " (" + "->".join(msg_route) + ")" msg_list_item = QTreeWidgetItem( [widget_name, str(rx_time), "- Hz"]) msg_list_item.msg_key = msg_key msg_list_item.msg = msg self.rx_message_list.addTopLevelItem(msg_list_item) self.rx_message_list.resizeColumnToContents(0) self.rx_msg_list[msg_key] = msg_list_item # Initialize a Deque with an empty iterable with a maxlen of 10 if self.thread_lock.acquire(): self.rx_msg_list_timestamps[msg_key] = collections.deque([], 10) self.thread_lock.release() if self.thread_lock.acquire(): self.rx_msg_list_timestamps[msg_key].appendleft(rx_time) self.thread_lock.release() self.rx_msg_list[msg_key].setText(1, str(rx_time)) self.rx_msg_list[msg_key].msg = msg
def addData(self, msg): # TODO what to do for things that can't be numerically expressed? just ascii strings, i guess? for line in self.lines: try: newDataPoint = Messaging.getFloat(msg, line.fieldInfo, line.fieldSubindex) except ValueError: print("ERROR! Plot of %s.%s cannot accept value %s" % (self.msgClass.MsgName(), line.fieldInfo.name, Messaging.get(msg, line.fieldInfo, line.fieldSubindex))) continue try: timestamp = msg.hdr.GetTime() if Messaging.findFieldInfo(msg.hdr.fields, "Time").units == "ms": timestamp = timestamp / 1000.0 newTime = float(elapsedSeconds(timestamp)) if newTime != 0: self.useHeaderTime = 1 if not self.useHeaderTime: newTime = elapsedSeconds(datetime.now().timestamp()) except AttributeError: # if header has no time, fallback to PC time. newTime = elapsedSeconds(datetime.now().timestamp()) # add data in the array until MAX_LENGTH is reached, then drop data off start of array # such that plot appears to scroll. The array size is limited to MAX_LENGTH. if len(line.dataArray) >= MsgPlot.MAX_LENGTH: line.dataArray[:-1] = line.dataArray[ 1:] # shift data in the array one sample left line.dataArray[-1] = newDataPoint line.timeArray[:-1] = line.timeArray[ 1:] # shift data in the array one sample left line.timeArray[-1] = newTime else: line.dataArray.append(newDataPoint) line.timeArray.append(newTime) if not self.pause: timeArray = line.timeArray dataArray = line.dataArray count = self.timeSlider.value() if len(line.dataArray) > count: timeArray = timeArray[-count:] dataArray = dataArray[-count:] line.curve.setData(timeArray, dataArray) line.curve.setPos(line.ptr1, 0)
def ProcessMessage(self, msg): self.messageCount += 1 id = msg.hdr.GetMessageID() # if we write CSV to multiple files, we'd probably look up a hash table for this message id, # and open it and write a header if(id in self.outputFiles): outputFile = self.outputFiles[id] else: # create a new file outputFile = open(self.outputName + "/" + msg.MsgName().replace("/","_") + ".csv", 'w') # store a pointer to it, so we can find it next time (instead of creating it again) self.outputFiles[id] = outputFile # add table header, one column for each message field tableHeader = msgcsv.csvHeader(msg, nameColumn=False, timeColumn=True) + '\n' outputFile.write(tableHeader) try: # \todo Detect time rolling. this only matters when we're processing a log file # with insufficient timestamp size, such that time rolls over from a large number # to a small one, during the log. thisTimestamp = msg.hdr.GetTime() if thisTimestamp < self._lastTimestamp: self._timestampOffset+=1 self._lastTimestamp = thisTimestamp timeSizeInBits = int(round(math.log(int(Messaging.findFieldInfo(msg.hdr.fields, "Time").maxVal), 2))) timestamp = (self._timestampOffset << timeSizeInBits) + thisTimestamp if Messaging.findFieldInfo(msg.hdr.fields, "Time").units == "ms": timestamp = timestamp / 1000.0 text = str(timestamp) + ", " except AttributeError: text = "unknown, " text += msgcsv.toCsv(msg, nameColumn=False, timeColumn=False) text += '\n' outputFile.write(text) # This is not efficient, but if we don't flush during socket processing # and the user hits Ctrl-C, we'll drop a bunch of data and end up with empty files. # So flush each message as it comes in. if self.connectionType !='file': outputFile.flush()
def logMessage(self, hdr): #write to log, if log is open if self.logFile != None: if self.logFileType and self.logFileType == "JSON": msgObj = Messaging.MsgFactory(hdr) self.logFile.write(msgjson.toJson(msgObj).encode('utf-8')) else: self.logFile.write(hdr.rawBuffer().raw)
def run(self): while True: # this blocks until message received, or timeout occurs timeout = 10.0 # value in seconds hdr = self.connection.get_message(timeout) if hdr: msg = Messaging.MsgFactory(hdr) self.db.handle_message(msg)
def recv(self, msgIds=[], timeout=None): # if user didn't pass a list, put the single param into a list if not isinstance(msgIds, list): msgIds = [msgIds] # if they passed classes, get the ID of each for i in range(0,len(msgIds)): if hasattr(msgIds[i], 'ID'): msgIds[i] = msgIds[i].ID if timeout != None and self._timeout != timeout: self._timeout = timeout if self._timeout == 0.0: self._sock.setblocking(0) else: self._sock.setblocking(1) self._sock.settimeout(self._timeout) while True: try: # see if there's enough for header data = self._sock.recv(Messaging.hdr.SIZE, socket.MSG_PEEK) if len(data) == Messaging.hdr.SIZE: # create header based on peek'd data hdr = Messaging.hdr(data) # see if there's enough for the body, too data += self._sock.recv(hdr.GetDataLength(), socket.MSG_PEEK) if len(data) != Messaging.hdr.SIZE + hdr.GetDataLength(): print("didn't get whole body, error!") continue # read out what we peek'd. data = self._sock.recv(Messaging.hdr.SIZE + hdr.GetDataLength()) # reset the header based on appended data hdr = Messaging.hdr(data) id = hdr.GetMessageID() if id in self._extra_msgs_to_record: self.received[id] = msg if len(msgIds) == 0 or id in msgIds: msg = Messaging.MsgFactory(hdr) self.received[id] = msg return msg except socket.timeout: return None except BlockingIOError: return None
def ProcessMessage(self, msg): hdr = msg.hdr msg_id = hex(hdr.GetMessageID()) msg_key = ",".join(Messaging.MsgRoute(msg)) + "," + msg_id self.display_message_in_rx_list(msg_key, msg) self.display_message_in_rx_tree(msg_key, msg) self.display_message_in_plots(msg_key, msg)
def setData(self, column, role, value): if self.index == None: return if column != 2: return if self.fieldInfo.name == "ID": return if self.fieldInfo.type == "int" and value.startswith("0x"): value = str(int(value, 0)) # set the value in the message/header buffer Messaging.set(self.msg, self.fieldInfo, value, int(self.index)) # get the value back from the message/header buffer and pass on to super-class' setData super(EditableFieldArrayItem, self).setData(column, role, Messaging.get(self.msg, self.fieldInfo, int(self.index)))
def GetDBValue(msg, fieldInfo, index=0): # what to do for arrays? val = Messaging.get(msg, fieldInfo, index) if fieldInfo.type == 'int': val = int(val) elif fieldInfo.type == 'float': val = float(val) #elif fieldInfo.type == 'Enum' # what? int value, string? return val
def logMsg(self, hdr): #write to log, if log is open if self.logFile != None: log = '' if self.logFileType == "csv": msg = Messaging.MsgFactory(hdr) if not msg.MsgName() in self.loggedMsgHeaderRow: self.loggedMsgHeaderRow[msg.MsgName()] = True log = msg.csvHeader(timeColumn=True) + '\n' log += msg.toCsv(timeColumn=True) + '\n' log = log.encode('utf-8') elif self.logFileType == "json": msg = Messaging.MsgFactory(hdr) log = msg.toJson(includeHeader=True) + '\n' log = log.encode('utf-8') elif self.logFileType == "bin": log = hdr.rawBuffer().raw self.logFile.write(log) self.logFile.flush()
def recv(self, msgIds=[], timeout=None): # if user didn't pass a list, put the single param into a list if not isinstance(msgIds, list): msgIds = [msgIds] # if they passed classes, get the ID of each for i in range(0,len(msgIds)): if hasattr(msgIds[i], 'ID'): msgIds[i] = msgIds[i].ID if timeout != None and self.timeout != timeout: self.timeout = timeout while True: try: data = self.synchronous_rx_queue.get(True, self.timeout) hdr = Messaging.hdr(data) id = hdr.GetMessageID() if len(msgIds) == 0 or id in msgIds: msg = Messaging.MsgFactory(hdr) return msg except queue.Empty: return None
def setData(self, column, role, value): if not column == 2: return if self.fieldInfo.name == "ID": return if self.fieldInfo.type == "int" and value.startswith("0x"): value = str(int(value, 0)) # if user deletes the value, for anything besides a string, # return without setting the new value if self.fieldInfo.type != "string" and value == "": return # set the value in the message/header buffer Messaging.set(self.msg, self.fieldInfo, value) # get the value back from the message/header buffer and pass on to super-class' setData super(FieldItem, self).setData(column, role, Messaging.get(self.msg, self.fieldInfo))
def onRxMessageFieldSelected(self, rxWidgetItem): if isinstance(rxWidgetItem, txtreewidget.FieldItem) or isinstance(rxWidgetItem, txtreewidget.FieldArrayItem): fieldInfo = rxWidgetItem.fieldInfo msg_id = hex(rxWidgetItem.msg.hdr.GetMessageID()) msg_key = ",".join(Messaging.MsgRoute(rxWidgetItem.msg)) + "," + msg_id try: msgPlot = self.addPlot(msg_key, type(rxWidgetItem.msg), rxWidgetItem.fieldName) if msgPlot: msgPlot.addData(rxWidgetItem.msg) except MsgPlot.PlotError as e: QMessageBox.warning(self, "Message Scope", str(e))
def addLine(self, msgClass, fieldName): fieldName, fieldIndex = MsgPlot.split_fieldname(fieldName) fieldInfo = Messaging.findFieldInfo(msgClass.fields, fieldName) if fieldInfo == None: raise MsgPlot.PlotError("Invalid field %s for message %s" % (fieldName, msgClass.MsgName())) if fieldInfo.units == "ASCII": raise MsgPlot.PlotError("Cannot plot %s.%s, it is a string" % (msgClass.MsgName(), fieldName)) # don't add if it's already there! for line in self.lines: if fieldInfo == line.fieldInfo and fieldIndex == line.fieldSubindex: name = fieldInfo.name if fieldInfo.count > 1: name = "%s[%d]" % (name, fieldIndex) raise MsgPlot.PlotError("Line %s already on plot" % name) if msgClass != self.msgClass: raise MsgPlot.NewPlotError( "Message %s != %s, cannot add to same plot" % (msgClass.__name__, self.msgClass.__name__)) if fieldInfo.units != self.units: raise MsgPlot.NewPlotError("Units %s != %s, not adding to plot" % (fieldInfo.units, self.units)) if fieldIndex != None: self._addLine(msgClass, fieldInfo, fieldIndex) elif fieldInfo.count == 1: self._addLine(msgClass, fieldInfo, 0) else: dups = [] for fieldIndex in range(0, fieldInfo.count): duplicate = False for line in self.lines: if fieldInfo == line.fieldInfo and fieldIndex == line.fieldSubindex: dups.append(fieldIndex) duplicate = True break if not duplicate: self._addLine(msgClass, fieldInfo, fieldIndex) if len(dups) > 0: if len(dups) == 1: s = ' ' + str(dups[0]) elif len(dups) == fieldInfo.count: s = 's %d-%d' % (0, fieldInfo.count) else: s = 's ' for d in dups: s += '%s,' % d raise MsgPlot.PlotError("Line%s already on plot" % s)
def addData(self, msg): # TODO what to do for things that can't be numerically expressed? just ascii strings, i guess? for line in self.lines: try: newDataPoint = Messaging.getFloat(msg, line.fieldInfo, line.fieldSubindex) except ValueError: print("ERROR! Plot of %s.%s cannot accept value %s" % ( self.msgClass.MsgName(), line.fieldInfo.name, Messaging.get(msg, line.fieldInfo, line.fieldSubindex))) continue try: timestamp = msg.hdr.GetTime() if Messaging.findFieldInfo(msg.hdr.fields, "Time").units == "ms": timestamp = timestamp / 1000.0 newTime = float(elapsedSeconds(timestamp)) if newTime != 0: self.useHeaderTime = 1 if not self.useHeaderTime: newTime = elapsedSeconds(datetime.now().timestamp()) except AttributeError: # if header has no time, fallback to PC time. newTime = elapsedSeconds(datetime.now().timestamp()) # add data in the array until MAX_LENGTH is reached, then drop data off start of array # such that plot appears to scroll. The array size is limited to MAX_LENGTH. if len(line.dataArray) >= MsgPlot.MAX_LENGTH: line.dataArray[:-1] = line.dataArray[1:] # shift data in the array one sample left line.dataArray[-1] = newDataPoint line.timeArray[:-1] = line.timeArray[1:] # shift data in the array one sample left line.timeArray[-1] = newTime else: line.dataArray.append(newDataPoint) line.timeArray.append(newTime) if not self.pause: self.refreshLine(line)
def addLine(self, msgClass, fieldName): fieldName, fieldIndex = MsgPlot.split_fieldname(fieldName) fieldInfo = Messaging.findFieldInfo(msgClass.fields, fieldName) if fieldInfo == None: raise MsgPlot.PlotError("Invalid field %s for message %s" % (fieldName, msgClass.MsgName())) if fieldInfo.units == "ASCII": raise MsgPlot.PlotError("Cannot plot %s.%s, it is a string" % (msgClass.MsgName(), fieldName)) # don't add if it's already there! for line in self.lines: if fieldInfo == line.fieldInfo and fieldIndex == line.fieldSubindex: name = fieldInfo.name if fieldInfo.count > 1: name = "%s[%d]" % (name, fieldIndex) raise MsgPlot.PlotError("Line %s already on plot" % name) if msgClass != self.msgClass: raise MsgPlot.NewPlotError("Message %s != %s, cannot add to same plot" % (msgClass.__name__, self.msgClass.__name__)) if fieldInfo.units != self.units: raise MsgPlot.NewPlotError("Units %s != %s, not adding to plot" % (fieldInfo.units, self.units)) if fieldIndex != None: self._addLine(msgClass, fieldInfo, fieldIndex) elif fieldInfo.count == 1: self._addLine(msgClass, fieldInfo, 0) else: dups = [] for fieldIndex in range(0, fieldInfo.count): duplicate = False for line in self.lines: if fieldInfo == line.fieldInfo and fieldIndex == line.fieldSubindex: dups.append(fieldIndex) duplicate = True break if not duplicate: self._addLine(msgClass, fieldInfo, fieldIndex) if len(dups) > 0: if len(dups) == 1: s = ' '+str(dups[0]) elif len(dups) == fieldInfo.count: s = 's %d-%d' % (0, fieldInfo.count) else: s = 's ' for d in dups: s += '%s,' % d raise MsgPlot.PlotError("Line%s already on plot" % s)
def test_csv_and_json(self): testData = [ ('TestCase4 ;', {"TestCase4": {}, "hdr" : {"DataLength": ";"}}), ('TestCase4 ', {"TestCase4": {"A":0, "B": [0,0,0], "C": [0,0,0], "D": ""}}), ('TestCase4 1, 2,3,4, 5,6,7,ei;ght', {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "ei;ght"}}), ("TestCase4 1, 2;", {"TestCase4": {"A":1, "B": [2]}, "hdr" : {"DataLength": ";"}}), ("TestCase4 1, 0x0203 ;", {"TestCase4": {"A":1, "B": [2,3]}, "hdr" : {"DataLength": ";"}}), ("TestCase4 1, 0x020304, 0x00050006 ;", {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6]}, "hdr" : {"DataLength": ";"}}), ("TestCase4 1, 2", {"TestCase4": {"A":1, "B": [2, 0,0],"C": [0,0,0], "D": ""}}), # note without semicolon, unspecified fields have default values ("TestCase4 1, 0x020304, 5,6,0x07", {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": ""}}), ("TestCase4 1, 2,3,4, 5,6,7, 0x8", {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "0x8"}}), ("TestCase4 1, 2,3,4, 5,6,7, eight", {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "eight"}}), ('TestCase4 1, 2,3,4, 5,6,7, "eight"', {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "eight"}}), ('TestCase4 1, 2,3,4, 5,6,7, "eig,ht"', {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "eig,ht"}}), ("TestCase4 1, 2,3,4, 5,6,7, ei ght", {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "ei ght"}}), ("TestCase4 1, 2,3,4, 5,6,7, 0x8;", {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "0x8"}, "hdr" : {"DataLength": ";"}}), ("TestCase4 1, 2,3,4, 5,6,7, eight;", {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "eight"}, "hdr" : {"DataLength": ";"}}), ('TestCase4 1, 2,3,4, 5,6,7, "eight;"', {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "eight;"}}), ('TestCase4 1, 2,3,4, 5,6,7, "eig,ht";', {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "eig,ht"}, "hdr" : {"DataLength": ";"}}), ("TestCase4 1, 2,3,4, 5,6,7, ei ght;", {"TestCase4": {"A":1, "B": [2,3,4], "C": [5,6,7], "D": "ei ght"}, "hdr" : {"DataLength": ";"}})] commaTestData = [] for tc in testData: newTestCase = (tc[0].replace("TestCase4 ", "TestCase4,"), tc[1]) commaTestData.append(newTestCase) testData.extend(commaTestData) tcNum = 0 for tc in testData: try: msg = msgcsv.csvToMsg(tc[0]) json = msgjson.toJson(msg) msg2 = msgjson.jsonToMsg(tc[1]) #print("csv is " + tc[0]) self.assertEqual(msg.hdr.GetDataLength(), msg2.hdr.GetDataLength(), self.info(tc, tcNum, "hdr.DataLength")) #print("json of csv is " + json) for fieldInfo in type(msg).fields: if(fieldInfo.count == 1): if len(fieldInfo.bitfieldInfo) == 0: self.assertEqual(Messaging.get(msg, fieldInfo), Messaging.get(msg2, fieldInfo), self.info(tc, tcNum, fieldInfo.name)) else: for bitInfo in fieldInfo.bitfieldInfo: self.assertEqual(Messaging.get(msg, bitInfo), Messaging.get(msg2, bitInfo), self.info(tc, tcNum, fieldInfo.name+"."+bitInfo.name)) else: for i in range(0,fieldInfo.count): self.assertEqual(Messaging.get(msg, fieldInfo, i), Messaging.get(msg2, fieldInfo, i), self.info(tc, tcNum, fieldInfo.name+"["+str(i)+"]")) except AssertionError: print("test_csv_and_json test case %d" % (tcNum)) raise except: print("Exception on test case %d, [%s] != [%s]" % (tcNum, tc[0], tc[1])) print(traceback.format_exc()) self.assertEqual(True, False) tcNum += 1
def __init__(self, parent=None): parser = argparse.ArgumentParser(description=DESCRIPTION) parser.add_argument('msgs', nargs='*', help=''' One or more messages that you wish to send. This entire list is sent each time the message period is up. Network messages are ignored. By default we send all known messages.''') parser = msgtools.lib.gui.Gui.addBaseArguments(parser) args = parser.parse_args() msgtools.lib.gui.Gui.__init__(self, "Noise Maker 0.1", args, parent) self.timeInfo = Messaging.findFieldInfo(Messaging.hdr.fields, "Time") # event-based way of getting messages self.RxMsg.connect(self.ProcessMessage) self.currentTime = 0 self.msgTimer = QtCore.QTimer(self) self.msgTimer.setInterval(50) self.msgTimer.timeout.connect(self.msgTimeout) vbox = QtWidgets.QVBoxLayout() w = QtWidgets.QWidget(self) w.setLayout(vbox) self.setCentralWidget(w) self.timeSlider = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.timeSlider.setMinimum(1) self.timeSlider.setMaximum(5) vbox.addWidget(self.timeSlider) t = int(self.settings.value('time_slider', 10)) self.timeSlider.setValue(t) self.timeSlider.valueChanged.emit(t) self.startStop = QtWidgets.QPushButton(self) self.startStop.setText('Stop') self.startStop.clicked.connect(self.startStopFn) vbox.addWidget(self.startStop) # check if user specified which messages we should output msgs = args.msgs # find a few messages to send at specified rates period = 0.1 self.fieldNumber = 0 self.msgPeriod = {} self.msgTxTime = {} for msgName in Messaging.MsgClassFromName: if not msgName.startswith("Network"): if len(msgs) > 0 and msgName not in msgs: continue print("found message " + msgName) self.msgPeriod[msgName] = period period = period + 0.050 if period > 0.5: period = 0.1 self.msgTxTime[msgName] = 0 self.msgTimer.start()
def processBinaryMessage(self, bytes): hdr = Messaging.hdr(bytes.data()) self.messagereceived.emit(hdr)
def __init__(self, msgClass, fieldName, runButton = None, clearButton = None, timeSlider = None, displayControls=True): super(QWidget,self).__init__() newFieldName, fieldIndex = MsgPlot.split_fieldname(fieldName) fieldInfo = Messaging.findFieldInfo(msgClass.fields, newFieldName) if fieldInfo == None: raise MsgPlot.PlotError("Invalid field %s for message %s" % (newFieldName, msgClass.MsgName())) layout = QVBoxLayout() self.setLayout(layout) self.msgClass = msgClass self.pause = 0 self.lineCount = 0 self.units = fieldInfo.units self.lines = [] yAxisLabel = fieldInfo.units xAxisLabel = "time (s)" self.plotWidget = pg.PlotWidget(labels={'left':yAxisLabel,'bottom':xAxisLabel}) layout.addWidget(self.plotWidget) self.plotWidget.addLegend() self.addLine(msgClass, fieldName) # set up click handler to pause graph self.plotWidget.scene().sigMouseClicked.connect(self.mouseClicked) hLayout = QHBoxLayout() layout.addLayout(hLayout) # add a Pause/Run button if runButton == None: self.runButton = QPushButton("Pause") else: self.runButton = runButton self.runButton.clicked.connect(self.pauseOrRun) if displayControls: hLayout.addWidget(self.runButton) # add a 'Clear' button if clearButton == None: self.clearButton = QPushButton("Clear") else: self.clearButton = clearButton self.clearButton.clicked.connect(self.clearData) if displayControls: hLayout.addWidget(self.clearButton) # add slider bar to control time scale if timeSlider == None: self.timeSlider = QSlider(Qt.Horizontal) self.timeSlider.setMinimum(50) self.timeSlider.setMaximum(MsgPlot.MAX_LENGTH) self.timeSlider.setSingleStep(10) self.timeSlider.setPageStep(50) else: self.timeSlider = timeSlider self.timeSlider.valueChanged.connect(self.timeScaleChanged) if displayControls: hLayout.addWidget(QLabel("Time Scale")) hLayout.addWidget(self.timeSlider) self.plotWidget.dragEnterEvent = self.dragEnterEvent self.plotWidget.dragMoveEvent = self.dragMoveEvent self.plotWidget.dropEvent = self.dropEvent self.plotWidget.setAcceptDrops(1)