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()
Exemple #2
0
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()
Exemple #3
0
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)