def fakeProcessNoScanner(self):
     """ Simulates arriving CAN frames """
     printT("Entered %s.fakeProcessNoScanner()" % (type(self).__name__, ))
     from random import randint
     fakeIdentifiers = {0x020, 0x220, 0x221, 0x560, 0x56a,
                        0x56b}  # random identifiers
     counterLoop = 0
     while True:
         durationCreation = 0.
         durationDispatch = 0.
         for canFrameIdentifier in fakeIdentifiers:
             canFrameData = bytearray(b"\x00\x00\x00\x00\x00")
             canFrameData.append(canFrameIdentifier % 256)
             canFrameData.append(randint(0, 0xFF))
             canFrameData.append(counterLoop % 256)
             timewatchStart = perf_counter()
             canFrame = CANFrame(
                 identifier=canFrameIdentifier,
                 isExtended=False,
                 isRTR=False,
                 DLC=8,
                 data=canFrameData,
             )
             durationCreation += perf_counter() - timewatchStart
             timewatchStart = perf_counter()
             self.handleNewFrame(canFrame)
             durationDispatch += perf_counter() - timewatchStart
             sleep(0.001)
         counterLoop += 1
예제 #2
0
 def run(self):
     httpd = ThreadedHTTPServer((self.ipAddress, self.tcpPort),
                                OBDRelayHTTPRequestHandler)
     httpd.thread = self
     printT("OBDRelayHTTPServerThread started:", self.ipAddress,
            self.tcpPort)
     httpd.serve_forever()
예제 #3
0
 def setParameters(self, parameters):
     self.parametersLock.acquire()
     obdLogOutputData = parameters["obdLogOutputData"]
     if obdLogOutputData != self.logOutputData:
         self.logOutputData = obdLogOutputData
         # Close any open file:
         if self.logOutputDataFile is not None:
             try:
                 self.logOutputDataFile.close()
             except:
                 pass
             self.logOutputDataFile = None
         # Attempt to open file:
         if self.logOutputData is not None:
             try:
                 self.logOutputDataFile = open(self.logOutputData,
                                               mode="wb")
             except Exception as e:
                 self.logOutputDataFile = None
                 printT("Unable to open the obdLogOutputData file:", e)
         # Cleanup for clean start:
         self.logOutputDataColumns = {}
         self.logOutputDataColumnsOrder = []
     self.logOutputDataCompact = parameters["obdLogOutputDataCompact"]
     self.parametersLock.release()
예제 #4
0
 def sendPong(self, content):  # thread-safe
     # Untested
     rawFrame = bytearray([0b10000000 | WebSocket.OPCODE_PONG])
     rawFrame.extend([len(content)])
     rawFrame.extend(content)
     self.sendMessageRaw(self, rawFrame)
     printT("Sent a PONG")  # debug
예제 #5
0
	def stopMonitoring( self, stopMonitoringReason ):
		""" Interrupts ATMA and displays the reason """
		if self.stopMonitoringAttempts>self.stopMonitoringMaxAttempts:
			raise ConnectionError( "Failed %u times to interrupt ATMA"%(self.stopMonitoringAttempts,) )
		self.write( b"\x0D" )
		self.stopMonitoringAttempts += 1
		if stopMonitoringReason:
			printT( "Interrupted monitoring: "+stopMonitoringReason )
예제 #6
0
def reloadParameters():
    if execfileIfNeeded(parametersFile, parameters, parametersFileInfo):
        for httpBinding in parameters["httpBindings"]:
            for httpd in httpServers:
                httpdParameters = httpd.getParameters()
                # Reload HTTP parameters for the HTTP server matching address & port:
                if httpBinding["address"] == httpdParameters[
                        "ipAddress"] and httpBinding[
                            "port"] == httpdParameters["tcpPort"]:
                    break
        printT("[main.py] Parameters have been reloaded.")
 def reloadSequence(self):
     global sequenceFile
     global outputList
     global outputListLock
     if execfileIfNeeded(sequenceFile, {"obd": self},
                         self.sequenceFileInfo):
         with outputListLock:
             outputList.clear()  # erase any obsolete JSON data
         printT("The OBD sequence has been reloaded.")
         if self.logger is not None:
             parameters = {}
             execfile(parametersFile, parameters)
             self.logger.setParameters(parameters)
 def run(self):
     while not self.terminating:
         self.continueProcessLock.acquire()
         presentPendingData = True
         with self.parametersLock:
             while presentPendingData:
                 with self.pendingDataLock:
                     pendingData = self.pendingData
                     self.pendingData = []  # cleanup
                 presentPendingData = len(pendingData) != 0
                 if self.netOutputSocket:
                     for frame in pendingData:
                         try:
                             self.netOutputSocket.sendto(
                                 canEthPacket(frame), self.canEthUdpDst)
                         except OSError as e:
                             # network error silently discarded
                             pass
                         except Exception as e:
                             printT("CAN-ETH converter error, stopping:",
                                    format_exc())
                             try:
                                 netOutputSocket = self.netOutputSocket
                                 self.netOutputSocket = None
                                 netOutputSocket.close()
                             except:
                                 pass
                             break
                 packet = None
                 if self.logOutputDataFile:
                     for frame in pendingData:
                         try:
                             self.logOutputDataFile.write(
                                 pcaprec_CAN(frame))
                         except Exception as e:
                             printT("LibPCAP logging error, stopping:",
                                    format_exc())
                             try:
                                 logOutputDataFile = self.logOutputDataFile
                                 self.logOutputDataFile = None
                                 logOutputDataFile.close()
                             except:
                                 pass
     try:
         self.logOutputDataFile.close()  # properly flush & close
     except:
         pass
 def read(self, size=1):
     result = self.ser.read(size)
     if len(result) != 0:
         if self.serialShowReceivedBytes:
             if not OBDRelayELM327Thread.lastShownReceived:
                 printT("ELM327 :")
             if result == b"\x0D":
                 print("")  # new line
             elif result == b"\x0A":
                 pass
             else:
                 sys.stdout.write(result.decode("latin_1"))
         OBDRelayELM327Thread.lastShownReceived = True
     else:
         if self.serialShowReceivedBytes:
             printT("ELM327 : <timeout>")
         OBDRelayELM327Thread.lastShownReceived = False
     return result
    def read(self, *, minReadCount=1, retryDelayIfEmpty=None):
        """
		Read 1 byte from the input buffer
		Reduced number of I/O operations (CPU optimization)
		- minReadCount: wait for this number of bytes or a timeout (not a length guarantee)
		- retryDelayIfEmpty: effective timeout (in seconds) when the timeout of the I/O operation is short (low delay)
		"""
        if self.serialLocalBufferEnabled:
            '''
			Optimiser encore : en cas de gros débit, c'est le manque de puissance CPU qui fera que le buffet de réception n'est pas vide, donc on ne résout pas l'utilisation CPU excessive ; on évite seulement les pertes de données.
			Il faut par exemple ajouter un temps d'attente paramétré si self.ser.in_waiting < minReadCount (très court, de valeur éventuellement calculée par rapport au débit => durée d'envoi d'octets).
			Attention, le code qui suit la lecture est lui aussi intensif, puisqu'il n'y avait plus d'erreurs de décodage quand la trame est mise à False.
			'''
            if not self.readBuffer:
                # The buffer is empty, fetching new bytes.
                # The awaiting bytes count is not unlimited so there should be no excessive memory usage.
                if retryDelayIfEmpty is not None:
                    readStartedAt = perf_counter()
                while True:
                    newBytes = self.ser.read(
                        max(minReadCount, self.ser.in_waiting))
                    if newBytes:
                        # bytes received: append new bytes
                        self.readBuffer.extend(newBytes)
                        break
                    elif retryDelayIfEmpty is None \
                      or ( perf_counter()-readStartedAt )>retryDelayIfEmpty:
                        # nothing received
                        break
            if self.readBuffer:
                # The buffer contains something, taking out the first byte.
                result = bytes([self.readBuffer.pop(0)])
            else:
                # The buffer contains nothing.
                result = b""
        else:
            result = self.ser.read()
        if self.serialShowReceivedBytes:
            if len(result) != 0:
                printT("ELM327 :", result.decode("ascii", "replace"))
            else:
                printT("ELM327 : <timeout>")
        return result
    def reloadParameters(self):
        global parametersFile
        parameters = {}
        if execfileIfNeeded(parametersFile, parameters,
                            self.parametersFileInfo):
            self.serialPort = parameters["serialPort"]
            self.serialBaudRateInitial = parameters["serialBaudRateInitial"]
            self.serialBaudRateDesired = parameters["serialBaudRateDesired"]
            self.serialBaudRateDesiredForce = parameters[
                "serialBaudRateDesiredForce"]
            self.serialTimeoutWhileOBD = parameters["serialTimeoutWhileOBD"]
            self.serialShowSentBytes = parameters["serialShowSentBytes"]
            self.serialShowReceivedBytes = parameters[
                "serialShowReceivedBytes"]
            self.scannerATSP = parameters["ATSP"]  # OBD bus selection
            self.obdShowIncorrectResult = parameters["obdShowIncorrectResult"]
            scannerATBRD = round(4000000 / self.serialBaudRateDesired)
            if scannerATBRD > 0xFF:
                printT(
                    "The parameter serialBaudRateDesired is set to an insufficient value!"
                )
            self.scannerATBRD = b"ATBRD" + bytes(
                "%.2X" % round(4000000 / self.serialBaudRateDesired),
                "ascii") + b"\x0D"  # desired baudrate

            obdCanFrameReq11 = parameters["obdCanFrameReq11"]
            obdCanFrameReq29 = parameters["obdCanFrameReq29"]
            self.obdCanFramePrep11 = self.obdCanFramePrepLoaded[
                obdCanFrameReq11]
            self.obdCanFramePrep29 = self.obdCanFramePrepLoaded[
                obdCanFrameReq29]
            self.obdCanFrameReq11 = self.obdCanFrameReqLoaded[obdCanFrameReq11]
            self.obdCanFrameReq29 = self.obdCanFrameReqLoaded[obdCanFrameReq29]
            self.obdCanFrameCleanup11 = self.obdCanFrameCleanupLoaded[
                obdCanFrameReq11]
            self.obdCanFrameCleanup29 = self.obdCanFrameCleanupLoaded[
                obdCanFrameReq29]

            if self.logger is not None:
                self.logger.setParameters(parameters)
            printT("[OBDRelayELM327.py] Parameters have been reloaded.")
