def connect(self, connectParams): if 'port' in connectParams: self.comPort = connectParams['port'] else: output = "'port' entry required in connection parameters" log.warning(output) raise ConnectionError(output) if 'baudrate' in connectParams: baudrate = connectParams['baudrate'] else: baudrate = self._BAUDRATE if 'errorQueue' in connectParams: self._ERRORQUEUE = connectParams['errorQueue'] try: self.pyserialHandler = serial.Serial(self.comPort, baudrate=baudrate) self.pyserialHandler.setRTS(False) self.pyserialHandler.setDTR(True) except serial.serialutil.SerialException as err: output = "could not open " + self.comPort + ", reason: " + str(err) log.warning(output) raise ConnectionError(output) else: log.info("opened port " + self.comPort) self._restart() self.connected = True self.connectcallback(self.connected) self.name = '{0}_HDLC'.format(self.comPort) self.start() return self
def _ackIfNeeded(self, cmdId, isResponse): if ((not isResponse) and (self.shouldAck)): # send normal ACK (with RC=0) self._sendInternal(cmdId, True, [0]) return True elif cmdId == self.HELLO_IDS['manager_hello'] and self.isConnect: raise ConnectionError("Unexpected manager_hello") return False
def send(self, message): if not self.connected: output = 'not connected' log.error(output) raise ConnectionError(output) # calculate fcs packetToSend = {} packetToSend['payload'] = message packetToSend['fcs'] = self.crc.calculate(packetToSend['payload']) packetToSend['valid'] = True # assemble packet packetBytes = packetToSend['payload'] + packetToSend['fcs'] # add HDLC escape characters index = 0 while index < len(packetBytes): if packetBytes[index] == self._HDLC_FLAG or packetBytes[ index] == self._HDLC_ESCAPE: packetBytes.insert(index, self._HDLC_ESCAPE) index += 1 packetBytes[index] = packetBytes[index] ^ self._HDLC_MASK index += 1 # add HDLC flags packetBytes.insert(0, self._HDLC_FLAG) packetBytes.insert(len(packetBytes), self._HDLC_FLAG) # log if log.isEnabledFor(logging.DEBUG): output = [] output += ['\npacketToSend:'] output += self._formatFrame(packetToSend) log.debug('\n'.join(output)) # reconstitute byteArray byteArray = ''.join([chr(byte) for byte in packetBytes]) # send over serial port try: with self.busySending: numWritten = self.pyserialHandler.write(byteArray) except IOError, e: raise ConnectionError(str(e))
def send(self, commandArray, fields): if not self.isConnected: output = "not connected" log.error(output) raise ConnectionError(output) # serialize the fields cmdId, serializedFields = self.api_def.serialize(commandArray, fields) return self._sendInternal(cmdId, False, serializedFields)
def connect(self,comPort,baudrate=_BAUDRATE): self.comPort = comPort try: self.pyserialHandler = serial.Serial(self.comPort,baudrate=baudrate) self.pyserialHandler.setRTS(False) self.pyserialHandler.setDTR(True) except serial.serialutil.SerialException as err: output = "could not open {0}@{1}baud, reason: {2}".format(self.comPort,baudrate,err) log.warning(output) raise ConnectionError(output) else: log.info("opened port {0}@{1}baud".format(self.comPort,baudrate)) self._restart() self.connected = True self.connectcallback(self.connected) self.name = '{0}_HDLC'.format(self.comPort) self.start() return self
def _hdlcRxCb(self, frameRx): ''' \brief called by HDLC when it's done receiving a complete frame. \note The frame received does not contain any of the HDLC-specific flags and espace characters \param frameRx The received frame, represented as a byteArray ''' if len(frameRx) < 3: output = "received packet too short" log.error(output) raise ConnectionError(output) cmdId, length, isResponse, packetId, payload = self._parseRxHeader( frameRx) # log if log.isEnabledFor(logging.DEBUG): log.debug( "cmdId={0} length={1} isResponse={2} packetId={3} payload={4}". format(cmdId, length, isResponse, packetId, payload)) if isResponse: log.debug( "<--------- pcToMote ACK ({0}) after {1:.3f} --------". format(packetId, time.time() - self.tsDataSent)) else: log.debug("<--------- moteToPc DATA ({0}) ----------".format( packetId)) self.tsDataReceived = time.time() # check packetId (wasValidPacketId, isRepeatId, updateRxPacketId) = self.isValidPacketId(cmdId, isResponse, packetId) # update RxPacketId self.paramLock.acquire() if isResponse == False and (not isRepeatId) and updateRxPacketId: self.RxPacketId = packetId self.paramLock.release() # send ACK if necessary try: ackSent = self._ackIfNeeded(cmdId, isResponse) except ConnectionError as ex: log.error("putting disconnect notification {0}".format(ex)) self.putDisconnectNotification(str(ex)) raise ex # log if log.isEnabledFor(logging.DEBUG): if ackSent: log.debug( "---------- moteToPc ACK ({0}) after {1:.3f} ------->". format(self.RxPacketId, time.time() - self.tsDataReceived)) log.debug("ack sent") else: log.debug("no ack needed") if isResponse or self.isHelloResponse(cmdId): # deserialize received packet try: nameArray, fields = self.api_def.deserialize( ApiDefinition.ApiDefinition.COMMAND, cmdId, payload) except Exception as err: self.responseBuf = err else: # put received packet in local response buffer self.responseBuf = fields # release semaphore if self.waitForResp: self.waitForResp = False self.waitForRespEvent.set() else: output = "unexpected response" log.error(output) raise ConnectionError(output) else: if not isRepeatId: # deserialize received packet nameArray, fields = self.api_def.deserialize( ApiDefinition.ApiDefinition.NOTIFICATION, cmdId, payload) # put received packet in notification buffer self.putNotification((nameArray, fields)) # raise error if packet id is wrong if not wasValidPacketId: output = "wrong packetId" log.error(output) raise ConnectionError(output)
def _sendInternal(self, cmdId, isResponse, serializedFields): try: if not isResponse: self.requestSendLock.acquire() # build packet to send packet = [] packet += self._buildTxHeader(cmdId, isResponse, serializedFields) packet += serializedFields retry = 0 # Prepare for reliable send if not isResponse: self.waitForResp = True self.waitForRespEvent.clear() while True: if log.isEnabledFor(logging.DEBUG): log.debug( "_sendInternal cmdId={0} retry={1} isResponse={2} serializedFields={3}" .format(cmdId, retry, isResponse, serializedFields)) if not isResponse: if log.isEnabledFor(logging.DEBUG): log.debug("---------- pcToMote DATA ({0}) ---------->". format(self.TxPacketId)) self.tsDataSent = time.time() # send packet through HDLC module with self.hdlcLock: if not self.hdlc: output = "no HDLC module, did I just disconnect?" log.error(output) raise ConnectionError(output) self.hdlc.send(packet) if isResponse: return None # wait for response. semaphore released by _hdlcRxCb() if self.waitForRespEvent.wait(RX_TIMEOUT): break log.info("retry {0}".format(retry)) # Timeout error if retry >= MAX_NUM_RETRY: output = "retried {0} times, max allowed is {1}".format( retry, MAX_NUM_RETRY) log.error(output) raise ConnectionError(output) retry = retry + 1 if isinstance(self.responseBuf, Exception): log.error("responseBuf contains exception {0}".format( self.responseBuf)) raise self.responseBuf if ( (ApiDefinition.ApiDefinition.RC in self.responseBuf) and ( self.responseBuf[ApiDefinition.ApiDefinition.RC]!= \ ApiDefinition.ApiDefinition.RC_OK ) ): temp_name = self.api_def.idToName( ApiDefinition.ApiDefinition.COMMAND, cmdId) temp_rc = self.responseBuf[ApiDefinition.ApiDefinition.RC] temp_desc = '({0})\n{1}'.format( self.api_def.fieldValueToDesc( ApiDefinition.ApiDefinition.COMMAND, [temp_name], ApiDefinition.ApiDefinition.RC, temp_rc), self.api_def.rcToDescription( temp_rc, [temp_name], ), ) if temp_rc not in [ 11, 18 ]: # rc==11:RC_NOT_FOUND, rc==18:RC_END_OF_LIST log.warning("received RC={0} for command {1}:\n{2}".format( temp_rc, temp_name, temp_desc)) raise APIError(temp_name, temp_rc, temp_desc) # return packet received return self.responseBuf finally: if not isResponse: self.requestSendLock.release()
def send(self, commandArray, fields): if not self.isConnected: output = "not connected" log.error(output) raise ConnectionError(output) log.debug('send commandArray={0} fields={1}'.format( commandArray, fields)) assert len(commandArray) == 1 or len(commandArray) == 2 commandName = commandArray[0] if len(commandArray) == 2: subCommandName = commandArray[1] else: subCommandName = None apiCommand = None for c in IpMoteDefinition.IpMoteDefinition.commands: if c['name'] == commandName: if ('subCommands' in c) and subCommandName: for sc in c['subCommands']: if sc['name'] == subCommandName: apiCommand = sc break else: apiCommand = c break assert apiCommand #===== send command to manager # create params on the fly params = [] varSizeParamLen = None for [paramname, type, length, _] in apiCommand['request']: thisparam = {} thisparam['name'] = paramname thisparam['C'] = _generateCParam(type, length, fields[paramname]) params += [thisparam] if length == None: varSizeParamLen = len(fields[paramname]) # create (dummy) buffer to hold reply reply = create_string_buffer(self.MAX_FRAME_LENGTH) # format params cparams = [] cparams += [p['C'] for p in params] if varSizeParamLen != None: cparams += [varSizeParamLen] cparams += [byref(reply)] # log log.debug('C parameters: {0}'.format(pp.pformat(params))) # issue request if subCommandName: funcName = 'dn_ipmt_{0}_{1}'.format(commandName, subCommandName) else: funcName = 'dn_ipmt_{0}'.format(commandName) rcApi = getattr(self.smipmt, funcName)(*cparams) assert rcApi == DN_ERR_NONE #===== wait for response if self.waitForResponse.wait(self.RESPONSE_TIMEOUT): self.waitForResponse.clear() else: self.smipmt.dn_ipmt_cancelTx() raise ConnectionError("timeout response") #===== convert C response to Python dictionary # create returned fields on the fly theseFields = [] theseFields += [('RC', _getResponseType(INT, 1))] for [name, type, length, validName] in apiCommand['response']['FIELDS']: theseFields += [(name, _getResponseType(type, length))] # Structure to hold the response class reply_t(Structure): _fields_ = theseFields # cast response to that structure notif = cast(reply, POINTER(reply_t)) # convert structure to dictionary returnVal = {} for (f, _) in theseFields: rawVal = getattr(notif.contents, f) if isinstance(rawVal, int): returnVal[f] = rawVal elif isinstance(rawVal, long): returnVal[f] = int(rawVal) else: returnVal[f] = [int(b) for b in rawVal] return returnVal
def connect(self, connectParams): log.debug("connect connectParams={0}".format(connectParams)) if 'port' not in connectParams: output = "'port' entry required in connection parameters" log.error(output) raise ValueError(output) #==== connect to serial port self.comPort = connectParams['port'] try: self.pyserialHandler = serial.Serial(self.comPort, baudrate=self._BAUDRATE) self.pyserialHandler.setRTS(False) self.pyserialHandler.setDTR(True) except serial.serialutil.SerialException as err: output = "could not open " + self.comPort + ", reason: " + str(err) log.warning(output) raise ConnectionError(output) else: log.info("opened port " + self.comPort) #==== import and initialize the C library global uart_txByte global ipmt_notif global ipmt_reply global ipmt_status # local variables uart_txByte = uart_txByte_cbt(self._uart_txByte) ipmt_notif = ipmt_notif_cbt(self._ipmt_notif) ipmt_reply = ipmt_reply_cbt(self._ipmt_reply) ipmt_status = ipmt_status_cbt(self._ipmt_status) # configure the callback function self.notifBuf = create_string_buffer(self.MAX_FRAME_LENGTH) # open the library under test self.smipmt = CDLL(CLIB_PATH) self.smipmt.dn_ipmt_init( ipmt_notif, # notifCb self.notifBuf, # notifBuf len(self.notifBuf), # notifBufLen ipmt_reply, # replyCb ipmt_status, # statusCb ) # install callbacks self.smipmt.dn_uart_register_txByte(uart_txByte) # initial thread threading.Thread.__init__(self) self.name = 'IpMoteConnectorSerial' # start myself self.start() #==== connect administratively # connect the parent class ApiConnector.connect(self)
class Hdlc(threading.Thread): _BAUDRATE = 115200 # the default baudrate used by this module _HDLC_FLAG = 0x7e # the HDLC flag at the beginning/end of a frame _HDLC_ESCAPE = 0x7d # escape character when the HDLC flag in payload _HDLC_MASK = 0x20 # mask used to recover from escape characters _FCS_LENGTH = 2 # number of bytes in the FCS field def __init__(self, rxcallback, connectcallback): # log log.info('Creating object') # store params self.rxcallback = rxcallback self.connectcallback = connectcallback # initialize parent class threading.Thread.__init__(self) self.name = 'HDLC' self.daemon = True # local variables self.crc = Crc.Crc() self.connected = False self.comPort = '' self.pyserialHandler = '' self.busySending = threading.Lock() self.busyReceiving = False self.lastRxByte = self._HDLC_FLAG # initialize state self._restart() def run(self): # log log.info('thread started') try: while self.connected == True: try: # received a byte try: rxByte = self.pyserialHandler.read(1) except Exception as err: # work-around for bug in pyserial # https://sourceforge.net/tracker/?func=detail&aid=3591432&group_id=46487&atid=446302 raise serial.SerialException(str(err)) if not len(rxByte): raise serial.SerialException() # convert the received byte from a character to an int rxByte = ord(rxByte) if ((not self.busyReceiving) and self.lastRxByte == self._HDLC_FLAG and rxByte != self._HDLC_FLAG): # start of frame self.busyReceiving = True self.receivedFrame = {} self.receivedFrame['payload'] = [rxByte] elif (self.busyReceiving and rxByte != self._HDLC_FLAG): # middle of frame if rxByte == self._HDLC_ESCAPE: self._escape = True # do not add this byte to the payload else: if self._escape == True: self.receivedFrame['payload'].append( rxByte ^ self._HDLC_MASK) self._escape = False else: self.receivedFrame['payload'].append(rxByte) elif (self.busyReceiving and rxByte == self._HDLC_FLAG): # end of frame self.busyReceiving = False # split payload and fcs if len(self.receivedFrame['payload'] ) > self._FCS_LENGTH: temp_payload = self.receivedFrame['payload'] self.receivedFrame['payload'] = temp_payload[:-2] self.receivedFrame['fcs'] = temp_payload[-2:] # check fcs, write 'valid' field recalculatedCrc = self.crc.calculate( self.receivedFrame['payload']) if recalculatedCrc == self.receivedFrame['fcs']: self.receivedFrame['valid'] = True else: self.receivedFrame['valid'] = False # log if log.isEnabledFor(logging.DEBUG): output = [] output += ['\nreceivedFrame:'] output += self._formatFrame(self.receivedFrame) log.debug('\n'.join(output)) # callback if self.receivedFrame['valid'] == True: try: self.rxcallback( self.receivedFrame['payload']) except (ConnectionError, CommandError) as err: output = "@Hdlc: {0}".format(err) log.error(output) print output else: output = "@Hdlc: received hdlc frame too short" log.error(output) print output self._restart() # remember the last byte I received self.lastRxByte = rxByte except serial.SerialException: self.connected = False except Exception as err: output = [] output += ['===== crash in thread {0} ====='.format(self.name)] output += ['\nerror:\n'] output += [str(err)] output += ['\ncall stack:\n'] output += [traceback.format_exc()] output = '\n'.join(output) print output # critical error log.critical(output) raise del self.crc del self.pyserialHandler self.connectcallback(self.connected) # log log.info('thread ended') #======================== public ========================================== def connect(self, comPort, baudrate=_BAUDRATE): self.comPort = comPort try: self.pyserialHandler = serial.Serial(self.comPort, baudrate=baudrate) self.pyserialHandler.setRTS(False) self.pyserialHandler.setDTR(True) except serial.serialutil.SerialException as err: output = "could not open {0}@{1}baud, reason: {2}".format( self.comPort, baudrate, err) log.warning(output) raise ConnectionError(output) else: log.info("opened port {0}@{1}baud".format(self.comPort, baudrate)) self._restart() self.connected = True self.connectcallback(self.connected) self.name = '{0}_HDLC'.format(self.comPort) self.start() return self def send(self, message): if not self.connected: output = 'not connected' log.error(output) raise ConnectionError(output) # calculate fcs packetToSend = {} packetToSend['payload'] = message packetToSend['fcs'] = self.crc.calculate(packetToSend['payload']) packetToSend['valid'] = True # assemble packet packetBytes = packetToSend['payload'] + packetToSend['fcs'] # add HDLC escape characters index = 0 while index < len(packetBytes): if packetBytes[index] == self._HDLC_FLAG or packetBytes[ index] == self._HDLC_ESCAPE: packetBytes.insert(index, self._HDLC_ESCAPE) index += 1 packetBytes[index] = packetBytes[index] ^ self._HDLC_MASK index += 1 # add HDLC flags packetBytes.insert(0, self._HDLC_FLAG) packetBytes.insert(len(packetBytes), self._HDLC_FLAG) # log if log.isEnabledFor(logging.DEBUG): output = [] output += ['\npacketToSend:'] output += self._formatFrame(packetToSend) log.debug('\n'.join(output)) # reconstitute byteArray byteArray = ''.join([chr(byte) for byte in packetBytes]) # send over serial port try: with self.busySending: numWritten = self.pyserialHandler.write(byteArray) except IOError, e: raise ConnectionError(str(e)) if numWritten != len(packetBytes): output = 'wrote ' + str(numWritten) + ' bytes, expected ' + str( len(packetBytes)) log.error(output) raise ConnectionError(output)
def connect(self,connectParams): ''' \brief Connect to the LBR. ''' # filter error if self.getStatus() not in [self.STATUS_DISCONNECTED]: self._abortConnectionWithError(connectParams,'is at wrong status to connect '+str(self.getStatus())) # record variables try: self.varLock.acquire() self.lbrAddr = connectParams['lbrAddr'] self.lbrPort = connectParams['lbrPort'] self.username = connectParams['username'] self.seclevel = connectParams['seclevel'] if self.seclevel==LbrProtocol.SECLEVEL_PASSWORD: self.password = connectParams['password'] elif self.seclevel==LbrProtocol.SECLEVEL_SSL: self.clientprivatekey = connectParams['clientprivatekey'] self.clientpublickey = connectParams['clientpublickey'] self.lbrpublickey = connectParams['lbrpublickey'] except KeyError as err: raise ConnectionError('Missing connection parameter: '+str(err)) finally: self.varLock.release() # connect the parent ApiConnector.connect(self) # update status self._updateStatus(self.STATUS_INIT_CONNECTION) # create TCP socket to connect to LBR try: self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect((self.lbrAddr,self.lbrPort)) except socket.error: self._abortConnectionWithError(connectParams,'could not open socket to LBR@'+self.lbrAddr+':'+str(self.lbrPort)+'.') # listen for at most AUTHTIMEOUT seconds during connection self.socket.settimeout(AUTHTIMEOUT) #=== Step 1. Send my security capability to LBR self._updateStatus(self.STATUS_TX_SECURITY) try: self.socket.send(LbrProtocol.CMD_SECURITY+chr(self.seclevel)) except socket.error: self._abortConnectionWithError(connectParams,'could not send security capability.') #=== Step 2. Receive security capabilities from LBR self._updateStatus(self.STATUS_RX_SECURITY) try: input = self.socket.recv(TCPRXBUFSIZE) except socket.timeout: self._abortConnectionWithError(connectParams,'waited too long for security reply.') if ( len(input)!=2 or input[0] !=LbrProtocol.CMD_SECURITY or ord(input[1]) !=self.seclevel): self._abortConnectionWithError(connectParams,'received incorrect security reply.') #=== Step 3. Send username self._updateStatus(self.STATUS_TX_USERNAME) try: self.socket.send(LbrProtocol.CMD_USERNAME+self.username) except socket.error: self._abortConnectionWithError(connectParams,'could not send username.') #=== Step 4. Receive username self._updateStatus(self.STATUS_RX_USERNAME) try: input = self.socket.recv(TCPRXBUFSIZE) except socket.timeout: self._abortConnectionWithError(connectParams,'waited too long for username echo.') if ( len(input)<=1 or input[0] !=LbrProtocol.CMD_USERNAME or input[1:] !=self.username): self._abortConnectionWithError(connectParams,'received incorrect echoed username.') #=== Step 5. Secure TCP session if self.seclevel==LbrProtocol.SECLEVEL_NONE: log.debug('TCP session securing: none') elif self.seclevel==LbrProtocol.SECLEVEL_PASSWORD: log.debug('TCP session securing: password') self._updateStatus(self.STATUS_TX_PASSWORD) try: self.socket.send(LbrProtocol.CMD_PASSWORD+self.password) except socket.error: self._abortConnectionWithError(connectParams,'could not send password.') elif self.seclevel==LbrProtocol.SECLEVEL_SSL: log.debug('TCP session securing: SSL') self._updateStatus(self.STATUS_SLL_WRAPPING) try: clientPrivateKeyPem = self._privatekeyToPem(self.clientprivatekey) clientPrivateKeyFileName = self.username+'.tempprivatekey' clientPrivateKeyFile = open(clientPrivateKeyFileName,'w') clientPrivateKeyFile.write(clientPrivateKeyPem) clientPrivateKeyFile.close() clientPublicKeyPem = self._publickeyToPem(self.clientpublickey) clientPublicKeyFileName = self.username+'.temppublickey' clientPublicKeyFile = open(clientPublicKeyFileName,'w') clientPublicKeyFile.write(clientPublicKeyPem) clientPublicKeyFile.close() lbrpublickeyPem = self._publickeyToPem(self.lbrpublickey) lbrpublickeyFileName = self.username+'.templbrpublickey' lbrpublickeyFile = open(lbrpublickeyFileName,'w') lbrpublickeyFile.write(lbrpublickeyPem) lbrpublickeyFile.close() self.socket = ssl.wrap_socket( self.socket, keyfile=clientPrivateKeyFileName, # client's private key certfile=clientPublicKeyFileName, # client's public key ca_certs=lbrpublickeyFileName, # server's public key cert_reqs=ssl.CERT_REQUIRED) except ssl.SSLError as err: self._abortConnectionWithError(connectParams,'SSL wrapping failed.') except Exception as err: print err else: output = '' output += 'Peer validated:\n' output += '- name: '+str(self.socket.getpeername())+'\n' output += '- cipher: '+str(self.socket.cipher())+'\n' output += '- cartificate: '+str(self.socket.getpeercert()) log.debug(output) finally: os.remove(clientPrivateKeyFileName) os.remove(clientPublicKeyFileName) os.remove(lbrpublickeyFileName) #=== Step 6. Receive assigned prefix from LBR self._updateStatus(self.STATUS_RX_PREFIX) try: input = self.socket.recv(TCPRXBUFSIZE) except (socket.timeout,ssl.SSLError): self._abortConnectionWithError(connectParams,'waited too long for prefix.') if (len(input)!=20 or input[0]!=LbrProtocol.CMD_PREFIX): self._abortConnectionWithError(connectParams,'received malformatted prefix.') # record the prefix self.prefix = input[1:] # if you get here, connection is succesfully established with LBR # update status self._updateStatus(self.STATUS_CONNECTED) # no socket timeout from now on self.socket.settimeout(None) # start an LbrListener thread temp = LbrListener(self.socket,self._rxCb,self._disconnectedCb) temp.start()
def _abortConnectionWithError(self,connectParams,reason): try: self._closeSocket() except (AttributeError,socket.error): pass raise ConnectionError(connectParams['username']+' '+reason)