class AMQPclient(IoTClient): def __init__(self, account, client, topics): self.account = account self.clientGUI = client self.parser = AMQPParser() self.nextHandle = 1 self.connectionState = None self.channel = 0 self.isSASLConfirm = False self.timers = TimersMap(self) self.usedIncomingMappings = {} self.usedOutgoingMappings = {} self.usedMappings = {} self.pendingMessages = [] self.timeout = 0 self.pending_topics = {} self.known_topics = {} for topic in topics: self.pending_topics[topic.topicName] = topic.qos self.known_topics[topic.topicName] = topic.qos self.can_connect = True def send(self, header): if self.connectionState == ConnectionState.CONNECTION_ESTABLISHED: message = self.parser.encode(header) self.clientFactory.send(message) else: return False def dataReceived(self, data): received = bytearray() messages = [] while len(received) < len(data): index = 0 part = self.parser.next(data, index) message = self.parser.decode(part) messages.append(message) index += len(part) received += part data = data[index:] for message in messages: self.handle_next_message(message) def handle_next_message(self, message): reactor.callFromThread(process_messageType_method, self, message.getCode().value, message) def goConnect(self): self.setState(ConnectionState.CONNECTING) header = AMQPProtoHeader(3) # SASL = 3 self.clientFactory = ClientFactory(self.parser.encode(header), self) if self.account.isSecure: ctx = CtxFactory(self.account.certificate, self.account.certPasw) reactor.connectSSL(self.account.serverHost, self.account.port, self.clientFactory, ctx) else: connector = reactor.connectTCP(self.account.serverHost, self.account.port, self.clientFactory) self.setState(ConnectionState.CONNECTION_ESTABLISHED) self.timers.goConnectTimer(None) def publish(self, name, qos, content, retain, dup): messageFormat = AMQPMessageFormat(0, None, None) transfer = AMQPTransfer(None, None, None, self.channel, None, None, None, messageFormat, True, False, None, None, None, None, None, None) data = AMQPData(bytes(content, encoding='utf_8')) sections = {} sections[SectionCode.DATA] = data transfer.setSections(sections) if name in self.usedOutgoingMappings: handle = self.usedOutgoingMappings[name] transfer.setHandle(np.int64(handle)) self.timers.goMessageTimer(transfer) else: currentHandler = self.nextHandle self.nextHandle += 1 self.usedOutgoingMappings[name] = currentHandler self.usedMappings[currentHandler] = name transfer.setHandle(np.int64(currentHandler)) self.pendingMessages.append(transfer) attach = AMQPAttach(None, None, None, self.channel, str(name), np.int64(currentHandler), RoleCode.SENDER, None, np.int16(ReceiveCode.SECOND.value), None, None, None, None, np.int64(0), None, None, None, None) source = AMQPSource(str(name), np.int64(TerminusDurability.NONE.value), None, np.int64(0), False, None, None, None, None, None, None) attach.setSource(source) self.send(attach) def subscribeTo(self, name, qos): if name in self.usedIncomingMappings: currentHandler = self.usedIncomingMappings[name] else: currentHandler = self.nextHandle self.nextHandle += 1 self.usedIncomingMappings[name] = currentHandler self.usedMappings[currentHandler] = name self.pending_topics[name] = qos attach = AMQPAttach(None, None, None, self.channel, str(name), np.int64(currentHandler), RoleCode.RECEIVER, np.int16(SendCode.MIXED.value), None, None, None, None, None, None, None, None, None, None) target = AMQPTarget(str(name), np.int64(TerminusDurability.NONE.value), None, np.int64(0), False, None, None) attach.setTarget(target) self.send(attach) def unsubscribeFrom(self, topicName): if topicName in self.usedIncomingMappings: del self.known_topics[topicName] detach = AMQPDetach(None, None, None, self.channel, np.int64(self.usedIncomingMappings[topicName]), True, None) self.send(detach) else: listTopics = [] listTopics.append(topicName) self.clientGUI.unsubackReceived(listTopics) def pingreq(self): ping = AMQPPing() self.send(ping) def disconnectWith(self, duration): self.timers.stopAllTimers() end = AMQPEnd(None, None, None, self.channel, None) self.send(end) def getPingreqMessage(self): return AMQPPing() def timeoutMethod(self): if self.can_connect: self.can_connect = False self.timers.stopAllTimers() self.clientGUI.timeout() def connectTimeoutMethod(self): if self.can_connect: self.can_connect = False self.timers.stopAllTimers() self.clientGUI.show_error_message("Connect Error", "Connection Timeout") self.clientGUI.timeout() def setTopics(self, topics): pass def setState(self, ConnectionState): self.connectionState = ConnectionState def ConnectionLost(self): if self.can_connect: self.can_connect = False if self.timers != None: self.timers.stopAllTimers() self.clientGUI.errorReceived()
class CoapClient(IoTClient): def __init__(self, account, client): self.account = account self.clientGUI = client self.parser = CoapParser() self.parserOption = OptionParser() self.resendperiod = 3000 self.connectionState = None self.data = None self.udpClient = None self.timers = TimersMap(self) self.Version = 1 self.forPublish = {} self.forSubscribe = {} self.forUnsubscribe = {} self.pingNum = 0 self.udpThread = None self.ping_timer_started = False self.disconnected = False def goConnect(self): self.setState(ConnectionState.CONNECTING) duration = self.account.keepAlive if self.timers is not None: self.timers.stopAllTimers() option = self.parserOption.encode(CoapOptionType.NODE_ID, self.account.clientID) options = [] options.append(option) message = CoapMessage(self.Version, CoapType.CONFIRMABLE, CoapCode.PUT, 0, None, options, None) if self.account.isSecure: self.loop = asyncio.new_event_loop() addr = (self.account.serverHost, self.account.port) sock = socket(AF_INET, SOCK_DGRAM) sock.settimeout(5) sock.setblocking(1) if self.account.certificate and len(self.account.certificate) > 0: fp = self.get_certificate_file(self.account.certificate, self.account.certPasw) self.sock_wrapped = wrapper.wrap_client(sock, keyfile=fp.name, certfile=fp.name, ca_certs=fp.name) else: self.sock_wrapped = wrapper.wrap_client(sock) self.sock_wrapped.connect(addr) datagram_endpoint = self.loop.create_datagram_endpoint(lambda: pyDTLSClient(self.account.serverHost, self.account.port, self.account.certificate, self, self.loop), sock=self.sock_wrapped) self.transport, protocol = self.loop.run_until_complete(datagram_endpoint) self.udpClient = protocol self.setState(ConnectionState.CONNECTION_ESTABLISHED) self.udpThread = Thread(target=self.init_loop) self.udpThread.daemon = True self.udpThread.start() else: self.udpClient = UDPClient(self.account.serverHost, self.account.port, self) self.udp_listener = reactor.listenUDP(0, self.udpClient) self.timers.goConnectTimer(message) def init_loop(self): self.loop.run_forever() self.transport.close() self.loop.close() self.udpClient.connection_lost(None) try: self.sock_wrapped._sock.close() except: pass def stop_udp_listener(self): if hasattr(self, "loop") and self.loop is not None: self.loop.stop() try: self.sock_wrapped._sock.close() except Exception as ex: pass elif hasattr(self, "udp_listener"): self.udp_listener.stopListening() def get_certificate_file(self, cert_body, cert_pwd): fp = tempfile.NamedTemporaryFile() fp.write(bytes(cert_body, 'utf-8')) fp.seek(0) if cert_pwd is not None and len(cert_pwd) > 0: try: cert: X509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, fp.read()) fp.seek(0) key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, fp.read(), cert_pwd.encode("utf-8")) fp1 = tempfile.NamedTemporaryFile() fp1.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)) fp1.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)) fp1.seek(0) except Exception as err: fp.close() raise err return fp1 else: return fp def send(self, message): if self.connectionState == ConnectionState.CONNECTION_ESTABLISHED: message = self.parser.encode(message) self.udpClient.sendMessage(message) else: return False def refresh_ping_timer(self): option = self.parserOption.encode(CoapOptionType.NODE_ID, self.account.clientID) options = [] options.append(option) message = CoapMessage(self.Version, CoapType.CONFIRMABLE, CoapCode.PUT, 0, None, options, None) reactor.callFromThread(self.timers.goPingTimer, message, self.account.keepAlive) def dataReceived(self, data): message = self.parser.decode(data) type = message.getType() code = message.getCode() if code == CoapCode.POST or code == CoapCode.PUT: if type != CoapType.ACKNOWLEDGEMENT: topic = None qosValue = None for option in message.getOptionsDecode(): if isinstance(option, CoapOption): if CoapOptionType(option.getType()) == CoapOptionType.URI_PATH: topic = self.parserOption.decode(CoapOptionType(option.getType()), option) break for option in message.getOptionsDecode(): if isinstance(option, CoapOption): if CoapOptionType(option.getType()) == CoapOptionType.ACCEPT: qosValue = self.parserOption.decode(CoapOptionType(option.getType()), option) break if len(topic) > 0: content = message.getPayload() qos = QoS(qosValue) topicResult = CoapTopic(topic, qos) reactor.callFromThread(self.clientGUI.publishReceived, topicResult, qos, content, False, False) else: textFormat = "text/plain" options = [] option = self.parserOption.encode(CoapOptionType.CONTENT_FORMAT, textFormat) options.append(option) option = self.parserOption.encode(CoapOptionType.NODE_ID, self.account.clientID) options.append(option) ack = CoapMessage(self.Version, CoapType.ACKNOWLEDGEMENT, CoapCode.BAD_OPTION, message.getPacketID(), message.getToken(), options, None) self.send(ack) else: if not self.ping_timer_started: self.timers.stopConnectTimer() self.ping_timer_started = True self.refresh_ping_timer() process_messageType_method(self, message.getType().value, message) def setState(self, ConnectionState): self.connectionState = ConnectionState def getConnectionState(self): return self.connectionState def publish(self, topicName, qosValue, content, retain, dup): options = [] option = self.parserOption.encode(CoapOptionType.URI_PATH, topicName) options.append(option) option = self.parserOption.encode(CoapOptionType.NODE_ID, self.account.clientID) options.append(option) option = self.parserOption.encode(CoapOptionType.ACCEPT, qosValue) options.append(option) message = CoapMessage(self.Version, CoapType.CONFIRMABLE, CoapCode.PUT, 0, '0', options, content) packetID = self.timers.goMessageTimer(message) message.setPacketID(packetID) self.forPublish[packetID] = message def subscribeTo(self, topicName, qosValue): options = [] option = self.parserOption.encode(CoapOptionType.OBSERVE, 0) options.append(option) option = self.parserOption.encode(CoapOptionType.URI_PATH, topicName) options.append(option) option = self.parserOption.encode(CoapOptionType.ACCEPT, qosValue) options.append(option) option = self.parserOption.encode(CoapOptionType.NODE_ID, self.account.clientID) options.append(option) message = CoapMessage(self.Version, CoapType.CONFIRMABLE, CoapCode.GET, 0, '0', options, None) packetID = self.timers.goMessageTimer(message) message.setPacketID(packetID) self.forSubscribe[packetID] = message def unsubscribeFrom(self, topicName): options = [] option = self.parserOption.encode(CoapOptionType.OBSERVE, 1) options.append(option) option = self.parserOption.encode(CoapOptionType.NODE_ID, self.account.clientID) options.append(option) option = self.parserOption.encode(CoapOptionType.URI_PATH, topicName) options.append(option) option = self.parserOption.encode(CoapOptionType.ACCEPT, 0) options.append(option) message = CoapMessage(self.Version, CoapType.CONFIRMABLE, CoapCode.GET, 0, '0', options, None) packetID = self.timers.goMessageTimer(message) message.setPacketID(packetID) self.forUnsubscribe[packetID] = message def pingreq(self): reactor.callFromThread(self.clientGUI.connackReceived, None) def disconnectWith(self, duration): self.disconnected = True self.timers.stopAllTimers() self.stop_udp_listener() def timeoutMethod(self): self.error_occured("Timeout was reached. Try to reconnect") def process_connection_closed(self): self.timers.stopAllTimers() if not self.disconnected: self.error_occurred("Connection closed by server") def error_occurred(self, message): self.timers.stopAllTimers() self.stop_udp_listener() reactor.callFromThread(self.clientGUI.show_error_message, "Warning", message) reactor.callFromThread(self.clientGUI.errorReceived) def connectTimeoutMethod(self): self.timers.stopAllTimers() self.stop_udp_listener() reactor.callFromThread(self.clientGUI.show_error_message, "Connect Error", "Connection Timeout") reactor.callFromThread(self.clientGUI.errorReceived) def ConnectionLost(self): self.setState(ConnectionState.CONNECTION_LOST) self.stop_udp_listener() def connected(self): self.setState(ConnectionState.CHANNEL_ESTABLISHED) def connectFailed(self): self.setState(ConnectionState.CHANNEL_FAILED) self.stop_udp_listener()
class MQTTclient(IoTClient): def __init__(self, account, client): self.account = account self.clientGUI = client self.parser = MQParser(None) self.resendperiod = 3000 self.connectionState = None self.data = None self.timers = TimersMap(self) self.publishPackets = {} self.can_connect = True def send(self, message): if self.connectionState == ConnectionState.CONNECTION_ESTABLISHED: self.parser.setMessage(message) message = self.parser.encode() self.clientFactory.send(message) else: return False def dataReceived(self, data): messages = [] index = 1 while len(data) - index > 0: length = self.parser.next(data, index) if length < 0: break part = data[index - 1:index + length] message = self.parser.decode(part) messages.append(message) index += length for message in messages: process_messageType_method(self, message.getType(), message) def setState(self, ConnectionState): self.connectionState = ConnectionState def isConnected(self): return self.connectionState == ConnectionState.CONNECTION_ESTABLISHED def closeChannel(self): if self.client is not None: self.client.stop() def goConnect(self): self.setState(ConnectionState.CONNECTING) if self.account.willTopic is not None: topic = MQTopic(self.account.willTopic, self.account.qos) will = Will(topic, self.account.will, self.account.isRetain) else: will = None connect = MQConnect(self.account.username, self.account.password, self.account.clientID, self.account.cleanSession, self.account.keepAlive, will) if self.timers is not None: self.timers.stopAllTimers() self.timers.goConnectTimer(connect) self.parser.setMessage(connect) self.clientFactory = ClientFactory(self.parser.encode(), self) if self.account.isSecure: ctx = CtxFactory(self.account.certificate, self.account.certPasw) self.connector = reactor.connectSSL(self.account.serverHost, self.account.port, self.clientFactory, ctx) else: self.connector = reactor.connectTCP(self.account.serverHost, self.account.port, self.clientFactory) def publish(self, name, qos, content, retain, dup): topic = MQTopic(name, qos) publish = MQPublish(0, topic, content, retain, dup) if (qos == 0): self.send(publish) else: if (qos in [1, 2]): self.timers.goMessageTimer(publish) def unsubscribeFrom(self, topicName): listTopics = [] listTopics.append(topicName) unsubscribe = MQUnsubscribe(0, listTopics) self.timers.goMessageTimer(unsubscribe) def subscribeTo(self, name, qos): topic = MQTopic(name, qos) listMQTopics = [topic] subscribe = MQSubscribe(0, listMQTopics) self.timers.goMessageTimer(subscribe) def pingreq(self): self.send(MQPingreq()) def disconnectWith(self, duration): self.send(MQDisconnect()) self.timers.stopAllTimers() self.clientFactory.client_close_connection() def timeoutMethod(self): if self.can_connect: self.can_connect = False self.timers.stopAllTimers() reactor.callFromThread(self.clientGUI.timeout) def connectTimeoutMethod(self): if self.can_connect: self.can_connect = False self.timers.stopAllTimers() reactor.callFromThread(self.clientGUI.show_error_message, "Connect Error", "Connection timeout") reactor.callFromThread(self.clientGUI.timeout) def ConnectionLost(self): if self.can_connect: self.can_connect = False if self.timers is not None: self.timers.stopAllTimers() self.connector.disconnect() reactor.callFromThread(self.clientGUI.errorReceived)
class WSclient(IoTClient): def __init__(self, account, client): self.account = account self.clientGUI = client self.resendperiod = 3000 self.connectionState = None self.data = None self.timers = TimersMap(self) self.publishPackets = {} self.publishPacketsOut = {} self.can_connect = True def send(self, message): if self.connectionState == ConnectionState.CONNECTION_ESTABLISHED: self.clientFactory.sendPacket(message) else: return False def dataReceived(self, data): message = json.loads(data.decode()) process_messageType_method(self, message['packet'], message) def setState(self, ConnectionState): self.connectionState = ConnectionState def goConnect(self): self.setState(ConnectionState.CONNECTING) if self.account.willTopic and len( self.account.willTopic) > 0 is not None: will = { "topic": { "name": self.account.willTopic, "qos": self.account.qos }, "content": base64.b64encode(self.account.will.encode()).decode("utf-8"), "retain": self.account.isRetain } willFlag = True else: will = None willFlag = False if self.account.isSecure: url = 'wss://' + str(self.account.serverHost) + ':' + str( self.account.port) + '/ws' self.clientFactory = WSSocketClientFactory(url, self) # self.clientFactory.setProtocolOptions(openHandshakeTimeout=10) ctx = CtxFactory(self.account.certificate, self.account.certPasw) self.connector = connectWS(self.clientFactory, ctx, int(self.account.keepAlive) * 2) else: url = 'ws://' + str(self.account.serverHost) + ':' + str( self.account.port) + '/ws' self.clientFactory = WSSocketClientFactory(url, self) self.connector = connectWS(self.clientFactory, None, int(self.account.keepAlive) * 2) if self.account.username is not None and len( self.account.username) > 0: usernameFlag = True else: usernameFlag = False if self.account.password is not None and len( self.account.password) > 0: passwordFlag = True else: passwordFlag = False connect = { "packet": 1, "protocolLevel": 4, "username": self.account.username, "password": self.account.password, "clientID": self.account.clientID, "cleanSession": self.account.cleanSession, "keepalive": self.account.keepAlive, "will": will, "willFlag": willFlag, "passwordFlag": passwordFlag, "usernameFlag": usernameFlag, "protocolName": "MQTT" } if self.timers is not None: self.timers.stopAllTimers() self.timers.goConnectTimer(connect) def publish(self, name, qos, content, retain, dup): publish = { "packet": 3, "packetID": None, "topic": { "name": name, "qos": qos }, "content": base64.b64encode(content.encode()).decode("utf-8"), "retain": retain, "dup": dup } if (qos == 0): self.send(publish) else: if (qos in [1, 2]): packetID = self.timers.goMessageTimer(publish) if qos == 2: self.publishPacketsOut[packetID] = publish def unsubscribeFrom(self, topicName): listTopics = [] listTopics.append(topicName) unsubscribe = { "packet": 10, "packetID": None, "topics": [topicName], } self.timers.goMessageTimer(unsubscribe) def subscribeTo(self, name, qos): topic = MQTopic(name, qos) listMQTopics = [topic] subscribe = { "packet": 8, "packetID": None, "topics": [{ "name": name, "qos": qos }] } self.timers.goMessageTimer(subscribe) def pingreq(self): ping = {"packet": 12} self.send(ping) def disconnectWith(self, duration): disconnect = {"packet": 14} self.send(disconnect) self.timers.stopAllTimers() self.connector.disconnect() def timeoutMethod(self): if self.can_connect: self.can_connect = False self.timers.stopAllTimers() self.clientGUI.timeout() def connectTimeoutMethod(self): if self.can_connect: self.can_connect = False self.timers.stopAllTimers() self.clientGUI.show_error_message("Connect Error", "Connection Timeout") self.clientGUI.timeout() def PacketReceived(self, ProtocolMessage): ProtocolMessage.processBy() def ConnectionLost(self): if self.can_connect: self.can_connect = False if self.isClean == True: self.clearAccountTopics() if self.timers != None: self.timers.stopAllTimers() if self.client != None: self.client.stop() self.setState(ConnectionState.CONNECTION_LOST) def connected(self): self.setState(ConnectionState.CHANNEL_ESTABLISHED) def connectFailed(self): self.setState(ConnectionState.CHANNEL_FAILED)