예제 #12
0
    def do_GET(self):
        f = None
        try:
            f = self.send_head()
            self.copyfile(f, self.wfile)
        except ConnectionError:
            return
        finally:
            if f is not None:
                f.close()
            del f

        if self.runWebSocket:
            try:
                ws = self.webSocketClass(self)
                ws.run()
            except ConnectionError:
                pass
            except StopIteration:
                pass
            except WebSocketBadRequest as e:
                printT(repr(e))
                return
 def run(self):
     canSource = self.canSource
     self.pendingFrames = []
     while True:
         self.continueProcessLock.acquire()
         presentPendingFrames = True
         while presentPendingFrames:
             self.pendingFramesLock.acquire()
             pendingFrames = self.pendingFrames
             self.pendingFrames = []  # cleanup
             self.pendingFramesLock.release()
             presentPendingFrames = len(pendingFrames) != 0
             for frame in pendingFrames:
                 try:
                     if canSource.passesFilters(
                             frame):  # thread-safety required
                         WebSocket_frames.broadcastFrame(frame)
                         if canSource.canExporter is not None:
                             canSource.canExporter.logFrame(frame)
                 except Exception as asyncException:
                     # This exception will be raised on next frame income, with an incorrect stack trace.
                     self.asyncException = asyncException
                     printT(format_exc())
 def selectATPB_rate(self, expectedRate):
     """ Select the most suitable bitrate for USER1 (B) protocol """
     try:
         from math import inf
     except ImportError:
         inf = float("inf")
     expectedRate = float(expectedRate)
     # Calculate all possible rates
     rates = {}
     for mul8by7 in (False, True):
         ratio = 1.
         if mul8by7:
             ratio = 8. / 7.
         for divider in range(1, 0x41):  # dividers from 1 to 0x40
             ATPB = divider
             if mul8by7:
                 ATPB |= 0b0001000000000000
             rates[ATPB] = (500. * ratio / float(divider), mul8by7)
     # Select the closest rate:
     selGap = inf
     selRate = 0.
     selATPB = None
     selMul8by7 = None
     for rateInfo in rates.items():
         ATPB = rateInfo[0]
         rate = rateInfo[1][0]
         mul8by7 = rateInfo[1][1]
         gap = abs(rate - expectedRate)
         if gap < selGap:
             selGap = gap
             selRate = rate
             selATPB = ATPB
             selMul8by7 = mul8by7
     printT("Selected USER1 CAN rate: %.2f kb/s" % (selRate, ))
     self.User1Mul8by7 = selMul8by7
     return selATPB
