diff --git a/.cproject b/.cproject index 6dc5729..e99b880 100644 --- a/.cproject +++ b/.cproject @@ -1,4 +1,5 @@ - + + @@ -22,6 +23,16 @@ + - + + + + + + + - + @@ -69,6 +90,15 @@ + - + + + + + + + - + + + + @@ -98,23 +141,29 @@ + + + + + + + + + + - + + + + + + + - - - - - - - - - - - \ No newline at end of file + diff --git a/.project b/.project index 09b1b50..e1e29d1 100644 --- a/.project +++ b/.project @@ -20,6 +20,7 @@ org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature diff --git a/MQTTSNClient/src/linux/linux.cpp b/MQTTSNClient/src/linux/linux.cpp index 222acda..0e9872c 100644 --- a/MQTTSNClient/src/linux/linux.cpp +++ b/MQTTSNClient/src/linux/linux.cpp @@ -53,7 +53,7 @@ public: rc = errno; } //} - return errno; + return rc; } int connect(const char* hostname, int port) @@ -97,7 +97,7 @@ public: mysock = socket(family, type, 0); if (mysock != -1) { - int opt = 1; + //int opt = 1; //if (setsockopt(mysock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt)) != 0) // printf("Could not set SO_NOSIGPIPE for socket %d", mysock); diff --git a/MQTTSNGateway/src/MQTTConnection.h b/MQTTSNGateway/src/MQTTConnection.h deleted file mode 100644 index e2df956..0000000 --- a/MQTTSNGateway/src/MQTTConnection.h +++ /dev/null @@ -1,559 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014, 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - *******************************************************************************/ - -#if !defined(MQTTCONNECTION_H) -#define MQTTCONNECTION_H - -#include "FP.h" -#include "MQTTPacket.h" -#include "stdio.h" -#include "MQTTLogging.h" - -#if !defined(MQTTCLIENT_QOS1) - #define MQTTCLIENT_QOS1 1 -#endif -#if !defined(MQTTCLIENT_QOS2) - #define MQTTCLIENT_QOS2 0 -#endif - -namespace MQTT -{ - - -enum QoS { QOS0, QOS1, QOS2 }; - -// all failure return codes must be negative -enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 }; - - -struct Message -{ - enum QoS qos; - bool retained; - bool dup; - unsigned short id; - void *payload; - size_t payloadlen; -}; - - -class PacketId -{ -public: - PacketId() - { - next = 0; - } - - int getNext() - { - return next = (next == MAX_PACKET_ID) ? 1 : ++next; - } - -private: - static const int MAX_PACKET_ID = 65535; - int next; -}; - - -/** - * @class Client - * @brief blocking, non-threaded MQTT client API - * - * This version of the API blocks on all method calls, until they are complete. This means that only one - * MQTT request can be in process at any one time. - * @param Network a network class which supports send, receive - * @param Timer a timer class with the methods: - */ -template -class Connection -{ - -public: - - /** Construct the client - * @param network - pointer to an instance of the Network class - must be connected to the endpoint - * before calling MQTT connect - * @param limits an instance of the Limit class - to alter limits as required - */ - Connection(unsigned int command_timeout_ms = 30000); - - - static void run(void const* arg); - - - /** MQTT Disconnect - send an MQTT disconnect packet, and clean up any state - * @return success code - - */ - int disconnect(); - - /** Is the client connected? - * @return flag - is the client connected or not? - */ - bool isConnected() - { - return isconnected; - } - - int sendPacket(int length, Timer& timer); - unsigned char sendbuf[MAX_MQTT_PACKET_SIZE]; - -private: - - int connect(MQTTPacket_connectData&); - int connect(); - - void cleanSession(); - int cycle(Timer& timer); - int waitfor(int packet_type, Timer& timer); - int keepalive(); - int publish(int len, Timer& timer, enum QoS qos); - - int decodePacket(int* value, int timeout); - int readPacket(Timer& timer); - - Network ipstack; - unsigned long command_timeout_ms; - unsigned char readbuf[MAX_MQTT_PACKET_SIZE]; - - Timer last_sent, last_received; - unsigned int keepAliveInterval; - bool ping_outstanding; - bool cleansession; - - PacketId packetid; - - bool isconnected; - -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - unsigned char pubbuf[MAX_MQTT_PACKET_SIZE]; // store the last publish for sending on reconnect - int inflightLen; - unsigned short inflightMsgid; - enum QoS inflightQoS; -#endif - -#if MQTTCLIENT_QOS2 - bool pubrel; - #if !defined(MAX_INCOMING_QOS2_MESSAGES) - #define MAX_INCOMING_QOS2_MESSAGES 10 - #endif - unsigned short incomingQoS2messages[MAX_INCOMING_QOS2_MESSAGES]; - bool isQoS2msgidFree(unsigned short id); - bool useQoS2msgid(unsigned short id); - void freeQoS2msgid(unsigned short id); -#endif - -}; - -} - - -template -void MQTT::Connection::run(void const* arg) -{ - - - - -} - - -template -void MQTT::Connection::cleanSession() -{ - ping_outstanding = false; - isconnected = false; - -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - inflightMsgid = 0; - inflightQoS = QOS0; -#endif - -#if MQTTCLIENT_QOS2 - pubrel = false; - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - incomingQoS2messages[i] = 0; -#endif -} - - -template -MQTT::Connection::Connection(unsigned int command_timeout_ms) : packetid() -{ - last_sent = Timer(); - last_received = Timer(); - this->command_timeout_ms = command_timeout_ms; - cleanSession(); -} - - -#if MQTTCLIENT_QOS2 -template -bool MQTT::Connection::isQoS2msgidFree(unsigned short id) -{ - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - { - if (incomingQoS2messages[i] == id) - return false; - } - return true; -} - - -template -bool MQTT::Connection::useQoS2msgid(unsigned short id) -{ - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - { - if (incomingQoS2messages[i] == 0) - { - incomingQoS2messages[i] = id; - return true; - } - } - return false; -} - - -template -void MQTT::Connection::freeQoS2msgid(unsigned short id) -{ - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - { - if (incomingQoS2messages[i] == id) - { - incomingQoS2messages[i] = 0; - return; - } - } -} -#endif - - -template -int MQTT::Connection::sendPacket(int length, Timer& timer) -{ - int rc = FAILURE, - sent = 0; - - while (sent < length && !timer.expired()) - { - rc = ipstack.write(&sendbuf[sent], length - sent, timer.left_ms()); - if (rc < 0) // there was an error writing the data - break; - sent += rc; - } - if (sent == length) - { - if (this->keepAliveInterval > 0) - last_sent.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet - rc = SUCCESS; - } - else - rc = FAILURE; - -#if defined(MQTT_DEBUG) - char printbuf[150]; - DEBUG("Rc %d from sending packet %s\n", rc, MQTTFormat_toServerString(printbuf, sizeof(printbuf), sendbuf, length)); -#endif - return rc; -} - - -template -int MQTT::Connection::decodePacket(int* value, int timeout) -{ - unsigned char c; - int multiplier = 1; - int len = 0; - const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4; - - *value = 0; - do - { - int rc = MQTTPACKET_READ_ERROR; - - if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) - { - rc = MQTTPACKET_READ_ERROR; /* bad data */ - goto exit; - } - rc = ipstack.read(&c, 1, timeout); - if (rc != 1) - goto exit; - *value += (c & 127) * multiplier; - multiplier *= 128; - } while ((c & 128) != 0); -exit: - return len; -} - - -/** - * If any read fails in this method, then we should disconnect from the network, as on reconnect - * the packets can be retried. - * @param timeout the max time to wait for the packet read to complete, in milliseconds - * @return the MQTT packet type, or -1 if none - */ -template -int MQTT::Connection::readPacket(Timer& timer) -{ - int rc = FAILURE; - MQTTHeader header = {0}; - int len = 0; - int rem_len = 0; - - /* 1. read the header byte. This has the packet type in it */ - if (ipstack.read(readbuf, 1, timer.left_ms()) != 1) - goto exit; - - len = 1; - /* 2. read the remaining length. This is variable in itself */ - decodePacket(&rem_len, timer.left_ms()); - len += MQTTPacket_encode(readbuf + 1, rem_len); /* put the original remaining length into the buffer */ - - if (rem_len > (MAX_MQTT_PACKET_SIZE - len)) - { - rc = BUFFER_OVERFLOW; - goto exit; - } - - /* 3. read the rest of the buffer using a callback to supply the rest of the data */ - if (rem_len > 0 && (ipstack.read(readbuf + len, rem_len, timer.left_ms()) != rem_len)) - goto exit; - - header.byte = readbuf[0]; - rc = header.bits.type; - if (this->keepAliveInterval > 0) - last_received.countdown(this->keepAliveInterval); // record the fact that we have successfully received a packet -exit: - -#if defined(MQTT_DEBUG) - if (rc >= 0) - { - char printbuf[50]; - DEBUG("Rc %d from receiving packet %s\n", rc, MQTTFormat_toClientString(printbuf, sizeof(printbuf), readbuf, len)); - } -#endif - return rc; -} - - -template -int MQTT::Connection::cycle(Timer& timer) -{ - /* get one piece of work off the wire and one pass through */ - - // read the socket, see what work is due - int packet_type = readPacket(timer); - - int len = 0, - rc = SUCCESS; - - switch (packet_type) - { - case FAILURE: - case BUFFER_OVERFLOW: - rc = packet_type; - break; - case CONNACK: - case PUBACK: - case SUBACK: - break; - case PUBLISH: - { - MQTTString topicName = MQTTString_initializer; - Message msg; - int intQoS; - if (MQTTDeserialize_publish((unsigned char*)&msg.dup, &intQoS, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName, - (unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_MQTT_PACKET_SIZE) != 1) - goto exit; - msg.qos = (enum QoS)intQoS; -#if MQTTCLIENT_QOS2 - if (msg.qos != QOS2) -#endif - ; //deliverMessage(topicName, msg); -#if MQTTCLIENT_QOS2 - else if (isQoS2msgidFree(msg.id)) - { - if (useQoS2msgid(msg.id)) - ; //deliverMessage(topicName, msg); - else - WARN("Maximum number of incoming QoS2 messages exceeded"); - } -#endif -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - if (msg.qos != QOS0) - { - if (msg.qos == QOS1) - len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBACK, 0, msg.id); - else if (msg.qos == QOS2) - len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREC, 0, msg.id); - if (len <= 0) - rc = FAILURE; - else - rc = sendPacket(len, timer); - if (rc == FAILURE) - goto exit; // there was a problem - } - break; -#endif - } -#if MQTTCLIENT_QOS2 - case PUBREC: - case PUBREL: - unsigned short mypacketid; - unsigned char dup, type; - if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) - rc = FAILURE; - else if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, - (packet_type == PUBREC) ? PUBREL : PUBCOMP, 0, mypacketid)) <= 0) - rc = FAILURE; - else if ((rc = sendPacket(len, timer)) != SUCCESS) // send the PUBREL packet - rc = FAILURE; // there was a problem - if (rc == FAILURE) - goto exit; // there was a problem - if (packet_type == PUBREL) - freeQoS2msgid(mypacketid); - break; - - case PUBCOMP: - break; -#endif - case PINGRESP: - ping_outstanding = false; - break; - } - keepalive(); -exit: - if (rc == SUCCESS) - rc = packet_type; - return rc; -} - - -template -int MQTT::Connection::keepalive() -{ - int rc = FAILURE; - - if (keepAliveInterval == 0) - { - rc = SUCCESS; - goto exit; - } - - if (last_sent.expired() || last_received.expired()) - { - if (!ping_outstanding) - { - Timer timer(1000); - int len = MQTTSerialize_pingreq(sendbuf, MAX_MQTT_PACKET_SIZE); - if (len > 0 && (rc = sendPacket(len, timer)) == SUCCESS) // send the ping packet - ping_outstanding = true; - } - } - -exit: - return rc; -} - - -template -int MQTT::Connection::connect(MQTTPacket_connectData& options) -{ - Timer connect_timer(command_timeout_ms); - int rc = FAILURE; - int len = 0; - - if (isconnected) // don't send connect packet again if we are already connected - goto exit; - - this->keepAliveInterval = options.keepAliveInterval; - this->cleansession = options.cleansession; - if ((len = MQTTSerialize_connect(sendbuf, MAX_MQTT_PACKET_SIZE, &options)) <= 0) - goto exit; - if ((rc = sendPacket(len, connect_timer)) != SUCCESS) // send the connect packet - goto exit; // there was a problem - - if (this->keepAliveInterval > 0) - last_received.countdown(this->keepAliveInterval); - // this will be a blocking call, wait for the connack - if (waitfor(CONNACK, connect_timer) == CONNACK) - { - unsigned char connack_rc = 255; - bool sessionPresent = false; - if (MQTTDeserialize_connack((unsigned char*)&sessionPresent, &connack_rc, readbuf, MAX_MQTT_PACKET_SIZE) == 1) - rc = connack_rc; - else - rc = FAILURE; - } - else - rc = FAILURE; - -#if MQTTCLIENT_QOS2 - // resend any inflight publish - if (inflightMsgid > 0 && inflightQoS == QOS2 && pubrel) - { - if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREL, 0, inflightMsgid)) <= 0) - rc = FAILURE; - else - rc = publish(len, connect_timer, inflightQoS); - } - else -#endif -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - if (inflightMsgid > 0) - { - memcpy(sendbuf, pubbuf, MAX_MQTT_PACKET_SIZE); - rc = publish(inflightLen, connect_timer, inflightQoS); - } -#endif - -exit: - if (rc == SUCCESS) - isconnected = true; - return rc; -} - - -template -int MQTT::Connection::connect() -{ - MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer; - return connect(default_options); -} - - - -template -int MQTT::Connection::disconnect() -{ - int rc = FAILURE; - Timer timer(command_timeout_ms); // we might wait for incomplete incoming publishes to complete - int len = MQTTSerialize_disconnect(sendbuf, MAX_MQTT_PACKET_SIZE); - if (len > 0) - rc = sendPacket(len, timer); // send the disconnect packet - - if (cleansession) - cleanSession(); - else - isconnected = false; - return rc; -} - - -#endif diff --git a/MQTTSNGateway/src/MQTTGWConnectionHandler.cpp b/MQTTSNGateway/src/MQTTGWConnectionHandler.cpp new file mode 100644 index 0000000..88263da --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWConnectionHandler.cpp @@ -0,0 +1,106 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTGWConnectionHandler.h" +#include "MQTTGWPacket.h" + +using namespace std; +using namespace MQTTSNGW; + +MQTTGWConnectionHandler::MQTTGWConnectionHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTGWConnectionHandler::~MQTTGWConnectionHandler() +{ + +} + +void MQTTGWConnectionHandler::handleConnack(Client* client, MQTTGWPacket* packet) +{ + uint8_t rc = MQTT_SERVER_UNAVAILABLE; + Connack resp; + packet->getCONNACK(&resp); + + /* convert MQTT ReturnCode to MQTT-SN one */ + if (resp.rc == MQTT_CONNECTION_ACCEPTED) + { + rc = MQTTSN_RC_ACCEPTED; + } + else if (resp.rc == MQTT_UNACCEPTABLE_PROTOCOL_VERSION) + { + rc = MQTTSN_RC_NOT_SUPPORTED; + WRITELOG(" ClientID : %s Requested Protocol version is not supported.\n", client->getClientId()); + } + else if (resp.rc == MQTT_IDENTIFIER_REJECTED) + { + rc = MQTTSN_RC_NOT_SUPPORTED; + WRITELOG(" ClientID : %s ClientID is collect UTF-8 but not allowed by the Server.\n", + client->getClientId()); + } + else if (resp.rc == MQTT_SERVER_UNAVAILABLE) + { + rc = MQTTSN_RC_REJECTED_CONGESTED; + WRITELOG(" ClientID : %s The Network Connection has been made but the MQTT service is unavailable.\n", + client->getClientId()); + } + else if (resp.rc == MQTT_BAD_USERNAME_OR_PASSWORD) + { + rc = MQTTSN_RC_NOT_SUPPORTED; + WRITELOG(" Gateway Configuration Error: The data in the user name or password is malformed.\n"); + } + else if (resp.rc == MQTT_NOT_AUTHORIZED) + { + rc = MQTTSN_RC_NOT_SUPPORTED; + WRITELOG(" Gateway Configuration Error: The Client is not authorized to connect.\n"); + } + + MQTTSNPacket* snPacket = new MQTTSNPacket(); + snPacket->setCONNACK(rc); + + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); + client->connackSended(rc); // update the client's status + _gateway->getClientSendQue()->post(ev1); + + MQTTSNPacket* sleepPacket = 0; + while ( (sleepPacket = client->getClientSleepPacket()) ) + { + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, sleepPacket); + _gateway->getClientSendQue()->post(ev1); + } +} + +void MQTTGWConnectionHandler::handlePingresp(Client* client, MQTTGWPacket* packet) +{ + MQTTSNPacket* snPacket = new MQTTSNPacket(); + snPacket->setPINGRESP(); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); + _gateway->getClientSendQue()->post(ev1); +} + +void MQTTGWConnectionHandler::handleDisconnect(Client* client, MQTTGWPacket* packet) +{ + MQTTSNPacket* snPacket = new MQTTSNPacket(); + snPacket->setDISCONNECT(0); + client->disconnected(); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); +} + diff --git a/MQTTSNGateway/src/MQTTGWConnectionHandler.h b/MQTTSNGateway/src/MQTTGWConnectionHandler.h new file mode 100644 index 0000000..8d8af3c --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWConnectionHandler.h @@ -0,0 +1,42 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTGWCONNECTIONHANDLER_H_ +#define MQTTGWCONNECTIONHANDLER_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNGateway.h" + +namespace MQTTSNGW +{ + +class MQTTGWConnectionHandler +{ +public: + MQTTGWConnectionHandler(Gateway* gateway); + ~MQTTGWConnectionHandler(); + void handleConnack(Client* client, MQTTGWPacket* packet); + void handlePingresp(Client* client, MQTTGWPacket* packet); + void handleDisconnect(Client* client, MQTTGWPacket* packet); + +private: + Gateway* _gateway; +}; + +} + + +#endif /* MQTTGWCONNECTIONHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTGWPacket.cpp b/MQTTSNGateway/src/MQTTGWPacket.cpp new file mode 100644 index 0000000..858bf35 --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWPacket.cpp @@ -0,0 +1,530 @@ +/************************************************************************************** + * Copyright (c) 2009, 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Tomoaki Yamaguchi - modify codes for MATT-SN Gateway + **************************************************************************************/ + +#include "MQTTGWPacket.h" +#include + +using namespace MQTTSNGW; + +#define MAX_NO_OF_REMAINING_LENGTH_BYTES 3 +/** + * List of the predefined MQTT v3 packet names. + */ +static const char* mqtt_packet_names[] = +{ "RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL", "PUBCOMP", "SUBSCRIBE", "SUBACK", + "UNSUBSCRIBE", "UNSUBACK", "PINGREQ", "PINGRESP", "DISCONNECT" }; + +/** + * Encodes the message length according to the MQTT algorithm + * @param buf the buffer into which the encoded data is written + * @param length the length to be encoded + * @return the number of bytes written to buffer + */ +int MQTTPacket_encode(char* buf, int length) +{ + int rc = 0; + do + { + char d = length % 128; + length /= 128; + /* if there are more digits to encode, set the top bit of this digit */ + if (length > 0) + d |= 0x80; + buf[rc++] = d; + } while (length > 0); + return rc; +} + +/** + * Calculates an integer from two bytes read from the input buffer + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the integer value calculated + */ +int readInt(char** pptr) +{ + char* ptr = *pptr; + int len = 256 * ((unsigned char) (*ptr)) + (unsigned char) (*(ptr + 1)); + *pptr += 2; + return len; +} + +/** + * Reads a "UTF" string from the input buffer. UTF as in the MQTT v3 spec which really means + * a length delimited string. So it reads the two byte length then the data according to + * that length. The end of the buffer is provided too, so we can prevent buffer overruns caused + * by an incorrect length. + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @param enddata pointer to the end of the buffer not to be read beyond + * @param len returns the calculcated value of the length bytes read + * @return an allocated C string holding the characters read, or NULL if the length read would + * have caused an overrun. + * + */ +char* readUTFlen(char** pptr, char* enddata, int* len) +{ + char* string = NULL; + + if (enddata - (*pptr) > 1) /* enough length to read the integer? */ + { + *len = readInt(pptr); + if (&(*pptr)[*len] <= enddata) + { + string = (char*)calloc(*len + 1, 1); + memcpy(string, *pptr, (size_t)*len); + string[*len] = '\0'; + *pptr += *len; + } + } + return string; +} + +/** + * Reads a "UTF" string from the input buffer. UTF as in the MQTT v3 spec which really means + * a length delimited string. So it reads the two byte length then the data according to + * that length. The end of the buffer is provided too, so we can prevent buffer overruns caused + * by an incorrect length. + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @param enddata pointer to the end of the buffer not to be read beyond + * @return an allocated C string holding the characters read, or NULL if the length read would + * have caused an overrun. + */ +char* readUTF(char** pptr, char* enddata) +{ + int len; + return readUTFlen(pptr, enddata, &len); +} + +/** + * Reads one character from the input buffer. + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the character read + */ +unsigned char readChar(char** pptr) +{ + unsigned char c = **pptr; + (*pptr)++; + return c; +} + +/** + * Writes one character to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param c the character to write + */ +void writeChar(unsigned char** pptr, char c) +{ + **pptr = c; + (*pptr)++; +} + +/** + * Writes an integer as 2 bytes to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param anInt the integer to write + */ +void writeInt(unsigned char** pptr, int anInt) +{ + **pptr = (unsigned char)(anInt / 256); + (*pptr)++; + **pptr = (unsigned char)(anInt % 256); + (*pptr)++; +} + +/** + * Writes a "UTF" string to an output buffer. Converts C string to length-delimited. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param string the C string to write + */ +void writeUTF(unsigned char** pptr, const char* string) +{ + int len = (int)strlen(string); + writeInt(pptr, len); + memcpy(*pptr, string, (size_t)len); + *pptr += len; +} + +/** + * Lapper class of MQTTPacket + * + */ +MQTTGWPacket::MQTTGWPacket() +{ + _data = 0; + _header.byte = 0; + _remainingLength = 0; +} + +MQTTGWPacket::~MQTTGWPacket() +{ + if (_data) + { + free(_data); + } +} + +int MQTTGWPacket::recv(Network* network) +{ + int len = 0; + int multiplier = 1; + unsigned char c; + + /* read First Byte of Packet */ + if (network->recv((unsigned char*)&_header.byte, 1) == -1) + { + return -1; + } + /* read RemainingLength */ + do + { + if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) + { + return -2; + } + if (network->recv(&c, 1) == -1) + { + return -2; + } + _remainingLength += (c & 127) * multiplier; + multiplier *= 128; + } while ((c & 128) != 0); + + /* allocate buffer */ + _data = (unsigned char*)calloc(_remainingLength, 1); + if ( !_data ) + { + return -3; + } + + /* read Payload */ + int remlen = network->recv(_data, _remainingLength); + + if (remlen == -1 || remlen != _remainingLength ) + { + return -2; + } + return 1 + len + _remainingLength; +} + +int MQTTGWPacket::send(Network* network) +{ + unsigned char buf[MQTTSNGW_MAX_PACKET_SIZE]; + memset(buf, 0, MQTTSNGW_MAX_PACKET_SIZE); + int len = getPacketData(buf); + return network->send(buf, len); + +} + +int MQTTGWPacket::getAck(Ack* ack) +{ + if (PUBACK != _header.bits.type && PUBREC != _header.bits.type && PUBREL != _header.bits.type + && PUBCOMP != _header.bits.type && UNSUBACK != _header.bits.type) + { + return 0; + } + char* ptr = (char*) _data; + ack->header.byte = _header.byte; + ack->msgId = readInt((char**) &ptr); + return 1; +} + +int MQTTGWPacket::getCONNACK(Connack* resp) +{ + if (_header.bits.type != CONNACK) + { + return 0; + } + char* ptr = (char*) _data; + resp->header.byte = _header.byte; + resp->flags.all = *ptr++; + resp->rc = readChar(&ptr); + return 1; +} + +int MQTTGWPacket::getSUBACK(unsigned short* msgId, unsigned char* rc) +{ + if (_header.bits.type != SUBACK) + { + return 0; + } + char *ptr = (char*) _data; + *msgId = readInt((char**) &ptr); + *rc = readChar(&ptr); + return 1; +} + +int MQTTGWPacket::getPUBLISH(Publish* pub) +{ + if (_header.bits.type != PUBLISH) + { + return 0; + } + char* ptr = (char*) _data; + pub->header = _header; + pub->topiclen = readInt((char**) &ptr); + pub->topic = (char*) _data + 2; + ptr += pub->topiclen; + pub->msgId = readInt(&ptr); + pub->payload = ptr; + pub->payloadlen = _remainingLength - pub->topiclen - 4; + return 1; +} + +int MQTTGWPacket::setCONNECT(Connect* connect, unsigned char* username, unsigned char* password) +{ + clearData(); + _header = connect->header; + + _remainingLength = ((connect->version == 3) ? 12 : 10) + (int)strlen(connect->clientID) + 2; + if (connect->flags.bits.will) + { + _remainingLength += (int)strlen(connect->willTopic) + 2 + (int)strlen(connect->willMsg) + 2; + } + if ( connect->flags.bits.username ) + { + _remainingLength += (int)strlen((char*) username) + 2; + } + if (connect->flags.bits.password) + { + _remainingLength += (int)strlen((char*) password) + 2; + } + + _data = (unsigned char*)calloc(_remainingLength, 1); + unsigned char* ptr = _data; + + if (connect->version == 3) + { + writeUTF(&ptr, "MQIsdp"); + writeChar(&ptr, (char) 3); + } + else if (connect->version == 4) + { + writeUTF(&ptr, "MQTT"); + writeChar(&ptr, (char) 4); + } + else + { + return 0; + } + + writeChar(&ptr, connect->flags.all); + writeInt(&ptr, connect->keepAliveTimer); + writeUTF(&ptr, connect->clientID); + if (connect->flags.bits.will) + { + writeUTF(&ptr, connect->willTopic); + writeUTF(&ptr, connect->willMsg); + } + + if (connect->flags.bits.username) + { + writeUTF(&ptr, (const char*) username); + } + if (connect->flags.bits.password) + { + writeUTF(&ptr, (const char*) password); + } + return 1; +} + +int MQTTGWPacket::setSUBSCRIBE(const char* topic, unsigned char qos, unsigned short msgId) +{ + clearData(); + _header.byte = 0; + _header.bits.type = SUBSCRIBE; + _header.bits.qos = 1; // Reserved + _remainingLength = (int)strlen(topic) + 5; + _data = (unsigned char*)calloc(_remainingLength, 1); + if (_data) + { + unsigned char* ptr = _data; + writeInt(&ptr, msgId); + writeUTF(&ptr, topic); + writeChar(&ptr, (char) qos); + return 1; + } + clearData(); + return 0; +} + +int MQTTGWPacket::setUNSUBSCRIBE(const char* topic, unsigned short msgid) +{ + clearData(); + _header.byte = 0; + _header.bits.type = UNSUBSCRIBE; + _header.bits.qos = 1; + _remainingLength = (int)strlen(topic) + 4; + _data = (unsigned char*)calloc(_remainingLength, 1); + if (_data) + { + unsigned char* ptr = _data; + writeInt(&ptr, msgid); + writeUTF(&ptr, topic); + return 1; + } + clearData(); + return 0; + +} + +int MQTTGWPacket::setPUBLISH(Publish* pub) +{ + clearData(); + _header.byte = pub->header.byte; + _header.bits.type = PUBLISH; + _remainingLength = 4 + pub->topiclen + pub->payloadlen; + _data = (unsigned char*)calloc(_remainingLength, 1); + if (_data) + { + unsigned char* ptr = _data; + writeInt(&ptr, pub->topiclen); + memcpy(ptr, pub->topic, pub->topiclen); + ptr += pub->topiclen; + writeInt(&ptr, pub->msgId); + memcpy(ptr, pub->payload, pub->payloadlen); + return 1; + } + else + { + clearData(); + return 0; + } +} + +int MQTTGWPacket::setAck(unsigned char msgType, unsigned short msgid) +{ + clearData(); + _remainingLength = 2; + _header.bits.type = msgType; + _header.bits.qos = (msgType == PUBREL) ? 1 : 0; + + _data = (unsigned char*)calloc(_remainingLength, 1); + if (_data) + { + unsigned char* data = _data; + writeInt(&data, msgid); + return 1; + } + return 0; +} + +int MQTTGWPacket::setHeader(unsigned char msgType) +{ + clearData(); + if (msgType < CONNECT || msgType > DISCONNECT) + { + return 0; + } + _header.bits.type = msgType; + return 0; +} + +int MQTTGWPacket::getType(void) +{ + return _header.bits.type; +} + +const char* MQTTGWPacket::getName(void) +{ + return getType() > DISCONNECT ? "UNKNOWN" : mqtt_packet_names[getType()]; +} + +int MQTTGWPacket::getPacketData(unsigned char* buf) +{ + unsigned char* ptr = buf; + *ptr++ = _header.byte; + int len = MQTTPacket_encode((char*)ptr, _remainingLength); + ptr += len; + memcpy(ptr, _data, _remainingLength); + return 1 + len + _remainingLength; +} + +int MQTTGWPacket::getPacketLength(void) +{ + char buf[4]; + return 1 + MQTTPacket_encode(buf, _remainingLength) + _remainingLength; +} + +void MQTTGWPacket::clearData(void) +{ + if (_data) + { + free(_data); + } + _header.byte = 0; + _remainingLength = 0; +} + +char* MQTTGWPacket::getMsgId(char* pbuf) +{ + int type = getType(); + + switch ( type ) + { + case PUBLISH: + Publish pub; + getPUBLISH(&pub); + if ( _header.bits.dup ) + { + sprintf(pbuf, "+%04X", pub.msgId); + } + else + { + sprintf(pbuf, " %04X", pub.msgId); + } + break; + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + case SUBSCRIBE: + case UNSUBSCRIBE: + case SUBACK: + case UNSUBACK: + sprintf(pbuf, " %02X%02X", _data[0], _data[1]); + break; + default: + sprintf(pbuf, " "); + break; + } + return pbuf; +} + +char* MQTTGWPacket::print(char* pbuf) +{ + char* ptr = pbuf; + char** pptr = &pbuf; + char digit[4]; + + sprintf(*pptr, " %02X",(const unsigned char)_header.byte); + *pptr += 3; + int len = MQTTPacket_encode((char*) digit, _remainingLength); + for (int i = 0; i < len; i++) + { + sprintf(*pptr, " %02X", digit[i]); + *pptr += 3; + } + + int size = _remainingLength > SIZEOF_LOG_PACKET ? SIZEOF_LOG_PACKET : _remainingLength; + for (int i = 0; i < size; i++) + { + sprintf(*pptr, " %02X", *(_data + i)); + *pptr += 3; + } + **pptr = 0; + return ptr; +} + diff --git a/MQTTSNGateway/src/MQTTGWPacket.h b/MQTTSNGateway/src/MQTTGWPacket.h new file mode 100644 index 0000000..ecba6bb --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWPacket.h @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2009, 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs, Allan Stockdill-Mander - SSL updates + * Ian Craggs - MQTT 3.1.1 support + * Tomoaki Yamaguchi - modify codes for MATT-SN Gateway + *******************************************************************************/ + +#ifndef MQTTGWPACKET_H_ +#define MQTTGWPACKET_H_ + +#include "Network.h" + +namespace MQTTSNGW +{ + +typedef void* (*pf)(unsigned char, char*, size_t); + +#define BAD_MQTT_PACKET -4 + +enum msgTypes +{ + CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, + PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, + PINGREQ, PINGRESP, DISCONNECT +}; + + +/** + * Bitfields for the MQTT header byte. + */ +typedef union +{ + /*unsigned*/ char byte; /**< the whole byte */ +#if defined(REVERSED) + struct + { + unsigned int type : 4; /**< message type nibble */ + bool dup : 1; /**< DUP flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + bool retain : 1; /**< retained flag bit */ + } bits; +#else + struct + { + bool retain : 1; /**< retained flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + bool dup : 1; /**< DUP flag bit */ + unsigned int type : 4; /**< message type nibble */ + } bits; +#endif +} Header; + + +/** + * Data for a connect packet. + */ + +enum MQTT_connackCodes{ + MQTT_CONNECTION_ACCEPTED , + MQTT_UNACCEPTABLE_PROTOCOL_VERSION, + MQTT_IDENTIFIER_REJECTED, + MQTT_SERVER_UNAVAILABLE, + MQTT_BAD_USERNAME_OR_PASSWORD, + MQTT_NOT_AUTHORIZED +}; + +typedef struct +{ + Header header; /**< MQTT header byte */ + union + { + unsigned char all; /**< all connect flags */ +#if defined(REVERSED) + struct + { + bool username : 1; /**< 3.1 user name */ + bool password : 1; /**< 3.1 password */ + bool willRetain : 1; /**< will retain setting */ + unsigned int willQoS : 2; /**< will QoS value */ + bool will : 1; /**< will flag */ + bool cleanstart : 1; /**< cleansession flag */ + int : 1; /**< unused */ + } bits; +#else + struct + { + int : 1; /**< unused */ + bool cleanstart : 1; /**< cleansession flag */ + bool will : 1; /**< will flag */ + unsigned int willQoS : 2; /**< will QoS value */ + bool willRetain : 1; /**< will retain setting */ + bool password : 1; /**< 3.1 password */ + bool username : 1; /**< 3.1 user name */ + } bits; +#endif + } flags; /**< connect flags byte */ + + char *Protocol, /**< MQTT protocol name */ + *clientID, /**< string client id */ + *willTopic, /**< will topic */ + *willMsg; /**< will payload */ + + int keepAliveTimer; /**< keepalive timeout value in seconds */ + unsigned char version; /**< MQTT version number */ +} Connect; + +/** + * Data for a willMessage. + */ +typedef struct +{ + char* topic; + char* msg; + int retained; + int qos; +}willMessages; + +/** + * Data for a connack packet. + */ +typedef struct +{ + Header header; /**< MQTT header byte */ + union + { + unsigned char all; /**< all connack flags */ +#if defined(REVERSED) + struct + { + unsigned int reserved : 7; /**< message type nibble */ + bool sessionPresent : 1; /**< was a session found on the server? */ + } bits; +#else + struct + { + bool sessionPresent : 1; /**< was a session found on the server? */ + unsigned int reserved : 7; /**< message type nibble */ + } bits; +#endif + } flags; /**< connack flags byte */ + char rc; /**< connack return code */ +} Connack; + + +/** + * Data for a publish packet. + */ +typedef struct +{ + Header header; /**< MQTT header byte */ + char* topic; /**< topic string */ + int topiclen; + int msgId; /**< MQTT message id */ + char* payload; /**< binary payload, length delimited */ + int payloadlen; /**< payload length */ +} Publish; + + +/** + * Data for one of the ack packets. + */ +typedef struct +{ + Header header; /**< MQTT header byte */ + int msgId; /**< MQTT message id */ +} Ack; + +/** + * Class MQTT Packet + */ +class MQTTGWPacket +{ +public: + MQTTGWPacket(); + ~MQTTGWPacket(); + int recv(Network* network); + int send(Network* network); + int getType(void); + int getPacketData(unsigned char* buf); + int getPacketLength(void); + const char* getName(void); + + int getAck(Ack* ack); + int getCONNACK(Connack* resp); + int getSUBACK(unsigned short* msgId, unsigned char* rc); + int getPUBLISH(Publish* pub); + + int setCONNECT(Connect* conect, unsigned char* username, unsigned char* password); + int setPUBLISH(Publish* pub); + int setAck(unsigned char msgType, unsigned short msgid); + int setHeader(unsigned char msgType); + int setSUBSCRIBE(const char* topic, unsigned char qos, unsigned short msgId); + int setUNSUBSCRIBE(const char* topics, unsigned short msgid); + char* getMsgId(char* buf); + char* print(char* buf); + +private: + void clearData(void); + Header _header; + int _remainingLength; + unsigned char* _data; +}; + +} + +#endif /* MQTTGWPACKET_H_ */ diff --git a/MQTTSNGateway/src/MQTTGWPublishHandler.cpp b/MQTTSNGateway/src/MQTTGWPublishHandler.cpp new file mode 100644 index 0000000..cebb633 --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWPublishHandler.cpp @@ -0,0 +1,217 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTGWPublishHandler.h" +#include "MQTTSNGateway.h" +#include "MQTTSNPacket.h" +#include + +using namespace std; +using namespace MQTTSNGW; + +char* currentDateTime(void); + +MQTTGWPublishHandler::MQTTGWPublishHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTGWPublishHandler::~MQTTGWPublishHandler() +{ + +} + +void MQTTGWPublishHandler::handlePublish(Client* client, MQTTGWPacket* packet) +{ + if ( !client->isActive() && !client->isSleep() ) + { + WRITELOG(" The client is neither active nor sleep %s\n", client->getStatus()); + return; + } + + Publish pub; + packet->getPUBLISH(&pub); + + MQTTSNPacket* snPacket = new MQTTSNPacket(); + + /* create MQTTSN_topicid */ + MQTTSN_topicid topicId; + + if (pub.topiclen == 2) + { + topicId.type = MQTTSN_TOPIC_TYPE_SHORT; + *(topicId.data.short_name) = *pub.topic; + *(topicId.data.short_name + 1) = *(pub.topic + 1); + } + else + { + topicId.type = MQTTSN_TOPIC_TYPE_NORMAL; + topicId.data.long_.len = pub.topiclen; + topicId.data.long_.name = pub.topic; + unsigned short id = client->getTopics()->getTopicId(&topicId); + topicId.data.id = id; + } + + if (topicId.data.id == 0) + { + /* This message might be subscribed with wild card. */ + Topic* topic = client->getTopics()->match(&topicId); + if (topic == 0) + { + WRITELOG(" Invalid Topic. PUBLISH message is canceled.\n"); + if (pub.header.bits.qos == 1) + { + replyACK(client, &pub, PUBACK); + } + else if ( pub.header.bits.qos == 2 ) + { + replyACK(client, &pub, PUBREC); + } + return; + } + + /* add the Topic and get a TopicId */ + topic = client->getTopics()->add(&topicId); + uint16_t id = topic->getTopicId(); + + if (id > 0) + { + /* create REGACK */ + MQTTSNPacket* regPacket = new MQTTSNPacket(); + + MQTTSNString topicName; + topicName.lenstring.len = topicId.data.long_.len; + topicName.lenstring.data = topicId.data.long_.name; + + uint16_t regackMsgId = client->getNextSnMsgId(); + regPacket->setREGISTER(id, regackMsgId, &topicName); + + if (client->isSleep()) + { + client->setClientSleepPacket(regPacket); + WRITELOG(FORMAT_BL_NL, currentDateTime(), regPacket->getName(), + RIGHTARROW, client->getClientId(), "is sleeping. REGISTER was saved."); + } + else if (client->isActive()) + { + /* send REGISTER */ + Event* evrg = new Event(); + evrg->setClientSendEvent(client, regPacket); + _gateway->getClientSendQue()->post(evrg); + } + + /* send PUBLISH */ + topicId.data.id = id; + snPacket->setPUBLISH((uint8_t) pub.header.bits.dup, (int) pub.header.bits.qos, + (uint8_t) pub.header.bits.retain, (uint16_t) pub.msgId, topicId, (uint8_t*) pub.payload, + pub.payloadlen); + client->getWaitREGACKPacketList()->setPacket(snPacket, regackMsgId); + } + else + { + WRITELOG("\x1b[0m\x1b[31mMQTTGWPublishHandler Can't create a Topic.\n"); + return; + } + } + + /* TopicId was aquired. */ + if (client->isSleep()) + { + /* client is sleeping. save PUBLISH */ + client->setClientSleepPacket(snPacket); + WRITELOG(FORMAT_YE_NL, currentDateTime(), packet->getName(), + RIGHTARROW, client->getClientId(), "is sleeping. a message was saved."); + int type = 0; + if (pub.header.bits.qos == 1) + { + type = PUBACK; + } + else if ( pub.header.bits.qos == 2) + { + WRITELOG(" While Client is sleeping, QoS2 is not supported.\n"); + type = PUBREC; + } + replyACK(client, &pub, type); + pub.header.bits.qos = 0; + replyACK(client, &pub, PUBACK); + pub.msgId = 0; + snPacket->setPUBLISH((uint8_t) pub.header.bits.dup, (int) pub.header.bits.qos, + (uint8_t) pub.header.bits.retain, (uint16_t) pub.msgId, topicId, (uint8_t*) pub.payload, + pub.payloadlen); + client->setClientSleepPacket(snPacket); + } + else if (client->isActive()) + { + snPacket->setPUBLISH((uint8_t) pub.header.bits.dup, (int) pub.header.bits.qos, (uint8_t) pub.header.bits.retain, + (uint16_t) pub.msgId, topicId, (uint8_t*) pub.payload, pub.payloadlen); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); + _gateway->getClientSendQue()->post(ev1); + } + +} + +void MQTTGWPublishHandler::replyACK(Client* client, Publish* pub, int type) +{ + MQTTGWPacket* pubAck = new MQTTGWPacket(); + pubAck->setAck(type, (uint16_t)pub->msgId); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, pubAck); + _gateway->getBrokerSendQue()->post(ev1); +} + +void MQTTGWPublishHandler::handlePuback(Client* client, MQTTGWPacket* packet) +{ + Ack ack; + packet->getAck(&ack); + uint16_t topicId = client->getWaitedPubTopicId((uint16_t)ack.msgId); + if (topicId) + { + MQTTSNPacket* mqttsnPacket = new MQTTSNPacket(); + mqttsnPacket->setPUBACK(topicId, (uint16_t)ack.msgId, 0); + + client->eraseWaitedPubTopicId((uint16_t)ack.msgId); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, mqttsnPacket); + _gateway->getClientSendQue()->post(ev1); + return; + } + WRITELOG(" PUBACK from the Broker is invalid. PacketID : %04X ClientID : %s \n", (uint16_t)ack.msgId, client->getClientId()); +} + +void MQTTGWPublishHandler::handleAck(Client* client, MQTTGWPacket* packet, int type) +{ + Ack ack; + packet->getAck(&ack); + MQTTSNPacket* mqttsnPacket = new MQTTSNPacket(); + if (type == PUBREC) + { + mqttsnPacket->setPUBREC((uint16_t) ack.msgId); + } + else if (type == PUBREL) + { + mqttsnPacket->setPUBREL((uint16_t) ack.msgId); + } + else if (type == PUBCOMP) + { + mqttsnPacket->setPUBCOMP((uint16_t) ack.msgId); + } + + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, mqttsnPacket); + _gateway->getClientSendQue()->post(ev1); +} + diff --git a/MQTTSNGateway/src/MQTTGWPublishHandler.h b/MQTTSNGateway/src/MQTTGWPublishHandler.h new file mode 100644 index 0000000..72ac8b1 --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWPublishHandler.h @@ -0,0 +1,45 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTGWPUBLISHHANDLER_H_ +#define MQTTGWPUBLISHHANDLER_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNGateway.h" + +namespace MQTTSNGW +{ + +class MQTTGWPublishHandler +{ +public: + MQTTGWPublishHandler(Gateway* gateway); + ~MQTTGWPublishHandler(); + void handlePublish(Client* client, MQTTGWPacket* packet); + void handlePuback(Client* client, MQTTGWPacket* packet); + void handleAck(Client* client, MQTTGWPacket* packet, int type); + +private: + void replyACK(Client* client, Publish* pub, int type); + + Gateway* _gateway; +}; + +} + + + +#endif /* MQTTGWPUBLISHHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTGWSubscribeHandler.cpp b/MQTTSNGateway/src/MQTTGWSubscribeHandler.cpp new file mode 100644 index 0000000..b31ac7d --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWSubscribeHandler.cpp @@ -0,0 +1,74 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTGWSubscribeHandler.h" +#include "MQTTGWPacket.h" + +using namespace std; +using namespace MQTTSNGW; + +MQTTGWSubscribeHandler::MQTTGWSubscribeHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTGWSubscribeHandler::~MQTTGWSubscribeHandler() +{ + +} + +void MQTTGWSubscribeHandler::handleSuback(Client* client, MQTTGWPacket* packet) +{ + uint16_t msgId; + uint8_t rc; + uint8_t returnCode; + int qos = 0; + + packet->getSUBACK(&msgId, &rc); + uint16_t topicId = client->getWaitedSubTopicId(msgId); + + if (topicId) + { + MQTTSNPacket* snPacket = new MQTTSNPacket(); + client->eraseWaitedSubTopicId(msgId); + + if (rc == 0x80) + { + returnCode = MQTTSN_RC_REJECTED_INVALID_TOPIC_ID; + } + else + { + returnCode = MQTTSN_RC_ACCEPTED; + qos = rc; + } + snPacket->setSUBACK(qos, topicId, msgId, returnCode); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); + _gateway->getClientSendQue()->post(ev1); + } +} + +void MQTTGWSubscribeHandler::handleUnsuback(Client* client, MQTTGWPacket* packet) +{ + Ack ack; + packet->getAck(&ack); + MQTTSNPacket* snPacket = new MQTTSNPacket(); + snPacket->setUNSUBACK(ack.msgId); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); + _gateway->getClientSendQue()->post(ev1); +} + diff --git a/MQTTSNGateway/src/MQTTGWSubscribeHandler.h b/MQTTSNGateway/src/MQTTGWSubscribeHandler.h new file mode 100644 index 0000000..1775826 --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWSubscribeHandler.h @@ -0,0 +1,41 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTGWSUBSCRIBEHANDLER_H_ +#define MQTTGWSUBSCRIBEHANDLER_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTGWPacket.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" + +namespace MQTTSNGW +{ + +class MQTTGWSubscribeHandler +{ +public: + MQTTGWSubscribeHandler(Gateway* gateway); + ~MQTTGWSubscribeHandler(); + void handleSuback(Client* clnode, MQTTGWPacket* packet); + void handleUnsuback(Client* clnode, MQTTGWPacket* packet); + +private: + Gateway* _gateway; +}; + +} + +#endif /* MQTTGWSUBSCRIBEHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.cpp b/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.cpp new file mode 100644 index 0000000..83f1c05 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.cpp @@ -0,0 +1,195 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWBrokerRecvTask.h" +#include "MQTTSNGWClient.h" + +using namespace std; +using namespace MQTTSNGW; + +char* currentDateTime(void); + +/*===================================== + Class BrokerRecvTask + =====================================*/ +BrokerRecvTask::BrokerRecvTask(Gateway* gateway) +{ + _gateway = gateway; + _gateway->attach((Thread*)this); + _light = 0; +} + +BrokerRecvTask::~BrokerRecvTask() +{ + +} + +/** + * Initialize attributs of this class + */ +void BrokerRecvTask::initialize(int argc, char** argv) +{ + _light = _gateway->getLightIndicator(); +} + +/** + * receive a MQTT messge from the broker and post a event. + */ +void BrokerRecvTask::run(void) +{ + struct timeval timeout; + MQTTGWPacket* packet = 0; + int rc; + Event* ev = 0; + fd_set rset; + fd_set wset; + + while (true) + { + timeout.tv_sec = 0; + timeout.tv_usec = 500000; // 500 msec + FD_ZERO(&rset); + FD_ZERO(&wset); + int maxSock = 0; + int sockfd = 0; + + /* Prepare sockets list to read */ + Client* client = _gateway->getClientList()->getClient(); + _light->blueLight(false); + + while (client > 0) + { + if (client->getNetwork()->isValid()) + { + sockfd = client->getNetwork()->getSock(); + FD_SET(sockfd, &rset); + FD_SET(sockfd, &wset); + if (sockfd > maxSock) + { + maxSock = sockfd; + } + } + client = client->getNextClient(); + } + + if (maxSock > 0) + { + /* Check sockets is ready to read */ + int activity = select(maxSock + 1, &rset, 0, 0, &timeout); + + if (activity > 0) + { + client = _gateway->getClientList()->getClient(); + + while (client > 0) + { + _light->blueLight(false); + if (client->getNetwork()->isValid()) + { + int sockfd = client->getNetwork()->getSock(); + if (FD_ISSET(sockfd, &rset)) + { + packet = new MQTTGWPacket(); + rc = 0; + /* read sockets */ + _light->blueLight(true); + rc = packet->recv(client->getNetwork()); + if ( rc > 0 ) + { + log(client, packet); + + /* post a BrokerRecvEvent */ + ev = new Event(); + ev->setBrokerRecvEvent(client, packet); + _gateway->getPacketEventQue()->post(ev); + } + else + { + _light->blueLight(false); + if ( rc == 0 ) + { + delete packet; + continue; + } + else if (rc == -1) + { + WRITELOG("%s BrokerRecvTask can't receive a packet from the broker errno=%d %s%s\n", ERRMSG_HEADER, errno, client->getClientId(), ERRMSG_FOOTER); + } + else if ( rc == -2 ) + { + WRITELOG("%s BrokerRecvTask receive invalid length of packet from the broker. DISCONNECT %s %s\n", ERRMSG_HEADER, client->getClientId(),ERRMSG_FOOTER); + } + else if ( rc == -3 ) + { + WRITELOG("%s BrokerRecvTask can't create the packet %s%s\n", ERRMSG_HEADER, client->getClientId(), ERRMSG_FOOTER); + } + + /* disconnect the client */ + client->disconnected(); + client->getNetwork()->disconnect(); + rc = 0; + delete packet; + } + } + } + client = client->getNextClient(); + } + _light->blueLight(false); + } + } + else + { + _light->greenLight(false); + } + maxSock = 0; + } +} + +/** + * write message content into stdout or Ringbuffer + */ +void BrokerRecvTask::log(Client* client, MQTTGWPacket* packet) +{ + char pbuf[SIZEOF_LOG_PACKET * 3]; + char msgId[6]; + + switch (packet->getType()) + { + case CONNACK: + case DISCONNECT: + WRITELOG(FORMAT_YE_GR_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + case PUBLISH: + WRITELOG(FORMAT_GR_MSGID_NL, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + WRITELOG(FORMAT_GR_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + case SUBACK: + case UNSUBACK: + WRITELOG(FORMAT_YE_GR_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + case PINGRESP: + WRITELOG(FORMAT_GR_NL, currentDateTime(), packet->getName(), LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + default: + WRITELOG(FORMAT_GR_NL, currentDateTime(), "UNKOWN_TYPE", LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + } +} diff --git a/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.h b/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.h new file mode 100644 index 0000000..e506e95 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.h @@ -0,0 +1,49 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWBROKERRECVTASK_H_ +#define MQTTSNGWBROKERRECVTASK_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGateway.h" + +namespace MQTTSNGW +{ + +#define ERRNO_APL_03 13 // Task Initialize Error +/*===================================== + Class BrokerRecvTask + =====================================*/ +class BrokerRecvTask: public Thread +{ +MAGIC_WORD_FOR_THREAD; + ; +public: + BrokerRecvTask(Gateway* gateway); + ~BrokerRecvTask(); + void initialize(int argc, char** argv); + void run(void); + +private: + void log(Client*, MQTTGWPacket*); + + Gateway* _gateway; + LightIndicator* _light; +}; + +} + + +#endif /* MQTTSNGWBROKERRECVTASK_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.cpp b/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.cpp new file mode 100644 index 0000000..b0ee3a2 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.cpp @@ -0,0 +1,162 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWBrokerSendTask.h" +#include "MQTTSNGWDefines.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" +#include "MQTTGWPacket.h" + +using namespace std; +using namespace MQTTSNGW; + +char* currentDateTime(); +#define ERRMSG_FORMAT "\n%s \x1b[0m\x1b[31merror:\x1b[0m\x1b[37m Can't Xmit to the Broker. errno=%d\n" + +/*===================================== + Class BrokerSendTask + =====================================*/ +BrokerSendTask::BrokerSendTask(Gateway* gateway) +{ + _gateway = gateway; + _gateway->attach((Thread*)this); + _host = 0; + _service = 0; + _light = 0; +} + +BrokerSendTask::~BrokerSendTask() +{ + if (_host) + { + free(_host); + } + if (_service) + { + free(_service); + } +} + +/** + * Initialize attributs of this class + */ +void BrokerSendTask::initialize(int argc, char** argv) +{ + char param[MQTTSNGW_PARAM_MAX]; + + if (_gateway->getParam("BrokerName", param) == 0) + { + _host = strdup(param); + } + if (_gateway->getParam("BrokerPortNo", param) == 0) + { + _service = strdup(param); + } + _light = _gateway->getLightIndicator(); +} + +/** + * connect to the broker and send MQTT messges + */ +void BrokerSendTask::run() +{ + Event* ev = 0; + MQTTGWPacket* packet = 0; + Client* client = 0; + + while (true) + { + int rc = 0; + ev = _gateway->getBrokerSendQue()->wait(); + client = ev->getClient(); + packet = ev->getMQTTGWPacket(); + + if ( !client->getNetwork()->isValid() ) + { + /* connect to the broker and send a packet */ + if ( !client->getNetwork()->connect(_host, _service) ) + { + /* disconnect the broker and chage the client's status */ + WRITELOG("%s BrokerSendTask can't open the socket. errno=%d %s%s\n", + ERRMSG_HEADER, rc == -1 ? errno : 0, client->getClientId(), ERRMSG_FOOTER); + client->disconnected(); + client->getNetwork()->disconnect(); + delete ev; + continue; + } + } + + /* send a packet */ + _light->blueLight(true); + if ( (rc = packet->send(client->getNetwork())) > 0 ) + { + if ( packet->getType() == CONNECT ) + { + client->setWaitWillMsgFlg(false); + client->connectSended(); + } + log(client, packet); + } + else + { + WRITELOG("%s BrokerSendTask can't send a packet. errno=%d %s%s\n", + ERRMSG_HEADER, rc == -1 ? errno : 0, client->getClientId(), ERRMSG_FOOTER); + WRITELOG("%s BrokerSendTask can't send a packet to the broker errno=%d %s%s\n", + ERRMSG_HEADER, rc == -1 ? errno : 0, client->getClientId(), ERRMSG_FOOTER); + client->disconnected(); + client->getNetwork()->disconnect(); + } + + _light->blueLight(false); + delete ev; + } +} + + +/** + * write message content into stdout or Ringbuffer + */ +void BrokerSendTask::log(Client* client, MQTTGWPacket* packet) +{ + char pbuf[SIZEOF_LOG_PACKET * 3]; + char msgId[6]; + + switch (packet->getType()) + { + case CONNECT: + WRITELOG(FORMAT_YE_GR, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case PUBLISH: + WRITELOG(FORMAT_WH_GR_MSGID_NL, currentDateTime(), packet->getName(), packet->getMsgId(msgId), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case SUBSCRIBE: + case UNSUBSCRIBE: + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + WRITELOG(FORMAT_WH_GR_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case PINGREQ: + WRITELOG(FORMAT_YE, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case DISCONNECT: + WRITELOG(FORMAT_YE, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + default: + break; + } +} diff --git a/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.h b/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.h new file mode 100644 index 0000000..0d30004 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.h @@ -0,0 +1,47 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWBROKERSENDTASK_H_ +#define MQTTSNGWBROKERSENDTASK_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" + +namespace MQTTSNGW +{ +#define ERRNO_APL_04 14 // Task Initialize Error +/*===================================== + Class BrokerSendTask + =====================================*/ +class BrokerSendTask : public Thread +{ + MAGIC_WORD_FOR_THREAD; +public: + BrokerSendTask(Gateway* gateway); + ~BrokerSendTask(); + void initialize(int argc, char** argv); + void run(); +private: + void log(Client*, MQTTGWPacket*); + + Gateway* _gateway; + char* _host; + char* _service; + LightIndicator* _light; +}; + +} +#endif /* MQTTSNGWBROKERSENDTASK_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWClient.cpp b/MQTTSNGateway/src/MQTTSNGWClient.cpp new file mode 100644 index 0000000..d8dd663 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClient.cpp @@ -0,0 +1,1142 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWClient.h" +#include "MQTTSNGateway.h" +#include "SensorNetwork.h" +#include "Network.h" +#include +#include +#include + +using namespace MQTTSNGW; + +/*===================================== + Class ClientList + =====================================*/ +ClientList::ClientList() +{ + _clientCnt = 0; + _authorize = false; + _firstClient = 0; + _endClient = 0; +} + +ClientList::~ClientList() +{ + _mutex.lock(); + Client* cl = _firstClient; + Client* ncl; + + while (cl != 0) + { + ncl = cl->_nextClient; + delete cl; + cl = ncl; + }; + _mutex.unlock(); +} + +/** + * Create ClientList from a client list file. + * @param File name of the client list + * @return true: Reject client connection that is not registered in the client list + * + * File format is: + * Lines bigning with # are comment line. + * ClientId, SensorNetAddress, "unstableLine", "secureConnection" + * in case of UDP, SensorNetAddress format is portNo@IPAddress. + * if the SensorNetwork is not stable, write unstableLine. + * if BrokerConnection is SSL, write secureConnection. + * + * Ex: + * #Client List + * ClientId1,11200@192.168.10.10 + * ClientID2,35000@192.168.50.200,unstableLine + * ClientID3,40000@192.168.200.50,secureConnection + * ClientID4,41000@192.168.200.51,unstableLine,secureConnection + */ +bool ClientList::authorize(const char* fileName) +{ + FILE* fp; + char buf[258]; + size_t pos;; + bool secure; + bool stable; + SensorNetAddress netAddr; + MQTTSNString clientId; + + clientId.cstring = 0; + clientId.lenstring.data = 0; + clientId.lenstring.len = 0; + + if ((fp = fopen(fileName, "r")) != 0) + { + while (fgets(buf, 256, fp) != 0) + { + if (*buf == '#') + { + continue; + } + string data = string(buf); + while ((pos = data.find_first_of("  \t\n")) != string::npos) + { + data.erase(pos, 1); + } + if (data.empty()) + { + continue; + } + pos = data.find_first_of(","); + string id = data.substr(0, pos); + + clientId.cstring = strdup(id.c_str()); + + string addr = data.substr(pos + 1); + + if (netAddr.setAddress(&addr) == 0) + { + secure = (data.find("secureConnection") != string::npos); + stable = !(data.find("unstableLine") != string::npos); + createClient(&netAddr, &clientId, stable, secure); + } + else + { + WRITELOG("Invalid address %s\n", data.c_str()); + } + } + fclose(fp); + _authorize = true; + } + return _authorize; +} + +void ClientList::erase(Client* client) +{ + _mutex.lock(); + Client* prev = client->_prevClient; + Client* next = client->_nextClient; + + if (prev) + { + prev->_nextClient = next; + } + else + { + _firstClient = next; + + } + if (next) + { + next->_prevClient = prev; + } + else + { + _endClient = prev; + } + _clientCnt--; + _authorize = false; + _mutex.unlock(); +} + +Client* ClientList::getClient(SensorNetAddress* addr) +{ + _mutex.lock(); + Client* client = _firstClient; + + while (client != 0) + { + if (client->getSensorNetAddress()->isMatch(addr) ) + { + _mutex.unlock(); + return client; + } + client = client->_nextClient; + } + _mutex.unlock(); + return 0; +} + +Client* ClientList::getClient(void) +{ + return _firstClient; +} + +Client* ClientList::createClient(SensorNetAddress* addr, MQTTSNString* clientId, bool unstableLine, bool secure) +{ + Client* client = 0; + + /* clients must be authorized */ + if ( _authorize ) + { + /* search cliene with sensorNetAddress from the list */ + return getClient(addr); + } + + /* anonimous clients */ + if ( _clientCnt > DEFAULT_MAX_CLIENTS ) + { + return 0; // full of clients + } + + client = getClient(addr); + if ( client ) + { + return client; + } + + /* creat a new client */ + client = new Client(secure); + client->setClientAddress(addr); + client->setSensorNetType(unstableLine); + if ( MQTTSNstrlen(*clientId) ) + { + client->setClientId(*clientId); + } + + /* add the list */ + if ( _firstClient == 0 ) + { + _firstClient = client; + _endClient = client; + } + else + { + _endClient->_nextClient = client; + client->_prevClient = _endClient; + _endClient = client; + } + _clientCnt++; + _mutex.unlock(); + return client; +} + +uint16_t ClientList::getClientCount() +{ + return _clientCnt; +} + +bool ClientList::isAuthorized() +{ + return _authorize; +} + +/*===================================== + Class Client + =====================================*/ +static const char* theClientStatus[] = { "Disconnected", "TryConnecting", "Connecting", "Active", "Awake", "Asleep", "Lost" }; + +Client::Client(bool secure) +{ + _packetId = 0; + _snMsgId = 0; + _status = Cstat_Disconnected; + _keepAliveMsec = 0; + _topics = new Topics(); + _clientId = 0; + _willTopic = 0; + _willMsg = 0; + _connectData.Protocol = 0; + _connectData.clientID = 0; + _connectData.flags.all = 0; + _connectData.header.byte = 0; + _connectData.keepAliveTimer = 0; + _connectData.version = 0; + _connectData.willMsg = 0; + _connectData.willTopic = 0; + _network = new Network(secure); + _secureNetwork = secure; + _sensorNetype = true; + _connAck = 0; + _waitWillMsgFlg = false; + _prevClient = 0; + _nextClient = 0; +} + +Client::~Client() +{ + if ( _topics ) + { + delete _topics; + } + + if ( _clientId ) + { + free(_clientId); + } + + if ( _willTopic ) + { + free(_willTopic); + } + + if ( _willMsg ) + { + free(_willMsg); + } + + if (_connAck) + { + delete _connAck; + } + + if (_network) + { + delete _network; + } +} + +uint16_t Client::getWaitedPubTopicId(uint16_t msgId) +{ + MQTTSN_topicTypes type; + return _waitedPubTopicIdMap.getTopicId(msgId, &type); +} + +uint16_t Client::getWaitedSubTopicId(uint16_t msgId) +{ + MQTTSN_topicTypes type; + return _waitedSubTopicIdMap.getTopicId(msgId, &type); +} + +MQTTSNPacket* Client::getClientSleepPacket() +{ + return _clientSleepPacketQue.getPacket(); +} + +Connect* Client::getConnectData(void) +{ + return &_connectData; +} + +void Client::eraseWaitedPubTopicId(uint16_t msgId) +{ + _waitedPubTopicIdMap.erase(msgId); +} + +void Client::eraseWaitedSubTopicId(uint16_t msgId) +{ + _waitedSubTopicIdMap.erase(msgId); +} + +void Client::clearWaitedPubTopicId(void) +{ + _waitedPubTopicIdMap.clear(); +} + +void Client::clearWaitedSubTopicId(void) +{ + _waitedSubTopicIdMap.clear(); +} + +void Client::setWaitedPubTopicId(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type) +{ + _waitedPubTopicIdMap.add(msgId, topicId, type); +} +void Client::setWaitedSubTopicId(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type) +{ + _waitedSubTopicIdMap.add(msgId, topicId, type); +} + +void Client::setClientSleepPacket(MQTTSNPacket* packet) +{ + updateStatus(packet); + _clientSleepPacketQue.post(packet); +} + +void Client::checkTimeover(void) +{ + if (_status == Cstat_Active && _keepAliveTimer.isTimeup()) + { + _status = Cstat_Lost; + _network->disconnect(); + } +} + +void Client::setKeepAlive(MQTTSNPacket* packet) +{ + MQTTSNPacket_connectData param; + if (packet->getCONNECT(¶m)) + { + _keepAliveMsec = param.duration * 1000UL; + _keepAliveTimer.start(_keepAliveMsec * 1.5); + } +} + +void Client::updateStatus(MQTTSNPacket* packet) +{ + if (((_status == Cstat_Disconnected) || (_status == Cstat_Lost)) && packet->getType() == MQTTSN_CONNECT) + { + setKeepAlive(packet); + } + else if (_status == Cstat_Active) + { + switch (packet->getType()) + { + case MQTTSN_PINGREQ: + case MQTTSN_PUBLISH: + case MQTTSN_SUBSCRIBE: + case MQTTSN_UNSUBSCRIBE: + case MQTTSN_PUBACK: + case MQTTSN_PUBCOMP: + case MQTTSN_PUBREL: + case MQTTSN_PUBREC: + _keepAliveTimer.start(_keepAliveMsec * 1.5); + break; + case MQTTSN_DISCONNECT: + { + uint16_t duration; + packet->getDISCONNECT(&duration); + if (duration) + { + _status = Cstat_Asleep; + _keepAliveMsec = duration * 1000UL; + } + else + { + disconnected(); + } + } + break; + default: + break; + } + + } + else if (_status == Cstat_Asleep) + { + if (packet->getType() == MQTTSN_CONNECT) + { + setKeepAlive(packet); + _status = Cstat_Connecting; + } + else if (packet->getType() == MQTTSN_PINGREQ) + { + if ( packet->getPINGREQ() > 0 ) + { + _status = Cstat_Awake; + } + } + } + else if (_status == Cstat_Awake) + { + switch (packet->getType()) + { + case MQTTSN_CONNECT: + _status = Cstat_Connecting; + setKeepAlive(packet); + break; + case MQTTSN_DISCONNECT: + disconnected(); + break; + case MQTTSN_PINGRESP: + _status = Cstat_Asleep; + break; + default: + break; + } + } +} + +void Client::updateStatus(ClientStatus stat) +{ + _status = stat; +} + +void Client::connectSended() +{ + _status = Cstat_Connecting; +} + +void Client::connackSended(int rc) +{ + if (rc == MQTTSN_RC_ACCEPTED) + { + _status = Cstat_Active; + } + else + { + disconnected(); + } +} + +void Client::disconnected(void) +{ + _status = Cstat_Disconnected; + _waitWillMsgFlg = false; +} + +bool Client::isConnectSendable(void) +{ + if (_status == Cstat_Lost || _status == Cstat_TryConnecting) + { + return false; + } + else + { + return true; + } +} + +uint16_t Client::getNextPacketId(void) +{ + _packetId++; + if ( _packetId == 0xffff ) + { + _packetId = 1; + } + return _packetId; +} + +uint8_t Client::getNextSnMsgId(void) +{ + _snMsgId++; + if (_snMsgId == 0) + { + _snMsgId++; + } + return _snMsgId; +} + +Topics* Client::getTopics(void) +{ + return _topics; +} + +Network* Client::getNetwork(void) +{ + return _network; +} + +void Client::setClientAddress(SensorNetAddress* sensorNetAddr) +{ + _sensorNetAddr = *sensorNetAddr; +} + +SensorNetAddress* Client::getSensorNetAddress(void) +{ + return &_sensorNetAddr; +} + +void Client::setSensorNetType(bool stable) +{ + _sensorNetype = stable; +} + +void Client::setTopics(Topics* topics) +{ + _topics = topics; +} + +void Client::setWaitWillMsgFlg(bool flg) +{ + _waitWillMsgFlg = flg; +} + +bool Client::isWaitWillMsg(void) +{ + return _waitWillMsgFlg; +} + +bool Client::isDisconnect(void) +{ + return (_status == Cstat_Disconnected); +} + +bool Client::isActive(void) +{ + return (_status == Cstat_Active); +} + +bool Client::isSleep(void) +{ + return (_status == Cstat_Asleep); +} + +bool Client::isSecureNetwork(void) +{ + return _secureNetwork; +} + +bool Client::isSensorNetStable(void) +{ + return _sensorNetype; +} + +WaitREGACKPacketList* Client::getWaitREGACKPacketList() +{ + return &_waitREGACKList; +} + +Client* Client::getNextClient(void) +{ + return _nextClient; +} + +void Client::setClientId(MQTTSNString id) +{ + if ( _clientId ) + { + free(_clientId); + } + + /* save clientId into (char*)_clientId NULL terminated */ + _clientId = (char*)calloc(MQTTSNstrlen(id) + 1, 1); + unsigned char* ptr = (unsigned char*)_clientId; + writeMQTTSNString((unsigned char**)&ptr, id); +} + +void Client::setWillTopic(MQTTSNString willTopic) +{ + if ( _willTopic ) + { + free(_willTopic); + } + + _willTopic = (char*)calloc(MQTTSNstrlen(willTopic) + 1, 1); + /* save willTopic into (char*)_willTopic with NULL termination */ + unsigned char* ptr = (unsigned char*)_willTopic; + writeMQTTSNString((unsigned char**)&ptr, willTopic); +} + +void Client::setWillMsg(MQTTSNString willMsg) +{ + if ( _willMsg) + { + free(_willMsg); + } + + _willMsg = (char*)calloc(MQTTSNstrlen(willMsg) + 1, 1); + /* save willMsg into (char*)_willMsg with NULL termination */ + unsigned char* ptr = (unsigned char*)_willMsg; + writeMQTTSNString((unsigned char**)&ptr, willMsg); +} + +char* Client::getClientId(void) +{ + return _clientId; +} + +char* Client::getWillTopic(void) +{ + return _willTopic; +} + +char* Client::getWillMsg(void) +{ + return _willMsg; +} + +const char* Client::getStatus(void) +{ + return theClientStatus[_status]; +} + +/*===================================== + Class Topic + ======================================*/ +Topic::Topic() +{ + _topicName = 0; + _topicId = 0; + _next = 0; +} + +Topic::Topic(string* topic) +{ + _topicName = topic; + _topicId = 0; + _next = 0; +} + +Topic::~Topic() +{ + if ( _topicName ) + { + delete _topicName; + } +} + +string* Topic::getTopicName(void) +{ + return _topicName; +} + +uint16_t Topic::getTopicId(void) +{ + return _topicId; +} + +int Topic::hasWildCard(unsigned int* pos) +{ + unsigned int p = _topicName->find("+", 0); + if (p != string::npos) + { + *pos = p; + return 1; + } + else + { + string::iterator it = _topicName->end(); + if (*it == '#') + { + *pos = _topicName->size() - 1; + return 2; + } + } + *pos = 0; + return 0; +} + +bool Topic::isMatch(string* topicName) +{ + unsigned int pos; + + if (topicName->size() < _topicName->size()) + { + return false; + } + + if (hasWildCard(&pos) == 1) + { + if (_topicName->compare(0, pos - 1, *topicName, 0, pos - 1) == 0) + { + if (_topicName->compare(pos + 1, 1, "/") == 0) + { + unsigned int loc = topicName->find('/', pos); + if (loc != 0) + { + if (_topicName->compare(pos + 1, _topicName->size() - pos - 1, *topicName, loc, + topicName->size() - pos - 1) == 0) + { + return true; + } + } + } + else + { + unsigned int loc = _topicName->find(pos, '/'); + if (loc != 0) + { + if (topicName->find('/', loc) != 0) + { + return false; + } + } + } + return true; + } + } + else if (hasWildCard(&pos) == 2 && (_topicName->compare(0, pos, *topicName, 0, pos) == 0)) + { + return true; + } + else if (_topicName->compare(*topicName) == 0) + { + return true; + } + return false; +} + +/*===================================== + Class Topics + ======================================*/ +Topics::Topics() +{ + _first = 0; + _nextTopicId = 0; +} + +Topics::~Topics() +{ + Topic* p = _first; + while (p) + { + Topic* q = p->_next; + delete p; + p = q; + } +} + +uint16_t Topics::getTopicId(MQTTSN_topicid* topicid) +{ + if (topicid->type != MQTTSN_TOPIC_TYPE_NORMAL) + { + return 0; + } + + Topic* p = _first; + while (p) + { + if (strncmp(p->_topicName->c_str(), topicid->data.long_.name, topicid->data.long_.len) == 0) + { + return p->_topicId; + } + p = p->_next; + } + return 0; +} + +Topic* Topics::getTopic(uint16_t id) +{ + Topic* p = _first; + while (p) + { + if (p->_topicId == id) + { + return p; + } + p = p->_next; + } + return 0; +} + +Topic* Topics::getTopic(MQTTSN_topicid* topicid) +{ + Topic* p = _first; + while (p) + { + if (p->_topicId == topicid->data.id) + { + return p; + } + p = p->_next; + } + return 0; +} + +Topic* Topics::add(MQTTSN_topicid* topicid) +{ + Topic* topic; + uint16_t id = 0; + string* topicName = 0; + + if (topicid->type != MQTTSN_TOPIC_TYPE_NORMAL) + { + return 0; + } + id = getTopicId(topicid); + + if (id) + { + topic = getTopic(id); + } + else + { + topicName = new string(topicid->data.long_.name, topicid->data.long_.len); + topic = add(topicName); + } + return topic; +} + + +Topic* Topics::add(string* topicName) +{ + Topic* topic = 0; + + Topic* tp = _first; + + topic = new Topic(); + + if (topic == 0) + { + return 0; + } + string* name = new string(*topicName); + topic->_topicName = name; + topic->_topicId = getNextTopicId(); + + if (tp == 0) + { + _first = topic; + } + + while (tp) + { + if (tp->_next == 0) + { + tp->_next = topic; + break; + } + else + { + tp = tp->_next; + } + } + return topic; +} + +uint16_t Topics::getNextTopicId() +{ + return ++_nextTopicId == 0xffff ? _nextTopicId += 2 : _nextTopicId; +} + +Topic* Topics::match(MQTTSN_topicid* topicid) +{ + if (topicid->type != MQTTSN_TOPIC_TYPE_NORMAL) + { + return 0; + } + string topicName(topicid->data.long_.name, topicid->data.long_.len); + + Topic* topic = _first; + while (topic) + { + if (topic->isMatch(&topicName)) + { + return topic; + } + topic = topic->_next; + } + return 0; +} + +/*===================================== + Class TopicIdMap + =====================================*/ +TopicIdMapelement::TopicIdMapelement(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type) +{ + _msgId = msgId; + _topicId = topicId; + _type = type; + _next = 0; + _prev = 0; +} + +TopicIdMapelement::~TopicIdMapelement() +{ + +} + +TopicIdMap::TopicIdMap() +{ + char param[MQTTSNGW_PARAM_MAX]; + + _maxInflight = DEFAULT_INFLIGHTMESSAGE; + if ( theProcess->getParam("MaxInflightMsg", param) == 0 ) + { + _maxInflight = atoi(param); + } + + _msgIds = 0; + _first = 0; + _end = 0; + _cnt = 0; +} + +TopicIdMap::~TopicIdMap() +{ + TopicIdMapelement* p = _first; + while ( p ) + { + TopicIdMapelement* q = p->_next; + delete p; + p = q; + } +} + +uint16_t TopicIdMap::getTopicId(uint16_t msgId, MQTTSN_topicTypes* type) +{ + TopicIdMapelement* p = _first; + while ( p ) + { + if ( p->_msgId == msgId ) + { + *type = p->_type; + return p->_topicId; + } + p = p->_next; + } + return 0; +} + +int TopicIdMap::add(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type) +{ + if ( _cnt > _maxInflight * 2 || topicId == 0) + { + return 0; + } + if ( getTopicId(msgId, &type) > 0 ) + { + erase(msgId); + } + + TopicIdMapelement* elm = new TopicIdMapelement(msgId, topicId, type); + if ( elm == 0 ) + { + return 0; + } + if ( _first == 0 ) + { + _first = elm; + _end = elm; + } + else + { + elm->_prev = _end; + _end->_next = elm; + _end = elm; + } + _cnt++; + return 1; +} + +void TopicIdMap::erase(uint16_t msgId) +{ + TopicIdMapelement* p = _first; + while ( p ) + { + if ( p->_msgId == msgId ) + { + if ( p->_prev == 0 ) + { + _first = p->_next; + } + else + { + p->_prev->_next = p->_next; + } + + if ( p->_next == 0 ) + { + _end = p->_prev; + } + else + { + p->_next->_prev = p->_prev; + } + delete p; + break; + + } + p = p->_next; + } + _cnt--; +} + +void TopicIdMap::clear(void) +{ + TopicIdMapelement* p = _first; + while ( p ) + { + TopicIdMapelement* q = p->_next; + delete p; + p = q; + } + _first = 0; + _end = 0; + _cnt = 0; +} + +/*===================================== + Class WaitREGACKPacket + =====================================*/ +waitREGACKPacket::waitREGACKPacket(MQTTSNPacket* packet, uint16_t REGACKMsgId) +{ + _packet = packet; + _msgId = REGACKMsgId; + _next = 0; + _prev = 0; +} + +waitREGACKPacket::~waitREGACKPacket() +{ + delete _packet; +} + +/*===================================== + Class WaitREGACKPacketList + =====================================*/ + +WaitREGACKPacketList::WaitREGACKPacketList() +{ + _first = 0; + _end = 0; +} + +WaitREGACKPacketList::~WaitREGACKPacketList() +{ + waitREGACKPacket* p = _first; + while (p) + { + waitREGACKPacket* q = p->_next; + delete p; + p = q; + } +} + +int WaitREGACKPacketList::setPacket(MQTTSNPacket* packet, uint16_t REGACKMsgId) +{ + waitREGACKPacket* elm = new waitREGACKPacket(packet, REGACKMsgId); + if (elm == 0) + { + return 0; + } + + if (_first == 0) + { + _first = elm; + _end = elm; + } + elm->_prev = _end; + _end->_next = elm; + return 1; +} + +MQTTSNPacket* WaitREGACKPacketList::getPacket(uint16_t REGACKMsgId) +{ + waitREGACKPacket* p = _first; + while (p) + { + if (p->_msgId == REGACKMsgId) + { + return p->_packet; + } + p = p->_next; + } + return 0; +} + +void WaitREGACKPacketList::erase(uint16_t REGACKMsgId) +{ + waitREGACKPacket* p = _first; + while (p) + { + if (p->_msgId == REGACKMsgId) + { + if (p->_prev == 0) + { + _first = p->_next; + + } + else + { + p->_prev->_next = p->_next; + } + if (p->_next == 0) + { + _end = p->_prev; + } + else + { + p->_next->_prev = p->_prev; + } + break; + // Do not delete element. Element is deleted after sending to Client. + } + p = p->_next; + } +} + diff --git a/MQTTSNGateway/src/MQTTSNGWClient.h b/MQTTSNGateway/src/MQTTSNGWClient.h new file mode 100644 index 0000000..48a7f9a --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClient.h @@ -0,0 +1,347 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef MQTTSNGWCLIENT_H_ +#define MQTTSNGWCLIENT_H_ + +#include "MQTTSNGWProcess.h" +#include "MQTTGWPacket.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNPacket.h" +#include "Network.h" +#include "SensorNetwork.h" +#include "MQTTSNPacket.h" +#include "linux.h" // Timer class + +namespace MQTTSNGW +{ + +#define MQTTSN_TOPIC_MULTI_WILDCARD '#' +#define MQTTSN_TOPIC_SINGLE_WILDCARD '+' + +/*===================================== + Class PacketQue + =====================================*/ +template class PacketQue +{ +public: + PacketQue() + { + _que = new Que; + } + + ~PacketQue() + { + clear(); + delete _que; + } + + T* getPacket() + { + T* packet; + if (_que->size() > 0) + { + _mutex.lock(); + packet = _que->front(); + _mutex.unlock(); + return packet; + } + else + { + return 0; + } + } + + void post(T* packet) + { + _mutex.lock(); + _que->post(packet); + _mutex.unlock(); + } + + void pop() + { + if (_que->size() > 0) + { + _mutex.lock(); + _que->pop(); + _mutex.unlock(); + } + } + + void clear() + { + _mutex.lock(); + while (_que->size() > 0) + { + delete _que->front(); + _que->pop(); + } + _mutex.unlock(); + } + +private: + Que* _que; + Mutex _mutex; +}; + + +/*===================================== + Class Topic + ======================================*/ +class Topic +{ + friend class Topics; +public: + Topic(); + Topic(string* topic); + ~Topic(); + + string* getTopicName(void); + uint16_t getTopicId(void); + int hasWildCard(unsigned int* pos); + bool isMatch(string* topicName); + +private: + uint16_t _topicId; + string* _topicName; + Topic* _next; +}; + +/*===================================== + Class Topics + ======================================*/ +class Topics +{ +public: + Topics(); + ~Topics(); + Topic* add(MQTTSN_topicid* topicid); + Topic* add(string* topic); + uint16_t getTopicId(MQTTSN_topicid* topic); + uint16_t getNextTopicId(); + Topic* getTopic(uint16_t topicId); + Topic* getTopic(MQTTSN_topicid* topicid); + Topic* match(MQTTSN_topicid* topicid); + +private: + uint16_t _nextTopicId; + Topic* _first; + +}; + +/*===================================== + Class TopicIdMap + =====================================*/ +class TopicIdMapelement +{ + friend class TopicIdMap; +public: + TopicIdMapelement(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type); + ~TopicIdMapelement(); + +private: + uint16_t _msgId; + uint16_t _topicId; + MQTTSN_topicTypes _type; + TopicIdMapelement* _next; + TopicIdMapelement* _prev; +}; + +class TopicIdMap +{ +public: + TopicIdMap(); + ~TopicIdMap(); + uint16_t getTopicId(uint16_t msgId, MQTTSN_topicTypes* type); + Topic* getTopic(MQTTSN_topicTypes type); + int add(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type); + void erase(uint16_t msgId); + void clear(void); +private: + int find(uint16_t msgId); + uint16_t* _msgIds; + TopicIdMapelement* _first; + TopicIdMapelement* _end; + int _cnt; + int _maxInflight; +}; + +/*===================================== + Class WaitREGACKPacket + =====================================*/ +class waitREGACKPacket +{ + friend class WaitREGACKPacketList; +public: + waitREGACKPacket(MQTTSNPacket* packet, uint16_t REGACKMsgId); + ~waitREGACKPacket(); + +private: + uint16_t _msgId; + MQTTSNPacket* _packet; + waitREGACKPacket* _next; + waitREGACKPacket* _prev; +}; + +/*===================================== + Class WaitREGACKPacketList + =====================================*/ +class WaitREGACKPacketList +{ +public: + WaitREGACKPacketList(); + ~WaitREGACKPacketList(); + int setPacket(MQTTSNPacket* packet, uint16_t REGACKMsgId); + MQTTSNPacket* getPacket(uint16_t REGACKMsgId); + void erase(uint16_t REGACKMsgId); + +private: + waitREGACKPacket* _first; + waitREGACKPacket* _end; +}; + +/*===================================== + Class Client + =====================================*/ +#define MQTTSN_CLIENTID_LENGTH 23 + +typedef enum +{ + Cstat_Disconnected = 0, Cstat_TryConnecting, Cstat_Connecting, Cstat_Active, Cstat_Asleep, Cstat_Awake, Cstat_Lost +} ClientStatus; + + +class Client +{ + friend class ClientList; +public: + Client(bool secure = false); + Client(uint8_t maxInflightMessages, bool secure); + ~Client(); + + Connect* getConnectData(void); + uint16_t getWaitedPubTopicId(uint16_t msgId); + uint16_t getWaitedSubTopicId(uint16_t msgId); + MQTTSNPacket* getClientSleepPacket(); + WaitREGACKPacketList* getWaitREGACKPacketList(void); + + void eraseWaitedPubTopicId(uint16_t msgId); + void eraseWaitedSubTopicId(uint16_t msgId); + void clearWaitedPubTopicId(void); + void clearWaitedSubTopicId(void); + + void setClientSleepPacket(MQTTSNPacket*); + void setWaitedPubTopicId(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type); + void setWaitedSubTopicId(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type); + + void checkTimeover(void); + void updateStatus(MQTTSNPacket*); + void updateStatus(ClientStatus); + void connectSended(void); + void connackSended(int rc); + void disconnected(void); + bool isConnectSendable(void); + + uint16_t getNextPacketId(void); + uint8_t getNextSnMsgId(void); + Topics* getTopics(void); + void setTopics(Topics* topics); + void setKeepAlive(MQTTSNPacket* packet); + + SensorNetAddress* getSensorNetAddress(void); + Network* getNetwork(void); + void setClientAddress(SensorNetAddress* sensorNetAddr); + void setSensorNetType(bool stable); + + void setClientId(MQTTSNString id); + void setWillTopic(MQTTSNString willTopic); + void setWillMsg(MQTTSNString willmsg); + char* getClientId(void); + char* getWillTopic(void); + char* getWillMsg(void); + const char* getStatus(void); + void setWaitWillMsgFlg(bool); + + bool isDisconnect(void); + bool isActive(void); + bool isSleep(void); + bool isSecureNetwork(void); + bool isSensorNetStable(void); + bool isWaitWillMsg(void); + + Client* getNextClient(void); + +private: + PacketQue _clientSleepPacketQue; + WaitREGACKPacketList _waitREGACKList; + + Topics* _topics; + TopicIdMap _waitedPubTopicIdMap; + TopicIdMap _waitedSubTopicIdMap; + + Connect _connectData; + MQTTSNPacket* _connAck; + + char* _clientId; + char* _willTopic; + char* _willMsg; + + Timer _keepAliveTimer; + uint32_t _keepAliveMsec; + + ClientStatus _status; + bool _waitWillMsgFlg; + + uint16_t _packetId; + uint8_t _snMsgId; + + Network* _network; // Broker + bool _secureNetwork; // SSL + bool _sensorNetype; // false: unstable network like a G3 + SensorNetAddress _sensorNetAddr; + + Client* _nextClient; + Client* _prevClient; + +}; + +/*===================================== + Class ClientList + =====================================*/ +class ClientList +{ +public: + ClientList(); + ~ClientList(); + bool authorize(const char* fileName); + void erase(Client*); + Client* getClient(SensorNetAddress* addr); + Client* createClient(SensorNetAddress* addr, MQTTSNString* clientId, bool unstableLine, + bool secure); + uint16_t getClientCount(void); + Client* getClient(void); + bool isAuthorized(); +private: + Client* _firstClient; + Client* _endClient; + Mutex _mutex; + uint16_t _clientCnt; + bool _authorize; +}; + +} +#endif /* MQTTSNGWCLIENT_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWClientRecvTask.cpp b/MQTTSNGateway/src/MQTTSNGWClientRecvTask.cpp new file mode 100644 index 0000000..c905459 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClientRecvTask.cpp @@ -0,0 +1,172 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWClientRecvTask.h" +#include "MQTTSNGateway.h" +char* currentDateTime(void); +/*===================================== + Class ClientRecvTask + =====================================*/ +ClientRecvTask::ClientRecvTask(Gateway* gateway) +{ + _gateway = gateway; + _gateway->attach((Thread*)this); + _sensorNetwork = _gateway->getSensorNetwork(); +} + +ClientRecvTask::~ClientRecvTask() +{ + +} + +/** + * Initialize SensorNetwork + */ +void ClientRecvTask::initialize(int argc, char** argv) +{ + if ( _sensorNetwork->initialize() < 0 ) + { + throw Exception(" Can't open the sensor network.\n"); + } +} + +/* + * Receive a packet from clients via sensor netwwork + * and generate a event to execute the packet handling procedure + * of MQTTSNPacketHandlingTask. + */ +void ClientRecvTask::run() +{ + Event* ev = 0; + Client* client = 0; + + while (true) + { + MQTTSNPacket* packet = new MQTTSNPacket(); + int packetLen = packet->recv(_sensorNetwork); + + if (packetLen < 3 ) + { + delete packet; + continue; + } + + if ( packet->getType() <= MQTTSN_ADVERTISE || packet->getType() == MQTTSN_GWINFO ) + { + delete packet; + continue; + } + + if ( packet->getType() == MQTTSN_SEARCHGW ) + { + /* write log and post Event */ + log(0, packet); + ev = new Event(); + ev->setBrodcastEvent(packet); + _gateway->getPacketEventQue()->post(ev); + continue; + } + + /* get client from the ClientList of Gateway by sensorNetAddress. */ + client = _gateway->getClientList()->getClient(_sensorNetwork->getSenderAddress()); + + if ( client ) + { + /* write log and post Event */ + log(client, packet); + ev = new Event(); + ev->setClientRecvEvent(client,packet); + _gateway->getPacketEventQue()->post(ev); + } + else + { + /* new client */ + if (packet->getType() == MQTTSN_CONNECT) + { + MQTTSNPacket_connectData data; + memset(&data, 0, sizeof(MQTTSNPacket_connectData)); + packet->getCONNECT(&data); + + /* create a client */ + client = _gateway->getClientList()->createClient(_sensorNetwork->getSenderAddress(), &data.clientID, false, false); + + if (!client) + { + WRITELOG("%s Can't create a Client. CONNECT message has been discarded.%s\n", ERRMSG_HEADER, ERRMSG_FOOTER); + delete packet; + continue; + } + + log(client, packet); + + /* set sensorNetAddress & post Event */ + client->setClientAddress(_sensorNetwork->getSenderAddress()); + ev = new Event(); + ev->setClientRecvEvent(client, packet); + _gateway->getPacketEventQue()->post(ev); + } + else + { + log(client, packet); + delete packet; + continue; + } + } + } +} + +void ClientRecvTask::log(Client* client, MQTTSNPacket* packet) +{ + char pbuf[SIZEOF_LOG_PACKET * 3]; + char msgId[6]; + const char* clientId = client ? (const char*)client->getClientId() :"Non Active Client !" ; + + switch (packet->getType()) + { + case MQTTSN_SEARCHGW: + WRITELOG(FORMAT_CY_NL, currentDateTime(), packet->getName(), LEFTARROW, CLIENT, packet->print(pbuf)); + break; + case MQTTSN_CONNECT: + WRITELOG(FORMAT_YE_WH_NL, currentDateTime(), packet->getName(), LEFTARROW, clientId, packet->print(pbuf)); + break; + case MQTTSN_WILLTOPIC: + case MQTTSN_WILLMSG: + case MQTTSN_DISCONNECT: + case MQTTSN_WILLTOPICUPD: + case MQTTSN_WILLMSGUPD: + WRITELOG(FORMAT_WHITE_NL, currentDateTime(), packet->getName(), LEFTARROW, clientId, packet->print(pbuf)); + break; + case MQTTSN_PUBLISH: + case MQTTSN_REGISTER: + case MQTTSN_SUBSCRIBE: + case MQTTSN_UNSUBSCRIBE: + WRITELOG(FORMAT_WH_MSGID_NL, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, clientId, packet->print(pbuf)); + break; + case MQTTSN_REGACK: + case MQTTSN_PUBACK: + case MQTTSN_PUBREC: + case MQTTSN_PUBREL: + case MQTTSN_PUBCOMP: + WRITELOG(FORMAT_WH_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, clientId, packet->print(pbuf)); + break; + case MQTTSN_PINGREQ: + WRITELOG(FORMAT_WH_NL, currentDateTime(), packet->getName(), LEFTARROW, clientId, packet->print(pbuf)); + break; + default: + WRITELOG(FORMAT_WH_NL, currentDateTime(), packet->getName(), LEFTARROW, clientId, packet->print(pbuf)); + break; + } +} diff --git a/MQTTSNGateway/src/MQTTSNGWClientRecvTask.h b/MQTTSNGateway/src/MQTTSNGWClientRecvTask.h new file mode 100644 index 0000000..4af4f0d --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClientRecvTask.h @@ -0,0 +1,46 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWCLIENTRECVTASK_H_ +#define MQTTSNGWCLIENTRECVTASK_H_ + +#include "SensorNetwork.h" +#include "MQTTSNGateway.h" + +namespace MQTTSNGW +{ + +/*===================================== + Class ClientRecvTask + =====================================*/ +class ClientRecvTask:public Thread +{ + MAGIC_WORD_FOR_THREAD; +public: + ClientRecvTask(Gateway*); + ~ClientRecvTask(); + virtual void initialize(int argc, char** argv); + void run(); + +private: + void log(Client*, MQTTSNPacket*); + + Gateway* _gateway; + SensorNetwork* _sensorNetwork; +}; + +} + +#endif /* MQTTSNGWCLIENTRECVTASK_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWClientSendTask.cpp b/MQTTSNGateway/src/MQTTSNGWClientSendTask.cpp new file mode 100644 index 0000000..ff74aae --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClientSendTask.cpp @@ -0,0 +1,105 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#include "MQTTSNGWClientSendTask.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNGateway.h" + +using namespace MQTTSNGW; +using namespace std; +char* currentDateTime(void); +/*===================================== + Class ClientSendTask + =====================================*/ +ClientSendTask::ClientSendTask(Gateway* gateway) +{ + _gateway = gateway; + _gateway->attach((Thread*)this); + _sensorNetwork = _gateway->getSensorNetwork(); +} + +ClientSendTask::~ClientSendTask() +{ + +} + +void ClientSendTask::run() +{ + Client* client = 0; + MQTTSNPacket* packet = 0; + + while (true) + { + Event* ev = _gateway->getClientSendQue()->wait(); + + if (ev->getEventType() == EtClientSend) + { + client = ev->getClient(); + packet = ev->getMQTTSNPacket(); + packet->unicast(_sensorNetwork, client->getSensorNetAddress()); + } + else if (ev->getEventType() == EtBroadcast) + { + packet = ev->getMQTTSNPacket(); + packet->broadcast(_sensorNetwork); + } + + log(client, packet); + delete ev; + } +} + +void ClientSendTask::log(Client* client, MQTTSNPacket* packet) +{ + char pbuf[SIZEOF_LOG_PACKET * 3]; + char msgId[6]; + + switch (packet->getType()) + { + case MQTTSN_ADVERTISE: + case MQTTSN_GWINFO: + WRITELOG(FORMAT_CY_NL, currentDateTime(), packet->getName(), RIGHTARROW, CLIENTS, packet->print(pbuf)); + break; + case MQTTSN_CONNACK: + case MQTTSN_DISCONNECT: + WRITELOG(FORMAT_YE_WH, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case MQTTSN_WILLTOPICREQ: + case MQTTSN_WILLMSGREQ: + case MQTTSN_WILLTOPICRESP: + case MQTTSN_WILLMSGRESP: + WRITELOG(FORMAT_GR, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case MQTTSN_REGISTER: + case MQTTSN_PUBLISH: + WRITELOG(FORMAT_GR_WH_MSGID_NL, currentDateTime(), packet->getName(), packet->getMsgId(msgId), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case MQTTSN_REGACK: + case MQTTSN_PUBACK: + case MQTTSN_PUBREC: + case MQTTSN_PUBREL: + case MQTTSN_PUBCOMP: + case MQTTSN_SUBACK: + case MQTTSN_UNSUBACK: + WRITELOG(FORMAT_GR_WH_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case MQTTSN_PINGRESP: + WRITELOG(FORMAT_CY, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + default: + break; + } +} diff --git a/MQTTSNGateway/src/MQTTSNGWClientSendTask.h b/MQTTSNGateway/src/MQTTSNGWClientSendTask.h new file mode 100644 index 0000000..d9e1646 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClientSendTask.h @@ -0,0 +1,45 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWCLIENTSENDTASK_H_ +#define MQTTSNGWCLIENTSENDTASK_H_ + +#include "MQTTSNGateway.h" +#include "SensorNetwork.h" + +namespace MQTTSNGW +{ + +/*===================================== + Class ClientSendTask + =====================================*/ +class ClientSendTask: public Thread +{ + MAGIC_WORD_FOR_THREAD; +public: + ClientSendTask(Gateway* gateway); + ~ClientSendTask(); + void run(); + +private: + void log(Client*, MQTTSNPacket*); + + Gateway* _gateway; + SensorNetwork* _sensorNetwork; +}; + +} + +#endif /* MQTTSNGWCLIENTSENDTASK_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWConnectionHandler.cpp b/MQTTSNGateway/src/MQTTSNGWConnectionHandler.cpp new file mode 100644 index 0000000..28baeae --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWConnectionHandler.cpp @@ -0,0 +1,251 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWConnectionHandler.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWPacket.h" +#include "MQTTGWPacket.h" + +using namespace std; +using namespace MQTTSNGW; + +/*===================================== + Class MQTTSNConnectionHandler + =====================================*/ +MQTTSNConnectionHandler::MQTTSNConnectionHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTSNConnectionHandler::~MQTTSNConnectionHandler() +{ + +} + +/* + * ADVERTISE + */ +void MQTTSNConnectionHandler::sendADVERTISE() +{ + MQTTSNPacket* adv = new MQTTSNPacket(); + adv->setADVERTISE(_gateway->getGWParams()->gatewayId, _gateway->getGWParams()->keepAlive); + Event* ev1 = new Event(); + ev1->setBrodcastEvent(adv); //broadcast + _gateway->getClientSendQue()->post(ev1); +} + +/* + * SEARCHGW + */ +void MQTTSNConnectionHandler::handleSearchgw(MQTTSNPacket* packet) +{ + if (packet->getType() == MQTTSN_SEARCHGW) + { + if (_gateway->getClientList()->getClientCount() < DEFAULT_MAX_CLIENTS) + { + MQTTSNPacket* gwinfo = new MQTTSNPacket(); + gwinfo->setGWINFO(_gateway->getGWParams()->gatewayId); + Event* ev1 = new Event(); + ev1->setBrodcastEvent(gwinfo); + _gateway->getClientSendQue()->post(ev1); + } + } +} + +/* + * CONNECT + */ +void MQTTSNConnectionHandler::handleConnect(Client* client, MQTTSNPacket* packet) +{ + MQTTSNPacket_connectData data; + packet->getCONNECT(&data); + + /* clear ConnectData of Client */ + Connect* connectData = client->getConnectData(); + memset(connectData, 0, sizeof(Connect)); + client->disconnected(); + + Topics* topics = client->getTopics(); + + /* CONNECT was not sent yet. prepare Connect data */ + connectData->header.bits.type = CONNECT; + connectData->clientID = client->getClientId(); + connectData->version = _gateway->getGWParams()->mqttVersion; + connectData->keepAliveTimer = data.duration; + connectData->flags.bits.will = data.willFlag; + + if ((const char*) _gateway->getGWParams()->loginId != 0 && (const char*) _gateway->getGWParams()->password != 0) + { + connectData->flags.bits.password = 1; + connectData->flags.bits.username = 1; + } + + if (data.cleansession) + { + connectData->flags.bits.cleanstart = 1; + /* reset the table of msgNo and TopicId pare */ + client->clearWaitedPubTopicId(); + client->clearWaitedSubTopicId(); + + /* renew the TopicList */ + if (topics) + { + delete topics; + } + topics = new Topics(); + client->setTopics(topics); + } + + if (data.willFlag) + { + /* create & send WILLTOPICREQ message to the client */ + MQTTSNPacket* reqTopic = new MQTTSNPacket(); + reqTopic->setWILLTOPICREQ(); + Event* evwr = new Event(); + evwr->setClientSendEvent(client, reqTopic); + + /* Send WILLTOPICREQ to the client */ + _gateway->getClientSendQue()->post(evwr); + } + else + { + + /* CONNECT message was not qued in. + * create CONNECT message & send it to the broker */ + MQTTGWPacket* mqMsg = new MQTTGWPacket(); + mqMsg->setCONNECT(client->getConnectData(), (unsigned char*)_gateway->getGWParams()->loginId, (unsigned char*)_gateway->getGWParams()->password); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, mqMsg); + _gateway->getBrokerSendQue()->post(ev1); + } +} + +/* + * WILLTOPIC + */ +void MQTTSNConnectionHandler::handleWilltopic(Client* client, MQTTSNPacket* packet) +{ + int willQos; + uint8_t willRetain; + MQTTSNString willTopic; + + packet->getWILLTOPIC(&willQos, &willRetain, &willTopic); + client->setWillTopic(willTopic); + Connect* connectData = client->getConnectData(); + + /* add the connectData for MQTT CONNECT message */ + connectData->willTopic = client->getWillTopic(); + connectData->flags.bits.willQoS = willQos; + connectData->flags.bits.willRetain = willRetain; + + /* Send WILLMSGREQ to the client */ + client->setWaitWillMsgFlg(true); + MQTTSNPacket* reqMsg = new MQTTSNPacket(); + reqMsg->setWILLMSGREQ(); + Event* evt = new Event(); + evt->setClientSendEvent(client, reqMsg); + _gateway->getClientSendQue()->post(evt); +} + +/* + * WILLMSG + */ +void MQTTSNConnectionHandler::handleWillmsg(Client* client, MQTTSNPacket* packet) +{ + if ( !client->isWaitWillMsg() ) + { + DEBUGLOG(" MQTTSNConnectionHandler::handleWillmsg WaitWillMsgFlg is off.\n"); + return; + } + + MQTTSNString willmsg; + Connect* connectData = client->getConnectData(); + + if( client->isConnectSendable() ) + { + /* save WillMsg in the client */ + packet->getWILLMSG(&willmsg); + client->setWillMsg(willmsg); + + /* create CONNECT message */ + MQTTGWPacket* mqttPacket = new MQTTGWPacket(); + connectData->willMsg = client->getWillMsg(); + mqttPacket->setCONNECT(connectData, (unsigned char*)_gateway->getGWParams()->loginId, (unsigned char*)_gateway->getGWParams()->password); + + /* Send CONNECT to the broker */ + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, mqttPacket); + _gateway->getBrokerSendQue()->post(ev1); + } +} + +/* + * DISCONNECT + */ +void MQTTSNConnectionHandler::handleDisconnect(Client* client, MQTTSNPacket* packet) +{ + MQTTGWPacket* mqMsg = new MQTTGWPacket(); + MQTTSNPacket* snMsg = new MQTTSNPacket(); + + mqMsg->setHeader(DISCONNECT); + snMsg->setDISCONNECT(0); + + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snMsg); + _gateway->getClientSendQue()->post(ev1); + + ev1 = new Event(); + ev1->setBrokerSendEvent(client, mqMsg); + _gateway->getBrokerSendQue()->post(ev1); +} + +/* + * WILLTOPICUPD + */ +void MQTTSNConnectionHandler::handleWilltopicupd(Client* client, MQTTSNPacket* packet) +{ + /* send NOT_SUPPORTED responce to the client */ + MQTTSNPacket* respMsg = new MQTTSNPacket(); + respMsg->setWILLTOPICRESP(MQTTSN_RC_NOT_SUPPORTED); + Event* evt = new Event(); + evt->setClientSendEvent(client, respMsg); + _gateway->getClientSendQue()->post(evt); +} + +/* + * WILLMSGUPD + */ +void MQTTSNConnectionHandler::handleWillmsgupd(Client* client, MQTTSNPacket* packet) +{ + /* send NOT_SUPPORTED responce to the client */ + MQTTSNPacket* respMsg = new MQTTSNPacket(); + respMsg->setWILLMSGRESP(MQTTSN_RC_NOT_SUPPORTED); + Event* evt = new Event(); + evt->setClientSendEvent(client, respMsg); + _gateway->getClientSendQue()->post(evt); +} + +/* + * PINGREQ + */ +void MQTTSNConnectionHandler::handlePingreq(Client* client, MQTTSNPacket* packet) +{ + /* send PINGREQ to the broker */ + MQTTGWPacket* pingreq = new MQTTGWPacket(); + pingreq->setHeader(PINGREQ); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, pingreq); +} diff --git a/MQTTSNGateway/src/MQTTSNGWConnectionHandler.h b/MQTTSNGateway/src/MQTTSNGWConnectionHandler.h new file mode 100644 index 0000000..044e766 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWConnectionHandler.h @@ -0,0 +1,46 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWCONNECTIONHANDLER_H_ +#define MQTTSNGWCONNECTIONHANDLER_H_ + +#include "MQTTSNGateway.h" +#include "MQTTSNGWPacket.h" + +namespace MQTTSNGW +{ + +class MQTTSNConnectionHandler +{ +public: + MQTTSNConnectionHandler(Gateway* gateway); + ~MQTTSNConnectionHandler(); + void sendADVERTISE(void); + void handleSearchgw(MQTTSNPacket* packet); + void handleConnect(Client* client, MQTTSNPacket* packet); + void handleWilltopic(Client* client, MQTTSNPacket* packet); + void handleWillmsg(Client* client, MQTTSNPacket* packet); + void handleDisconnect(Client* client, MQTTSNPacket* packet); + void handleWilltopicupd(Client* client, MQTTSNPacket* packet); + void handleWillmsgupd(Client* client, MQTTSNPacket* packet); + void handlePingreq(Client* client, MQTTSNPacket* packet); +private: + char _pbuf[MQTTSNGW_MAX_PACKET_SIZE * 3]; + Gateway* _gateway; +}; + +} + +#endif /* MQTTSNGWCONNECTIONHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWDefines.h b/MQTTSNGateway/src/MQTTSNGWDefines.h new file mode 100644 index 0000000..541dad5 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWDefines.h @@ -0,0 +1,62 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef MQTTSNGWDEFINES_H_ +#define MQTTSNGWDEFINES_H_ + +namespace MQTTSNGW +{ + +/*================================= + * Starting prompt + ==================================*/ +#define GATEWAY_VERSION "(Ver 0.1.1)" + +/*================================= + * Log controls + ==================================*/ +//#define DEBUG // print out log for debug +//#define RINGBUFFER // print out Packets log into shared memory + +/*================================= + * Parametrs + ==================================*/ +#define MQTTSNGW_MAX_PACKET_SIZE (1024) // Max Packet size +#define SIZEOF_LOG_PACKET (128) // Length of the packet log in bytes + +#define MQTTSNGW_CONFIG_FILE "/usr/local/etc/mqttsnGateway/config/param.conf" +#define MQTTSNGW_CLIENT_LIST "/usr/local/etc/mqttsnGateway/config/clientList.conf" +#define MQTTSNGW_TLS_CA_DIR "/etc/ssl/certs" + +/*================================= + * Data Type + ==================================*/ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +/*================================= + * Macros + ==================================*/ +#ifdef DEBUG +#define DEBUGLOG(...) printf(__VA_ARGS__) +#undef RINGBUFFER +#else +#define DEBUGLOG(...) +#endif + +} +#endif /* MQTTSNGWDEFINES_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWLogmonitor.cpp b/MQTTSNGateway/src/MQTTSNGWLogmonitor.cpp new file mode 100644 index 0000000..50dc482 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWLogmonitor.cpp @@ -0,0 +1,50 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#define RINGBUFFER + +#include "MQTTSNGWProcess.h" +#include "MQTTSNGWLogmonitor.h" +#include + +using namespace std; +using namespace MQTTSNGW; + +Logmonitor::Logmonitor() +{ + theProcess = this; +} + +Logmonitor::~Logmonitor() +{ + +} + +void Logmonitor::run() +{ + while (true) + { + const char* data = getLog(); + if ( *data == 0 ) + { + break; + } + else + { + printf("%s", data); + } + } +} + diff --git a/MQTTSNGateway/src/MQTTSNGWLogmonitor.h b/MQTTSNGateway/src/MQTTSNGWLogmonitor.h new file mode 100644 index 0000000..cb5838a --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWLogmonitor.h @@ -0,0 +1,33 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWLOGMONITOR_H_ +#define MQTTSNGWLOGMONITOR_H_ + +#include "MQTTSNGWProcess.h" + +namespace MQTTSNGW +{ +class Logmonitor: public Process +{ +public: + Logmonitor(); + ~Logmonitor(); + void run(); +}; + +} + +#endif /* MQTTSNGWLOGMONITOR_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWPacket.cpp b/MQTTSNGateway/src/MQTTSNGWPacket.cpp new file mode 100644 index 0000000..b52116a --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPacket.cpp @@ -0,0 +1,410 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGateway.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNPacket.h" +#include "SensorNetwork.h" +#include +#include +#include + +using namespace std; +using namespace MQTTSNGW; + +MQTTSNPacket::MQTTSNPacket() +{ + _buf = 0; + _bufLen = 0; +} +MQTTSNPacket::~MQTTSNPacket() +{ + if (_buf) + { + free(_buf); + } +} + +int MQTTSNPacket::unicast(SensorNetwork* network, SensorNetAddress* sendTo) +{ + return network->unicast(_buf, _bufLen, sendTo); +} + +int MQTTSNPacket::broadcast(SensorNetwork* network) +{ + return network->broadcast(_buf, _bufLen); +} + +int MQTTSNPacket::serialize(uint8_t* buf) +{ + buf = _buf; + return _bufLen; +} + +int MQTTSNPacket::desirialize(unsigned char* buf, unsigned short len) +{ + if ( _buf ) + { + free(_buf); + } + + _buf = (unsigned char*)calloc(len, sizeof(unsigned char)); + if ( _buf ) + { + memcpy(_buf, buf, len); + _bufLen = len; + } + else + { + _bufLen = 0; + } + return _bufLen; +} + +int MQTTSNPacket::recv(SensorNetwork* network) +{ + uint8_t buf[MQTTSNGW_MAX_PACKET_SIZE]; + int len = network->read((uint8_t*) buf, MQTTSNGW_MAX_PACKET_SIZE); + if (len >= 3) + { + len = desirialize(buf, len); + } + else + { + len = 0; + } + return len; + +} + +int MQTTSNPacket::getType(void) +{ + if ( _bufLen == 0 ) + { + return 0; + } + int value = 0; + int p = MQTTSNPacket_decode(_buf, _bufLen, &value); + return _buf[p]; +} + +unsigned char* MQTTSNPacket::getPacketData(void) +{ + return _buf; +} + +int MQTTSNPacket::getPacketLength(void) +{ + return _bufLen; +} + +const char* MQTTSNPacket::getName() +{ + return MQTTSNPacket_name(getType()); +} + +int MQTTSNPacket::setADVERTISE(uint8_t gatewayid, uint16_t duration) +{ + unsigned char buf[5]; + int len = MQTTSNSerialize_advertise(buf, 5, (unsigned char) gatewayid, + (unsigned short) duration); + return desirialize(buf, len); +} + +int MQTTSNPacket::setGWINFO(uint8_t gatewayId) +{ + unsigned char buf[3]; + int len = MQTTSNSerialize_gwinfo(buf, 3, (unsigned char) gatewayId, 0, 0); + return desirialize(buf, len); +} + +int MQTTSNPacket::setConnect(void) +{ + unsigned char buf[40]; + MQTTSNPacket_connectData data; + data.clientID.cstring = (char*)"client01"; + int len = MQTTSNSerialize_connect(buf, 40, &data); + return desirialize(buf, len); +} + +int MQTTSNPacket::setCONNACK(uint8_t returnCode) +{ + unsigned char buf[3]; + int len = MQTTSNSerialize_connack(buf, 3, (int) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::setWILLTOPICREQ(void) +{ + unsigned char buf[2]; + int len = MQTTSNSerialize_willtopicreq(buf, 2); + return desirialize(buf, len); +} + +int MQTTSNPacket::setWILLMSGREQ(void) +{ + unsigned char buf[2]; + int len = MQTTSNSerialize_willmsgreq(buf, 2); + return desirialize(buf, len); +} + +int MQTTSNPacket::setREGISTER(uint16_t topicId, uint16_t msgId, MQTTSNString* topicName) +{ + unsigned char buf[MQTTSNGW_MAX_PACKET_SIZE]; + int len = MQTTSNSerialize_register(buf, MQTTSNGW_MAX_PACKET_SIZE, (unsigned short) topicId, (unsigned short) msgId, + topicName); + return desirialize(buf, len); +} + +int MQTTSNPacket::setREGACK(uint16_t topicId, uint16_t msgId, uint8_t returnCode) +{ + unsigned char buf[7]; + int len = MQTTSNSerialize_regack(buf, 7, (unsigned short) topicId, (unsigned short) msgId, + (unsigned char) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPUBLISH(uint8_t dup, int qos, uint8_t retained, uint16_t msgId, MQTTSN_topicid topic, + uint8_t* payload, uint16_t payloadlen) +{ + unsigned char buf[MQTTSNGW_MAX_PACKET_SIZE]; + int len = MQTTSNSerialize_publish(buf, MQTTSNGW_MAX_PACKET_SIZE, (unsigned char) dup, qos, (unsigned char) retained, + (unsigned short) msgId, topic, (unsigned char*) payload, (int) payloadlen); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPUBACK(uint16_t topicId, uint16_t msgId, uint8_t returnCode) +{ + unsigned char buf[7]; + int len = MQTTSNSerialize_puback(buf, 7, (unsigned short) topicId, (unsigned short) msgId, + (unsigned char) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPUBREC(uint16_t msgId) +{ + unsigned char buf[4]; + int len = MQTTSNSerialize_pubrec(buf, 4, (unsigned short) msgId); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPUBREL(uint16_t msgId) +{ + unsigned char buf[4]; + int len = MQTTSNSerialize_pubrel(buf, 4, (unsigned short) msgId); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPUBCOMP(uint16_t msgId) +{ + unsigned char buf[4]; + int len = MQTTSNSerialize_pubcomp(buf, 4, (unsigned short) msgId); + return desirialize(buf, len); +} + +int MQTTSNPacket::setSUBACK(int qos, uint16_t topicId, uint16_t msgId, uint8_t returnCode) +{ + unsigned char buf[8]; + int len = MQTTSNSerialize_suback(buf, 8, qos, (unsigned short) topicId, + (unsigned short) msgId, (unsigned char) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::setUNSUBACK(uint16_t msgId) +{ + unsigned char buf[4]; + int len = MQTTSNSerialize_unsuback(buf, 4, (unsigned short) msgId); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPINGRESP(void) +{ + unsigned char buf[32]; + int len = MQTTSNSerialize_pingresp(buf, 32); + return desirialize(buf, len); +} + +int MQTTSNPacket::setDISCONNECT(uint16_t duration) +{ + unsigned char buf[4]; + int len = MQTTSNSerialize_disconnect(buf, 4, (int) duration); + return desirialize(buf, len); +} + +int MQTTSNPacket::setWILLTOPICRESP(uint8_t returnCode) +{ + unsigned char buf[MQTTSNGW_MAX_PACKET_SIZE]; + int len = MQTTSNSerialize_willtopicresp(buf, MQTTSNGW_MAX_PACKET_SIZE, (int) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::setWILLMSGRESP(uint8_t returnCode) +{ + unsigned char buf[MQTTSNGW_MAX_PACKET_SIZE]; + int len = MQTTSNSerialize_willmsgresp(buf, MQTTSNGW_MAX_PACKET_SIZE, (int) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::getSERCHGW(uint8_t* radius) +{ + return MQTTSNDeserialize_searchgw((unsigned char*) radius, (unsigned char*) _buf, _bufLen); +} + +int MQTTSNPacket::getCONNECT(MQTTSNPacket_connectData* data) +{ + return MQTTSNDeserialize_connect(data, _buf, _bufLen); +} + +int MQTTSNPacket::getCONNACK(uint8_t* returnCode) +{ + return MQTTSNSerialize_connack(_buf, _bufLen, (int) *returnCode); +} + +int MQTTSNPacket::getWILLTOPIC(int* willQoS, uint8_t* willRetain, MQTTSNString* willTopic) +{ + return MQTTSNDeserialize_willtopic((int*) willQoS, (unsigned char*) willRetain, willTopic, _buf, _bufLen); +} + +int MQTTSNPacket::getWILLMSG(MQTTSNString* willmsg) +{ + return MQTTSNDeserialize_willmsg(willmsg, _buf, _bufLen); +} + +int MQTTSNPacket::getREGISTER(uint16_t* topicId, uint16_t* msgId, MQTTSNString* topicName) +{ + return MQTTSNDeserialize_register((unsigned short*) topicId, (unsigned short*) msgId, topicName, + _buf, _bufLen); +} + +int MQTTSNPacket::getREGACK(uint16_t* topicId, uint16_t* msgId, uint8_t* returnCode) +{ + return MQTTSNDeserialize_regack((unsigned short*) topicId, (unsigned short*) msgId, (unsigned char*) returnCode, _buf, _bufLen); +} + +int MQTTSNPacket::getPUBLISH(uint8_t* dup, int* qos, uint8_t* retained, uint16_t* msgId, MQTTSN_topicid* topic, + uint8_t** payload, int* payloadlen) +{ + return MQTTSNDeserialize_publish((unsigned char*) dup, qos, (unsigned char*) retained, (unsigned short*) msgId, + topic, (unsigned char**) payload, (int*) payloadlen, _buf, _bufLen); +} + +int MQTTSNPacket::getPUBACK(uint16_t* topicId, uint16_t* msgId, uint8_t* returnCode) +{ + return MQTTSNDeserialize_puback((unsigned short*) topicId, (unsigned short*) msgId, (unsigned char*) returnCode, + _buf, _bufLen); +} + +int MQTTSNPacket::getACK(uint16_t* msgId) +{ + unsigned char type; + return MQTTSNDeserialize_ack(&type, (unsigned short*) msgId, _buf, _bufLen); +} + +int MQTTSNPacket::getSUBSCRIBE(uint8_t* dup, int* qos, uint16_t* msgId, MQTTSN_topicid* topicFilter) +{ + return MQTTSNDeserialize_subscribe((unsigned char*) dup, qos, (unsigned short*) msgId, topicFilter, _buf, _bufLen); +} + +int MQTTSNPacket::getUNSUBSCRIBE(uint16_t* msgId, MQTTSN_topicid* topicFilter) +{ + return MQTTSNDeserialize_unsubscribe((unsigned short*) msgId, topicFilter, _buf, _bufLen); +} + +int MQTTSNPacket::getPINGREQ(void) +{ + if (getType() == MQTTSN_PINGRESP && _bufLen > 2 ) + { + return _bufLen - 2; + } + return 0; +} + +int MQTTSNPacket::getDISCONNECT(uint16_t* duration) +{ + return MQTTSNDeserialize_disconnect((int*) duration, _buf, _bufLen); +} + +int MQTTSNPacket::getWILLTOPICUPD(uint8_t* willQoS, uint8_t* willRetain, MQTTSNString* willTopic) +{ + return MQTTSNDeserialize_willtopicupd((int*) willQoS, (unsigned char*) willRetain, willTopic, _buf, _bufLen); +} + +int MQTTSNPacket::getWILLMSGUPD(MQTTSNString* willMsg) +{ + return MQTTSNDeserialize_willmsgupd(willMsg, _buf, _bufLen); +} + +char* MQTTSNPacket::print(char* pbuf) +{ + char* ptr = pbuf; + char** pptr = &pbuf; + int value = 0; + + int i = MQTTSNPacket_decode(_buf, _bufLen, &value); + int size = _bufLen > SIZEOF_LOG_PACKET ? SIZEOF_LOG_PACKET : _bufLen; + + for (; i < size; i++) + { + sprintf(*pptr, " %02X", *(_buf + i)); + *pptr += 3; + } + **pptr = 0; + return ptr; +} + +char* MQTTSNPacket::getMsgId(char* pbuf) +{ + int value = 0; + int p = 0; + + switch ( getType() ) + { + case MQTTSN_PUBLISH: + p = MQTTSNPacket_decode(_buf, _bufLen, &value); + if ( _buf[p + 1] & 0x80 ) + { + sprintf(pbuf, "+%02X%02X", _buf[p + 4], _buf[p + 5]); + } + else + { + sprintf(pbuf, " %02X%02X", _buf[p + 4], _buf[p + 5]); + } + break; + case MQTTSN_PUBACK: + case MQTTSN_REGISTER: + case MQTTSN_REGACK: + sprintf(pbuf, " %02X%02X", _buf[4], _buf[5]); + break; + case MQTTSN_PUBREC: + case MQTTSN_PUBREL: + case MQTTSN_PUBCOMP: + sprintf(pbuf, " %02X%02X", _buf[2], _buf[3]); + break; + case MQTTSN_SUBSCRIBE: + case MQTTSN_UNSUBSCRIBE: + p = MQTTSNPacket_decode(_buf, _bufLen, &value); + sprintf(pbuf, " %02X%02X", _buf[p + 2], _buf[p + 3]); + break; + case MQTTSN_SUBACK: + case MQTTSN_UNSUBACK: + sprintf(pbuf, " %02X%02X", _buf[5], _buf[6]); + break; + default: + sprintf(pbuf, " "); + break; + } + return pbuf; +} diff --git a/MQTTSNGateway/src/MQTTSNGWPacket.h b/MQTTSNGateway/src/MQTTSNGWPacket.h new file mode 100644 index 0000000..5cd7578 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPacket.h @@ -0,0 +1,88 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWPACKET_H_ +#define MQTTSNGWPACKET_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNPacket.h" +#include "SensorNetwork.h" + +namespace MQTTSNGW +{ + +class MQTTSNPacket +{ +public: + MQTTSNPacket(); + ~MQTTSNPacket(); + int unicast(SensorNetwork* network, SensorNetAddress* sendTo); + int broadcast(SensorNetwork* network); + int recv(SensorNetwork* network); + int serialize(uint8_t* buf); + int desirialize(unsigned char* buf, unsigned short len); + int getType(void); + unsigned char* getPacketData(void); + int getPacketLength(void); + const char* getName(); + + int setConnect(void); // Debug + int setADVERTISE(uint8_t gatewayid, uint16_t duration); + int setGWINFO(uint8_t gatewayId); + int setCONNACK(uint8_t returnCode); + int setWILLTOPICREQ(void); + int setWILLMSGREQ(void); + int setREGISTER(uint16_t topicId, uint16_t msgId, MQTTSNString* TopicName); + int setREGACK(uint16_t topicId, uint16_t msgId, uint8_t returnCode); + int setPUBLISH(uint8_t dup, int qos, uint8_t retained, uint16_t msgId, + MQTTSN_topicid topic, uint8_t* payload, uint16_t payloadlen); + int setPUBACK(uint16_t topicId, uint16_t msgId, uint8_t returnCode); + int setPUBREC(uint16_t msgId); + int setPUBREL(uint16_t msgId); + int setPUBCOMP(uint16_t msgId); + int setSUBACK(int qos, uint16_t topicId, uint16_t msgId, uint8_t returnCode); + int setUNSUBACK(uint16_t msgId); + int setPINGRESP(void); + int setDISCONNECT(uint16_t duration); + int setWILLTOPICRESP(uint8_t returnCode); + int setWILLMSGRESP(uint8_t returnCode); + + int getSERCHGW(uint8_t* radius); + int getCONNECT(MQTTSNPacket_connectData* option); + int getCONNACK(uint8_t* returnCode); + int getWILLTOPIC(int* willQoS, uint8_t* willRetain, MQTTSNString* willTopic); + int getWILLMSG(MQTTSNString* willmsg); + int getREGISTER(uint16_t* topicId, uint16_t* msgId, MQTTSNString* topicName); + int getREGACK(uint16_t* topicId, uint16_t* msgId, uint8_t* returnCode); + int getPUBLISH(uint8_t* dup, int* qos, uint8_t* retained, uint16_t* msgId, + MQTTSN_topicid* topic, unsigned char** payload, int* payloadlen); + int getPUBACK(uint16_t* topicId, uint16_t* msgId, uint8_t* returnCode); + int getACK(uint16_t* msgId); + int getSUBSCRIBE(uint8_t* dup, int* qos, uint16_t* msgId, MQTTSN_topicid* topicFilter); + int getUNSUBSCRIBE(uint16_t* msgId, MQTTSN_topicid* topicFilter); + int getPINGREQ(void); + int getDISCONNECT(uint16_t* duration); + int getWILLTOPICUPD(uint8_t* willQoS, uint8_t* willRetain, MQTTSNString* willTopic); + int getWILLMSGUPD(MQTTSNString* willMsg); + char* getMsgId(char* buf); + char* print(char* buf); + +private: + unsigned char* _buf; // Ptr to a packet data + int _bufLen; // length of the packet data +}; + +} +#endif /* MQTTSNGWPACKET_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.cpp b/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.cpp new file mode 100644 index 0000000..c24659b --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.cpp @@ -0,0 +1,236 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGWPacketHandleTask.h" +#include "MQTTSNGWProcess.h" +#include "MQTTGWPacket.h" +#include "MQTTSNGWClient.h" +#include "MQTTSNGWProcess.h" +#include "MQTTGWConnectionHandler.h" +#include "MQTTGWPublishHandler.h" +#include "MQTTGWSubscribeHandler.h" +#include "MQTTSNGWConnectionHandler.h" +#include "MQTTSNGWPublishHandler.h" +#include "MQTTSNGWSubscribeHandler.h" + +using namespace std; +using namespace MQTTSNGW; + +#define EVENT_QUE_TIME_OUT 2000 // 2000 msecs + +/*===================================== + Class PacketHandleTask + =====================================*/ + +PacketHandleTask::PacketHandleTask(Gateway* gateway) +{ + _gateway = gateway; + _gateway->attach((Thread*)this); + _mqttConnection = new MQTTGWConnectionHandler(_gateway); + _mqttPublish = new MQTTGWPublishHandler(_gateway); + _mqttSubscribe = new MQTTGWSubscribeHandler(_gateway); + _mqttsnConnection = new MQTTSNConnectionHandler(_gateway); + _mqttsnPublish = new MQTTSNPublishHandler(_gateway); + _mqttsnSubscribe = new MQTTSNSubscribeHandler(_gateway); +} + +/** + * Destructor is called by Gateway's destructor indirectly. + */ +PacketHandleTask::~PacketHandleTask() +{ + if ( _mqttConnection ) + { + delete _mqttConnection; + } + if ( _mqttPublish ) + { + delete _mqttPublish; + } + if ( _mqttSubscribe ) + { + delete _mqttSubscribe; + } + if ( _mqttsnConnection ) + { + delete _mqttsnConnection; + } + if ( _mqttsnPublish ) + { + delete _mqttsnPublish; + } + if ( _mqttsnSubscribe ) + { + delete _mqttsnSubscribe; + } +} + +void PacketHandleTask::run() +{ + Event* ev = 0; + EventQue* eventQue = _gateway->getPacketEventQue(); + ClientList* clist = _gateway->getClientList(); + Client* client = 0; + MQTTSNPacket* snPacket = 0; + MQTTGWPacket* brPacket = 0; + char msgId[6]; + memset(msgId, 0, 6); + + _advertiseTimer.start(_gateway->getGWParams()->keepAlive * 1000UL); + + while (true) + { + if (theProcess->checkSignal() == SIGINT) + { + throw Exception("Terminated by CTL-C"); + } + + /* wait Event */ + ev = eventQue->timedwait(EVENT_QUE_TIME_OUT); + + if (ev->getEventType() == EtTimeout) + { + /*------ Is Client Lost ? ---------*/ + client = clist->getClient(); + while (client > 0) + { + client->checkTimeover(); + client = client->getNextClient(); + } + /*------ Check Keep Alive Timer & send Advertise ------*/ + if (_advertiseTimer.isTimeup()) + { + _mqttsnConnection->sendADVERTISE(); + _advertiseTimer.start(_gateway->getGWParams()->keepAlive * 1000UL); + } + } + + /*------ Handle SEARCHGW Message ---------*/ + else if (ev->getEventType() == EtBroadcast) + { + snPacket = ev->getMQTTSNPacket(); + _mqttsnConnection->handleSearchgw(snPacket); + } + + /*------ Handle Messages form Clients ---------*/ + else if (ev->getEventType() == EtClientRecv) + { + client = ev->getClient(); + snPacket = ev->getMQTTSNPacket(); + + DEBUGLOG(" PacketHandleTask gets %s %s from the client.\n", snPacket->getName(), snPacket->getMsgId(msgId)); + + switch (snPacket->getType()) + { + case MQTTSN_CONNECT: + _mqttsnConnection->handleConnect(client, snPacket); + break; + case MQTTSN_WILLTOPIC: + _mqttsnConnection->handleWilltopic(client, snPacket); + break; + case MQTTSN_WILLMSG: + _mqttsnConnection->handleWillmsg(client, snPacket); + break; + case MQTTSN_DISCONNECT: + _mqttsnConnection->handleDisconnect(client, snPacket); + break; + case MQTTSN_WILLMSGUPD: + _mqttsnConnection->handleWillmsgupd(client, snPacket); + break; + case MQTTSN_PINGREQ: + _mqttsnConnection->handlePingreq(client, snPacket); + break; + case MQTTSN_PUBLISH: + _mqttsnPublish->handlePublish(client, snPacket); + break; + case MQTTSN_PUBACK: + _mqttsnPublish->handlePuback(client, snPacket); + break; + _mqttsnPublish->handleAck(client, snPacket, PUBREC); + break; + case MQTTSN_PUBREL: + _mqttsnPublish->handleAck(client, snPacket, PUBREL); + break; + case MQTTSN_PUBCOMP: + _mqttsnPublish->handleAck(client, snPacket, PUBCOMP); + break; + case MQTTSN_REGISTER: + _mqttsnPublish->handleRegister(client, snPacket); + break; + case MQTTSN_REGACK: + // NOP + break; + case MQTTSN_SUBSCRIBE: + _mqttsnSubscribe->handleSubscribe(client, snPacket); + break; + case MQTTSN_UNSUBSCRIBE: + _mqttsnSubscribe->handleUnsubscribe(client, snPacket); + break; + default: + break; + } + + /* Reset the Timer for PINGREQ. */ + client->updateStatus(snPacket); + } + + /*------ Handle Messages form Broker ---------*/ + else if (ev->getEventType() == EtBrokerRecv) + { + client = ev->getClient(); + brPacket = ev->getMQTTGWPacket(); + DEBUGLOG(" PacketHandleTask gets %s %s from the broker.\n", brPacket->getName(), brPacket->getMsgId(msgId)); + switch (brPacket->getType()) + { + case CONNACK: + _mqttConnection->handleConnack(client, brPacket); + break; + case DISCONNECT: + _mqttConnection->handleDisconnect(client, brPacket); + break; + case PINGRESP: + _mqttConnection->handlePingresp(client, brPacket); + break; + case PUBLISH: + _mqttPublish->handlePublish(client, brPacket); + break; + case PUBACK: + _mqttPublish->handlePuback(client, brPacket); + break; + case PUBREC: + _mqttPublish->handleAck(client, brPacket, PUBREC); + break; + case PUBREL: + _mqttPublish->handleAck(client, brPacket, PUBREL); + break; + case PUBCOMP: + _mqttPublish->handleAck(client, brPacket, PUBCOMP); + break; + case SUBACK: + _mqttSubscribe->handleSuback(client, brPacket); + break; + case UNSUBACK: + _mqttSubscribe->handleUnsuback(client, brPacket); + break; + default: + break; + } + } + delete ev; + } +} + diff --git a/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.h b/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.h new file mode 100644 index 0000000..c91e690 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.h @@ -0,0 +1,65 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef MQTTSNGWPACKETHANDLETASK_H_ +#define MQTTSNGWPACKETHANDLETASK_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" +#include "MQTTSNGWPacket.h" +#include "MQTTGWPacket.h" +#include "MQTTGWConnectionHandler.h" +#include "MQTTGWPublishHandler.h" +#include "MQTTGWSubscribeHandler.h" +#include "MQTTSNGWConnectionHandler.h" +#include "MQTTSNGWPublishHandler.h" +#include "MQTTSNGWSubscribeHandler.h" +#include "SensorNetwork.h" +#include "linux.h" + + +namespace MQTTSNGW +{ + +#define ERRNO_APL_01 11 // Task Initialize Error + +/*===================================== + Class PacketHandleTask + =====================================*/ +class PacketHandleTask : public Thread +{ + MAGIC_WORD_FOR_THREAD; +public: + PacketHandleTask(Gateway* gateway); + ~PacketHandleTask(); + void run(); +private: + Gateway* _gateway; + Timer _advertiseTimer; + Timer _sendUnixTimer; + MQTTGWConnectionHandler* _mqttConnection; + MQTTGWPublishHandler* _mqttPublish; + MQTTGWSubscribeHandler* _mqttSubscribe; + MQTTSNConnectionHandler* _mqttsnConnection; + MQTTSNPublishHandler* _mqttsnPublish; + MQTTSNSubscribeHandler* _mqttsnSubscribe; +}; + + +} + +#endif /* MQTTSNGWPACKETHANDLETASK_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWProcess.cpp b/MQTTSNGateway/src/MQTTSNGWProcess.cpp new file mode 100644 index 0000000..c8e07fb --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWProcess.cpp @@ -0,0 +1,333 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "MQTTSNGWProcess.h" +#include "Threading.h" +#include "linux.h" + +using namespace std; +using namespace MQTTSNGW; + +char* currentDateTime(void); + +/*===================================== + Global Variables & Functions + ======================================*/ +Process* MQTTSNGW::theProcess = 0; +MultiTaskProcess* MQTTSNGW::theMultiTaskProcess = 0; + +/* + * Save the type of signal + */ +volatile int theSignaled = 0; + +static void signalHandler(int sig) +{ + theSignaled = sig; +} + +/*===================================== + Class Process + ====================================*/ +Process::Process() +{ + _argc = 0; + _argv = 0; + _rbsem = new Semaphore(MQTTSNGW_RB_SEMAPHOR_NAME, 0); + _rb = new RingBuffer(); +} + +Process::~Process() +{ + delete _rb; + delete _rbsem; +} + +void Process::run() +{ + +} + +void Process::initialize(int argc, char** argv) +{ + _argc = argc; + _argv = argv; + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + signal(SIGHUP, signalHandler); +} + +int Process::getArgc() +{ + return _argc; +} + +char** Process::getArgv() +{ + return _argv; +} + +int Process::getParam(const char* parameter, char* value) +{ + char str[MQTTSNGW_PARAM_MAX]; + char param[MQTTSNGW_PARAM_MAX]; + FILE *fp; + int i = 0, j = 0; + + if ((fp = fopen(MQTTSNGW_CONFIG_FILE, "r")) == NULL) + { + WRITELOG("No config file:[%s]\n", MQTTSNGW_CONFIG_FILE); + return -1; + } + + while (true) + { + if (fgets(str, MQTTSNGW_PARAM_MAX - 1, fp) == NULL) + { + fclose(fp); + return -3; + } + if (!strncmp(str, parameter, strlen(parameter))) + { + while (str[i++] != '=') + { + ; + } + while (str[i] != '\n') + { + param[j++] = str[i++]; + } + param[j] = '\0'; + + for (i = strlen(param) - 1; i >= 0 && isspace(param[i]); i--) + ; + param[i + 1] = '\0'; + for (i = 0; isspace(param[i]); i++) + ; + if (i > 0) + { + j = 0; + while (param[i]) + param[j++] = param[i++]; + param[j] = '\0'; + } + strcpy(value, param); + fclose(fp); + return 0; + } + } + fclose(fp); + return -2; +} + +void Process::putLog(const char* format, ...) +{ + _mt.lock(); + va_list arg; + va_start(arg, format); + vsprintf(_rbdata, format, arg); + va_end(arg); + if (strlen(_rbdata)) + { + _rb->put(_rbdata); + _rbsem->post(); + } + _mt.unlock(); +} + +const char* Process::getLog() +{ + int len = 0; + _mt.lock(); + while ((len = _rb->get(_rbdata, PROCESS_LOG_BUFFER_SIZE)) == 0) + { + _rbsem->timedwait(1000); + if ( checkSignal() == SIGINT) + { + break; + } + } + *(_rbdata + len) = 0; + _mt.unlock(); + return _rbdata; +} + +void Process::resetRingBuffer() +{ + _rb->reset(); +} + +int Process::checkSignal(void) +{ + return theSignaled; +} + +/*===================================== + Class MultiTaskProcess + ====================================*/ +MultiTaskProcess::MultiTaskProcess() +{ + theMultiTaskProcess = this; + _threadCount = 0; +} + +MultiTaskProcess::~MultiTaskProcess() +{ + for (int i = 0; i < _threadCount; i++) + { + if ( _threadList[i] ) + { + delete _threadList[i]; + } + } +} + +void MultiTaskProcess::initialize(int argc, char** argv) +{ + Process::initialize(argc, argv); + for (int i = 0; i < _threadCount; i++) + { + _threadList[i]->initialize(argc, argv); + } + +} + +void MultiTaskProcess::run(void) +{ + for (int i = 0; i < _threadCount; i++) + { + _threadList[i]->start(); + } + + try + { + _stopProcessEvent.wait(); + } + catch (Exception* ex) + { + ex->writeMessage(); + } +} + +Semaphore* MultiTaskProcess::getStopProcessEvent(void) +{ + return &_stopProcessEvent; +} + +void MultiTaskProcess::attach(Thread* thread) +{ + if (_threadCount < MQTTSNGW_MAX_TASK) + { + _threadList[_threadCount] = thread; + _threadCount++; + } + else + { + throw Exception("Full of Threads"); + } +} + +int MultiTaskProcess::getParam(const char* parameter, char* value) +{ + _mutex.lock(); + int rc = Process::getParam(parameter, value); + _mutex.unlock(); + if (rc == -1) + { + throw Exception("No config file."); + } + return rc; +} + +/*===================================== + Class Exception + ======================================*/ +Exception::Exception(const string& message) +{ + _message = message; + _exNo = 0; + _fileName = 0; + _functionName = 0; + _line = 0; +} + +Exception::Exception(const int exNo, const string& message) +{ + _message = message; + _exNo = exNo; + _fileName = 0; + _functionName = 0; + _line = 0; +} + +Exception::Exception(const int exNo, const string& message, const char* file, + const char* function, const int line) +{ + _message = message; + _exNo = exNo; + _fileName = file; + _functionName = function; + _line = line; +} + +Exception::~Exception() throw () +{ + +} + +const char* Exception::what() const throw () +{ + return _message.c_str(); +} + +const char* Exception::getFileName() +{ + return _fileName; +} + +const char* Exception::getFunctionName() +{ + return _functionName; +} + +const int Exception::getLineNo() +{ + return _line; +} + +const int Exception::getExceptionNo() +{ + return _exNo; +} + +void Exception::writeMessage() +{ + if (getExceptionNo() == 0 ) + { + WRITELOG("%s : %s\n", currentDateTime(), what()); + } + else + { + WRITELOG("%s:%-6d %s line %-4d %s() : %s\n", currentDateTime(), getExceptionNo(), + getFileName(), getLineNo(), getFunctionName(), what()); + } +} diff --git a/MQTTSNGateway/src/MQTTSNGWProcess.h b/MQTTSNGateway/src/MQTTSNGWProcess.h new file mode 100644 index 0000000..55b223b --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWProcess.h @@ -0,0 +1,248 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef MQTTSNGWPROCESS_H_ +#define MQTTSNGWPROCESS_H_ + +#include +#include +#include "MQTTSNGWDefines.h" +#include "Threading.h" + +using namespace std; + +namespace MQTTSNGW +{ + +/*================================= + * Parameters + ==================================*/ +#define MQTTSNGW_MAX_TASK 10 // number of Tasks +#define PROCESS_LOG_BUFFER_SIZE 16384 // Ring buffer size for Logs +#define MQTTSNGW_PARAM_MAX 128 // Max length of config records. + +/*================================= + * Macros + ==================================*/ +#ifdef RINGBUFFER +#define WRITELOG theProcess->putLog +#else +#define WRITELOG printf +#endif + + +/*================================= + Class Process + ==================================*/ +class Process +{ +public: + Process(); + virtual ~Process(); + virtual void initialize(int argc, char** argv); + virtual void run(void); + void putLog(const char* format, ...); + void resetRingBuffer(void); + int getArgc(void); + char** getArgv(void); + int getParam(const char* parameter, char* value); + const char* getLog(void); + int checkSignal(void); + +private: + int _argc; + char** _argv; + RingBuffer* _rb; + Semaphore* _rbsem; + Mutex _mt; + char _rbdata[PROCESS_LOG_BUFFER_SIZE + 1]; +}; + +/*===================================== + Class MultiTaskProcess + ====================================*/ +class MultiTaskProcess: public Process +{ +public: + MultiTaskProcess(void); + ~MultiTaskProcess(); + virtual void initialize(int argc, char** argv); + virtual int getParam(const char* parameter, char* value); + void run(void); + void attach(Thread* thread); + Semaphore* getStopProcessEvent(void); + +private: + Thread* _threadList[MQTTSNGW_MAX_TASK]; + Semaphore _stopProcessEvent; + Mutex _mutex; + int _threadCount; +}; + +/*===================================== + Class Exception + =====================================*/ +class Exception: public exception +{ +public: + Exception(const string& message); + Exception(const int exNo, const string& message); + Exception(const int exNo, const string& message, + const char* file, const char* func, const int line); + virtual ~Exception() throw (); + const char* getFileName(); + const char* getFunctionName(); + const int getLineNo(); + const int getExceptionNo(); + virtual const char* what() const throw (); + void writeMessage(); + +private: + int _exNo; + string _message; + const char* _fileName; + const char* _functionName; + int _line; +}; + + +/*===================================== + Class QueElement + ====================================*/ +template +class QueElement +{ + template friend class Que; +public: + QueElement(T* t) + { + _element = t; + _next = 0; + _prev = 0; + } + + ~QueElement() + { + } + +private: + T* _element; + QueElement* _next; + QueElement* _prev; +}; + +/*===================================== + Class Que + ====================================*/ +template +class Que +{ +public: + Que() + { + _head = 0; + _tail = 0; + _cnt = 0; + } + + ~Que() + { + QueElement* elm = _head; + while (elm) + { + QueElement* next = elm->_next; + delete elm->_element; + delete elm; + elm = next; + } + } + + void pop(void) + { + if ( _head ) + { + QueElement* head = _head; + if ( _head == _tail ) + { + _head = _tail = 0; + } + else + { + _head = head->_next; + head->_prev = 0; + } + delete head; + _cnt--; + } + } + + T* front(void) + { + { + if ( _head ) + { + return _head->_element; + } + else + { + return 0; + } + } + } + + int post(T* t) + { + QueElement* elm = new QueElement(t); + if ( _head ) + { + if ( _tail == _head ) + { + elm->_prev = _tail; + _tail = elm; + } + else + { + _tail->_next = elm; + elm->_prev = _tail; + _tail = elm; + } + } + else + { + _head = elm; + _tail = elm; + } + _cnt++; + return _cnt; + } + + int size(void) + { + return _cnt; + } + +private: + int _cnt; + QueElement* _head; + QueElement* _tail; +}; + + +extern Process* theProcess; +extern MultiTaskProcess* theMultiTaskProcess; + +} +#endif /* MQTTSNGWPROCESS_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWPublishHandler.cpp b/MQTTSNGateway/src/MQTTSNGWPublishHandler.cpp new file mode 100644 index 0000000..7029fce --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPublishHandler.cpp @@ -0,0 +1,195 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWPublishHandler.h" +#include "MQTTSNGWPacket.h" +#include "MQTTGWPacket.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" + +using namespace std; +using namespace MQTTSNGW; + +MQTTSNPublishHandler::MQTTSNPublishHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTSNPublishHandler::~MQTTSNPublishHandler() +{ + +} + +void MQTTSNPublishHandler::handlePublish(Client* client, MQTTSNPacket* packet) +{ + uint8_t dup; + int qos; + uint8_t retained; + uint16_t msgId; + MQTTSN_topicid topicid; + uint8_t* payload; + int payloadlen; + Publish pub; + char shortTopic[2]; + + if ( !client->isActive() ) + { + /* Reply DISCONNECT to the client */ + WRITELOG(" The client is not active. status = %s\n", client->getStatus()); + Event* ev = new Event(); + MQTTSNPacket* disconnect = new MQTTSNPacket(); + disconnect->setDISCONNECT(0); + ev->setClientSendEvent(client, disconnect); + _gateway->getClientSendQue()->post(ev); + return; + } + + packet->getPUBLISH(&dup, &qos, &retained, &msgId, &topicid, &payload, &payloadlen); + pub.msgId = msgId; + pub.header.bits.dup = dup; + pub.header.bits.qos = qos; + pub.header.bits.retain = retained; + + Topic* topic = 0; + + if ( topicid.type == MQTTSN_TOPIC_TYPE_PREDEFINED) + { + /* + * ToDo: PUBLISH predefined Topic procedures. + */ + + if(msgId) + { + /* Reply PubAck to the client */ + MQTTSNPacket* pubAck = new MQTTSNPacket(); + pubAck->setPUBACK( topicid.data.id, msgId, MQTTSN_RC_ACCEPTED); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, pubAck); + _gateway->getClientSendQue()->post(ev1); + } + return; + } + + if( topicid.type == MQTTSN_TOPIC_TYPE_SHORT ) + { + shortTopic[0] = topicid.data.short_name[0]; + shortTopic[1] = topicid.data.short_name[1]; + pub.topic = shortTopic; + pub.topiclen = 2; + } + + if ( topicid.type == MQTTSN_TOPIC_TYPE_NORMAL ) + { + topic = client->getTopics()->getTopic(topicid.data.id); + if( !topic && msgId && qos > 0 ) + { + /* Reply PubAck of INVALID_TOPIC_ID to the client */ + MQTTSNPacket* pubAck = new MQTTSNPacket(); + pubAck->setPUBACK( topicid.data.id, msgId, MQTTSN_RC_REJECTED_INVALID_TOPIC_ID); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, pubAck); + _gateway->getClientSendQue()->post(ev1); + return; + } + if ( topic ) + { + pub.topic = (char*)topic->getTopicName()->data(); + pub.topiclen = topic->getTopicName()->length(); + } + } + /* Save a msgId & a TopicId pare for PUBACK */ + if( msgId && qos > 0 ) + { + client->setWaitedPubTopicId(msgId, topicid.data.id, topicid.type); + } + + pub.payload = (char*)payload; + pub.payloadlen = payloadlen; + + MQTTGWPacket* pulish = new MQTTGWPacket(); + pulish->setPUBLISH(&pub); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, pulish); + _gateway->getBrokerSendQue()->post(ev1); +} + +void MQTTSNPublishHandler::handlePuback(Client* client, MQTTSNPacket* packet) +{ + uint16_t topicId; + uint16_t msgId; + uint8_t rc; + + if ( !client->isActive() ) + { + return; + } + MQTTGWPacket* pubAck = new MQTTGWPacket(); + packet->getPUBACK(&topicId, &msgId, &rc); + if ( rc == MQTTSN_RC_ACCEPTED) + { + pubAck->setAck(PUBACK, msgId); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, pubAck); + _gateway->getBrokerSendQue()->post(ev1); + } + else if ( rc == MQTTSN_RC_REJECTED_INVALID_TOPIC_ID) + { + WRITELOG(" PUBACK %d : Invalid Topic ID\n", msgId); + } +} + +void MQTTSNPublishHandler::handleAck(Client* client, MQTTSNPacket* packet, uint8_t packetType) +{ + uint16_t msgId; + + if ( !client->isActive() ) + { + return; + } + packet->getACK(&msgId); + MQTTGWPacket* ackPacket = new MQTTGWPacket(); + ackPacket->setAck(packetType, msgId); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, ackPacket); + _gateway->getBrokerSendQue()->post(ev1); +} + +void MQTTSNPublishHandler::handleRegister(Client* client, MQTTSNPacket* packet) +{ + uint16_t id; + uint16_t msgId; + MQTTSNString topicName; + MQTTSN_topicid topicid; + + + if ( !client->isActive() ) + { + return; + } + MQTTSNPacket* regAck = new MQTTSNPacket(); + packet->getREGISTER(&id, &msgId, &topicName); + + topicid.type = MQTTSN_TOPIC_TYPE_NORMAL; + topicid.data.long_.len = topicName.lenstring.len; + topicid.data.long_.name = topicName.lenstring.data; + + id = client->getTopics()->add(&topicid)->getTopicId(); + regAck->setREGACK(id, msgId, MQTTSN_RC_ACCEPTED); + Event* ev = new Event(); + ev->setClientSendEvent(client, regAck); + _gateway->getClientSendQue()->post(ev); + +} diff --git a/MQTTSNGateway/src/MQTTSNGWPublishHandler.h b/MQTTSNGateway/src/MQTTSNGWPublishHandler.h new file mode 100644 index 0000000..7213cbe --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPublishHandler.h @@ -0,0 +1,40 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWPUCLISHHANDLER_H_ +#define MQTTSNGWPUCLISHHANDLER_H_ + +#include "MQTTGWPacket.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" + +namespace MQTTSNGW +{ +class MQTTSNPublishHandler +{ +public: + MQTTSNPublishHandler(Gateway* gateway); + ~MQTTSNPublishHandler(); + void handlePublish(Client* client, MQTTSNPacket* packet); + void handlePuback(Client* client, MQTTSNPacket* packet); + void handleAck(Client* client, MQTTSNPacket* packet, uint8_t packetType); + void handleRegister(Client* client, MQTTSNPacket* packet); + +private: + Gateway* _gateway; +}; + +} +#endif /* MQTTSNGWPUCLISHHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.cpp b/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.cpp new file mode 100644 index 0000000..6dcfb98 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.cpp @@ -0,0 +1,192 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWSubscribeHandler.h" +#include "MQTTSNGWPacket.h" +#include "MQTTGWPacket.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" + +using namespace std; +using namespace MQTTSNGW; + +MQTTSNSubscribeHandler::MQTTSNSubscribeHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTSNSubscribeHandler::~MQTTSNSubscribeHandler() +{ + +} + +void MQTTSNSubscribeHandler::handleSubscribe(Client* client, MQTTSNPacket* packet) +{ + MQTTGWPacket* subscribe = new MQTTGWPacket(); + uint8_t dup; + int qos; + uint16_t msgId; + MQTTSN_topicid topicFilter; + Topic* topic = 0; + + packet->getSUBSCRIBE(&dup, &qos, &msgId, &topicFilter); + + if (topicFilter.type <= MQTTSN_TOPIC_TYPE_SHORT) + { + if (topicFilter.type == MQTTSN_TOPIC_TYPE_PREDEFINED) + { + /*----- Predefined TopicId ------*/ + MQTTSNPacket* sSuback = new MQTTSNPacket(); + + if (msgId) + { + int rc = MQTTSN_RC_ACCEPTED; + + switch (topicFilter.data.id) + { + case 1: // check topicIds are defined. + case 2: + break; + default: + rc = MQTTSN_RC_REJECTED_INVALID_TOPIC_ID; + } + sSuback->setSUBACK(qos, topicFilter.data.id, msgId, rc); + Event* evsuback = new Event(); + evsuback->setClientSendEvent(client, sSuback); + _gateway->getClientSendQue()->post(evsuback); + } + switch (topicFilter.data.id) + { + case 1: + /* + * ToDo: write here Predefined Topic 01 Procedures. + */ + break; + case 2: + /* + * ToDo: write here Predefined Topic 02 Procedures. so on + */ + break; + default: + break; + } + } + else + { + topic = client->getTopics()->getTopic(&topicFilter); + if (topic == 0) + { + if (topicFilter.type == MQTTSN_TOPIC_TYPE_NORMAL) + { + topic = client->getTopics()->add(&topicFilter); + subscribe->setSUBSCRIBE((char*)topic->getTopicName()->c_str(), (uint8_t)qos, (uint16_t)msgId); + } + else if (topicFilter.type == MQTTSN_TOPIC_TYPE_SHORT) + { + char topic[3]; + topic[0] = topicFilter.data.short_name[0]; + topic[1] = topicFilter.data.short_name[1]; + topic[2] = 0; + subscribe->setSUBSCRIBE(topic, (uint8_t)qos, (uint16_t)msgId); + } + } + else + { + subscribe->setSUBSCRIBE((char*)topic->getTopicName()->c_str(), (uint8_t)qos, (uint16_t)msgId); + } + + if ( msgId > 0 ) + { + client->setWaitedSubTopicId(msgId, topic->getTopicId(), topicFilter.type); + } + + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, subscribe); + _gateway->getBrokerSendQue()->post(ev1); + return; + } + } + else + { + /*-- Invalid TopicIdType --*/ + if (msgId) + { + MQTTSNPacket* sSuback = new MQTTSNPacket(); + sSuback->setSUBACK(qos, topicFilter.data.id, msgId, MQTTSN_RC_REJECTED_INVALID_TOPIC_ID); + Event* evsuback = new Event(); + evsuback->setClientSendEvent(client, sSuback); + _gateway->getClientSendQue()->post(evsuback); + } + } +} +void MQTTSNSubscribeHandler::handleUnsubscribe(Client* client, MQTTSNPacket* packet) +{ + uint16_t msgId; + MQTTSN_topicid topicFilter; + + packet->getUNSUBSCRIBE(&msgId, &topicFilter); + + if ( topicFilter.type == MQTTSN_TOPIC_TYPE_PREDEFINED ) + { + /* + * ToDo: procedures for Predefined Topic + */ + return; + } + + Topic* topic = client->getTopics()->getTopic(&topicFilter); + MQTTGWPacket* unsubscribe = new MQTTGWPacket(); + + if (topicFilter.type == MQTTSN_TOPIC_TYPE_NORMAL) + { + if ( topic == 0 ) + { + if (msgId) + { + MQTTSNPacket* sUnsuback = new MQTTSNPacket(); + sUnsuback->setUNSUBACK(msgId); + Event* evsuback = new Event(); + evsuback->setClientSendEvent(client, sUnsuback); + _gateway->getClientSendQue()->post(evsuback); + delete unsubscribe; + return; + } + } + else + { + unsubscribe->setUNSUBSCRIBE(topic->getTopicName()->c_str(), msgId); + } + } + else if (topicFilter.type == MQTTSN_TOPIC_TYPE_SHORT) + { + MQTTGWPacket* unsubscribe = new MQTTGWPacket(); + char shortTopic[3]; + shortTopic[0] = topicFilter.data.short_name[0]; + shortTopic[1] = topicFilter.data.short_name[1]; + shortTopic[2] = 0; + unsubscribe->setUNSUBSCRIBE(shortTopic, msgId); + } + else + { + delete unsubscribe; + return; + } + + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, unsubscribe); + _gateway->getBrokerSendQue()->post(ev1); +} + diff --git a/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.h b/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.h new file mode 100644 index 0000000..21410d5 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.h @@ -0,0 +1,44 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWSUBSCRIBEHANDLER_H_ +#define MQTTSNGWSUBSCRIBEHANDLER_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNGWClient.h" + +namespace MQTTSNGW +{ +/*===================================== + Class MQTTSNSubscribeHandler + =====================================*/ +class MQTTSNSubscribeHandler +{ +public: + MQTTSNSubscribeHandler(Gateway* gateway); + ~MQTTSNSubscribeHandler(); + void handleSubscribe(Client* client, MQTTSNPacket* packet); + void handleUnsubscribe(Client* client, MQTTSNPacket* packet); + +private: + Gateway* _gateway; +}; + +} + + +#endif /* MQTTSNGWSUBSCRIBEHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGateway.cpp b/MQTTSNGateway/src/MQTTSNGateway.cpp new file mode 100644 index 0000000..9dd10ee --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGateway.cpp @@ -0,0 +1,328 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGateway.h" +#include "MQTTSNGWProcess.h" +#include "SensorNetwork.h" + +using namespace MQTTSNGW; + +char* currentDateTime(void); + +/*===================================== + Class Gateway + =====================================*/ +Gateway::Gateway() : + MultiTaskProcess() +{ + theMultiTaskProcess = this; + theProcess = this; + resetRingBuffer(); + _params.loginId = 0; + _params.password = 0; +} + +Gateway::~Gateway() +{ + if ( _params.loginId ) + { + free(_params.loginId); + } + if ( _params.password ) + { + free(_params.password); + } +} + +void Gateway::initialize(int argc, char** argv) +{ + char param[MQTTSNGW_PARAM_MAX]; + + _params.gatewayId = 0; + if (getParam("GatewayID", param) == 0) + { + _params.gatewayId = atoi(param); + } + + if (_params.gatewayId == 0 || _params.gatewayId > 255) + { + throw Exception( "Gateway::initialize: invalid Gateway Id"); + } + + _params.mqttVersion = DEFAULT_MQTT_VERSION; + if (getParam("MQTTVersion", param) == 0) + { + _params.mqttVersion = atoi(param); + } + + _params.maxInflightMsgs = DEFAULT_MQTT_VERSION; + if (getParam("MaxInflightMsgs", param) == 0) + { + _params.maxInflightMsgs = atoi(param); + } + + _params.keepAlive = DEFAULT_KEEP_ALIVE_TIME; + + if (getParam("KeepAlive", param) == 0) + { + _params.keepAlive = atoi(param); + } + + if (_params.keepAlive > 65536) + { + throw Exception("Gateway::initialize: KeepAliveTime is grater than 65536 Secs"); + } + + if (getParam("LoginID", param) == 0) + { + _params.loginId = (uint8_t*) strdup(param); + } + + if (getParam("Password", param) == 0) + { + _params.password = (uint8_t*) strdup(param); + } + + if (getParam("ClientAuthorization", param) == 0) + { + if (!strcasecmp(param, "YES")) + { + if (!_clientList.authorize(MQTTSNGW_CLIENT_LIST)) + { + throw Exception("Gateway::initialize: can't authorize clients."); + } + } + } + + MultiTaskProcess::initialize(argc, argv); +} + +void Gateway::run(void) +{ + _lightIndicator.redLight(true); + WRITELOG("%s MQTT-SN Gateway has been started. %s %s\n", currentDateTime(), _sensorNetwork.getType(), GATEWAY_VERSION); + if ( getClientList()->isAuthorized() ) + { + WRITELOG("\n Client authentication is required by the configuration settings.\n"); + } + + /* execute threads & wait StopProcessEvent MQTTSNGWPacketHandleTask posts by CTL-C */ + MultiTaskProcess::run(); + WRITELOG("%s MQTT-SN Gateway stoped\n", currentDateTime()); + _lightIndicator.allLightOff(); +} + +EventQue* Gateway::getPacketEventQue() +{ + return &_packetEventQue; +} + +EventQue* Gateway::getClientSendQue() +{ + return &_clientSendQue; +} + +EventQue* Gateway::getBrokerSendQue() +{ + return &_brokerSendQue; +} + +ClientList* Gateway::getClientList() +{ + return &_clientList; +} + +SensorNetwork* Gateway::getSensorNetwork() +{ + return &_sensorNetwork; +} + +LightIndicator* Gateway::getLightIndicator() +{ + return &_lightIndicator; +} + +GatewayParams* Gateway::getGWParams(void) +{ + return &_params; +} + +/*===================================== + Class EventQue + =====================================*/ +EventQue::EventQue() +{ + +} + +EventQue::~EventQue() +{ + +} + +Event* EventQue::wait(void) +{ + Event* ev; + while ( true ) + { + _sem.wait(); + _mutex.lock(); + ev = _que.front(); + _que.pop(); + _mutex.unlock(); + if ( ev ) + { + return ev; + } + } +} + +Event* EventQue::timedwait(uint16_t millsec) +{ + Event* ev; + _sem.timedwait(millsec); + _mutex.lock(); + + if (_que.size() == 0) + { + ev = new Event(); + ev->setTimeout(); + } + else + { + ev = _que.front(); + _que.pop(); + if ( !ev ) + { + ev = new Event(); + ev->setTimeout(); + } + } + _mutex.unlock(); + return ev; +} + +int EventQue::post(Event* ev) +{ + if ( ev ) + { + _mutex.lock(); + _que.post(ev); + _sem.post(); + _mutex.unlock(); + } + return 0; +} + +int EventQue::size() +{ + _mutex.lock(); + int sz = _que.size(); + _mutex.unlock(); + return sz; +} + + +/*===================================== + Class Event + =====================================*/ +Event::Event() +{ + _eventType = Et_NA; + _client = 0; + _mqttSNPacket = 0; + _mqttGWPacket = 0; +} + +Event::Event(EventType type) +{ + _eventType = type; + _client = 0; + _mqttSNPacket = 0; + _mqttGWPacket = 0; +} + +Event::~Event() +{ + if (_mqttSNPacket) + { + delete _mqttSNPacket; + } + + if (_mqttGWPacket) + { + delete _mqttGWPacket; + } +} + +EventType Event::getEventType() +{ + return _eventType; +} + +void Event::setClientSendEvent(Client* client, MQTTSNPacket* packet) +{ + _client = client; + _eventType = EtClientSend; + _mqttSNPacket = packet; +} + +void Event::setBrokerSendEvent(Client* client, MQTTGWPacket* packet) +{ + _client = client; + _eventType = EtBrokerSend; + _mqttGWPacket = packet; +} + +void Event::setClientRecvEvent(Client* client, MQTTSNPacket* packet) +{ + _client = client; + _eventType = EtClientRecv; + _mqttSNPacket = packet; +} + +void Event::setBrokerRecvEvent(Client* client, MQTTGWPacket* packet) +{ + _client = client; + _eventType = EtBrokerRecv; + _mqttGWPacket = packet; +} + +void Event::setTimeout(void) +{ + _eventType = EtTimeout; +} + +void Event::setBrodcastEvent(MQTTSNPacket* msg) +{ + _mqttSNPacket = msg; + _eventType = EtBroadcast; +} + +Client* Event::getClient(void) +{ + return _client; +} + +MQTTSNPacket* Event::getMQTTSNPacket() +{ + return _mqttSNPacket; +} + +MQTTGWPacket* Event::getMQTTGWPacket(void) +{ + return _mqttGWPacket; +} diff --git a/MQTTSNGateway/src/MQTTSNGateway.h b/MQTTSNGateway/src/MQTTSNGateway.h index 94ffa59..8115a65 100644 --- a/MQTTSNGateway/src/MQTTSNGateway.h +++ b/MQTTSNGateway/src/MQTTSNGateway.h @@ -1,5 +1,5 @@ -/******************************************************************************* - * Copyright (c) 2014, 2016 IBM Corp. +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -11,286 +11,168 @@ * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - *******************************************************************************/ - -#if !defined(MQTTSNGATEWAY_H) -#define MQTTSNGATEWAY_H + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGATEWAY_H_ +#define MQTTSNGATEWAY_H_ +#include "MQTTSNGWProcess.h" +#include "MQTTSNGWClient.h" #include "MQTTSNPacket.h" -#include "MQTTConnection.h" +#include "MQTTGWPacket.h" -namespace MQTTSN +namespace MQTTSNGW { - -#define MAX_PACKET_SIZE 256 -#define MQTTSNGATEWAY_QOS2 0 -#define MQTTSNGATEWAY_QOS1 0 -#define MAX_MQTT_CONNECTIONS 5 - -struct Parms -{ - char* hostname; - int port; - char* username; - char* password; -}; -enum QoS { QOS0, QOS1, QOS2 }; +/*========================================================== + * Log Formats + ===========================================================*/ +#define BROKER "Broker" +#define GATEWAY "Gateway" +#define CLIENT "Client" +#define CLIENTS "Clients" +#define LEFTARROW "<---" +#define RIGHTARROW "--->" -enum Modes { AGGREGATING, TRANSPARENT }; -// all failure return codes must be negative -enum returnCode { MAX_SUBSCRIPTIONS_EXCEEDED = -3, BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 }; +#define FORMAT_WHITE_NL "%s %-18s%-6s%-26s%s\n" +#define FORMAT_WH_NL "\n%s %-18s%-6s%-26s%s\n" +#define FORMAT_WH_MSGID "%s %-11s%-5s %-6s%-26s%s\n" +#define FORMAT_WH_MSGID_NL "\n%s %-11s%-5s %-6s%-26s%s\n" +#define FORMAT_WH_GR "%s %-18s%-6s\x1b[0m\x1b[32m%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_WH_GR_MSGID "%s %-11s%-5s %-6s\x1b[0m\x1b[32m%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_WH_GR_MSGID_NL "\n%s %-11s%-5s %-6s\x1b[0m\x1b[32m%-26s\x1b[0m\x1b[37m%s\n" -struct Message -{ - enum QoS qos; - bool retained; - bool dup; - unsigned short id; - void *payload; - size_t payloadlen; +#define FORMAT_GR "%s \x1b[0m\x1b[32m%-18s%-6s\x1b[0m\x1b[37m%-26s%s\n" +#define FORMAT_GR_NL "\n%s \x1b[0m\x1b[32m%-18s%-6s\x1b[0m\x1b[37m%-26s%s\n" +#define FORMAT_GR_MSGID "%s \x1b[0m\x1b[32m%-11s%-5s %-6s%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_GR_MSGID_NL "\n%s \x1b[0m\x1b[32m%-11s%-5s %-6s%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_GR_WH_MSGID "%s \x1b[0m\x1b[32m%-11s%-5s %-6s\x1b[0m\x1b[37m%-26s%s\n" +#define FORMAT_GR_WH_MSGID_NL "\n%s \x1b[0m\x1b[32m%-11s%-5s %-6s\x1b[0m\x1b[37m%-26s%s\n" + +#define FORMAT_YE "%s \x1b[0m\x1b[33m%-18s%-6s%-44s\x1b[0m\x1b[37m%s\n" +#define FORMAT_YE_NL "\n%s \x1b[0m\x1b[33m%-18s%-6s%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_YE_WH "%s \x1b[0m\x1b[33m%-18s%-6s\x1b[0m\x1b[37m%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_YE_WH_NL "\n%s \x1b[0m\x1b[33m%-18s%-6s\x1b[0m\x1b[37m%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_YE_GR "%s \x1b[0m\x1b[33m%-18s%-6s\x1b[0m\x1b[32m%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_YE_GR_MSGID "%s \x1b[0m\x1b[33m%-11s%-5s %-6s\x1b[0m\x1b[32m%-26s\x1b[0m\x1b[37m%s\n" + +#define FORMAT_CY_ANY "%s \x1b[0m\x1b[36m%-18s%-6s%-44s\x1b[0m\x1b[37m%s\n" +#define FORMAT_CY "%s \x1b[0m\x1b[36m%-18s%-6s%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_CY_NL "\n%s \x1b[0m\x1b[36m%-18s%-6s%-26s\x1b[0m\x1b[37m%s\n" + +#define FORMAT_BL_NL "\n%s \x1b[0m\x1b[34m%-18s%-6s%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_RED "%s \x1b[0m\x1b[31m%-18s%-6s%-44s\x1b[0m\x1b[37m%s\n" +#define FORMAT_RED_NL "\n%s \x1b[0m\x1b[31m%-18s%-6s%-26s\x1b[0m\x1b[37m%s\n" +#define ERRMSG_HEADER "\x1b[0m\x1b[31mError:" +#define ERRMSG_FOOTER "\x1b[0m\x1b[37m" + +/*========================================================== + * Gateway default parameters + ===========================================================*/ +#define DEFAULT_KEEP_ALIVE_TIME (900) // 900 secs = 15 mins +#define DEFAULT_MAX_CLIENTS (100) // Number of Clients can be handled. +#define DEFAULT_MQTT_VERSION (4) // Defualt MQTT version +#define DEFAULT_INFLIGHTMESSAGE (10) // Number of inflight messages + +/*===================================== + Class Event + ====================================*/ +enum EventType{ + Et_NA = 0, + EtTimeout, + EtBrokerRecv, + EtBrokerSend, + EtClientRecv, + EtClientSend, + EtBroadcast, + EtSocketAlive }; -template -class Gateway +class Event{ +public: + Event(); + Event(EventType); + ~Event(); + EventType getEventType(void); + void setClientRecvEvent(Client*, MQTTSNPacket*); + void setClientSendEvent(Client*, MQTTSNPacket*); + void setBrokerRecvEvent(Client*, MQTTGWPacket*); + void setBrokerSendEvent(Client*, MQTTGWPacket*); + void setBrodcastEvent(MQTTSNPacket*); // ADVERTISE and GWINFO + void setTimeout(void); // Required by EventQue.timedwait() + Client* getClient(void); + MQTTSNPacket* getMQTTSNPacket(void); + MQTTGWPacket* getMQTTGWPacket(void); + +private: + EventType _eventType; + Client* _client; + MQTTSNPacket* _mqttSNPacket; + MQTTGWPacket* _mqttGWPacket; +}; + +/*===================================== + Class EventQue + ====================================*/ +class EventQue { public: + EventQue(); + ~EventQue(); + Event* wait(void); + Event* timedwait(uint16_t millsec); + int post(Event*); + int size(); - Gateway(UDPNetwork&, unsigned int command_timeout_ms = 30000); - - void run(void const* arg); - private: - - int cycle(Timer& timer); - int sendPacket(int length, Timer& timer); - int readPacket(Timer& timer); - - UDPNetwork& udpstack; // Not restricted to UDP - for want of a better name - - unsigned char sendbuf[MAX_PACKET_SIZE]; - unsigned char readbuf[MAX_PACKET_SIZE]; - - enum Modes mode; - - MQTT::Connection connections[MAX_MQTT_CONNECTIONS]; - + Que _que; + Mutex _mutex; + Semaphore _sem; }; - -} // end namespace - - -template -MQTTSN::Gateway::Gateway(UDPNetwork& network, unsigned int command_timeout_ms) : udpstack(network) -{ - mode = AGGREGATING; -} - - -template -void MQTTSN::Gateway::run(void const* arg) -{ - Timer timer; - - printf("Gateway run 0\n"); - if (mode == AGGREGATING) - { - // set up connection 0 information - Thread mythread(&connections[0].run); - } - - printf("Gateway run 1\n"); - while (true) - { - printf("Gateway cycle\n"); - cycle(timer); - } - -} - - -template -int MQTTSN::Gateway::cycle(Timer& timer) -{ - /* get one piece of work off the wire and one pass through */ - - // read the socket, see what work is due - unsigned short packet_type = readPacket(timer); - - printf("read packet\n"); - - int len = 0; - int rc = SUCCESS; - - switch (packet_type) - { - case MQTTSN_CONNECT: - { - MQTTSNPacket_connectData data = MQTTSNPacket_connectData_initializer; - MQTTSNDeserialize_connect(&data, readbuf, MAX_PACKET_SIZE); - - if (mode == TRANSPARENT) - { - //start a new MQTT connection at this point - } - - len = MQTTSNSerialize_connack(sendbuf, MAX_PACKET_SIZE, 0); - if (len <= 0) - rc = FAILURE; - else - rc = sendPacket(len, timer); - break; - } - - case MQTTSN_REGISTER: - { - unsigned short topicid, packetid; - MQTTSNString topicName; - unsigned char reg_rc = MQTTSN_RC_ACCEPTED; - if (MQTTSNDeserialize_register(&topicid, &packetid, &topicName, readbuf, MAX_PACKET_SIZE) != 1) - goto exit; - - // store topic registration info - - len = MQTTSNSerialize_regack(connections[0].sendbuf, MAX_PACKET_SIZE, topicid, packetid, reg_rc); - if (len <= 0) - rc = FAILURE; - else - rc = connections[0].sendPacket(len, timer); - - break; - } - - case MQTTSN_SUBSCRIBE: - { - unsigned char dup; - int qos; - unsigned short packetid; - MQTTSN_topicid topicFilter; - MQTTSNDeserialize_subscribe(&dup, &qos, &packetid, &topicFilter, readbuf, MAX_PACKET_SIZE); - MQTTString topic = MQTTString_initializer; - - if (topicFilter.type == MQTTSN_TOPIC_TYPE_NORMAL) - { - topic.lenstring.len = topicFilter.data.long_.len; - topic.lenstring.data = topicFilter.data.long_.name; - } - - len = MQTTSerialize_subscribe(connections[0].sendbuf, MAX_PACKET_SIZE, 0, packetid, 1, &topic, (int*)&qos); - if (len <= 0) - goto exit; - if ((rc = connections[0].sendPacket(len, timer)) != SUCCESS) // send the subscribe packet - goto exit; // there was a problem - - break; - } - - case MQTTSN_PUBLISH: - { - MQTTString topic = MQTTString_initializer; - MQTTSN_topicid topicid; - Message msg; - - if (MQTTSNDeserialize_publish((unsigned char*)&msg.dup, (int*)&msg.qos, (unsigned char*)&msg.retained, &msg.id, &topicid, - (unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_PACKET_SIZE) != 1) - goto exit; - - - len = MQTTSerialize_publish(connections[0].sendbuf, MAX_PACKET_SIZE, msg.dup, msg.qos, msg.retained, msg.id, topic, - (unsigned char*)msg.payload, msg.payloadlen); - if (len <= 0) - goto exit; - if ((rc = connections[0].sendPacket(len, timer)) != SUCCESS) // send the subscribe packet - goto exit; // there was a problem - - } - - - case MQTTSN_PINGRESP: - //ping_outstanding = false; - break; - } - //keepalive(); -exit: - if (rc == SUCCESS) - rc = packet_type; - return rc; -} - - - -template -int MQTTSN::Gateway::sendPacket(int length, Timer& timer) -{ - int rc = FAILURE, - sent = 0; - - do - { - sent = udpstack.write(sendbuf, length, timer.left_ms()); - printf("sendPacket, rc %d from write of %d bytes\n", sent, length); - if (sent < 0) // there was an error writing the data - break; - } - while (sent != length && !timer.expired()); - - if (sent == length) - { - //if (this->duration > 0) - // last_sent.countdown(this->duration); // record the fact that we have successfully sent the packet - rc = SUCCESS; - } - else - rc = FAILURE; - -#if defined(MQTT_DEBUG) - char printbuf[50]; - DEBUG("Rc %d from sending packet %s\n", rc, MQTTSNPacket_toString(printbuf, sizeof(printbuf), sendbuf, length)); -#endif - return rc; -} - - -/** - * If any read fails in this method, then we should disconnect from the network, as on reconnect - * the packets can be retried. - * @param timeout the max time to wait for the packet read to complete, in milliseconds - * @return the MQTT packet type, or -1 if none +/* + * GatewayParams */ -template -int MQTTSN::Gateway::readPacket(Timer& timer) +typedef struct { - int rc = FAILURE; - int len = 0; // the length of the whole packet including length field - int lenlen = 0; - int datalen = 0; + uint8_t* loginId; + uint8_t* password; + uint16_t keepAlive; + uint8_t gatewayId; + uint8_t mqttVersion; + uint16_t maxInflightMsgs; +}GatewayParams; + +/*===================================== + Class Gateway + =====================================*/ +class Gateway: public MultiTaskProcess{ +public: + Gateway(); + ~Gateway(); + virtual void initialize(int argc, char** argv); + void run(void); + + EventQue* getPacketEventQue(void); + EventQue* getClientSendQue(void); + EventQue* getBrokerSendQue(void); + ClientList* getClientList(void); + SensorNetwork* getSensorNetwork(void); + LightIndicator* getLightIndicator(void); + GatewayParams* getGWParams(void); + +private: + ClientList _clientList; + EventQue _packetEventQue; + EventQue _brokerSendQue; + EventQue _clientSendQue; + LightIndicator _lightIndicator; + GatewayParams _params; + SensorNetwork _sensorNetwork; +}; - #define MQTTSN_MIN_PACKET_LENGTH 3 - // 1. read the packet, datagram style - if ((len = udpstack.read(readbuf, MAX_PACKET_SIZE, timer.left_ms())) < MQTTSN_MIN_PACKET_LENGTH) - goto exit; - - // 2. read the length. This is variable in itself - lenlen = MQTTSNPacket_decode(readbuf, len, &datalen); - if (datalen != len) - goto exit; // there was an error - - rc = readbuf[lenlen]; - //if (this->duration > 0) - // last_received.countdown(this->duration); // record the fact that we have successfully received a packet -exit: - -#if defined(MQTT_DEBUG) - char printbuf[50]; - DEBUG("Rc %d from receiving packet %s\n", rc, MQTTSNPacket_toString(printbuf, sizeof(printbuf), readbuf, len)); -#endif - return rc; } - - -#endif +#endif /* MQTTSNGATEWAY_H_ */ diff --git a/MQTTSNGateway/src/linux/Network.cpp b/MQTTSNGateway/src/linux/Network.cpp new file mode 100644 index 0000000..cde1d02 --- /dev/null +++ b/MQTTSNGateway/src/linux/Network.cpp @@ -0,0 +1,563 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Network.h" +#include "MQTTSNGWDefines.h" +#include "MQTTSNGWProcess.h" + +using namespace std; +using namespace MQTTSNGW; + +#define SOCKET_MAXCONNECTIONS 5 +char* currentDateTime(); + +/*======================================== + Class TCPStack + =======================================*/ +TCPStack::TCPStack() +{ + _addrinfo = 0; + _sockfd = 0; +} + +TCPStack::~TCPStack() +{ + if (_addrinfo) + { + freeaddrinfo(_addrinfo); + } +} + +bool TCPStack::isValid() +{ + return (_sockfd > 0); +} + +void TCPStack::close() +{ + if (_sockfd > 0) + { + ::close(_sockfd); + _sockfd = 0; + if (_addrinfo) + { + freeaddrinfo(_addrinfo); + _addrinfo = 0; + } + } + +} + +bool TCPStack::bind(const char* service) +{ + if (isValid()) + { + return false; + } + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + if (_addrinfo) + { + freeaddrinfo(_addrinfo); + } + int err = getaddrinfo(0, service, &hints, &_addrinfo); + if (err) + { + WRITELOG("\n%s \x1b[0m\x1b[31merror:\x1b[0m\x1b[37mgetaddrinfo(): %s\n", currentDateTime(), + gai_strerror(err)); + return false; + } + + _sockfd = socket(_addrinfo->ai_family, _addrinfo->ai_socktype, _addrinfo->ai_protocol); + if (_sockfd < 0) + { + return false; + } + int on = 1; + if (setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)) == -1) + { + return false; + } + + if (::bind(_sockfd, _addrinfo->ai_addr, _addrinfo->ai_addrlen) < 0) + { + return false; + } + return true; +} + +bool TCPStack::listen() +{ + if (!isValid()) + { + return false; + } + int listen_return = ::listen(_sockfd, SOCKET_MAXCONNECTIONS); + if (listen_return == -1) + { + return false; + } + return true; +} + +bool TCPStack::accept(TCPStack& new_socket) +{ + sockaddr_storage sa; + socklen_t len = sizeof(sa); + new_socket._sockfd = ::accept(_sockfd, (struct sockaddr*) &sa, &len); + if (new_socket._sockfd <= 0) + { + return false; + } + else + { + return true; + } +} + +int TCPStack::send(const uint8_t* buf, int length) +{ + return ::send(_sockfd, buf, length, MSG_NOSIGNAL); +} + +int TCPStack::recv(uint8_t* buf, int len) +{ + return ::recv(_sockfd, buf, len, 0); +} + +bool TCPStack::connect(const char* host, const char* service) +{ + if (isValid()) + { + return false; + } + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + if (_addrinfo) + { + freeaddrinfo(_addrinfo); + } + + int err = getaddrinfo(host, service, &hints, &_addrinfo); + if (err) + { + WRITELOG("\n%s \x1b[0m\x1b[31merror:\x1b[0m\x1b[37mgetaddrinfo(): %s\n", currentDateTime(), + gai_strerror(err)); + return false; + } + + int sockfd = socket(_addrinfo->ai_family, _addrinfo->ai_socktype, _addrinfo->ai_protocol); + + if (sockfd < 0) + { + return false; + } + int on = 1; + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)) == -1) + { + return false; + } + + if (::connect(sockfd, _addrinfo->ai_addr, _addrinfo->ai_addrlen) < 0) + { + //perror("TCPStack connect"); + ::close(sockfd); + return false; + } + + _sockfd = sockfd; + return true; +} + +void TCPStack::setNonBlocking(const bool b) +{ + int opts; + + opts = fcntl(_sockfd, F_GETFL); + + if (opts < 0) + { + return; + } + + if (b) + { + opts = (opts | O_NONBLOCK); + } + else + { + opts = (opts & ~O_NONBLOCK); + } + fcntl(_sockfd, F_SETFL, opts); +} + +int TCPStack::getSock() +{ + return _sockfd; +} + +/*======================================== + Class Network + =======================================*/ +int Network::_numOfInstance = 0; +SSL_CTX* Network::_ctx = 0; +SSL_SESSION* Network::_session = 0; + +Network::Network(bool secure) : + TCPStack() +{ + char error[256]; + if (secure) + { + _numOfInstance++; + if (_ctx == 0) + { + SSL_load_error_strings(); + SSL_library_init(); + _ctx = SSL_CTX_new(TLSv1_2_client_method()); + if (_ctx == 0) + { + ERR_error_string_n(ERR_get_error(), error, sizeof(error)); + WRITELOG("SSL_CTX_new() %s\n", error); + throw Exception( ERR_get_error(), "Network can't create SSL context."); + } + if (!SSL_CTX_load_verify_locations(_ctx, 0, MQTTSNGW_TLS_CA_DIR)) + { + ERR_error_string_n(ERR_get_error(), error, sizeof(error)); + WRITELOG("SSL_CTX_load_verify_locations() %s\n", error); + throw Exception( ERR_get_error(), "Network can't load CA_LIST."); + } + } + } + _ssl = 0; + _disconReq = false; + _secureFlg = secure; + _busy = false; +} + +Network::~Network() +{ + if (_secureFlg) + { + _numOfInstance--; + } + if (_ssl) + { + SSL_free(_ssl); + } + if (_session && _numOfInstance == 0) + { + SSL_SESSION_free(_session); + _session = 0; + } + if (_ctx && _numOfInstance == 0) + { + SSL_CTX_free(_ctx); + _ctx = 0; + ERR_free_strings(); + } +} + +bool Network::connect(const char* host, const char* service) +{ + char errmsg[256]; + int rc = 0; + char peer_CN[256]; + SSL_SESSION* sess = 0; + X509* peer; + + if (isValid()) + { + return false; + } + if (!TCPStack::connect(host, service)) + { + return false; + } + if (!_secureFlg) + { + return true; + } + + SSL* ssl = SSL_new(_ctx); + if (ssl == 0) + { + ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); + WRITELOG("SSL_new() %s\n", errmsg); + return false; + } + + rc = SSL_set_fd(ssl, TCPStack::getSock()); + if (rc == 0) + { + SSL_free(ssl); + return false; + } + + if (_session) + { + rc = SSL_set_session(ssl, sess); + } + else + { + rc = SSL_connect(ssl); + } + if (rc != 1) + { + ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); + WRITELOG("SSL_connect() %s\n", errmsg); + SSL_free(ssl); + return false; + } + + if (SSL_get_verify_result(ssl) != X509_V_OK) + { + WRITELOG("SSL_get_verify_result() error: Certificate doesn't verify.\n"); + SSL_free(ssl); + return false; + } + + peer = SSL_get_peer_certificate(ssl); + X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, peer_CN, 256); + if (strcasecmp(peer_CN, host)) + { + WRITELOG("SSL_get_peer_certificate() error: Broker dosen't much host name.\n"); + SSL_free(ssl); + return false; + } + if (_session == 0) + { + _session = sess; + } + _ssl = ssl; + return true; +} + +int Network::send(const uint8_t* buf, uint16_t length) +{ + char errmsg[256]; + fd_set rset; + fd_set wset; + bool writeBlockedOnRead = false; + int bpos = 0; + + if (_secureFlg) + { + _mutex.lock(); + _busy = true; + + while (true) + { + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_SET(getSock(), &rset); + FD_SET(getSock(), &wset); + + int activity = select(getSock() + 1, &rset, &wset, 0, 0); + if (activity > 0) + { + if (FD_ISSET(getSock(), &wset) || (writeBlockedOnRead && FD_ISSET(getSock(), &rset))) + { + + writeBlockedOnRead = false; + int r = SSL_write(_ssl, buf + bpos, length); + + switch (SSL_get_error(_ssl, r)) + { + case SSL_ERROR_NONE: + length -= r; + bpos += r; + if (length == 0) + { + _busy = false; + _mutex.unlock(); + return bpos; + } + break; + case SSL_ERROR_WANT_WRITE: + break; + case SSL_ERROR_WANT_READ: + writeBlockedOnRead = true; + break; + default: + ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); + WRITELOG("TLSStack::send() default %s\n", errmsg); + _busy = false; + _mutex.unlock(); + return -1; + } + } + } + } + } + else + { + return TCPStack::send(buf, length); + } +} + +int Network::recv(uint8_t* buf, uint16_t len) +{ + char errmsg[256]; + bool writeBlockedOnRead = false; + bool readBlockedOnWrite = false; + bool readBlocked = false; + int rlen = 0; + int bpos = 0; + fd_set rset; + fd_set wset; + + if (_secureFlg) + { + if (_busy) + { + return 0; + } + _mutex.lock(); + _busy = true; + + loop: do + { + readBlockedOnWrite = false; + readBlocked = false; + + rlen = SSL_read(_ssl, buf + bpos, len - bpos); + + switch (SSL_get_error(_ssl, rlen)) + { + case SSL_ERROR_NONE: + _busy = false; + _mutex.unlock(); + return rlen + bpos; + break; + case SSL_ERROR_ZERO_RETURN: + SSL_shutdown(_ssl); + _ssl = 0; + TCPStack::close(); + _busy = false; + _mutex.unlock(); + return -1; + break; + case SSL_ERROR_WANT_READ: + readBlocked = true; + break; + case SSL_ERROR_WANT_WRITE: + readBlockedOnWrite = true; + break; + default: + ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); + WRITELOG("TLSStack::recv() default %s\n", errmsg); + _busy = false; + _mutex.unlock(); + return -1; + } + } while (SSL_pending(_ssl) && !readBlocked); + + bpos += rlen; + while (true) + { + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_SET(getSock(), &rset); + FD_SET(getSock(), &wset); + + int activity = select(getSock() + 1, &rset, &wset, 0, 0); + if (activity > 0) + { + if ((FD_ISSET(getSock(),&rset) && !writeBlockedOnRead) + || (readBlockedOnWrite && FD_ISSET(getSock(), &wset))) + { + goto loop; + } + } + else + { + ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); + WRITELOG("TLSStack::recv() select %s\n", errmsg); + _busy = false; + _mutex.unlock(); + return -1; + } + } + } + return TCPStack::recv(buf, len); +} + +bool Network::isValid() +{ + if (!_secureFlg) + { + return TCPStack::isValid(); + } + if (_ssl) + { + return true; + } + return false; +} + +void Network::disconnect() +{ + if (_ssl) + { + SSL_shutdown(_ssl); + _ssl = 0; + TCPStack::close(); + } + else + { + TCPStack::close(); + } +} + +int Network::getSock() +{ + return TCPStack::getSock(); +} + +SSL* Network::getSSL() +{ + if (_secureFlg) + { + return _ssl; + } + else + { + return 0; + } +} + +bool Network::isSecure() +{ + return _secureFlg; +} + diff --git a/MQTTSNGateway/src/linux/Network.h b/MQTTSNGateway/src/linux/Network.h new file mode 100644 index 0000000..5c6b79f --- /dev/null +++ b/MQTTSNGateway/src/linux/Network.h @@ -0,0 +1,95 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef NETWORK_H_ +#define NETWORK_H_ +#include +#include +#include +#include +#include +#include +#include + +#include "Threading.h" +#include "MQTTSNGWDefines.h" + +using namespace std; +using namespace MQTTSNGW; + +/*======================================== + Class TCPStack + =======================================*/ +class TCPStack +{ +public: + TCPStack(); + virtual ~TCPStack(); + + // Server initialization + bool bind(const char* service); + bool listen(); + bool accept(TCPStack&); + + // Client initialization + bool connect(const char* host, const char* service); + + int send(const uint8_t* buf, int length); + int recv(uint8_t* buf, int len); + void close(); + + void setNonBlocking(const bool); + + bool isValid(); + int getSock(); + +private: + int _sockfd; + addrinfo* _addrinfo; +}; + +/*======================================== + Class Network + =======================================*/ +class Network: public TCPStack +{ +public: + Network(bool secure); + virtual ~Network(); + + bool connect(const char* host, const char* service); + void disconnect(); + int send(const uint8_t* buf, uint16_t length); + int recv(uint8_t* buf, uint16_t len); + + bool isValid(); + bool isSecure(); + int getSock(); + SSL* getSSL(); + +private: + static SSL_CTX* _ctx; + static int _numOfInstance; + static SSL_SESSION* _session; + + SSL* _ssl; + bool _secureFlg; + bool _disconReq; + Mutex _mutex; + bool _busy; +}; + +#endif /* NETWORK_H_ */ diff --git a/MQTTSNGateway/src/linux/Threading.cpp b/MQTTSNGateway/src/linux/Threading.cpp new file mode 100644 index 0000000..4dee15a --- /dev/null +++ b/MQTTSNGateway/src/linux/Threading.cpp @@ -0,0 +1,502 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWProcess.h" +#include "Threading.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace MQTTSNGW; + +/*===================================== + Class Mutex + =====================================*/ + +Mutex::Mutex(void) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&_mutex, &attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); + _shmid = 0; + _pmutex = 0; +} + +Mutex::Mutex(const char* fileName) +{ + pthread_mutexattr_t attr; + + key_t key = ftok(fileName, 1); + + if ((_shmid = shmget(key, sizeof(pthread_mutex_t), IPC_CREAT | 0666)) < 0) + { + throw Exception( -1, "Mutex can't create a shared memory."); + } + _pmutex = (pthread_mutex_t*) shmat(_shmid, NULL, 0); + if (_pmutex < 0) + { + throw Exception( -1, "Mutex can't attach shared memory."); + } + + pthread_mutexattr_init(&attr); + + if (pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) != 0) + { + throw Exception( -1, "Mutex can't set the process-shared flag"); + } + if (pthread_mutex_init(_pmutex, &attr) != 0) + { + throw Exception( -1, "Mutex can't initialize."); + } +} + +Mutex::~Mutex(void) +{ + if (_pmutex) + { + pthread_mutex_lock(_pmutex); + pthread_mutex_unlock(_pmutex); + pthread_mutex_destroy(_pmutex); + } + else + { + pthread_mutex_lock(&_mutex); + pthread_mutex_unlock(&_mutex); + pthread_mutex_destroy(&_mutex); + } + if (_shmid) + { + shmctl(_shmid, IPC_RMID, NULL); + } +} + +void Mutex::lock(void) +{ + if (_pmutex) + { + pthread_mutex_lock(_pmutex); + } + else + { + try + { + if (pthread_mutex_lock(&_mutex)) + { + throw; + } + } catch (char* errmsg) + { + throw Exception( -1, "The same thread can't aquire a mutex twice."); + } + } +} + +void Mutex::unlock(void) +{ + + if (_pmutex) + { + pthread_mutex_unlock(_pmutex); + } + else + { + try + { + if (pthread_mutex_unlock(&_mutex)) + { + throw; + } + } catch (char* errmsg) + { + throw Exception( -1, "Mutex can't unlock."); + } + } +} + +/*===================================== + Class Semaphore + =====================================*/ + +Semaphore::Semaphore() +{ + sem_init(&_sem, 0, 0); + _name = 0; + _psem = 0; +} + +Semaphore::Semaphore(unsigned int val) +{ + sem_init(&_sem, 0, val); + _name = 0; + _psem = 0; +} + +Semaphore::Semaphore(const char* name, unsigned int val) +{ + _psem = sem_open(name, O_CREAT, 0666, val); + if (_psem == SEM_FAILED) + { + throw Exception( -1, "Semaphore can't be created."); + } + _name = (char*) calloc(strlen(name + 1), 1); + if (_name == NULL) + { + throw Exception( -1, "Semaphore can't allocate memories."); + } + _name = strdup(name); +} + +Semaphore::~Semaphore() +{ + if (_name) + { + sem_close(_psem); + sem_unlink(_name); + free((void*) _name); + } + else + { + sem_destroy(&_sem); + } +} + +void Semaphore::post(void) +{ + int val = 0; + if (_psem) + { + sem_getvalue(_psem, &val); + if (val <= 0) + { + sem_post(_psem); + } + } + else + { + sem_getvalue(&_sem, &val); + if (val <= 0) + { + sem_post(&_sem); + } + } +} + +void Semaphore::wait(void) +{ + if (_psem) + { + sem_wait(_psem); + } + else + { + sem_wait(&_sem); + } +} + +void Semaphore::timedwait(uint16_t millsec) +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += millsec / 1000; + ts.tv_nsec = (millsec % 1000) * 1000000; + if (_psem) + { + sem_timedwait(_psem, &ts); + } + else + { + sem_timedwait(&_sem, &ts); + } +} + +/*========================================= + Class RingBuffer + =========================================*/ +RingBuffer::RingBuffer() +{ + key_t key = ftok(MQTTSNGW_RINGBUFFER_KEY, 1); + + if ((_shmid = shmget(key, PROCESS_LOG_BUFFER_SIZE, + IPC_CREAT | IPC_EXCL | 0666)) >= 0) + { + if ((_shmaddr = (uint16_t*) shmat(_shmid, NULL, 0)) > 0) + { + _length = (uint16_t*) _shmaddr; + _start = (uint16_t*) _length + sizeof(uint16_t*); + _end = (uint16_t*) _start + sizeof(uint16_t*); + _buffer = (char*) _end + sizeof(uint16_t*); + _createFlg = true; + + *_length = PROCESS_LOG_BUFFER_SIZE - sizeof(uint16_t*) * 3 - 16; + *_start = *_end = 0; + } + else + { + throw Exception(-1, "RingBuffer can't attach shared memory."); + } + } + else if ((_shmid = shmget(key, PROCESS_LOG_BUFFER_SIZE, IPC_CREAT | 0666)) >= 0) + { + if ((_shmaddr = (uint16_t*) shmat(_shmid, NULL, 0)) > 0) + { + _length = (uint16_t*) _shmaddr; + _start = (uint16_t*) _length + sizeof(uint16_t*); + _end = (uint16_t*) _start + sizeof(uint16_t*); + _buffer = (char*) _end + sizeof(uint16_t*); + _createFlg = false; + } + else + { + throw Exception(-1, "RingBuffer can't create a shared memory."); + } + } + else + { + throw Exception(-1, "RingBuffer can't create a shared memory."); + } + + _pmx = new Mutex(MQTTSNGW_RB_MUTEX_KEY); +} + +RingBuffer::~RingBuffer() +{ + if (_createFlg) + { + if (_shmid > 0) + { + shmctl(_shmid, IPC_RMID, NULL); + } + if (_pmx > 0) + { + delete _pmx; + } + } + else + { + if (_shmid > 0) + { + shmdt(_shmaddr); + } + } +} + +void RingBuffer::put(char* data) +{ + _pmx->lock(); + + uint16_t dlen = strlen(data); + uint16_t blen = *_length - *_end; + + if (*_end > *_start) + { + if (dlen < blen) + { + strncpy(_buffer + *_end, data, dlen); + if (*_end - *_start == 1) + { // Buffer is empty. + *_start = *_end; + } + *_end += dlen; + } + else + { + strncpy(_buffer + *_end, data, blen); + strncpy(_buffer, data + blen, dlen - blen); + if (*_end - *_start == 1) + { // Buffer is empty. + *_start = *_end; + *_end = dlen - blen; + } + else + { + *_end = dlen - blen; + *_start = *_end + 1; + } + } + } + else if (*_end == *_start) + { + if (dlen < blen) + { + strncpy(_buffer + *_end, data, dlen); + *_end += dlen; + } + else + { + const char* errmsg = "RingBuffer Error: data is too long"; + strcpy(_buffer + *_end, errmsg); + *_end += strlen(errmsg); + } + } + else + { // *_end < *_start + if (dlen < *_start - *_end) + { + strncpy(_buffer + *_end, data, dlen); + *_end += dlen; + *_start = *_end + 1; + } + else + { + if (dlen < blen) + { + strncpy(_buffer + *_end, data, dlen); + *_end += dlen; + *_start = *_end + 1; + } + else + { + strncpy(_buffer + *_end, data, blen); + strncpy(_buffer, data + blen, dlen - blen); + *_start = *_end; + *_end = dlen - blen; + } + } + } + _pmx->unlock(); +} + +int RingBuffer::get(char* buf, int length) +{ + int len = 0; + _pmx->lock(); + + if (*_end > *_start) + { + if (length > *_end - *_start) + { + len = *_end - *_start; + if (len == 1) + { + len = 0; + } + strncpy(buf, _buffer + *_start, len); + *_start = *_end - 1; + } + else + { + len = length; + strncpy(buf, _buffer + *_start, len); + *_start = *_start + len; + } + } + else if (*_end < *_start) + { + int blen = *_length - *_start; + if (length > blen) + { + strncpy(buf, _buffer + *_start, blen); + *_start = 0; + if (length - (blen + *_end) > 0) + { + strncpy(buf + blen, _buffer, *_end); + len = blen + *_end; + if (*_end > 0) + { + *_start = *_end - 1; + } + } + else + { + strncpy(buf + blen, _buffer, length - blen); + len = length; + *_start = length - blen; + } + } + else + { + strncpy(buf, _buffer + *_start, length); + *_start += length; + len = length; + } + } + _pmx->unlock(); + return len; +} + +void RingBuffer::reset() +{ + _pmx->lock(); + if ( _start && _end ) + { + *_start = *_end = 0; + } + else + { + throw Exception(-1, "RingBuffer can't reset. need to clear shared memory."); + } + _pmx->unlock(); +} + +/*===================================== + Class Thread + =====================================*/ +Thread::Thread() +{ + _stopProcessEvent = theMultiTaskProcess->getStopProcessEvent(); + _threadID = 0; +} + +Thread::~Thread() +{ + pthread_cancel(_threadID); + pthread_join(_threadID, 0); +} + +void* Thread::_run(void* runnable) +{ + static_cast(runnable)->EXECRUN(); + return 0; +} + +void Thread::initialize(int argc, char** argv) +{ + +} + +pthread_t Thread::getID() +{ + return pthread_self(); +} + +bool Thread::equals(pthread_t *t1, pthread_t *t2) +{ + return (pthread_equal(*t1, *t2) ? false : true); +} + +int Thread::start(void) +{ + Runnable *runnable = this; + return pthread_create(&_threadID, 0, _run, runnable); +} + +void Thread::stopProcess(void) +{ + _stopProcessEvent->post(); +} + +void Thread::testThreadCancel(void) +{ + pthread_testcancel(); +} diff --git a/MQTTSNGateway/src/linux/Threading.h b/MQTTSNGateway/src/linux/Threading.h index 016421e..3c86d31 100644 --- a/MQTTSNGateway/src/linux/Threading.h +++ b/MQTTSNGateway/src/linux/Threading.h @@ -1,36 +1,145 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef THREADING_H_ +#define THREADING_H_ -extern "C" -{ - #include "Thread.h" -} +#include +#include +#include "MQTTSNGWDefines.h" - -class Thread +namespace MQTTSNGW { -/* -Thread(void (*fn)(void const *argument), void *argument) -{ - Thread_start(fn, arg); -}*/ - -public: - - Thread(void (*fn)(void const *argument)) - { - const void* arg = NULL; - - //Thread_start((void (*)(void *))fn, arg); - } - -}; - - - +#define MQTTSNGW_RINGBUFFER_KEY "/usr/local/etc/mqttsnGateway/config/ringbuffer.key" +#define MQTTSNGW_RB_MUTEX_KEY "/usr/local/etc/mqttsnGateway/config/rbmutex.key" +#define MQTTSNGW_RB_SEMAPHOR_NAME "/rbsemaphor" +/*===================================== + Class Mutex + ====================================*/ class Mutex { +public: + Mutex(); + Mutex(const char* name); + ~Mutex(); + void lock(void); + void unlock(void); +private: + pthread_mutex_t _mutex; + pthread_mutex_t* _pmutex; + int _shmid; +}; + +/*===================================== + Class Semaphore + ====================================*/ +class Semaphore +{ +public: + Semaphore(); + Semaphore(unsigned int val); + Semaphore(const char* name, unsigned int val); + ~Semaphore(); + void post(void); + void wait(void); + void timedwait(uint16_t millsec); + +private: + sem_t* _psem; + sem_t _sem; + char* _name; +}; + +/*===================================== + Class RingBuffer + =====================================*/ +class RingBuffer +{ +public: + RingBuffer(); + ~RingBuffer(); + void put(char* buffer); + int get(char* buffer, int bufferLength); + void reset(); +private: + void* _shmaddr; + uint16_t* _length; + uint16_t* _start; + uint16_t* _end; + char* _buffer; + int _shmid; + Mutex* _pmx; + bool _createFlg; +}; + + +/*===================================== + Class Runnable + ====================================*/ +class Runnable +{ +public: + Runnable(){} + virtual ~Runnable(){} + virtual void EXECRUN(){} +}; + + +#define MAGIC_WORD_FOR_THREAD \ +public: void EXECRUN() \ +{ \ + try \ + { \ + run(); \ + } \ + catch(Exception& ex) \ + { \ + ex.writeMessage();\ + stopProcess(); \ + } \ + catch(...) \ + { \ + throw; \ + } \ +} + +/*===================================== + Class Thread + ====================================*/ +class Thread : virtual public Runnable{ +public: + Thread(); + ~Thread(); + int start(void); + static pthread_t getID(); + static bool equals(pthread_t*, pthread_t*); + virtual void initialize(int argc, char** argv); + void stopProcess(void); + void testThreadCancel(void); +private: + pthread_t _threadID; + Semaphore* _stopProcessEvent; + + static void* _run(void*); }; + +} + +#endif /* THREADING_H_ */ diff --git a/MQTTSNGateway/src/linux/build.sh b/MQTTSNGateway/src/linux/build.sh deleted file mode 100644 index e597508..0000000 --- a/MQTTSNGateway/src/linux/build.sh +++ /dev/null @@ -1,2 +0,0 @@ -g++ main.cpp -I MQTTPacket/src -I MQTTSNPacket/src -I linux MQTTPacket/src/*.c MQTTSNPacket/src/*.c - diff --git a/MQTTSNGateway/src/linux/linux.cpp b/MQTTSNGateway/src/linux/linux.cpp index 222acda..1b948c6 100644 --- a/MQTTSNGateway/src/linux/linux.cpp +++ b/MQTTSNGateway/src/linux/linux.cpp @@ -1,5 +1,5 @@ -/******************************************************************************* - * Copyright (c) 2014, 2015 IBM Corp. +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -11,214 +11,214 @@ * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - *******************************************************************************/ + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include #include -#include #include - -#include #include -#include +#include "MQTTSNGWDefines.h" +#include "linux.h" -class IPStack +using namespace std; + +using namespace MQTTSNGW; + +/*===================================== + Print Current Date & Time + =====================================*/ +char theCurrentTime[32]; + +char* currentDateTime() { -public: - IPStack() - { + struct timeval now; + struct tm tstruct; + gettimeofday(&now, 0); + tstruct = *localtime(&now.tv_sec); + strftime(theCurrentTime, sizeof(theCurrentTime), "%Y%m%d %H%M%S", &tstruct); + sprintf(theCurrentTime + 15, " %03d", (int)now.tv_usec / 1000 ); + return theCurrentTime; +} - } - - int Socket_error(const char* aString) - { - int rc = 0; - //if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && errno != EWOULDBLOCK) - //{ - if (strcmp(aString, "shutdown") != 0 || (errno != ENOTCONN && errno != ECONNRESET)) - { - if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && errno != EWOULDBLOCK) - printf("Socket error %s in %s for socket %d\n", strerror(errno), aString, mysock); - rc = errno; - } - //} - return errno; - } - - int connect(const char* hostname, int port) - { - int type = SOCK_STREAM; - struct sockaddr_in address; - int rc = -1; - sa_family_t family = AF_INET; - struct addrinfo *result = NULL; - struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL}; - - if ((rc = getaddrinfo(hostname, NULL, &hints, &result)) == 0) - { - struct addrinfo* res = result; - - /* prefer ip4 addresses */ - while (res) - { - if (res->ai_family == AF_INET) - { - result = res; - break; - } - res = res->ai_next; - } - - if (result->ai_family == AF_INET) - { - address.sin_port = htons(port); - address.sin_family = family = AF_INET; - address.sin_addr = ((struct sockaddr_in*)(result->ai_addr))->sin_addr; - } - else - rc = -1; - - freeaddrinfo(result); - } - - if (rc == 0) - { - mysock = socket(family, type, 0); - if (mysock != -1) - { - int opt = 1; - - //if (setsockopt(mysock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt)) != 0) - // printf("Could not set SO_NOSIGPIPE for socket %d", mysock); - - rc = ::connect(mysock, (struct sockaddr*)&address, sizeof(address)); - } - } - - return rc; - } - - int read(unsigned char* buffer, int len, int timeout_ms) - { - struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000}; - if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 0)) - { - interval.tv_sec = 0; - interval.tv_usec = 100; - } - - setsockopt(mysock, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval, sizeof(struct timeval)); - - int bytes = 0; - while (bytes < len) - { - int rc = ::recv(mysock, &buffer[bytes], (size_t)(len - bytes), 0); - if (rc == -1) - { - if (Socket_error("read") != 0) - { - bytes = -1; - break; - } - } - else - bytes += rc; - } - return bytes; - } - - int write(unsigned char* buffer, int len, int timeout) - { - struct timeval tv; - - tv.tv_sec = 0; /* 30 Secs Timeout */ - tv.tv_usec = timeout * 1000; // Not init'ing this can cause strange errors - - setsockopt(mysock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)); - int rc = ::write(mysock, buffer, len); - //printf("write rc %d\n", rc); - return rc; - } - - int disconnect() - { - return ::close(mysock); - } - -private: - - int mysock; - -}; - - -class Countdown +/*============================================ + Timer + ============================================*/ +Timer::Timer(void) { -public: - Countdown() - { - - } + stop(); +} - Countdown(int ms) - { - countdown_ms(ms); - } - +Timer::~Timer(void) +{ - bool expired() - { - struct timeval now, res; - gettimeofday(&now, NULL); - timersub(&end_time, &now, &res); - //printf("left %d ms\n", (res.tv_sec < 0) ? 0 : res.tv_sec * 1000 + res.tv_usec / 1000); - //if (res.tv_sec > 0 || res.tv_usec > 0) - // printf("expired %d %d\n", res.tv_sec, res.tv_usec); - return res.tv_sec < 0 || (res.tv_sec == 0 && res.tv_usec <= 0); - } - +} - void countdown_ms(int ms) - { - struct timeval now; - gettimeofday(&now, NULL); - struct timeval interval = {ms / 1000, (ms % 1000) * 1000}; - //printf("interval %d %d\n", interval.tv_sec, interval.tv_usec); - timeradd(&now, &interval, &end_time); - } +void Timer::start(uint32_t msecs) +{ + gettimeofday(&_startTime, 0); + _millis = msecs; +} - - void countdown(int seconds) - { - struct timeval now; - gettimeofday(&now, NULL); - struct timeval interval = {seconds, 0}; - timeradd(&now, &interval, &end_time); - } +bool Timer::isTimeup(void) +{ + return isTimeup(_millis); +} - - int left_ms() - { - struct timeval now, res; - gettimeofday(&now, NULL); - timersub(&end_time, &now, &res); - //printf("left %d ms\n", (res.tv_sec < 0) ? 0 : res.tv_sec * 1000 + res.tv_usec / 1000); - return (res.tv_sec < 0) ? 0 : res.tv_sec * 1000 + res.tv_usec / 1000; - } - -private: +bool Timer::isTimeup(uint32_t msecs) +{ + struct timeval curTime; + long secs, usecs; + if (_startTime.tv_sec == 0) + { + return false; + } + else + { + gettimeofday(&curTime, 0); + secs = (curTime.tv_sec - _startTime.tv_sec) * 1000; + usecs = (curTime.tv_usec - _startTime.tv_usec) / 1000.0; + return ((secs + usecs) > (long) msecs); + } +} + +void Timer::stop() +{ + _startTime.tv_sec = 0; + _millis = 0; +} + +/*===================================== +Class LightIndicator +=====================================*/ + +LightIndicator::LightIndicator() +{ + _greenStatus = false; + for ( int i = 0; i <= MAX_GPIO; i++) + { + _gpio[i] = 0; + } + init(); +} + +LightIndicator::~LightIndicator() +{ + for ( int i = 0; i <= MAX_GPIO; i++) + { + if ( _gpio[i] ) + { + close( _gpio[i]); + } + } +} + +void LightIndicator::greenLight(bool on) +{ + if (on) + { + if (!_greenStatus) + { + _greenStatus = true; + //Turn Green on & turn Red off + lit(LIGHT_INDICATOR_GREEN, "1"); + lit(LIGHT_INDICATOR_RED, "0"); + } + } + else + { + if (_greenStatus) + { + _greenStatus = false; + //Turn Green off & turn Red on + lit(LIGHT_INDICATOR_GREEN, "0"); + lit(LIGHT_INDICATOR_RED, "1"); + } + } +} +void LightIndicator::blueLight(bool on) +{ + if (on) + { + lit(LIGHT_INDICATOR_BLUE, "1"); + if ( !_greenStatus ) + { + greenLight(true); + } + } + else + { + lit(LIGHT_INDICATOR_BLUE, "0"); + } +} + +void LightIndicator::redLight(bool on) +{ + if (on) + { + lit(LIGHT_INDICATOR_RED, "1"); + } + else + { + lit(LIGHT_INDICATOR_RED, "0"); + } +} + +void LightIndicator::allLightOff(void) +{ + lit(LIGHT_INDICATOR_RED, "0"); + lit(LIGHT_INDICATOR_BLUE, "0"); + lit(LIGHT_INDICATOR_GREEN, "0"); + _greenStatus = false; +} + +void LightIndicator::init() +{ + pinMode(LIGHT_INDICATOR_GREEN); + pinMode(LIGHT_INDICATOR_RED); + pinMode(LIGHT_INDICATOR_BLUE); +} + +void LightIndicator::lit(int gpioNo, const char* onoff) +{ + if( _gpio[gpioNo] ) + { + write(_gpio[gpioNo], onoff, 1); + } +} + +void LightIndicator::pinMode(int gpioNo) +{ + int fd = open("/sys/class/gpio/export", O_WRONLY); + if ( fd < 0 ) + { + return; + } + char no[4]; + sprintf(no,"%d", gpioNo); + write(fd, no, strlen(no)); + close(fd); + + char fileName[64]; + sprintf( fileName, "/sys/class/gpio/gpio%d/direction", gpioNo); + + fd = open(fileName, O_WRONLY); + if ( fd < 0 ) + { + return; + } + write(fd,"out", 3); + close(fd); + + sprintf( fileName, "/sys/class/gpio/gpio%d/value", gpioNo); + fd = open(fileName, O_WRONLY); + if ( fd > 0 ) + { + _gpio[gpioNo] = fd; + } +} - struct timeval end_time; -}; diff --git a/MQTTSNGateway/src/linux/linux.h b/MQTTSNGateway/src/linux/linux.h new file mode 100644 index 0000000..c73e544 --- /dev/null +++ b/MQTTSNGateway/src/linux/linux.h @@ -0,0 +1,70 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation + **************************************************************************************/ +#ifndef MQTTSNGATEWAY_SRC_LINUX_LINUX_H_ +#define MQTTSNGATEWAY_SRC_LINUX_LINUX_H_ + +namespace MQTTSNGW +{ +/*========================================================== + * Light Indicators + ===========================================================*/ +#define MAX_GPIO 27 // GPIO02 - GPIO27 +#define LIGHT_INDICATOR_GREEN 23 // RPi connector 16 +#define LIGHT_INDICATOR_RED 24 // RPi connector 18 +#define LIGHT_INDICATOR_BLUE 25 // RPi connector 22 + +/*============================================ + Timer + ============================================*/ +class Timer +{ +public: + Timer(void); + ~Timer(void); + void start(uint32_t msecs = 0); + bool isTimeup(void); + bool isTimeup(uint32_t msecs); + void stop(); + +private: + struct timeval _startTime; + uint32_t _millis; +}; + +/*===================================== + Class LightIndicator + =====================================*/ +class LightIndicator +{ +public: + LightIndicator(); + ~LightIndicator(); + void greenLight(bool on); + void blueLight(bool on); + void redLight(bool on); + void allLightOff(void); + +private: + void init(); + void lit(int gpioNo, const char* onoff); + void pinMode(int gpioNo); + bool _greenStatus; + int _gpio[MAX_GPIO + 1]; +}; + +} + +#endif /* MQTTSNGATEWAY_SRC_LINUX_LINUX_H_ */ diff --git a/MQTTSNGateway/src/linux/main.cpp b/MQTTSNGateway/src/linux/main.cpp deleted file mode 100644 index 3505782..0000000 --- a/MQTTSNGateway/src/linux/main.cpp +++ /dev/null @@ -1,24 +0,0 @@ - -#include "Threading.h" - -//#include "MQTTSNUDP.h" -//#include "MQTTEthernet.h" -#include "linux.cpp" -#include "MQTTSNGateway.h" - -MQTTSN::Parms parms(); - - -int main() -{ - // set up MQTT-SN network listening - UDPStack net; - net.listen(1884); - - MQTTSN::Gateway gateway = - MQTTSN::Gateway(net); - - gateway.run(NULL); - - return 0; -} diff --git a/MQTTSNGateway/src/linux/udp/SensorNetwork.cpp b/MQTTSNGateway/src/linux/udp/SensorNetwork.cpp new file mode 100644 index 0000000..0144e79 --- /dev/null +++ b/MQTTSNGateway/src/linux/udp/SensorNetwork.cpp @@ -0,0 +1,336 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "SensorNetwork.h" +#include "MQTTSNGWProcess.h" + +using namespace std; +using namespace MQTTSNGW; + +/*=========================================== + Class SensorNetAddreess + ============================================*/ +SensorNetAddress::SensorNetAddress() +{ + _portNo = 0; + _IpAddr = 0; +} + +SensorNetAddress::~SensorNetAddress() +{ + +} + +void SensorNetAddress::setAddress(uint32_t IpAddr, uint16_t port) +{ + _IpAddr = IpAddr; + _portNo = port; +} + +/** + * convert Text data to SensorNetAddress + * @param buf is pointer of PortNo@IP_Address format text + * @return success = 0, Invalid format = -1 + */ +int SensorNetAddress::setAddress(string* data) +{ + size_t pos = data->find_first_of("@"); + + if ( pos == string::npos ) + { + _portNo = 0; + _IpAddr = INADDR_NONE; + return -1; + } + + string port = data->substr(0, pos); + string ip = data->substr(pos + 1); + int portNo = 0; + + if ((portNo = atoi(port.c_str())) == 0 || (_IpAddr = inet_addr(ip.c_str())) == INADDR_NONE) + { + return -1; + } + _portNo = htons(portNo); + return 0; +} + +bool SensorNetAddress::isMatch(SensorNetAddress* addr) +{ + return ((this->_portNo == addr->_portNo) && (this->_IpAddr == addr->_IpAddr)); +} + +SensorNetAddress& SensorNetAddress::operator =(SensorNetAddress& addr) +{ + this->_portNo = addr._portNo; + this->_IpAddr = addr._IpAddr; + return *this; +} + +/*=========================================== + Class SensorNetwork + ============================================*/ +SensorNetwork::SensorNetwork() +{ + +} + +SensorNetwork::~SensorNetwork() +{ + +} + +int SensorNetwork::unicast(const uint8_t* payload, uint16_t payloadLength, SensorNetAddress* sendToAddr) +{ + return UDPPort::unicast(payload, payloadLength, sendToAddr); +} + +int SensorNetwork::broadcast(const uint8_t* payload, uint16_t payloadLength) +{ + return UDPPort::broadcast(payload, payloadLength); +} + +int SensorNetwork::read(uint8_t* buf, uint16_t bufLen) +{ + return UDPPort::recv(buf, bufLen, &_clientAddr); +} + +int SensorNetwork::initialize(void) +{ + char param[MQTTSNGW_PARAM_MAX]; + uint16_t multicastPortNo = 0; + uint16_t unicastPortNo = 0; + + if (theProcess->getParam("MulticastPortNo", param) == 0) + { + multicastPortNo = atoi(param); + } + if (theProcess->getParam("GatewayPortNo", param) == 0) + { + unicastPortNo = atoi(param); + } + + theProcess->getParam("MulticastIP", param); + return UDPPort::open(param, multicastPortNo, unicastPortNo); +} + +const char* SensorNetwork::getType(void) +{ + return "UDP"; +} + +/*========================================= + Class udpStack + =========================================*/ + +UDPPort::UDPPort() +{ + _disconReq = false; + _sockfdUnicast = -1; + _sockfdMulticast = -1; +} + +UDPPort::~UDPPort() +{ + close(); +} + +void UDPPort::close(void) +{ + if (_sockfdUnicast > 0) + { + ::close(_sockfdUnicast); + _sockfdUnicast = -1; + } + if (_sockfdMulticast > 0) + { + ::close(_sockfdMulticast); + _sockfdMulticast = -1; + } +} + +int UDPPort::open(char* ipAddress, uint16_t multiPortNo, uint16_t uniPortNo) +{ + char loopch = 0; + const int reuse = 1; + + if (uniPortNo == 0 || multiPortNo == 0) + { + D_NWSTACK("error portNo undefined in UDPPort::open\n"); + return -1; + } + + uint32_t ip = inet_addr(ipAddress); + _grpAddr.setAddress(ip, htons(multiPortNo)); + _clientAddr.setAddress(ip, htons(uniPortNo)); + + /*------ Create unicast socket --------*/ + _sockfdUnicast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (_sockfdUnicast < 0) + { + D_NWSTACK("error can't create unicast socket in UDPPort::open\n"); + return -1; + } + + setsockopt(_sockfdUnicast, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + sockaddr_in addru; + addru.sin_family = AF_INET; + addru.sin_port = htons(uniPortNo); + addru.sin_addr.s_addr = INADDR_ANY; + + if (::bind(_sockfdUnicast, (sockaddr*) &addru, sizeof(addru)) < 0) + { + D_NWSTACK("error can't bind unicast socket in UDPPort::open\n"); + return -1; + } + if (setsockopt(_sockfdUnicast, IPPROTO_IP, IP_MULTICAST_LOOP, (char*) &loopch, sizeof(loopch)) < 0) + { + D_NWSTACK("error IP_MULTICAST_LOOP in UDPPort::open\n"); + close(); + return -1; + } + + /*------ Create Multicast socket --------*/ + _sockfdMulticast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (_sockfdMulticast < 0) + { + D_NWSTACK("error can't create multicast socket in UDPPort::open\n"); + close(); + return -1; + } + + setsockopt(_sockfdMulticast, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + sockaddr_in addrm; + addrm.sin_family = AF_INET; + addrm.sin_port = _grpAddr.getPortNo(); + addrm.sin_addr.s_addr = INADDR_ANY; + + if (::bind(_sockfdMulticast, (sockaddr*) &addrm, sizeof(addrm)) < 0) + { + D_NWSTACK("error can't bind multicast socket in UDPPort::open\n"); + return -1; + } + if (setsockopt(_sockfdMulticast, IPPROTO_IP, IP_MULTICAST_LOOP, (char*) &loopch, sizeof(loopch)) < 0) + { + D_NWSTACK("error IP_MULTICAST_LOOP in UDPPort::open\n"); + close(); + return -1; + } + + ip_mreq mreq; + mreq.imr_interface.s_addr = INADDR_ANY; + mreq.imr_multiaddr.s_addr = _grpAddr.getIpAddress(); + + if (setsockopt(_sockfdMulticast, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + D_NWSTACK("error Multicast IP_ADD_MEMBERSHIP in UDPPort::open\n"); + close(); + return -1; + } + + if (setsockopt(_sockfdUnicast, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + D_NWSTACK("error Unicast IP_ADD_MEMBERSHIP in UDPPort::open\n"); + close(); + return -1; + } + return 0; +} + +int UDPPort::unicast(const uint8_t* buf, uint32_t length, SensorNetAddress* addr) +{ + sockaddr_in dest; + dest.sin_family = AF_INET; + dest.sin_port = addr->getPortNo(); + dest.sin_addr.s_addr = addr->getIpAddress(); + ; + + int status = ::sendto(_sockfdUnicast, buf, length, 0, (const sockaddr*) &dest, sizeof(dest)); + if (status < 0) + { + D_NWSTACK("errno == %d in UDPPort::sendto\n", errno); + } + D_NWSTACK("sendto %s:%u length = %d\n", inet_ntoa(dest.sin_addr), htons(addr->getPortNo()), status); + return status; +} + +int UDPPort::broadcast(const uint8_t* buf, uint32_t length) +{ + return unicast(buf, length, &_grpAddr); +} + +int UDPPort::recv(uint8_t* buf, uint16_t len, SensorNetAddress* addr) +{ + fd_set recvfds; + int maxSock = 0; + + FD_ZERO(&recvfds); + FD_SET(_sockfdUnicast, &recvfds); + FD_SET(_sockfdMulticast, &recvfds); + + if (_sockfdMulticast > _sockfdUnicast) + { + maxSock = _sockfdMulticast; + } + else + { + maxSock = _sockfdUnicast; + } + + select(maxSock + 1, &recvfds, 0, 0, 0); + + if (FD_ISSET(_sockfdUnicast, &recvfds)) + { + return recvfrom(_sockfdUnicast, buf, len, 0, addr); + } + else if (FD_ISSET(_sockfdMulticast, &recvfds)) + { + return recvfrom(_sockfdMulticast, buf, len, 0, &_grpAddr); + } + return 0; +} + +int UDPPort::recvfrom(int sockfd, uint8_t* buf, uint16_t len, uint8_t flags, SensorNetAddress* addr) +{ + sockaddr_in sender; + socklen_t addrlen = sizeof(sender); + memset(&sender, 0, addrlen); + + int status = ::recvfrom(sockfd, buf, len, flags, (sockaddr*) &sender, &addrlen); + + if (status < 0 && errno != EAGAIN) + { + D_NWSTACK("errno == %d in UDPPort::recvfrom\n", errno); + return -1; + } + addr->setAddress(sender.sin_addr.s_addr, sender.sin_port); + D_NWSTACK("recved from %s:%d length = %d\n", inet_ntoa(sender.sin_addr), htons(addr->getPortNo()), status); + return status; +} + diff --git a/MQTTSNGateway/src/linux/udp/SensorNetwork.h b/MQTTSNGateway/src/linux/udp/SensorNetwork.h new file mode 100644 index 0000000..b474f16 --- /dev/null +++ b/MQTTSNGateway/src/linux/udp/SensorNetwork.h @@ -0,0 +1,114 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef SENSORNETWORK_H_ +#define SENSORNETWORK_H_ + +#include "MQTTSNGWDefines.h" +#include + +using namespace std; + +namespace MQTTSNGW +{ + +#ifdef DEBUG_NWSTACK + #define D_NWSTACK(...) printf(__VA_ARGS__) +#else + #define D_NWSTACK(...) +#endif + +#define SENSORNETWORK_TYPE "UDP" +/*=========================================== + Class SensorNetAddreess + ============================================*/ +class SensorNetAddress +{ +public: + SensorNetAddress(); + ~SensorNetAddress(); + void setAddress(uint32_t IpAddr, uint16_t port); + int setAddress(string* data); + uint32_t getIpAddress(void) + { + return _IpAddr; + } + uint16_t getPortNo(void) + { + return _portNo; + } + bool isMatch(SensorNetAddress* addr); + SensorNetAddress& operator =(SensorNetAddress& addr); + +private: + uint16_t _portNo; + uint32_t _IpAddr; +}; + +/*======================================== + Class UpdPort + =======================================*/ +class UDPPort +{ +public: + UDPPort(); + virtual ~UDPPort(); + + int open(char* ipAddress, uint16_t multiPortNo, uint16_t uniPortNo); + void close(void); + int unicast(const uint8_t* buf, uint32_t length, SensorNetAddress* sendToAddr); + int broadcast(const uint8_t* buf, uint32_t length); + int recv(uint8_t* buf, uint16_t len, SensorNetAddress* addr); + +private: + void setNonBlocking(const bool); + int recvfrom(int sockfd, uint8_t* buf, uint16_t len, uint8_t flags, SensorNetAddress* addr); + + int _sockfdUnicast; + int _sockfdMulticast; + + SensorNetAddress _grpAddr; + SensorNetAddress _clientAddr; + bool _disconReq; + +}; + +/*=========================================== + Class SensorNetwork + ============================================*/ +class SensorNetwork: public UDPPort +{ +public: + SensorNetwork(); + ~SensorNetwork(); + + int unicast(const uint8_t* payload, uint16_t payloadLength, SensorNetAddress* sendto); + int broadcast(const uint8_t* payload, uint16_t payloadLength); + int read(uint8_t* buf, uint16_t bufLen); + int initialize(void); + const char* getType(void); + SensorNetAddress* getSenderAddress(void) + { + return &_clientAddr; + } + + +private: + SensorNetAddress _clientAddr; // Sender's address. not gateway's one. +}; + +} +#endif /* SENSORNETWORK_H_ */ diff --git a/MQTTSNGateway/src/linux/xbee/SensorNetwork.cpp b/MQTTSNGateway/src/linux/xbee/SensorNetwork.cpp new file mode 100644 index 0000000..5996401 --- /dev/null +++ b/MQTTSNGateway/src/linux/xbee/SensorNetwork.cpp @@ -0,0 +1,434 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation + **************************************************************************************/ + +#include "SensorNetwork.h" +#include "MQTTSNGWProcess.h" +#include "Threading.h" +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace MQTTSNGW; + +/*=========================================== + Class SensorNetAddreess + ============================================*/ +SensorNetAddress::SensorNetAddress() +{ + memset(_address64, 0, sizeof(_address64)); + memset(_address16, 0, sizeof(_address16)); +} + +SensorNetAddress::~SensorNetAddress() +{ + +} + +void SensorNetAddress::setAddress(uint8_t* address64, uint8_t* address16) +{ + memcpy(_address64, address64, 8); + memcpy(_address16, address16, 2); +} + + +int SensorNetAddress::setAddress(string* address64) +{ + memcpy(_address64, address64->c_str(), 8); + memset(_address16, 0, sizeof(_address16)); + return 0; +} + +void SensorNetAddress::setBroadcastAddress(void) +{ + memset(_address64, 0, 6); + _address64[6] = 0xff; + _address64[7] = 0xff; + _address16[0] = 0xff; + _address16[1] = 0xfe; +} + +bool SensorNetAddress::isMatch(SensorNetAddress* addr) +{ + + return (memcmp(this->_address64, addr->_address64, 8 ) == 0 && memcmp(this->_address16, addr->_address16, 2) == 0); +} + +SensorNetAddress& SensorNetAddress::operator =(SensorNetAddress& addr) +{ + memcpy(_address64, addr._address64, 8); + memcpy(_address16, addr._address16, 2); + return *this; +} + +/*=========================================== + Class SensorNetwork + ============================================*/ +SensorNetwork::SensorNetwork() +{ + +} + +SensorNetwork::~SensorNetwork() +{ + +} + +int SensorNetwork::unicast(const uint8_t* payload, uint16_t payloadLength, SensorNetAddress* sendToAddr) +{ + return XBee::unicast(payload, payloadLength, sendToAddr); +} + +int SensorNetwork::broadcast(const uint8_t* payload, uint16_t payloadLength) +{ + return XBee::broadcast(payload, payloadLength); +} + +int SensorNetwork::read(uint8_t* buf, uint16_t bufLen) +{ + return XBee::recv(buf, bufLen, &_clientAddr); +} + +int SensorNetwork::initialize(void) +{ + char param[MQTTSNGW_PARAM_MAX]; + uint16_t baudrate = 9600; + + if (theProcess->getParam("Baudrate", param) == 0) + { + baudrate = (uint16_t)atoi(param); + } + + theProcess->getParam("SerialDevice", param); + + return XBee::open(param, baudrate); +} + +const char* SensorNetwork::getType(void) +{ + return "XBee"; +} + +/*=========================================== + Class XBee + ============================================*/ +XBee::XBee(){ + _serialPort = new SerialPort(); + _respCd = 0; + _dataLen = 0; + _frameId = 0; +} + +XBee::~XBee(){ + if ( _serialPort ) + { + delete _serialPort; + } +} + +int XBee::open(char* device, int baudrate) +{ + int rate = B9600; + + switch (baudrate) + { + case 9600: + rate = B9600; + break; + case 19200: + rate = B19200; + break; + case 38400: + rate = B38400; + break; + case 57600: + rate = B57600; + break; + case 115200: + rate = B115200; + break; + default: + return -1; + } + + return _serialPort->open(device, rate, false, 1, O_RDWR | O_NOCTTY); +} + +int XBee::broadcast(const uint8_t* payload, uint16_t payloadLen){ + SensorNetAddress addr; + addr.setBroadcastAddress(); + send(payload, (uint8_t) payloadLen, &addr); + return 1; +} + +int XBee:: unicast(const uint8_t* payload, uint16_t payloadLen, SensorNetAddress* addr){ + send(payload, (uint8_t) payloadLen, addr); + return 1; +} + +int XBee::recv(uint8_t* buf, uint16_t bufLen, SensorNetAddress* clientAddr) +{ + uint8_t data[128]; + int len; + + while ( true ) + { + + if ( (len = readApiFrame(data)) > 0 ) + { + + if ( data[0] == API_RESPONSE ) + { + memcpy(clientAddr->_address64, data + 1, 8); + memcpy(clientAddr->_address16, data + 9, 2); + len -= 12; + memcpy( buf, data + 12, len); + return len; + } + else if ( data[0] == API_XMITSTATUS ) + { + _respCd = data[5]; + _respId = data[1]; + _sem.post(); + } + } + } +} + +int XBee::readApiFrame(uint8_t* recvData){ + uint8_t buf; + uint8_t pos = 0; + uint8_t checksum = 0; + uint8_t len = 0; + + while ( _serialPort->recv(&buf) ) + { + if ( buf == START_BYTE) + { + pos = 1; + D_NWSTACK("\r\n===> Recv: "); + break; + } + } + + if ( pos == 0 ) + { + goto errexit; + } + + if ( recv(&buf) < 0 ) // MSB length + { + goto errexit; + } + + if ( recv(&buf) < 0 ) // LSB length + { + goto errexit; + } + else + { + len = buf; + } + + pos = 0; + while ( len-- ) + { + if ( recv(&buf) < 0 ) + { + goto errexit; + } + recvData[pos++] = buf; + checksum += buf; + } + + recv(&buf); // checksum + if ( (0xff - checksum ) == buf ){ + D_NWSTACK(" checksum ok\r\n"); + return pos; + } + else + { + D_NWSTACK(" checksum error %02x\r\n", 0xff - checksum); + goto errexit; + } +errexit: + _serialPort->flush(); + return -1; +} + +int XBee::send(const uint8_t* payload, uint8_t pLen, SensorNetAddress* addr){ + D_NWSTACK("\r\n===> Send: "); + uint8_t checksum = 0; + _respCd = -1; + + _serialPort->send(START_BYTE); + send(0x00); // Message Length + send(14 + pLen); // Message Length + + _serialPort->send(API_XMITREQUEST); // Transmit Request API + checksum += API_XMITREQUEST; + + if (_frameId++ == 0x00 ) // Frame ID + { + _frameId = 1; + } + send(_frameId); + checksum += _frameId; + + for ( int i = 0; i < 8; i++) // Address64 + { + send(addr->_address64[i]); + checksum += addr->_address64[i]; + } + for ( int i = 0; i < 2; i++) // Address16 + { + send(addr->_address16[i]); + checksum += addr->_address16[i]; + } + + send(0x00); // Broadcast Radius + checksum += 0x00; + + send(0x00); // Option: Use the extended transmission timeout 0x40 + checksum += 0x00; + + D_NWSTACK("\r\n Payload: "); + + for ( uint8_t i = 0; i < pLen; i++ ){ + send(payload[i]); // Payload + checksum += payload[i]; + } + + checksum = 0xff - checksum; + D_NWSTACK(" checksum "); + send(checksum); + D_NWSTACK("\r\n"); + + /* wait Txim Status 0x8B */ + _sem.timedwait(5000); // 5sec + + if ( _respCd || _frameId != _respId ) + { + D_NWSTACK(" frameId = %02x Not Acknowleged\r\n", _frameId); + return -1; + } + return 0; +} + +void XBee::send(uint8_t c) +{ + if(c == START_BYTE || c == ESCAPE || c == XON || c == XOFF){ + _serialPort->send(ESCAPE); + _serialPort->send(c ^ 0x20); + }else{ + _serialPort->send(c); + } +} + +int XBee::recv(uint8_t* buf) +{ + if (_serialPort->recv(buf) ) + { + if ( *buf == ESCAPE) + { + _serialPort->recv(buf); + *buf = 0x20 ^ *buf; + } + return 0; + } + return -1; +} + +/*========================================= + Class SerialPort + =========================================*/ +SerialPort::SerialPort() +{ + _tio.c_iflag = IGNBRK | IGNPAR; + _tio.c_cflag = CS8 | CLOCAL | CRTSCTS; + _tio.c_cc[VINTR] = 0; + _tio.c_cc[VTIME] = 0; + _tio.c_cc[VMIN] = 1; + _fd = 0; +} + +SerialPort::~SerialPort() +{ + if (_fd) + { + ::close(_fd); + } +} + +int SerialPort::open(char* devName, unsigned int baudrate, bool parity, + unsigned int stopbit, unsigned int flg) +{ + _fd = ::open(devName, flg); + if (_fd < 0) + { + return _fd; + } + + if (parity) + { + _tio.c_cflag = _tio.c_cflag | PARENB; + } + if (stopbit == 2) + { + _tio.c_cflag = _tio.c_cflag | CSTOPB; + } + + if (cfsetspeed(&_tio, baudrate) < 0) + { + return errno; + } + return tcsetattr(_fd, TCSANOW, &_tio); +} + +bool SerialPort::send(unsigned char b) +{ + if (write(_fd, &b, 1) < 0) + { + return false; + } + else + { + D_NWSTACK( " %02x", b); + return true; + } +} + +bool SerialPort::recv(unsigned char* buf) +{ + if (read(_fd, buf, 1) == 0) + { + return false; + } + else + { + D_NWSTACK( " %02x",buf[0] ); + return true; + } +} + +void SerialPort::flush(void) +{ + tcsetattr(_fd, TCSAFLUSH, &_tio); +} + diff --git a/MQTTSNGateway/src/linux/xbee/SensorNetwork.h b/MQTTSNGateway/src/linux/xbee/SensorNetwork.h new file mode 100644 index 0000000..f4a18ee --- /dev/null +++ b/MQTTSNGateway/src/linux/xbee/SensorNetwork.h @@ -0,0 +1,141 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation + **************************************************************************************/ +#ifndef SENSORNETWORKX_H_ +#define SENSORNETWORKX_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGWProcess.h" +#include +#include + +using namespace std; + +namespace MQTTSNGW +{ +//#define DEBUG_NWSTACK + +#ifdef DEBUG_NWSTACK + #define D_NWSTACK(...) printf(__VA_ARGS__) +#else + #define D_NWSTACK(...) +#endif + +#define API_XMITREQUEST 0x10 +#define API_RESPONSE 0x90 +#define API_MODEMSTATUS 0x8A +#define API_XMITSTATUS 0x8B + +#define START_BYTE 0x7e +#define ESCAPE 0x7d +#define XON 0x11 +#define XOFF 0x13 + +/*=========================================== + Class SerialPort + ============================================*/ +class SerialPort{ +public: + SerialPort(); + ~SerialPort(); + int open(char* devName, unsigned int baudrate, bool parity, unsigned int stopbit, unsigned int flg); + bool send(unsigned char b); + bool recv(unsigned char* b); + void flush(); + +private: + int _fd; // file descriptor + struct termios _tio; +}; + +/*=========================================== + Class SensorNetAddreess + ============================================*/ +class SensorNetAddress +{ + friend class XBee; +public: + SensorNetAddress(); + ~SensorNetAddress(); + void setAddress(uint8_t* address64, uint8_t* address16); + int setAddress(string* data); + void setBroadcastAddress(void); + bool isMatch(SensorNetAddress* addr); + SensorNetAddress& operator =(SensorNetAddress& addr); + +private: + uint8_t _address16[2]; + uint8_t _address64[8]; +}; + +/*======================================== + Class XBee + =======================================*/ +class XBee +{ +public: + XBee(); + ~XBee(); + + int open(char* device, int boudrate); + void close(void); + int unicast(const uint8_t* buf, uint16_t length, SensorNetAddress* sendToAddr); + int broadcast(const uint8_t* buf, uint16_t length); + int recv(uint8_t* buf, uint16_t len, SensorNetAddress* addr); + +private: + int readApiFrame(uint8_t* recvData); + int recv(uint8_t* buf); + int send(const uint8_t* payload, uint8_t pLen, SensorNetAddress* addr); + void send(uint8_t b); + + Semaphore _sem; + Mutex _meutex; + SerialPort* _serialPort; + uint8_t _frameId; + + uint8_t _respCd; + uint8_t _respId; + uint8_t _dataLen; +}; + +/*=========================================== + Class SensorNetwork + ============================================*/ +class SensorNetwork: public XBee +{ +public: + SensorNetwork(); + ~SensorNetwork(); + + int unicast(const uint8_t* payload, uint16_t payloadLength, SensorNetAddress* sendto); + int broadcast(const uint8_t* payload, uint16_t payloadLength); + int read(uint8_t* buf, uint16_t bufLen); + int initialize(void); + const char* getType(void); + + SensorNetAddress* getSenderAddress(void) + { + return &_clientAddr; + } + + +private: + SensorNetAddress _clientAddr; // Sender's address. not gateway's one. +}; + +} + +#endif /* SENSORNETWORKX_H_ */ diff --git a/MQTTSNGateway/src/main.cpp b/MQTTSNGateway/src/main.cpp deleted file mode 100644 index ae9c9fb..0000000 --- a/MQTTSNGateway/src/main.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * main.cpp - * - * Created on: Jan 5, 2016 - * Author: icraggs - */ - - -void read_config() -{ - -} - - - -int main(int argc, char* argv[]) -{ - - - // 1. listen for MQTT-SN connections - - // 2. connect to MQTT broker - - // 3. listen for MQTT-SN packets, and act on them - // 4. listen for MQTT packets too, and act on them - - - - - - return 0; -} - diff --git a/MQTTSNGateway/src/mainGateway.cpp b/MQTTSNGateway/src/mainGateway.cpp new file mode 100644 index 0000000..e33d915 --- /dev/null +++ b/MQTTSNGateway/src/mainGateway.cpp @@ -0,0 +1,46 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#include "MQTTSNGateway.h" +#include "MQTTSNGWBrokerRecvTask.h" +#include "MQTTSNGWBrokerSendTask.h" +#include "MQTTSNGWClientRecvTask.h" +#include "MQTTSNGWClientSendTask.h" +#include "MQTTSNGWPacketHandleTask.h" + +using namespace MQTTSNGW; + +/* + * Gateway Process + * + * Configure file "/usr/local/etc/mqttsnGateway/config/param.conf" + * Clientlist file "/usr/local/etc/mqttsnGateway/config/clientList.conf" + * Certificate file "/etc/ssl/certs" + * These are defined in MQTTSNGWDefines.h + */ +Gateway* gateway = new Gateway(); +PacketHandleTask* t0 = new PacketHandleTask(gateway); +ClientRecvTask* t1 = new ClientRecvTask(gateway); +ClientSendTask* t2 = new ClientSendTask(gateway); +BrokerRecvTask* t3 = new BrokerRecvTask(gateway); +BrokerSendTask* t4 = new BrokerSendTask(gateway); + +int main(int argc, char** argv) +{ + theProcess->initialize(argc, argv); + theProcess->run(); + return 0; +} + diff --git a/MQTTSNGateway/src/mainLogmonitor.cpp b/MQTTSNGateway/src/mainLogmonitor.cpp new file mode 100644 index 0000000..f73a2a9 --- /dev/null +++ b/MQTTSNGateway/src/mainLogmonitor.cpp @@ -0,0 +1,33 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWProcess.h" +#include "MQTTSNGWLogmonitor.h" + +using namespace MQTTSNGW; + + +/* + * Logmonitor process + */ +int main(int argc, char** argv) +{ + Logmonitor monitor = Logmonitor(); + monitor.initialize(argc, argv); + monitor.run(); + return 0; +} + diff --git a/MQTTSNPacket/src/MQTTSNConnectClient.c b/MQTTSNPacket/src/MQTTSNConnectClient.c index 5a1179f..d62d3c1 100644 --- a/MQTTSNPacket/src/MQTTSNConnectClient.c +++ b/MQTTSNPacket/src/MQTTSNConnectClient.c @@ -20,6 +20,7 @@ #include + /** * Determines the length of the MQTT connect packet that would be produced using the supplied connect options. * @param options the options to be used to build the connect packet @@ -116,7 +117,7 @@ int MQTTSNSerialize_disconnectLength(int duration) int len = 0; FUNC_ENTRY; - len = (duration >= 0) ? 3 : 1; + len = (duration > 0) ? 3 : 1; FUNC_EXIT_RC(len); return len; } @@ -144,7 +145,7 @@ int MQTTSNSerialize_disconnect(unsigned char* buf, int buflen, int duration) ptr += MQTTSNPacket_encode(ptr, len); /* write length */ writeChar(&ptr, MQTTSN_DISCONNECT); /* write message type */ - if (duration >= 0) + if (duration > 0) writeInt(&ptr, duration); rc = ptr - buf; diff --git a/MQTTSNPacket/src/MQTTSNDeserializePublish.c b/MQTTSNPacket/src/MQTTSNDeserializePublish.c index 9aae227..8dd0f7e 100644 --- a/MQTTSNPacket/src/MQTTSNDeserializePublish.c +++ b/MQTTSNPacket/src/MQTTSNDeserializePublish.c @@ -56,7 +56,7 @@ int MQTTSNDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retai *qos = flags.bits.QoS; *retained = flags.bits.retain; - topic->type = flags.bits.topicIdType; + topic->type = (MQTTSN_topicTypes)flags.bits.topicIdType; if (topic->type == MQTTSN_TOPIC_TYPE_NORMAL && *qos == 3) { /* special arrangement for long topic names in QoS -1 publishes. The length of the topic is in the topicid field */ diff --git a/MQTTSNPacket/src/MQTTSNPacket.c b/MQTTSNPacket/src/MQTTSNPacket.c index 4f98b6a..80f55f4 100644 --- a/MQTTSNPacket/src/MQTTSNPacket.c +++ b/MQTTSNPacket/src/MQTTSNPacket.c @@ -19,7 +19,7 @@ #include -static char* packet_names[] = +static const char* packet_names[] = { "ADVERTISE", "SEARCHGW", "GWINFO", "RESERVED", "CONNECT", "CONNACK", "WILLTOPICREQ", "WILLTOPIC", "WILLMSGREQ", "WILLMSG", "REGISTER", "REGACK", @@ -35,7 +35,7 @@ static char* packet_names[] = * @param code MsgType code * @return the corresponding packet name */ -char* MQTTSNPacket_name(int code) +const char* MQTTSNPacket_name(int code) { return (code >= 0 && code <= MQTTSN_WILLMSGRESP) ? packet_names[code] : "UNKNOWN"; } @@ -51,7 +51,6 @@ int MQTTSNPacket_len(int length) return (length > 255) ? length + 3 : length + 1; } - /** * Encodes the MQTT-SN message length * @param buf the buffer into which the encoded data is written @@ -95,7 +94,7 @@ int MQTTSNPacket_decode(unsigned char* buf, int buflen, int* value) if (buf[0] == 1) { unsigned char* bufptr = &buf[1]; - if (buflen < 3) + if (buflen < MAX_NO_OF_LENGTH_BYTES) goto exit; *value = readInt(&bufptr); len = 3; @@ -188,7 +187,7 @@ void writeMQTTSNString(unsigned char** pptr, MQTTSNString MQTTSNString) { if (MQTTSNString.lenstring.len > 0) { - memcpy(*pptr, MQTTSNString.lenstring.data, MQTTSNString.lenstring.len); + memcpy(*pptr, (const unsigned char*)MQTTSNString.lenstring.data, MQTTSNString.lenstring.len); *pptr += MQTTSNString.lenstring.len; } else if (MQTTSNString.cstring) diff --git a/MQTTSNPacket/src/MQTTSNPacket.h b/MQTTSNPacket/src/MQTTSNPacket.h index 974619f..a3466c6 100644 --- a/MQTTSNPacket/src/MQTTSNPacket.h +++ b/MQTTSNPacket/src/MQTTSNPacket.h @@ -12,6 +12,7 @@ * * Contributors: * Ian Craggs - initial API and implementation and/or initial documentation + * TomoakiiYamaguchi - modify for C++ *******************************************************************************/ #ifndef MQTTSNPACKET_H_ @@ -35,15 +36,15 @@ enum MQTTSN_connackCodes MQTTSN_RC_ACCEPTED, MQTTSN_RC_REJECTED_CONGESTED, MQTTSN_RC_REJECTED_INVALID_TOPIC_ID, + MQTTSN_RC_NOT_SUPPORTED }; -enum MQTTSN_topicTypes +typedef enum { MQTTSN_TOPIC_TYPE_NORMAL, /* topic id in publish, topic name in subscribe */ MQTTSN_TOPIC_TYPE_PREDEFINED, MQTTSN_TOPIC_TYPE_SHORT, -}; - +}MQTTSN_topicTypes; enum MQTTSN_msgTypes { @@ -60,7 +61,7 @@ enum MQTTSN_msgTypes typedef struct { - enum MQTTSN_topicTypes type; + MQTTSN_topicTypes type; union { unsigned short id; @@ -73,7 +74,6 @@ typedef struct } data; } MQTTSN_topicid; - /** * Bitfields for the MQTT-SN flags byte. */ @@ -126,7 +126,7 @@ int MQTTSNstrlen(MQTTSNString mqttsnstring); #include "MQTTSNUnsubscribe.h" #include "MQTTSNSearch.h" -char* MQTTSNPacket_name(int ptype); +const char* MQTTSNPacket_name(int ptype); int MQTTSNPacket_len(int length); int MQTTSNPacket_encode(unsigned char* buf, int length); diff --git a/MQTTSNPacket/src/MQTTSNPublish.h b/MQTTSNPacket/src/MQTTSNPublish.h index 937817b..a020f96 100644 --- a/MQTTSNPacket/src/MQTTSNPublish.h +++ b/MQTTSNPacket/src/MQTTSNPublish.h @@ -41,5 +41,6 @@ int MQTTSNSerialize_regack(unsigned char* buf, int buflen, unsigned short topici unsigned char return_code); int MQTTSNDeserialize_regack(unsigned short* topicid, unsigned short* packetid, unsigned char* return_code, unsigned char* buf, int buflen); +int MQTTSNSerialize_pubrel(unsigned char* buf, int buflen, unsigned short packetid); #endif /* MQTTSNPUBLISH_H_ */ diff --git a/MQTTSNPacket/src/MQTTSNSubscribeServer.c b/MQTTSNPacket/src/MQTTSNSubscribeServer.c index d16f80a..8e48919 100644 --- a/MQTTSNPacket/src/MQTTSNSubscribeServer.c +++ b/MQTTSNPacket/src/MQTTSNSubscribeServer.c @@ -53,7 +53,7 @@ int MQTTSNDeserialize_subscribe(unsigned char* dup, int* qos, unsigned short* pa *packetid = readInt(&curdata); - topicFilter->type = flags.bits.topicIdType; + topicFilter->type = (MQTTSN_topicTypes)flags.bits.topicIdType; if (topicFilter->type == MQTTSN_TOPIC_TYPE_NORMAL) { diff --git a/MQTTSNPacket/src/MQTTSNUnsubscribeServer.c b/MQTTSNPacket/src/MQTTSNUnsubscribeServer.c index 4dff54f..c2205d9 100644 --- a/MQTTSNPacket/src/MQTTSNUnsubscribeServer.c +++ b/MQTTSNPacket/src/MQTTSNUnsubscribeServer.c @@ -38,7 +38,7 @@ int MQTTSNDeserialize_unsubscribe(unsigned short* packetid, MQTTSN_topicid* topi flags.all = readChar(&curdata); *packetid = readInt(&curdata); - topicFilter->type = flags.bits.topicIdType; + topicFilter->type = (MQTTSN_topicTypes)flags.bits.topicIdType; if (topicFilter->type == MQTTSN_TOPIC_TYPE_NORMAL) { topicFilter->data.long_.len = enddata - curdata; diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8c8ec4c --- /dev/null +++ b/Makefile @@ -0,0 +1,106 @@ +PROGNAME := MQTT-SNGateway +APPL := mainGateway + +LPROGNAME := MQTT-SNLogmonitor +LAPPL := mainLogmonitor + +SRCDIR := MQTTSNGateway/src +SUBDIR := MQTTSNPacket/src + +OS := linux +SENSORNET := udp + +CPPSRCS := \ +$(SRCDIR)/MQTTGWConnectionHandler.cpp \ +$(SRCDIR)/MQTTGWPacket.cpp \ +$(SRCDIR)/MQTTGWPublishHandler.cpp \ +$(SRCDIR)/MQTTGWSubscribeHandler.cpp \ +$(SRCDIR)/MQTTSNGateway.cpp \ +$(SRCDIR)/MQTTSNGWBrokerRecvTask.cpp \ +$(SRCDIR)/MQTTSNGWBrokerSendTask.cpp \ +$(SRCDIR)/MQTTSNGWClient.cpp \ +$(SRCDIR)/MQTTSNGWClientRecvTask.cpp \ +$(SRCDIR)/MQTTSNGWClientSendTask.cpp \ +$(SRCDIR)/MQTTSNGWConnectionHandler.cpp \ +$(SRCDIR)/MQTTSNGWLogmonitor.cpp \ +$(SRCDIR)/MQTTSNGWPacket.cpp \ +$(SRCDIR)/MQTTSNGWPacketHandleTask.cpp \ +$(SRCDIR)/MQTTSNGWProcess.cpp \ +$(SRCDIR)/MQTTSNGWPublishHandler.cpp \ +$(SRCDIR)/MQTTSNGWSubscribeHandler.cpp \ +$(SRCDIR)/$(OS)/$(SENSORNET)/SensorNetwork.cpp \ +$(SRCDIR)/$(OS)/$(OS).cpp \ +$(SRCDIR)/$(OS)/Network.cpp \ +$(SRCDIR)/$(OS)/Threading.cpp + +CSRCS := $(SUBDIR)/MQTTSNConnectClient.c \ +$(SUBDIR)/MQTTSNConnectServer.c \ +$(SUBDIR)/MQTTSNDeserializePublish.c \ +$(SUBDIR)/MQTTSNPacket.c \ +$(SUBDIR)/MQTTSNSearchClient.c \ +$(SUBDIR)/MQTTSNSearchServer.c \ +$(SUBDIR)/MQTTSNSerializePublish.c \ +$(SUBDIR)/MQTTSNSubscribeClient.c \ +$(SUBDIR)/MQTTSNSubscribeServer.c \ +$(SUBDIR)/MQTTSNUnsubscribeClient.c \ +$(SUBDIR)/MQTTSNUnsubscribeServer.c + +CXX := g++ +CPPFLAGS += + +INCLUDES += -IMQTTSNGateway/src \ +-IMQTTSNGateway/src/$(OS) \ +-IMQTTSNGateway/src/$(OS)/$(SENSORNET) \ +-IMQTTSNPacket/src + +DEFS := +LIBS += +LDFLAGS := +CXXFLAGS := -Wall -O3 -std=c++11 +LDADD := -lpthread -lssl -lcrypto +OUTDIR := Build + +PROG := $(OUTDIR)/$(PROGNAME) +LPROG := $(OUTDIR)/$(LPROGNAME) +OBJS := $(CPPSRCS:%.cpp=$(OUTDIR)/%.o) +OBJS += $(CSRCS:%.c=$(OUTDIR)/%.o) +DEPS := $(CPPSRCS:%.cpp=$(OUTDIR)/%.d) +DEPS += $(CSRCS:%.c=$(OUTDIR)/%.d) + +.PHONY: install clean + +all: $(PROG) + +monitor: $(LPROG) + +-include $(DEPS) + +$(PROG): $(OBJS) $(OUTDIR)/$(SRCDIR)/$(APPL).o + $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) $(LDADD) + +$(LPROG): $(OBJS) $(OUTDIR)/$(SRCDIR)/$(LAPPL).o + $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) $(LDADD) + +$(OUTDIR)/$(SRCDIR)/%.o:$(SRCDIR)/%.cpp + @if [ ! -e `dirname $@` ]; then mkdir -p `dirname $@`; fi + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(INCLUDES) $(DEFS) -o $@ -c -MMD -MP -MF $(@:%.o=%.d) $< + +$(OUTDIR)/$(SRCDIR)/$(APPL).o:$(SRCDIR)/$(APPL).cpp + @if [ ! -e `dirname $@` ]; then mkdir -p `dirname $@`; fi + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(INCLUDES) $(DEFS) -o $@ -c -MMD -MP -MF $(@:%.o=%.d) $< + +$(OUTDIR)/$(SRCDIR)/$(LAPPL).o:$(SRCDIR)/$(LAPPL).cpp + @if [ ! -e `dirname $@` ]; then mkdir -p `dirname $@`; fi + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(INCLUDES) $(DEFS) -o $@ -c -MMD -MP -MF $(@:%.o=%.d) $< + +$(OUTDIR)/$(SUBDIR)/%.o:$(SUBDIR)/%.c + @if [ ! -e `dirname $@` ]; then mkdir -p `dirname $@`; fi + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(INCLUDES) $(DEFS) -o $@ -c -MMD -MP -MF $(@:%.o=%.d) $< + +clean: + rm -rf $(OUTDIR) + +install: + cp -pf $(PROG) ../ + + \ No newline at end of file diff --git a/README.md b/README.md index bc7a72d..9396809 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Eclipse Paho MQTT-SN Embedded C client +# Eclipse Paho MQTT-SN Embedded C ## Project description: