def find_channel_for_controller(self, controllerMac: str, controllerName: str, vfo: str, cardAddress: int) -> None: response = { "address": cardAddress, "controllerMac": controllerMac, "controllerName": controllerName, "vfo": vfo, "status": "failed" } if controllerMac: # Attempting to take control of transverter try: channel = self.get_free_channels()[0] except IndexError: channel = None if channel and channel.request_card_control(cardAddress): channel.set_controller(controllerMac, controllerName, vfo) response["status"] = "success" response["channel"] = channel.name else: # Attempting to release transverter for x in self.channels: if x.cardAddress == cardAddress: if x.close_card_control(): response["status"] = "success" self.mqtt.publish("/{}/requestResponse".format(get_mac()), json.dumps(response))
def __init__(self, ipAddr, ipPort, warnings): self.warnings = warnings self.client = mqtt.Client() self.client.on_connect = self.on_connect self.client.on_message = self.on_message self.ipAddr = ipAddr self.ipPort = ipPort self.callbacks = {} try: x = { "mac": "{}".format(get_mac()), "name": "{}".format(NAME) } self.client.will_set("/discovery/lwt", json.dumps(x)) self.client.connect(ipAddr, ipPort, keepalive=5) self.client.loop_start() except socket.timeout: self.warnings.add_error( NAME, "MQTT", "Broker not found at at {}:{}".format( self.ipAddr, self.ipPort ), broadcast=False )
def send_discovery_info(self, msg): """ Publishes all the discovery info to the "discovery/info" topic Takes message argument as all callback functions must take a message but is useless - anything sent to this topic will result in discovery information being sent. Args: msg (str): payload of discovery message. Does nothing with this but all MQTT callbacks take this argument Returns: None Raises: None """ x = { "type": "controller", "mac": get_mac(), "ip": get_ip(), "name": config_user.NAME, "api": MQTT_API_VERSION, "link": get_link_speed() } self.mqtt.publish("/discovery/info", json.dumps(x))
def process_transverter_control_request_response(self, msg): self.transverterMutex.acquire() self.transverterResponse = msg if (msg["status"] == "success" and msg["controllerMac"] == get_mac() and msg["vfo"] == self.name): self.sdrChannel = msg["channel"] self.transverterMutex.release() self.transverterResponded.set()
def request_transverter_control(self, transverter: Transverter) -> bool: """ Requests control of a specific transverter """ if self.transverter: self.surrender_transverter_control() assert self.transverter is None assert self.sdrChannel is None logging.info('Attempting to take control of transverter "{}"'.format( transverter.name)) self.transverterMutex.release() self.mqtt.register_callback( "/{}/requestResponse".format(transverter.sdrMac), self.process_transverter_control_request_response, requiresMainThread=False) self.transverterResponded.clear() try: self.mqtt.publish( "/{}/requests".format(transverter.sdrMac), json.dumps({ "address": transverter.address, "controllerName": NAME, "controllerMac": get_mac(), "vfo": self.name })) if not self.transverterResponded.wait(timeout=5): # Requested timed out self.warnings.add_warning( NAME, "MQTT", f"Timed out waiting for response from {transverter.sdrMac}" ) else: # Got here so SDR responded, self.sdrChannel now either # contains the channel name if everything was successfuly # or None if it failed if self.sdrChannel: self.transverter = transverter self.mqttTopic = "/{}/channel{}".format( transverter.sdrMac, self.sdrChannel) self.mqtt.register_callback(self.mqttTopic, self.rx_status) else: # SDR responded but refused connection self.warnings.add_warning( NAME, "MQTT", f"Refused transverter control by {transverter.sdrMac}") finally: self.mqtt.remove_callback("/{}/requestResponse".format( transverter.sdrMac)) self.transverterMutex.acquire() return bool(self.transverter)
def __init__(self, jsonDict, warningHandler: WarningHandler, tabWidget: QTabWidget): """ Base class for any object on the MQTT network """ self.ipAddr = jsonDict['ip'] self.mac = jsonDict['mac'] self.name = jsonDict['name'] self.apiVersion = jsonDict['api'] self.warningHandler = warningHandler self.uptime = "" self.linkSpeed = jsonDict['link'] self._tabWidget = tabWidget self.updated = False # Flag if any info has been updated self.warnings = [] self.errors = [] self.state = None # Create tab in the tab widget for this device self._tab = QWidget(self._tabWidget) if (self.mac == get_mac()): # Ensure that the controller in use goes first self._tabWidget.insertTab(0, self._tab, f"{self.name}\n{self.ipAddr}") else: # Other device, put at end self._tabWidget.addTab(self._tab, f"{self.name}\n{self.ipAddr}") self._horizontalLayout = QHBoxLayout(self._tab) self._grid = QGridLayout() self._iconLayout = QVBoxLayout() self._horizontalLayout.addLayout(self._grid) self._horizontalLayout.addLayout(self._iconLayout) self._horizontalLayout.setStretchFactor(self._iconLayout, 1) self._ipLabel = self.add_value_row("IP Address:", self.ipAddr) # These are the definition of a unique device so never need updating self.add_value_row("Type:", self.get_type()) self.add_value_row("MAC Address:", self.mac) self._apiLabel = self.add_value_row("MQTT API Version:", self.apiVersion) self._uptimeLabel = self.add_value_row("Uptime:", self.uptime) self._onlineLabel = self.add_value_row("Online:", "") self._update_online_state(True) self._linkSpeedLabel = self.add_value_row( "Link Speed:", str(self.linkSpeed) + " Mbps") self._grid.setRowStretch(self._grid.rowCount(), 1) self._tab.setLayout(self._horizontalLayout)
def __init__(self, name, mqtt, statusregs, warnings: WarningHandler, cards: CardHandler, mode="LSB", freq=28000000, adcClk=80e6, supportsRx=True, supportsTx=False, supportsDuplex=False): self.name = name self.mqtt = mqtt self.statusregs = statusregs self.warnings = warnings self.cards = cards self.supportedModes = { "LSB": self.LSB, "USB": self.USB, "CW": self.CW, "Tone": self.TONE } self.offset = 0 self.adcClk = adcClk self.setTopic = "/{}/channel{}/set".format(get_mac(), self.name) self.broadcastTopic = "/{}/channel{}".format(get_mac(), self.name) self.set_freq(freq, update=False) self.set_mode(mode) self.supportsRx = supportsRx self.supportsTx = supportsTx self.supportsDuplex = supportsDuplex if supportsDuplex: assert supportsRx and supportsTx self.controllerMac = None self.controllerName = None self.vfo = None self.cardAddress = None self.mqtt.register_callback(self.setTopic, self.handle_command)
def send_status_info(self, msg: str) -> None: """ Publishes all the status info to the "status/info" topic Takes message argument as all callback functions must take a message but is useless - anything sent to this topic will result in status information being sent. Args: msg (str): payload of status message. Does nothing with this but all MQTT callbacks take this argument """ x = { "type": "controller", "mac": get_mac(), "uptime": self.get_uptime() } self.mqtt.publish("/status/info", json.dumps(x))
def send_status_info(self, _: dict = None) -> None: """ Publishes all the discovery info to the "discovery/info" topic Takes message argument as all callback functions must take a message but is useless - anything sent to this topic will result in discovery information being sent. Args: _ (str): All MQTT callbacks get passed the message. The contents is irrelevant here """ x = { "type": "sdr", "mac": get_mac(), "warnings": self.warnings.get_warnings(), "errors": self.warnings.get_errors(), "uptime": self.get_uptime(), "channels": self.channels.get_status_info(), "cards": self.cards.get_all_status_info() } self.mqtt.publish("/status/info", json.dumps(x))
def __init__(self, ipAddr, ipPort, warnings): super().__init__() self.client = mqtt.Client() self.client.on_connect = self.on_connect self.client.on_message = self.on_message self.warnings = warnings self.ipAddr = ipAddr self.ipPort = ipPort self.callbacks = {} try: x = {"mac": "{}".format(get_mac()), "name": "{}".format(NAME)} self.client.will_set("/discovery/lwt", json.dumps(x)) self.client.connect(ipAddr, ipPort, keepalive=5) self.client.loop_start() self.messageReceived.connect(self.message_handler) except (socket.timeout, ConnectionRefusedError): self.warnings.add_error( NAME, "MQTT", f"Broker not found at at {self.ipAddr}:{self.ipPort}", broadcast=False)
def send_discovery_info(self, _: dict = None) -> None: """ Publishes all the discovery info to the "discovery/info" topic Takes message argument as all callback functions must take a message but is useless - anything sent to this topic will result in discovery information being sent. Args: _ (dict): All MQTT callbacks get passed the message. The contents is irrelevant here """ x = { "type": "sdr", "ip": get_ip(), "mac": get_mac(), "name": NAME, "api": MQTT_API_VERSION, "link": get_link_speed(), "numSlots": self.cards.numSlots, "channels": self.channels.get_discovery_info(), "cards": self.cards.get_all_discovery_info(), } self.mqtt.publish("/discovery/info", json.dumps(x))
def update_mac(self): self.ui.label_mac.setText(get_mac())
def __init__(self): logging.info("Application started. Waiting for NTP sync...") client = ntplib.NTPClient() while True: try: response = client.request(NTP_SERVER) if (response.offset < 1): # We have a valid NTP time and are within 1s of it break except (ntplib.NTPException, socket.gaierror): logging.info("NTP Sync failed. Retrying...") time.sleep(1) logging.info( "NTP sync successful. IP Address: {}. " "Starting main application".format(get_ip()) ) self.startTime = datetime.now(timezone.utc) LED_STATUS.write(GPIO.HIGH) with WarningHandler() as self.warnings, \ MqttHandler( MQTT_SERVER_IP_ADDRESS, MQTT_SERVER_PORT, self.warnings ) as self.mqtt: self.warnings.register_mqtt(self.mqtt) self.status = StatusRegs( STATUS_REGISTERS_FILE, self.warnings ) self.slots = slotsEeprom( SLOTS_I2C_FILE, "Baseboard Config", SLOTS_EEPROM_ADDRESS, PIN_WP, self.warnings ) with \ CardHandler( TRANSVERTER_RS485_UART, numSlots=self.slots.num_slots() ) as self.cards, \ \ ChannelHandler(channels=[ ChannelPrototype( name='A', supportsRx=True, supportsTx=True ) ], cards=self.cards, mqtt=self.mqtt, warnings=self.warnings, status=self.status ) as self.channels: self.mqtt.register_callback( "/discovery/request", self.send_discovery_info ) self.mqtt.register_callback( "/status/request", self.send_status_info ) self.mqtt.register_callback( "/discovery/lwt", self.handle_lwt ) self.mqtt.register_callback( "/{}/requests".format(get_mac()), self.handle_control_request ) self.send_discovery_info() self.send_status_info() self.run()