예제 #15
0
 def run(self):
     while not self.terminating:
         self.continueProcessLock.acquire()
         presentPendingData = True
         self.parametersLock.acquire()
         while presentPendingData:
             self.pendingDataLock.acquire()
             pendingData = self.pendingData
             self.pendingData = []  # cleanup
             self.pendingDataLock.release()
             presentPendingData = len(pendingData) != 0
             for onePendingData in pendingData:
                 key = onePendingData[0]
                 outputData = onePendingData[1]
                 dataDateTime = onePendingData[2]
                 try:
                     if key not in self.logOutputDataColumns:
                         self.logOutputDataColumns[key] = True
                         self.logOutputDataColumnsOrder = list(
                             self.logOutputDataColumns.keys())
                         self.logOutputDataColumnsOrder.sort()
                         self.logOutputDataFile.seek(0)
                         self.logOutputDataFile.truncate()
                         self.logOutputDataFile.write(
                             b'"Time","Elapsed","Updated","' +
                             b'","'.join(self.logOutputDataColumnsOrder) +
                             b'"\x0D\x0A')
                         self.startDateTime = datetime.now()
                     CSVDataColumns = [
                         b'"' +
                         str(dataDateTime).encode("ascii", "replace") +
                         b'"',
                         str((dataDateTime - self.startDateTime
                              ).total_seconds()).encode("ascii"),
                     ]
                     if self.logOutputDataCompact:
                         CSVDataColumns.append(b'')
                     else:
                         CSVDataColumns.append(b'"' + key + b'"')
                     for key1 in self.logOutputDataColumnsOrder:
                         dataValue = outputData
                         dataType = type(dataValue)
                         CSVDataColumn = b'"#"'
                         if self.logOutputDataCompact and key1 != key:
                             CSVDataColumn = b''
                         else:
                             try:
                                 if dataType is float or dataType is int:
                                     CSVDataColumn = str(dataValue).encode(
                                         "ascii")
                                 elif dataType is bool:
                                     if dataValue:
                                         CSVDataColumn = b'1'
                                     else:
                                         CSVDataColumn = b'0'
                                 elif dataValue is None:
                                     CSVDataColumn = b'""'
                                 else:
                                     if dataType is bytes or dataType is bytearray:
                                         pass
                                     else:
                                         dataValue = str(dataValue).encode(
                                             "utf_8", "replace")
                                     CSVDataColumn = b'"' + dataValue.replace(
                                         b'"', b"'") + b'"'
                             except:
                                 pass
                         CSVDataColumns.append(CSVDataColumn)
                     self.logOutputDataFile.write(
                         b','.join(CSVDataColumns) + b'\x0D\x0A')
                 except Exception as e:
                     printT("Logging error, stopping:", e)
                     try:
                         logOutputDataFile = self.logOutputDataFile
                         self.logOutputDataFile = None
                         logOutputDataFile.close()
                     except:
                         pass
         self.parametersLock.release()
     try:
         self.logOutputDataFile.close()  # properly flush & close with
     except:
         pass
    def reloadParameters(self):
        parameters = {}
        if execfileIfNeeded(parametersFile, parameters,
                            self.parametersFileInfo):
            ### Serial port ###
            self.serialPort = parameters["serialPort"]
            self.serialBaudRateInitial = parameters["serialBaudRateInitial"]
            self.serialBaudRateDesired = parameters["serialBaudRateDesired"]
            self.serialBaudRateDesiredForce = parameters[
                "serialBaudRateDesiredForce"]
            self.serialLocalBufferEnabled = parameters[
                "serialLocalBufferEnabled"]
            if self.serialLocalBufferEnabled:
                self.serialLocalBufferAccuATMA = parameters[
                    "serialLocalBufferAccuATMA"]
                self.serialLocalBufferMinFillATMA = parameters[
                    "serialLocalBufferMinFillATMA"]
                self.serialLocalBufferWaitTimeATMA = parameters[
                    "serialLocalBufferWaitTimeATMA"]
            else:
                self.serialLocalBufferAccuATMA = None
                self.serialLocalBufferMinFillATMA = None
                self.serialLocalBufferWaitTimeATMA = None
            self.serialShowSentBytes = parameters["serialShowSentBytes"]
            self.serialShowReceivedBytes = parameters[
                "serialShowReceivedBytes"]

            ### CAN bus ###
            self.testObdCompliant = parameters["canBusTestObdCompliant"]
            self.scannerATSP = parameters["ATSP"]
            #self.obdShowIncorrectResult = parameters["obdShowIncorrectResult"]
            scannerATBRD = round(4000000 / self.serialBaudRateDesired)
            if scannerATBRD > 0xFF:
                printT(
                    "The parameter serialBaudRateDesired is set to an insufficient value!"
                )
            self.scannerATBRD = b"ATBRD" + bytes(
                "%.2X" % round(4000000 / self.serialBaudRateDesired),
                "ascii") + b"\x0D"  # desired baudrate
            if self.canExporter is not None:
                self.canExporter.setParameters(parameters)
            self.allowMixedIdentifiers = parameters[
                "canBusAllowMixedIdentifiers"]
            self.inactivityTimeout = parameters["canBusInactivityTimeout"]
            if DEBUG_DISCONNECTED_CAN_BUS:
                self.inactivityTimeout = 20.
            self.stopMonitoringWait = parameters["canBusStopMonitoringWait"]
            self.stopMonitoringMaxAttempts = parameters[
                "canBusStopMonitoringMaxAttempts"]
            self.maxStraightInvalidFrames = parameters[
                "canBusMaxStraightInvalidFrames"]
            self.maskOver = parameters["canBusMaskOver"]
            if self.maskOver is None:
                self.maskOver = 0x1FFFFFFF
            # USER1 CAN bus specification:
            scannerATPB = 0
            if parameters["ATPB_11bit"]:
                scannerATPB |= 0b1000000000000000
            if parameters["ATPB_variableDataLength"]:
                scannerATPB |= 0b0100000000000000
            if self.allowMixedIdentifiers:
                scannerATPB |= 0b0010000000000000
            ATPB_dataFormat = parameters["ATPB_dataFormat"]
            if ATPB_dataFormat == None:
                pass
            elif ATPB_dataFormat == "ISO 15765-4":
                scannerATPB |= 0b0000000100000000
            elif ATPB_dataFormat == "SAE J1939":
                scannerATPB |= 0b0000001000000000
            else:
                raise NotImplementedError('Unknown CAN data format "%s"' %
                                          (ATPB_dataFormat, ))
            scannerATPB |= self.selectATPB_rate(parameters["ATPB_rate"])
            self.scannerATPB = ("%.4X" % (scannerATPB, )).encode("ascii")

            printT("[CANCaptureELM327.py] Parameters have been reloaded.")
 def run(self):
     self.reloadParameters()
     self.reloadSequence()
     lastReloadAttempt = int(time())
     self.lastPid = -1
     if DEBUG_DISCONNECTED_SCANNER:
         self.fakeProcessNoScanner()
     self.ser = serial.Serial(port=None,
                              bytesize=serial.EIGHTBITS,
                              parity=serial.PARITY_NONE,
                              stopbits=serial.STOPBITS_ONE,
                              xonxoff=False,
                              rtscts=False,
                              write_timeout=None,
                              dsrdtr=False,
                              inter_byte_timeout=None)
     self.ser.exclusive = True  # silently fails if "exclusive" does not exist
     isFirstAttempt = True
     while True:
         setConsoleColorWindows("4F")
         setConsoleTitle("ELM327 CAN " + self.serialPort + ": Disconnected")
         if self.ser.is_open:
             self.ser.close()
         if not isFirstAttempt:
             sleep(1)
         try:
             # Startup
             if not self.ser.is_open:
                 # Configure and open the serial port
                 self.reloadParameters()
                 printT("New connection to " + self.serialPort + "...")
                 self.ser.port = self.serialPort
                 self.ser.open()
                 # Communication attempt
                 self.ser.baudrate = self.serialBaudRateInitial
                 self.ser.timeout = 0.5
                 connectionConfirmed = False
                 while not connectionConfirmed:
                     self.write(b"ATH\x0D")  # command that does nothing
                     connectionConfirmed = self.waitForPrompt(
                         noSilentTest=True)
                     # Alternate between initial and desired baudrates
                     if not connectionConfirmed:
                         self.reloadParameters()
                         if self.ser.baudrate == self.serialBaudRateInitial:
                             self.ser.baudrate = self.serialBaudRateDesired
                         else:
                             self.ser.baudrate = self.serialBaudRateInitial
                 printT("Connection works at " + str(self.ser.baudrate) +
                        " b/s")
                 # Reset
                 if self.ser.baudrate == self.serialBaudRateDesired:
                     # Note: on my Icar01 ELM327 V1.5, ATWS resets the baud rate. This is a workaround.
                     self.write(b"ATD\x0D")
                 else:
                     self.write(b"ATWS\x0D")
                 self.ser.timeout = 5
                 self.waitForPrompt("No prompt after ATWS or ATD!"
                                    )  # resets the timeout to 0.5
                 # TODO - alternate rate instead of keeping serialBaudRateInitial
                 # Apply output parameters
                 self.write(b"ATE0\x0D")  # no echo
                 self.waitForPrompt("No prompt after ATE0!")
                 self.write(b"ATL0\x0D")  # no <LF> after <CR>
                 self.waitForPrompt("No prompt after ATL0!")
                 self.write(b"ATS0\x0D")  # no spaces
                 self.waitForPrompt("No prompt after ATS0!")
                 self.write(
                     b"ATR1\x0D")  # wait for response after sending message
                 self.waitForPrompt("No prompt after ATR1!")
                 self.write(
                     b"ATD1\x0D"
                 )  # display DLC of CAN frames between identifier & data
                 self.waitForPrompt("No prompt after ATD1!")
                 self.write(b"ATCAF0\x0D")  # raw CAN data formatting
                 self.waitForPrompt("No prompt after ATCAF0!")
                 self.write(
                     b"ATH1\x0D"
                 )  # send CAN headers (otherwise identifiers not shown!)
                 self.waitForPrompt("No prompt after ATH1!")
                 self.write(
                     b"ATJHF0\x0D"
                 )  # send J1939 SAE identifiers as ordinary CAN identifiers
                 self.waitForPrompt("No prompt after ATJHF0!")
                 self.write(
                     b"ATJS\x0D")  # disable byte-reordering for J1939 SAE
                 self.waitForPrompt("No prompt after ATJS!")
                 # Setup the CAN bus characteristics
                 self.write(b"ATCSM1\x0D")  # CAN spy mode
                 self.waitForPrompt("No prompt after ATCSM1!")
                 self.write(
                     b"ATPB" + self.scannerATPB + b"\x0D"
                 )  # configuration of the USER1 CAN bus specification
                 self.waitForPrompt("No prompt after ATPB!")
                 self.write(
                     b"ATSP" + self.scannerATSP +
                     b"\x0D")  # selection of the CAN bus specification
                 self.waitForPrompt("No prompt after ATSP!")
                 # Attempt to contact the ECU
                 printT("Contacting the ECU...")
                 self.ser.timeout = max(MAX_OBD_NEGOCIATION_TIME,
                                        self.inactivityTimeout
                                        )  # very conservative (1st request)
                 if not DEBUG_DISCONNECTED_CAN_BUS:
                     if self.testObdCompliant:
                         busConnectionConfirmed = False
                         while not busConnectionConfirmed:
                             self.write(b"0100\x0D")  # test OBD request
                             busConfirmAnswer = self.readAnwer(
                                 "No prompt after 0100!")
                             if "ERROR" in busConfirmAnswer:  # BUS ERROR or CAN ERROR
                                 continue
                             elif "UNABLE" in busConfirmAnswer:  # UNABLE TO CONNECT
                                 continue
                             else:
                                 busConnectionConfirmed = True
                 # Get the found bus information
                 self.ser.timeout = 0.5
                 self.write(b"ATDP\x0D")  # return active bus protocol
                 busSpecification = self.readAnwer("No prompt after ATDP!")
                 busSpecificationPieces = self.busSpecificationRegex.search(
                     busSpecification)
                 if busSpecificationPieces:
                     self.canBusFamily = busSpecificationPieces.group(1)
                     self.canBusIsExtended = (
                         busSpecificationPieces.group(2) == "29")
                     self.canBusRate = float(
                         busSpecificationPieces.group(3))
                     if "USER1" in self.canBusFamily and self.User1Mul8by7:
                         self.canBusRate *= 8. / 7.  # ATDP reports the rate unaffected by 8/7
                     printT("Connected to CAN bus: %u-bit, %.2f kb/s (%s)" %
                            (self.canBusIsExtended and 29
                             or 11, self.canBusRate, self.canBusFamily))
                 else:
                     raise Exception(
                         "The found OBD bus does not seem to be a CAN bus: %s"
                         % (busSpecification, ))
                 # Apply the desired baudrate
                 if not self.applyDesiredBaudRate():
                     if self.serialBaudRateDesiredForce:
                         raise Exception(
                             "The desired baud rate could not be selected!")
                 printT("Connection established at " +
                        str(self.ser.baudrate) + " b/s")
                 setConsoleColorWindows("2F")
                 setConsoleTitle("ELM327 " + self.serialPort + " CAN: " +
                                 str(self.ser.baudrate) + " b/s")
             # Read CAN frames until thread exit
             counter = 0  # counts the number of straight monitoring episodes
             self.filter1RemoteChanged = True  # unknown state: re-apply ELM327-side filter
             while True:
                 try:
                     if self.filter1RemoteChanged:
                         with self.filter1RemoteLock:
                             self.write(b"ATCF " + bytes(
                                 "%.8X" % (self.filter1RemoteResult
                                           & self.maskOver, ), "ascii") +
                                        b"\x0D")
                             self.waitForPrompt("No prompt after ATCF!")
                             self.write(b"ATCM " + bytes(
                                 "%.8X" % (self.filter1RemoteMask
                                           & self.maskOver, ), "ascii") +
                                        b"\x0D")
                             self.waitForPrompt("No prompt after ATCM!")
                             self.filter1RemoteChanged = False
                     self.straightInvalidFramesCount = 0
                     self.write(
                         b"ATMA\x0D"
                     )  # start monitoring (may fail with "?" error, no particular handling needed)
                     self.stopMonitoringAttempts = 0
                     stopMonitoringNextAttempt = None
                     while True:
                         # Live-refresh of configuration:
                         if int(time()) != lastReloadAttempt:
                             if counter % 2 == 0:
                                 self.reloadSequence()
                             else:
                                 self.reloadParameters()
                             lastReloadAttempt = int(time())
                         # Read the next frame
                         frame = self.readFrame()
                         stopMonitoringReason = None  # if set, monitoring is stopped
                         if self.filter1RemoteChanged:
                             stopMonitoringReason = "Changed identifier mask"
                         if frame is None:  # nothing read
                             stopMonitoringReason = "Nothing received while monitoring"
                         elif frame == False:  # invalid frame
                             pass
                         else:
                             self.handleNewFrame(frame)
                         if stopMonitoringReason:
                             stopMonitoringTime = perf_counter()
                             if stopMonitoringNextAttempt is None \
                             or stopMonitoringTime>=stopMonitoringNextAttempt:
                                 self.stopMonitoring(stopMonitoringReason)
                                 stopMonitoringNextAttempt = stopMonitoringTime + self.stopMonitoringWait
                 except BaseException as e:
                     if type(e) in (MemoryError, InterruptedError,
                                    ConnectionAbortedError):
                         printT(e)
                     else:
                         raise e
                 # Update the sequence counter:
                 counter = counter + 1
         except serial.SerialException as e:
             printT(e)
             isFirstAttempt = False
         except:
             printT(format_exc())
             isFirstAttempt = False
    def send_head(self, headersOnly=False):
        headers = {}

        currentThread = threading.currentThread()
        currentThread.name = "%s %s" % (self.command, self.path)
        path = self.path.split("?", 1)[0]

        if path == "/frames.ws":
            self.webSocketClass = WebSocket_frames
            info = WebSocket.prepareHeaders(self)
            encoded = info["encoded"]
            response = info["response"]
            contentType = info["contentType"]
            headers = info["headers"]
        elif path == "/threads.txt":
            contentLines = [b"List of known running threads:"]
            currentThread = threading.currentThread()
            for thread in threading.enumerate():
                if thread is currentThread:
                    contentLines.append(
                        ("%5u: This request" % (thread.ident or 0, )).encode(
                            "utf_8", errors="replace"))
                else:
                    threadModuleName = str(thread.run.__func__.__module__)
                    threadClassName = type(thread).__name__
                    threadTargetClassName = None
                    if type(
                            thread
                    ) is threading.Thread:  # is not of a meaningful class
                        try:
                            threadTargetClass = type(thread._target.__self__)
                            threadModuleName = str(
                                threadTargetClass.__module__)
                            threadClassName = str(threadTargetClass.__name__)
                        except:
                            pass
                    contentLines.append(("%5u: %s.%s(%s, %s) [%s]" % (
                        thread.ident or 0,
                        threadModuleName,
                        threadClassName,
                        str(thread._args)[1:-1],
                        str(thread._kwargs)[1:-1],
                        thread.name,
                    )).encode("utf_8", errors="replace"))
            contentLines.append(
                b"\r\nOn Windows the CPU usage can be monitored for each thread using SysInternals Autoruns."
            )
            encoded = b"\r\n".join(contentLines)
            response = HTTPStatus.OK
            contentType = "text/plain"
        elif path[:5] == "/api/":
            contentType = "application/json"
            response = HTTPStatus.OK
            errorString = None
            data = {}
            canBus = self.server.thread.canBus
            try:
                postData = None
                if self.command == "POST":
                    postContentLength = None
                    try:
                        postContentLength = int(
                            self.headers.get("Content-Length"))
                    except TypeError:
                        pass
                    except ValueError:
                        pass
                    if postContentLength is None or postContentLength < 0:
                        raise StatusLengthRequired()
                    if postContentLength > self.maxPostLength:
                        raise StatusPayloadTooLarge()
                    postPayload = self.rfile.read(postContentLength)
                    postContentType = self.headers.get(
                        "Content-Type", "application/x-www-form-urlencoded")
                    if postContentType[:16] == "application/json":
                        try:
                            postData = json_loads(postPayload.decode("utf_8"))
                        except ValueError:
                            pass
                    elif postContentType[:
                                         33] == "application/x-www-form-urlencoded":
                        postData_ = None
                        try:
                            postData_ = parse_qs(postPayload.decode("ascii"),
                                                 keep_blank_values=True,
                                                 strict_parsing=True)
                        except ValueError:
                            pass
                        if postData_ is not None:
                            postData = {}
                            for pair in postData_.items():
                                # dict of lists => dict of {str and lists}
                                if pair[0][-2:] == "[]":
                                    # explicit list
                                    postData[pair[0][:-2]] = pair[1]
                                elif len(pair[1]) == 1:
                                    # single str
                                    postData[pair[0]] = pair[1][0]
                                else:
                                    # implicit list
                                    postData[pair[0]] = pair[1]
                        del postData_
                    if postData is None:
                        raise StatusBadRequest()
                # Note: each POST data can be str or list or int!
                if path == "/api/filter1/installByMask":
                    if postData is None:
                        raise StatusMethodNotAllowed()
                    try:
                        mask = self.postFieldToIdentifier(postData["mask"])
                        maskingResult = self.postFieldToIdentifier(
                            postData["maskingResult"])
                    except KeyError:
                        raise StatusBadRequest()
                    canBus.setFilter1Remote(mask, maskingResult)
                elif path == "/api/filter1/installByIds":
                    if postData is None:
                        raise StatusMethodNotAllowed()
                    try:
                        whitelist = self.postFieldToIdentifiersSet(
                            postData, "whitelist")
                    except KeyError:
                        raise StatusBadRequest()
                    if whitelist is None:
                        # only vanish whitelist
                        canBus.setFilter1Local(None)
                    else:
                        # set filter as well
                        canBus.setFilter1(whitelist)
                elif path == "/api/filter1/getInstalled":
                    maskInfo = canBus.getFilter1Remote()
                    data["mask"] = maskInfo[0]
                    data["maskingResult"] = maskInfo[1]
                    del maskInfo
                    whitelist = canBus.getFilter1Local()
                    whitelist = (whitelist
                                 is not None) and list(whitelist) or None
                    data["whitelist"] = whitelist
                    del whitelist
                elif path == "/api/filter1/reset":
                    if postData is None:
                        raise StatusMethodNotAllowed()
                    canBus.setFilter1(None)
                elif path == "/api/filter2/setExcluded":
                    if postData is None:
                        raise StatusMethodNotAllowed()
                    try:
                        blacklist = self.postFieldToIdentifiersSet(
                            postData, "blacklist")
                    except KeyError:
                        raise StatusBadRequest()
                    canBus.setFilter2(blacklist)
                elif path == "/api/filter2/getExcluded":
                    blacklist = canBus.getFilter2()
                    blacklist = (blacklist
                                 is not None) and list(blacklist) or None
                    data["blacklist"] = blacklist
                    del blacklist
                elif path == "/api/filter2/reset":
                    if postData is None:
                        raise StatusMethodNotAllowed()
                    canBus.setFilter2(None)
                elif path == "/api/setInactivityTimeout":
                    if postData is None:
                        raise StatusMethodNotAllowed()
                    try:
                        inactivityTimeout = float(postData["timeout"])
                    except TypeError:
                        raise StatusBadRequest()
                    except ValueError:
                        raise StatusBadRequest()
                    if inactivityTimeout <= 0.:
                        raise StatusBadRequest()
                    canBus.inactivityTimeout = inactivityTimeout
                elif path == "/api/getInactivityTimeout":
                    data["timeout"] = canBus.inactivityTimeout
                else:
                    raise StatusNotFound()
            except Status as e:
                response = e.status
                errorString = e.text
            except Exception as e:
                printT(format_exc())
                response = HTTPStatus.INTERNAL_SERVER_ERROR
                errorString = repr(e)
            del canBus
            if response == HTTPStatus.OK:
                data["status"] = 200
            else:
                data = {
                    "status": int(response),
                    "error": errorString,
                }
            encoded = json_dumps(data).encode("utf_8", errors="replace")
        elif path in self.staticFiles:
            diskFileExcept = None
            try:
                diskFile = open(self.staticFiles[path], "rb")
                encoded = diskFile.read()
                diskFile.close()
                response = HTTPStatus.OK
                ext_4 = path[-5:].lower()
                ext_3 = ext_4[-4:]
                ext_2 = ext_4[-3:]
                contentType = "application/octet-stream"
                if ext_3 == ".htm" or ext_4 == ".html":
                    contentType = "text/html; charset=utf-8"
                elif ext_2 == ".js":
                    contentType = "text/javascript"
                elif ext_3 == ".css":
                    contentType = "text/css"
            except FileNotFoundError as e:
                diskFileExcept = e
                response = HTTPStatus.NOT_FOUND
            except PermissionError as e:
                diskFileExcept = e
                response = HTTPStatus.FORBIDDEN
            except Exception as e:
                printT(format_exc())
                diskFileExcept = e
                response = HTTPStatus.INTERNAL_SERVER_ERROR
            if diskFileExcept:
                encoded = repr(diskFileExcept).encode("utf_8", "replace")
                contentType = "text/plain"
        else:
            encoded = b"Not found"
            response = HTTPStatus.NOT_FOUND
            contentType = "text/plain"

        f = BytesIO()
        f.write(encoded)
        f.seek(0)
        self.send_response(response)
        if contentType is not None:
            self.send_header("Content-type", contentType)
        if headersOnly:
            self.send_header("Content-Length", "0")
        else:
            self.send_header("Content-Length", str(len(encoded)))
        for header in headers.items():
            self.send_header(header[0], header[1])
        self.send_header("Access-Control-Allow-Origin", "*")
        self.end_headers()
        return f
 def applyDesiredBaudRate(self):
     if self.ser.baudrate != self.serialBaudRateDesired:
         printT("Switching baud rate (", self.scannerATBRD, ")...")
         self.write(b"ATBRT00\x0D")
         self.waitForPrompt("No prompt after ATBRT00!")
         self.write(self.scannerATBRD)
         self.ser.timeout = 2
         receivedO = False
         receivedOK = False
         unsupported = False
         newByte = None
         # Wait for "OK"
         for numByte in range(8):
             newByte = self.read()
             if len(newByte) == 0 or newByte == b'>':
                 raise Exception(
                     "No answer or invalid answer while applying the desired baudrate!"
                 )
             elif newByte == b'?':  # unsupported
                 printT(
                     "This chip version does not support changing the serial link bitrate, or wrong argument in "
                     + self.scannerATBRD.decode("ascii") + ".")
                 self.ser.timeout = 0.5
                 unsupported = True
                 self.waitForPrompt("No prompt after unsupported ATBRD!")
                 break
             elif newByte == b'O':
                 receivedO = True
             elif newByte == b'K':
                 if receivedO:
                     receivedOK = True
                     break
             else:
                 receivedO = False
         if unsupported:
             return False
         elif not receivedOK:
             raise Exception(
                 "Invalid answer while applying the desired baudrate!")
         # Switch baudrate
         self.ser.baudrate = self.serialBaudRateDesired
         # Wait for "ELM327" (without order checking)
         unsupported = False
         receivedStepsATI = {
             b'E': False,
             b'L': False,
             b'M': False,
             b'3': False,
             b'2': False,
             b'7': False,
         }
         receivedATI = False
         for numByte in range(8):
             newByte = self.read()
             if len(newByte) == 0:
                 unsupported = True
                 break
             elif newByte == b'7':
                 receivedStepsATI[newByte] = True
                 for byte in receivedStepsATI.keys():
                     if not receivedStepsATI[byte]:
                         unsupported = True
                 if not unsupported:
                     receivedATI = True
                 else:
                     self.waitForPrompt()
                     self.ser.timeout = 0.5
                 break
             elif newByte in receivedStepsATI:
                 receivedStepsATI[newByte] = True
             else:
                 for byte in receivedStepsATI.keys():
                     receivedStepsATI[byte] = False
         # Wait for <CR>
         receivedCR = False
         if receivedATI and not unsupported:
             for numByte in range(8):
                 newByte = self.read()
                 if newByte == b"\x0D":
                     receivedCR = True
                     break
         if (not receivedATI) or (not receivedCR) or unsupported:
             printT(
                 "The communication did not work after applying the desired baudrate!"
             )
             self.ser.baudrate = self.serialBaudRateInitial
             self.waitForPrompt()
             self.ser.timeout = 0.5
             return False
         # Send confirmation
         self.write(b"\x0D")
         self.ser.timeout = 0.5
         # Wait for prompt and reset waiting delay
         self.waitForPrompt("No prompt after setting the desired baudrate!")
         self.write(b"ATBRT0F\x0D")
         self.waitForPrompt("No prompt after ATBRT0F!")
     return True
    def readFrame(self, maxBytesPerLine=128):
        """
		Read a line that should contain a frame (during ATMA operation)
		Return a CANFrame on success
		Return False if invalid frame
		Return None if nothing read
		Throws a MemoryError if received "BUFFER FULL"
		Throws a InterruptedError if received "STOPPED"
		Throws a ConnectionAbortedError if prompt received in other cases
		Throws a ValueError if invalid bytes received
		Throws a ChildProcessError if unexpected reboot
		Bug: When ATMA is interrupted, frames can be truncated but still seem valid (especially 29-bit-identifier frames decoded as 11-bit ones).
		"""
        canFrame = None
        # Read an incoming line
        if self.serialLocalBufferAccuATMA:
            # short I/O timeout (several attempts until effective timeout)
            self.ser.timeout = self.serialLocalBufferWaitTimeATMA
        else:
            # I/O timeout = effective timeout
            self.ser.timeout = self.inactivityTimeout
        currentLine = bytearray()
        timedOutEmpty = False
        try:
            for numByte in range(maxBytesPerLine):
                if self.serialLocalBufferAccuATMA:
                    newByte = self.read(
                        minReadCount=self.serialLocalBufferMinFillATMA,
                        retryDelayIfEmpty=self.inactivityTimeout)
                else:
                    newByte = self.read()
                if len(newByte) == 0:
                    if len(currentLine) == 0:
                        timedOutEmpty = True
                    #printT( "readFrame() - len( newByte )==0" ) # debug
                    break
                newByteInt = newByte[0]
                if newByte == b'\x0D':
                    break  # end of line
                elif newByte == b'>':
                    raise ConnectionAbortedError(
                        "ELM327: Unexpected prompt during ATMA reading")
                elif newByte == b'\x00' or newByte == b' ':
                    pass  # ignore spaces and nulls
                elif newByteInt > 0x00 and newByteInt < 0x80:
                    currentLine.extend(newByte)
                else:
                    raise ValueError(
                        "ELM327: Received an illegal byte during ATMA processing!"
                    )
            else:  # exceeded max length
                raise ValueError(
                    "ELM327: Received too many bytes in a single line during ATMA processing!"
                )
            # Decode the line
            if not timedOutEmpty:
                # Note: spaces are removed, messages are spaceless.
                # Note: receiving a 29-bit frame on an 11-bit CAN bus & vice-versa are not documented but seem to be accepted, so they are handled as they come.
                if len(currentLine) == 0:
                    canFrame = False  # nothing on the line (like corrupted frame)
                    printT("ELM327: Received an empty ATMA line")
                elif b"ELM327" in currentLine:
                    raise ChildProcessError(
                        "ELM327: Detected a reboot during ATMA processing!")
                elif b"BUFFERFULL" in currentLine:
                    raise MemoryError(
                        "ELM327: Received a BUFFER FULL alert during ATMA processing!"
                    )
                elif b"STOPPED" in currentLine:
                    raise InterruptedError(
                        "ELM327: Received a STOPPED alert during ATMA processing!"
                    )
                elif b"<RXERROR" in currentLine:
                    canFrame = False  # corrupted frame (ignored frame)
                elif b"<DATAERROR" in currentLine:
                    # Warning: incorrect CRCs may produce "DATA ERROR" but no way to distinguish them!
                    pass
                if canFrame is None:  # frame not corrupted
                    canFrameIsRTR = False
                    if b"RTR" in currentLine:
                        canFrameIsRTR = True
                    # Try parsing the frame with both identifier lengths
                    for canFrameIsExtended in self.allowMixedIdentifiers and (
                            self.canBusIsExtended, not self.canBusIsExtended
                    ) or (self.canBusIsExtended, ):
                        canFrameRegex = canFrameIsExtended and self.canFrameRegex29 or self.canFrameRegex11
                        canFramePieces = canFrameRegex.match(currentLine)
                        if canFramePieces:
                            canFrameData = None
                            try:
                                if canFrameIsRTR:
                                    canFrameData = b''
                                else:
                                    canFrameData = bytes.fromhex(
                                        canFramePieces.group(4).decode(
                                            "ascii", "replace"))
                                # Note: canFrameData must have a maximum of 8 bytes, otherwise readings are incorrect.
                            except ValueError:
                                # The 11-bit identifier has an odd number of digits.
                                # The 29-bit identifier has an even number of digits.
                                # This exception is raised for wrong identifier sizes + for some incomplete lines.
                                continue  # incomplete byte in frame (ignored frame)
                            canFrameIdentifier = int(canFramePieces.group(1),
                                                     base=16)
                            if canFrameIdentifier > (canFrameIsExtended
                                                     and 0x1FFFFFFF or 0x7FF):
                                continue  # invalid identifier (over maximum)
                            elif canFrameIdentifier < 0:
                                continue  # invalid identifier (negative)
                            canFrameDLC = int(canFramePieces.group(2), base=16)
                            if not canFrameIsRTR:
                                canFrameLengthActual = len(canFrameData)
                                if canFrameLengthActual > 8:
                                    continue  # impossible frame containing more than 8 bytes (ignored frame)
                                canFrameLength = (
                                    canFrameDLC <= 8
                                ) and canFrameDLC or 8  # DLC, truncated to 8 bytes (max transmitted length)
                                if canFrameLengthActual < canFrameLength:
                                    continue  # DLC comparison: missing bytes in frame (ignored frame)
                                if canFrameLengthActual > canFrameLength:
                                    continue  # impossible: data length longer than DLC
                            canFrame = CANFrame(
                                identifier=canFrameIdentifier,
                                isExtended=canFrameIsExtended,
                                isRTR=canFrameIsRTR,
                                DLC=canFrameDLC,
                                data=canFrameData,
                            )
                            # canFrame = False # debug: pretend malformed frame instead of processing it
                            break
                    else:
                        canFrame = False
                        printT("ELM327: Cannot decode ATMA line: %s" %
                               (str(currentLine), ))
        finally:
            exceptionType = exc_info()[0]
            if exceptionType is None:
                # Will continue: ready for next call of this function
                self.straightInvalidFramesCount = 0
                if timedOutEmpty:
                    # Will recover (nothing received): restore timeout
                    self.ser.timeout = 0.5
            elif exceptionType == MemoryError or exceptionType == InterruptedError:
                # Will recover (no problem): restore timeout + wait for prompt
                promptTimeoutMessage = None
                if exceptionType == MemoryError:
                    promptTimeoutMessage = "ELM327: No prompt after BUFFER FULL alert during ATMA processing!"
                elif exceptionType == InterruptedError:
                    promptTimeoutMessage = "ELM327: No prompt after STOPPED alert during ATMA processing!"
                self.ser.timeout = 0.5
                # The prompt may show up anywhere, wait for it when not included in currentLine:
                if b'>' not in currentLine:
                    self.waitForPrompt(promptTimeoutMessage,
                                       64,
                                       noSilentTest=True)
            elif exceptionType == ValueError:
                # Will recover (invalid bytes received): restore timeout + wait for prompt
                if self.straightInvalidFramesCount > self.maxStraightInvalidFrames:
                    self.stopMonitoring(
                        "Received more than %u invalid CAN frames in a row" %
                        (self.straightInvalidFramesCount))
                    self.straightInvalidFramesCount = 0
                self.ser.timeout = 0.5
                self.waitForPrompt(
                    "ELM327: No prompt received after stopping ATMA (ValueError)",
                    16777215)
                self.straightInvalidFramesCount += 1
            elif exceptionType == ConnectionAbortedError:
                # Will recover (prompt received): restore timeout
                self.ser.timeout = 0.5
            else:
                # Impossible recover: restore timeout just in case
                self.ser.timeout = 0.5
        return canFrame
 def handleOBDResult(self, resultLine):
     resultLineBytes = None
     try:
         resultLineBytes = bytes.fromhex(resultLine)
     except:
         if resultLine == "STOPPED":
             # The OBD interface was not ready, let it cool down for a while...
             printT("Received a STOPPED alert")
             self.write(b"0100\x0D"
                        )  # retry initiating communication to the OBD bus
             self.ser.timeout = max(
                 MAX_OBD_NEGOCIATION_TIME,
                 self.serialTimeoutWhileOBD)  # very conservative
             if not self.waitForPrompt():
                 printT("Prompt not received after STOPPED!"
                        )  # no exception: case handled naturally
             self.ser.timeout = self.serialTimeoutWhileOBD
             return
         else:
             if self.obdShowIncorrectResult:
                 printT("Incorrect OBD result (PID " +
                        ("0x%.2X" % self.lastPid) + "): " + resultLine)
     if resultLineBytes is not None:
         resultType = resultLineBytes[0:1]
         if resultType == b"\x41":
             resultPid = resultLineBytes[1]
             resultData = resultLineBytes[2:(
                 2 + self.pidResponseLengths[resultPid])]
             if not self.pidResponseReturnByteArrays[resultPid]:
                 resultData = int.from_bytes(resultData, 'big')
             callback = self.pidResponseCallbacks.get(resultPid)
             if callback is not None:
                 try:
                     callback(resultPid, resultData)
                 except:
                     printT(format_exc())
             self.lastResponseDatas[
                 resultPid] = resultData  # memorizing to make the value available from other callbacks
         elif resultType == b"\x7F":
             # The vehicle reported something. If unsupported then fix the sequence.
             # Unsupported PIDs may report this or time out.
             printT("The ECU reported a 7F code for PID " +
                    ("0x%.2X" % self.lastPid) + "): ")
         else:
             printT("Unexpected OBD result type in: " + resultLine)
 def write(self, data):
     if self.serialShowSentBytes:
         printT("    PC :", data.decode("ascii", "replace"))
     return self.ser.write(data)
 def write(self, data):
     if self.serialShowSentBytes:
         printT("    PC :", data.decode("ascii", "replace"))
     OBDRelayELM327Thread.lastShownReceived = False
     return self.ser.write(data)
