class Radio(object): def connect(self, port): self.serial = SerialPort(RadioReceiverProtocol(self), port, reactor, baudrate=57600) self.serial.flushInput() def __init__(self): self.listeners = [] self.serial = None def send(self, packet): data = array.array('B', packet.encode()).tostring() print 'tx:', packet if self.serial is not None: self.serial.write(data) def gotPacket(self, packet): print 'rx:', packet for l in self.listeners: l(packet)
class IridiumSimulator(Thread): def __init__(self, comPort, moIP, moPort, mtPort, delay): Thread.__init__(self) self.udpMavlink = mavlink.MAVLink(0) self.serialMavlink = mavlink.MAVLink(0) self.udpRx = [] self.serialRx = None self.ipLock = Lock() self.serialLock = Lock() self.ipTransport = None self.status = SatcomStatus.idle self.serialTime = 0 self.delay = delay self.terminate = False # serial interface to PX4 self.serialInterface = SerialPort(PX4UartInterface(self.receivedPX4), comPort, reactor, baudrate = 115200) self.serialInterface.flushInput() self.serialInterface.flushOutput() # enpoint for sending MO messages from PX to the relay self.relayMoEndpoint = TCP4ClientEndpoint(reactor, moIP, moPort) # enpoint receiving MT messages coming from the relay self.relayMtEndpoint = TCP4ServerEndpoint(reactor, mtPort) self.relayMtEndpoint.listen(relay.IridiumMtReceiverFactory(self.receivedRelay)) def run(self): while not self.terminate: self.serialLock.acquire() if self.serialTime + self.delay <= time(): if self.status == SatcomStatus.sending: self.sendMsgToPX4(0x01, chr(0x00)) self.ipLock.acquire() # prepare and send an iridium message try: m = self.udpMavlink.decode(self.serialRx[8:]) if m is not None: msgId = m.get_msgId() print '<-- PX4 {0} ({1})'.format(mavlink.mavlink_map[msgId].name, msgId) except Exception as e: print '<-- PX4 ' + str(e) moMsgData = self.prepareMoMessage(self.serialRx) prot = relay.IridiumMoSender(moMsgData) connectProtocol(self.relayMoEndpoint, prot) self.ipLock.release() #print 'SEND RQ: complete' self.status = SatcomStatus.idle elif self.status == SatcomStatus.receiving: if len(self.udpRx) != 0: self.ipLock.acquire() msg = self.udpRx.pop(0) try: m = self.udpMavlink.decode(msg) except Exception as e: print e m = None if m is not None: msgId = m.get_msgId() print 'QGC --> {0} ({1})'.format(mavlink.mavlink_map[msgId].name, msgId) self.sendMsgToPX4(0x02, chr(0x00) + msg) else: self.sendMsgToPX4(0x02, chr(0x2F)) # no msg self.ipLock.release() #print 'RCV RQ: complete' else: self.sendMsgToPX4(0x02, chr(0x2F)) # no msg #print 'RCV RQ: no msg' self.status = SatcomStatus.idle self.serialLock.release() sleep(0.1) def receivedPX4(self, msg): # send msg if msg.id == 0x01: self.satcomSendRequest(msg.data) # retrieve msg if msg.id == 0x02: self.satcomReceiveRequest() # check network status if msg.id == 0x05: self.satcomGetNetworkStatus() # check signal strength if msg.id == 0x06: self.satcomGetSignalStrength() def receivedRelay(self, msg): # print 'MT MSG CALLBACK' for ie in msg.ieList: # print 'IE: {0}: {1}'.format(ie.id, ie.name) if ie.name == 'MT Payload IE': # print ie.payload try: #m = self.udpMavlink.decode(ie.payload) #if m is not None: # print 'MT mavlink msg ID {0}'.format(m.get_msgId()) self.ipLock.acquire() self.udpRx.append(ie.payload) self.ipLock.release() except: pass def prepareMoMessage(self, mavlinkMsg): msg = iridium.IridiumMessage() header = iridium.MoHeaderIe() header.autoId = 123 header.imei = bytearray([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE]) header.sessionStatus = iridium.MoHeaderIe.SessionStatus.SUCCESSFUL header.momsn = 1 header.mtmsn = 2 header.timeOfSession = 100000 header.prepareRaw() msg.ieList.append(header) payload = iridium.MoPayloadIe() payload.payload = mavlinkMsg payload.prepareRaw() msg.ieList.append(payload) msg.prepareRaw() return msg.rawData def satcomSendRequest(self, msgData): self.serialLock.acquire() if self.status == SatcomStatus.idle: #print 'SEND RQ: pending' self.status = SatcomStatus.sending self.serialTime = time() scsHeader = bytearray() packetLen = len(msgData) + 8 scsHeader.append(packetLen / 256) scsHeader.append(packetLen % 256) scsHeader.append(2) # PX4 packet scsHeader += bytearray(5) # 5 empty bytes - only used when sending images self.serialRx = scsHeader + msgData else: print 'SEND RQ: busy <<<<<<<<<<<<<<<<<<<<<<<' # THIS SHOULD NOT HAPPEN - PX4 SATCOM DRIVER SHOULD WAIT FOR PREVIOUS COMMAND REPLY self.sendMsgToPX4(0x01, chr(0x01)) self.serialLock.release() def satcomReceiveRequest(self): self.serialLock.acquire() if self.status == SatcomStatus.idle: #print 'RCV RQ: pending' self.status = SatcomStatus.receiving self.serialTime = time() else: print 'RCV RQ: busy <<<<<<<<<<<<<<<<<<<<<<<' # THIS SHOULD NOT HAPPEN - PX4 SATCOM DRIVER SHOULD WAIT FOR PREVIOUS COMMAND REPLY self.sendMsgToPX4(0x02, chr(0x01)) self.serialLock.release() def satcomGetNetworkStatus(self): self.serialLock.acquire() self.sendMsgToPX4(0x05, chr(0x00) + chr(0x01)) self.serialLock.release() def satcomGetSignalStrength(self): self.serialLock.acquire() self.sendMsgToPX4(0x06, chr(0x00) + chr(0x05)) self.serialLock.release() def sendMsgToPX4(self, id, data): msg = [0x5B, 0, 0, 0] msg[1] = (id) msg[2] = (len(data)/256) msg[3] = (len(data)%256) msg += data data = bytes(bytearray(msg)) self.serialInterface.write(data)
class AqiMonitor(service.Service): ''' AQI monitor service. ''' name = 'aqi_monitor' sensor_reconnect_timeout = 5 def __init__(self, aqi_storage, sensor_device, sensor_baudrate, poll_period, debug=False): self.debug = debug self.sensor = None self.sensor_device = sensor_device self.sensor_baudrate = sensor_baudrate self.poll_period = poll_period self.pm_timestamp = None self.pm_25 = None self.pm_10 = None self.aqi_storage = aqi_storage self.listeners = [] def startService(self): self._bot = self.parent.getServiceNamed(TelegramBot.name) reactor.callLater(0, self.connect_sensor) # @UndefinedVariable def add_listener(self, listener): if listener not in self.listeners: self.listeners.append(listener) def connect_sensor(self): self.sensor = Sds011(self, debug=self.debug) try: self.serial_port = SerialPort(self.sensor, self.sensor_device, reactor, baudrate=self.sensor_baudrate) self.serial_port.flushInput() except SerialException as se: log.error("Can't connect to sensor: %s" % se) reactor.callLater( self.sensor_reconnect_timeout, # @UndefinedVariable self.connect_sensor) @defer.inlineCallbacks def init_sensor(self): yield self.sensor.query_data() period = yield self.sensor.set_working_period(self.poll_period) if self.debug: log.debug("Sensor initialized, poll period set to %d minute(s)" % period) def sensor_connected(self): log.info("Connected to sensor") reactor.callLater(1, self.init_sensor) # @UndefinedVariable def sensor_disconnected(self, reason): log.warn("Disconnected from sensor, reason: %r" % reason) reactor.callLater(self.sensor_reconnect_timeout, self.connect_sensor) # @UndefinedVariable def sensor_data(self, pm_25, pm_10): if pm_25 <= 0 or pm_10 <= 0: # could be at sensor startup log.warn("Ignore invalid sensor data: PM2.5: %.1f, PM10: %.1f" % (pm_25, pm_10)) return log.info("Sensor data received, PM2.5: %.1f, PM10: %.1f" % (pm_25, pm_10)) self.pm_25 = pm_25 self.pm_10 = pm_10 self.pm_timestamp = time.time() self.aqi_storage.add_pm_data(int(self.pm_timestamp), pm_25, pm_10) for listener in self.listeners: try: listener.pm_data_updated(pm_25, pm_10, self.aqi) except Exception: log.failure("Can't notify PM data listener {listener}", listener=listener) @property def sensor_firmware_version(self): if self.sensor is None or not self.sensor.connected: raise SensorDisconnected( "Can't get sensor firmware version: sensor not connected") return self.sensor.get_firmware_version() @staticmethod def to_aqi_level(aqi): if aqi <= 50: return 0 # Good elif aqi <= 100: return 1 # Moderate elif aqi <= 150: return 2 # Unhealthy for Sensitive Groups elif aqi <= 200: return 3 # Unhealthy elif aqi <= 300: return 4 # Very Unhealthy return 5 # Hazardous @property def aqi_level(self): if self.pm_timestamp is None: return None return self.to_aqi_level(self.aqi) @property def aqi(self): if self.pm_timestamp is None: return None return aqi_calc.to_aqi([(aqi_calc.POLLUTANT_PM25, self.pm_25), (aqi_calc.POLLUTANT_PM10, self.pm_10)]) @property def pm(self): return self.pm_25, self.pm_10