예제 #24
0
    def readFrame(self):  # blocking, non-thread-safe
        fragmentedOpcode = self.fragmentedOpcode

        readBytes = self._readBytes(2)
        flagFin = (readBytes[0] & 0b10000000) != 0
        if flagFin:
            self.fragmentedOpcode = None
        #flagRSV1 = ( readBytes[0]&0b01000000 )!=0
        #flagRSV2 = ( readBytes[0]&0b00100000 )!=0
        #flagRSV3 = ( readBytes[0]&0b00010000 )!=0
        headerOpcode = readBytes[0] & 0b00001111
        messageOpcode = headerOpcode
        fragmented = (fragmentedOpcode is not None)
        isDataFrame = (headerOpcode & 0b1000) == 0
        if headerOpcode != WebSocket.OPCODE_FRAGMENT_CONTINUATION:
            if fragmentedOpcode is not None:
                raise WebSocketBadRequest(
                    "WebSocket new message frame received with unfinished fragmented message"
                )
        if headerOpcode == WebSocket.OPCODE_FRAGMENT_CONTINUATION:
            if (not fragmented) or (self.fragments is None):
                raise WebSocketBadRequest(
                    "WebSocket fragment continuation frame received with no starting frame"
                )
            messageOpcode = fragmentedOpcode
        elif headerOpcode == WebSocket.OPCODE_TEXT:
            if not self.allowFramesText:
                raise WebSocketBadRequest(
                    "Text WebSocket frames are not allowed")
        elif headerOpcode == WebSocket.OPCODE_BINARY:
            if not self.allowFramesBinary:
                raise WebSocketBadRequest(
                    "Binary WebSocket frames are not allowed")
        elif isDataFrame:
            raise WebSocketBadRequest(
                "Unknown WebSocket data frames are not supported")
        if isDataFrame:
            if not flagFin:
                # Setup a new unfinished message:
                self.fragmentedOpcode = headerOpcode
                self.fragments = None  # start a new fresh list of fragments
                self.fragmentsTotalLen = 0
                fragmented = True
        flagMask = (readBytes[1] & 0b10000000) != 0
        if not flagMask:
            raise WebSocketBadRequest(
                "The Mask bit is mandatory but unset in an incoming WebSocket frame"
            )
        headerPayloadLen = readBytes[1] & 0b01111111
        if not isDataFrame:
            if headerPayloadLen >= 126 or not flagFin:
                raise WebSocketBadRequest(
                    "Fragmented WebSocket control frame received")

        readBytes = None
        if headerPayloadLen == 126:
            readBytes = self._readBytes(2)
            headerPayloadLen = int.from_bytes(readBytes, 'big', signed=False)
        elif headerPayloadLen == 127:
            readBytes = self._readBytes(8)
            headerPayloadLen = int.from_bytes(readBytes, 'big', signed=False)
        if self.maxReceivedLen and (headerPayloadLen + self.fragmentsTotalLen
                                    ) > self.maxReceivedLen:
            raise WebSocketBadRequest(
                "Received a WebSocket message exceeding the maximum allowed message length"
            )

        readBytes = None
        headerMaskingKey = None
        if flagMask:
            readBytes = self._readBytes(4)
            headerMaskingKey = readBytes

        readBytes = None
        if headerMaskingKey is not None:
            readBytes = self._readBytes(headerPayloadLen)
            data = bytearray(readBytes)
            for byteInfo in enumerate(data):
                k = byteInfo[0]
                data[k] = byteInfo[1] ^ headerMaskingKey[k % 4]

        if fragmented:
            if self.fragments is None:
                self.fragments = []
                self.fragmentsTotalLen = 0
            self.fragments.append(data)
            self.fragmentsTotalLen += headerPayloadLen

        if isDataFrame:
            if flagFin:
                if fragmented:
                    data = bytearray.join(bytearray(), self.fragments)
                    self.fragments = None
                    self.fragmentsTotalLen = 0
                try:
                    self.handleMessage(data)
                except:
                    printT(format_exc())
        elif headerOpcode == WebSocket.OPCODE_PING:
            self.sendPong(data)
        elif headerOpcode == WebSocket.OPCODE_CLOSE:
            raise StopIteration("WebSocket graceful connection termination")
 def setParameters(self, parameters):
     with self.parametersLock:
         pcapOutputFile = parameters["pcapOutputFile"]
         if pcapOutputFile != self.logOutputDataFileName:
             self.logOutputDataFileName = pcapOutputFile
             # Close any open file:
             if self.logOutputDataFile is not None:
                 try:
                     self.logOutputDataFile.close()
                 except:
                     pass
                 self.logOutputDataFile = None
             # Attempt to open file:
             if self.logOutputDataFileName is not None:
                 try:
                     self.logOutputDataFile = open(
                         self.logOutputDataFileName, mode="wb")
                     self.logOutputDataFile.write(pcap_hdr_t)
                 except Exception as e:
                     self.logOutputDataFile = None
                     printT("Unable to open the pcapOutputFile file:", e)
             # Cleanup for clean start:
         if socket:
             canEthUdpEnabled = parameters.get("canEthUdpEnabled", False)
             canEthUdpIpVersion = parameters.get("canEthUdpIpVersion", 4)
             if canEthUdpIpVersion == 6:
                 canEthUdpIpVersion = socket.AF_INET6
             elif canEthUdpIpVersion == 4:
                 canEthUdpIpVersion = socket.AF_INET
             canEthUdpAddrSrc = parameters.get("canEthUdpAddrSrc", None)
             canEthUdpPortSrc = parameters.get("canEthUdpPortSrc", None)
             canEthUdpAddrDst = parameters.get(
                 "canEthUdpAddrDst",
                 canEthUdpIpVersion == socket.AF_INET6 and "::1"
                 or "127.0.0.1")
             canEthUdpPortDst = parameters.get("canEthUdpPortDst", 11898)
             netOutputConfig = (
                 canEthUdpEnabled,
                 canEthUdpIpVersion,
                 canEthUdpAddrSrc,
                 canEthUdpPortSrc,
                 canEthUdpAddrDst,
                 canEthUdpPortDst,
             )
             if not self.netOutputSocket or netOutputConfig != self.netOutputConfig:
                 if self.netOutputSocket:
                     self.netOutputSocket.close()
                     self.netOutputSocket = None
                 if canEthUdpEnabled:
                     try:
                         netOutputSocket = None
                         netOutputSocket = socket.socket(
                             canEthUdpIpVersion, socket.SOCK_DGRAM)
                         netOutputSocket.setsockopt(socket.SOL_SOCKET,
                                                    socket.SO_REUSEADDR, 1)
                         if canEthUdpAddrSrc and canEthUdpPortSrc:
                             netOutputSocket.bind(
                                 (canEthUdpAddrSrc, canEthUdpPortSrc))
                         self.canEthUdpDst = (canEthUdpAddrDst,
                                              canEthUdpPortDst)
                         self.netOutputSocket = netOutputSocket
                     except:
                         printT(format_exc())
                         try:
                             netOutputSocket.close()
                         except:
                             pass
                 self.netOutputConfig = netOutputConfig
 def run(self):
     self.reloadParameters()
     self.reloadSequence()
     self.lastPid = -1
     self.ser = serial.Serial(port=None,
                              bytesize=serial.EIGHTBITS,
                              parity=serial.PARITY_NONE,
                              stopbits=serial.STOPBITS_ONE,
                              xonxoff=False,
                              rtscts=False,
                              write_timeout=None,
                              dsrdtr=False,
                              inter_byte_timeout=None)
     self.ser.exclusive = True  # silently fails if "exclusive" does not exist
     isFirstAttempt = True
     while True:
         setConsoleColorWindows("4F")
         setConsoleTitle("ELM327: Disconnected")
         if self.ser.is_open:
             self.ser.close()
         if not isFirstAttempt:
             sleep(1)
         try:
             # Startup
             if not self.ser.is_open:
                 # Configure and open the serial port
                 self.reloadParameters()
                 printT("New connection to " + self.serialPort + "...")
                 self.ser.port = self.serialPort
                 self.ser.open()
                 # Communication attempt
                 self.ser.baudrate = self.serialBaudRateInitial
                 self.ser.timeout = 0.5
                 printT("Contacting the ELM327 chip...")
                 connectionConfirmed = False
                 while not connectionConfirmed:
                     self.write(b"ATH\x0D")  # command that does nothing
                     connectionConfirmed = self.waitForPrompt()
                     # Alternate between initial and desired baudrates
                     if not connectionConfirmed:
                         self.reloadParameters()
                         if self.ser.baudrate == self.serialBaudRateInitial:
                             self.ser.baudrate = self.serialBaudRateDesired
                         else:
                             self.ser.baudrate = self.serialBaudRateInitial
                 printT("Connection works at " + str(self.ser.baudrate) +
                        " b/s")
                 # Reset
                 if self.ser.baudrate == self.serialBaudRateDesired:
                     # Note: on my Icar01 ELM327 V1.5, ATWS resets the baud rate. This is a workaround.
                     self.write(b"ATD\x0D")
                 else:
                     self.write(b"ATWS\x0D")
                 self.ser.timeout = 5
                 self.waitForPrompt("No prompt after ATWS or ATD!"
                                    )  # resets the timeout to 0.5
                 # Apply parameters (except new baudrate)
                 self.write(b"ATE0\x0D")  # no echo
                 self.waitForPrompt("No prompt after ATE0!")
                 self.write(b"ATL0\x0D")  # no <LF> after <CR>
                 self.waitForPrompt("No prompt after ATL0!")
                 self.write(b"ATS0\x0D")  # no spaces
                 self.waitForPrompt("No prompt after ATS0!")
                 self.write(b"ATCAF1\x0D")  # display only the OBD payload
                 self.waitForPrompt("No prompt after ATCAF1!")
                 self.write(b"ATSP" + self.scannerATSP +
                            b"\x0D")  # selection of the OBD bus
                 self.waitForPrompt("No prompt after ATSP!")
                 self.write(b"0100\x0D"
                            )  # try initiating communication to the OBD bus
                 self.ser.timeout = max(
                     MAX_OBD_NEGOCIATION_TIME,
                     self.serialTimeoutWhileOBD)  # very conservative
                 self.waitForPrompt(
                     "No prompt after initial 0100! Is the ignition on?")
                 self.ser.timeout = 5
                 # Apply the desired baudrate
                 if not self.applyDesiredBaudRate():
                     if self.serialBaudRateDesiredForce:
                         raise Exception(
                             "The desired baud rate could not be selected!")
                 printT("Connection established at " +
                        str(self.ser.baudrate) + " b/s")
                 setConsoleColorWindows("2F")
                 setConsoleTitle("ELM327: " + str(self.ser.baudrate) +
                                 " b/s")
             # Read OBD information until thread exit
             straightErrorCount = 0
             counter = 0  # counts the number of executed sequences
             self.ser.timeout = max(MAX_OBD_NEGOCIATION_TIME,
                                    self.serialTimeoutWhileOBD
                                    )  # very conservative (1st request)
             isFirstRequest = True
             isReadingCan29 = False
             isReadingCan11 = False
             while True:
                 # Send and handle requests from the configured sequence:
                 for pid in self.sequence:
                     self.ser.reset_input_buffer()
                     if isinstance(pid, self.CanFrameRequest):
                         # Reading of a CAN frame identifier:
                         req = pid
                         if req.identifierBits == 29:
                             if not isReadingCan29:
                                 self.obdCanFramePrep29(self)
                                 isReadingCan29 = True
                             resultData = self.obdCanFrameReq29(self, req)
                         else:
                             if not isReadingCan11:
                                 self.obdCanFramePrep11(self)
                                 isReadingCan11 = True
                             resultData = self.obdCanFrameReq11(self, req)
                         if resultData:
                             callback = self.pidResponseCallbacks.get(req)
                             if callback is not None:
                                 try:
                                     callback(req.identifier, resultData)
                                 except:
                                     printT(format_exc())
                                 callback = None
                         req = None
                         resultData = None
                     else:
                         # Reading of an OBD PID:
                         if isReadingCan29 or isReadingCan11:
                             if self.obdCanFrameCleanup29 == self.obdCanFrameCleanup11:
                                 self.obdCanFrameCleanup29(self)
                             else:
                                 if isReadingCan29:
                                     self.obdCanFrameCleanup29(self)
                                 if isReadingCan11:
                                     self.obdCanFrameCleanup11(self)
                             isReadingCan29 = False
                             isReadingCan11 = False
                         self.write(self.pidToCommand[pid])
                         self.lastPid = pid
                         line = self.readAnwer()
                         if line == False:
                             straightErrorCount = straightErrorCount + 1
                         else:
                             straightErrorCount = 0
                             self.handleOBDResult(line)
                     if isFirstRequest:
                         isFirstRequest = False
                         self.ser.timeout = self.serialTimeoutWhileOBD
                 # Live-refresh of configuration:
                 if counter % 2 == 0:
                     self.reloadSequence()
                 else:
                     self.reloadParameters()
                 # Handle erroring communication:
                 if straightErrorCount >= len(self.sequence):
                     raise Exception(
                         "Unable to communicate on the serial port anymore!"
                     )
                 # Update the sequence counter:
                 counter = counter + 1
         except serial.SerialException as e:
             printT(e)
             isFirstAttempt = False
         except:
             printT(format_exc())
             isFirstAttempt = False
예제 #27
0
 def callback(pid, data):
     global obd
     obd.setCurrentOutputData(b"throttlePosition",
                              (data - min) * coef)  # no unit
     printT("ACCELERATOR =", data)
 def reloadSequence(self):
     if execfileIfNeeded(sequenceFile, {"canBus": self},
                         self.sequenceFileInfo):
         printT("The CAN sequence has been reloaded.")
예제 #29
0
 def handleMessage(self, data):
     printT("Received:", data)
예제 #30
0
del httpd

# Reload the parameters
from utility import printT


def reloadParameters():
    if execfileIfNeeded(parametersFile, parameters, parametersFileInfo):
        for httpBinding in parameters["httpBindings"]:
            for httpd in httpServers:
                httpdParameters = httpd.getParameters()
                # Reload HTTP parameters for the HTTP server matching address & port:
                if httpBinding["address"] == httpdParameters[
                        "ipAddress"] and httpBinding[
                            "port"] == httpdParameters["tcpPort"]:
                    break
        printT("[main.py] Parameters have been reloaded.")


# Main work in an endless loop
try:
    from time import sleep
    while True:
        sleep(3)
        reloadParameters()
except KeyboardInterrupt:
    printT("Exiting...")
    canExporter.terminate()
except BaseException as e:
    printT(e)