2018-03-31 22:42:28 +02:00
|
|
|
/*
|
2018-04-01 19:31:18 +02:00
|
|
|
Copyright (c) 2013, 2014 Paolo Patierno
|
2018-03-31 22:42:28 +02:00
|
|
|
|
2018-04-01 19:31:18 +02:00
|
|
|
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.
|
2018-03-31 22:42:28 +02:00
|
|
|
|
2018-04-01 19:31:18 +02:00
|
|
|
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.
|
2018-03-31 22:42:28 +02:00
|
|
|
|
2018-04-01 19:31:18 +02:00
|
|
|
Contributors:
|
|
|
|
Paolo Patierno - initial API and implementation and/or initial documentation
|
2018-03-31 22:42:28 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
using System;
|
2018-04-01 19:31:18 +02:00
|
|
|
using System.Text;
|
2018-03-31 22:42:28 +02:00
|
|
|
|
2019-11-26 15:34:16 +01:00
|
|
|
namespace uPLibrary.Networking.M2Mqtt.Messages {
|
|
|
|
/// <summary>
|
|
|
|
/// Base class for all MQTT messages
|
|
|
|
/// </summary>
|
|
|
|
public abstract class MqttMsgBase {
|
|
|
|
#region Constants...
|
|
|
|
|
|
|
|
// mask, offset and size for fixed header fields
|
|
|
|
internal const Byte MSG_TYPE_MASK = 0xF0;
|
|
|
|
internal const Byte MSG_TYPE_OFFSET = 0x04;
|
|
|
|
internal const Byte MSG_TYPE_SIZE = 0x04;
|
|
|
|
internal const Byte MSG_FLAG_BITS_MASK = 0x0F; // [v3.1.1]
|
|
|
|
internal const Byte MSG_FLAG_BITS_OFFSET = 0x00; // [v3.1.1]
|
|
|
|
internal const Byte MSG_FLAG_BITS_SIZE = 0x04; // [v3.1.1]
|
|
|
|
internal const Byte DUP_FLAG_MASK = 0x08;
|
|
|
|
internal const Byte DUP_FLAG_OFFSET = 0x03;
|
|
|
|
internal const Byte DUP_FLAG_SIZE = 0x01;
|
|
|
|
internal const Byte QOS_LEVEL_MASK = 0x06;
|
|
|
|
internal const Byte QOS_LEVEL_OFFSET = 0x01;
|
|
|
|
internal const Byte QOS_LEVEL_SIZE = 0x02;
|
|
|
|
internal const Byte RETAIN_FLAG_MASK = 0x01;
|
|
|
|
internal const Byte RETAIN_FLAG_OFFSET = 0x00;
|
|
|
|
internal const Byte RETAIN_FLAG_SIZE = 0x01;
|
|
|
|
|
|
|
|
// MQTT message types
|
|
|
|
internal const Byte MQTT_MSG_CONNECT_TYPE = 0x01;
|
|
|
|
internal const Byte MQTT_MSG_CONNACK_TYPE = 0x02;
|
|
|
|
internal const Byte MQTT_MSG_PUBLISH_TYPE = 0x03;
|
|
|
|
internal const Byte MQTT_MSG_PUBACK_TYPE = 0x04;
|
|
|
|
internal const Byte MQTT_MSG_PUBREC_TYPE = 0x05;
|
|
|
|
internal const Byte MQTT_MSG_PUBREL_TYPE = 0x06;
|
|
|
|
internal const Byte MQTT_MSG_PUBCOMP_TYPE = 0x07;
|
|
|
|
internal const Byte MQTT_MSG_SUBSCRIBE_TYPE = 0x08;
|
|
|
|
internal const Byte MQTT_MSG_SUBACK_TYPE = 0x09;
|
|
|
|
internal const Byte MQTT_MSG_UNSUBSCRIBE_TYPE = 0x0A;
|
|
|
|
internal const Byte MQTT_MSG_UNSUBACK_TYPE = 0x0B;
|
|
|
|
internal const Byte MQTT_MSG_PINGREQ_TYPE = 0x0C;
|
|
|
|
internal const Byte MQTT_MSG_PINGRESP_TYPE = 0x0D;
|
|
|
|
internal const Byte MQTT_MSG_DISCONNECT_TYPE = 0x0E;
|
|
|
|
|
|
|
|
// [v3.1.1] MQTT flag bits
|
|
|
|
internal const Byte MQTT_MSG_CONNECT_FLAG_BITS = 0x00;
|
|
|
|
internal const Byte MQTT_MSG_CONNACK_FLAG_BITS = 0x00;
|
|
|
|
internal const Byte MQTT_MSG_PUBLISH_FLAG_BITS = 0x00; // just defined as 0x00 but depends on publish props (dup, qos, retain)
|
|
|
|
internal const Byte MQTT_MSG_PUBACK_FLAG_BITS = 0x00;
|
|
|
|
internal const Byte MQTT_MSG_PUBREC_FLAG_BITS = 0x00;
|
|
|
|
internal const Byte MQTT_MSG_PUBREL_FLAG_BITS = 0x02;
|
|
|
|
internal const Byte MQTT_MSG_PUBCOMP_FLAG_BITS = 0x00;
|
|
|
|
internal const Byte MQTT_MSG_SUBSCRIBE_FLAG_BITS = 0x02;
|
|
|
|
internal const Byte MQTT_MSG_SUBACK_FLAG_BITS = 0x00;
|
|
|
|
internal const Byte MQTT_MSG_UNSUBSCRIBE_FLAG_BITS = 0x02;
|
|
|
|
internal const Byte MQTT_MSG_UNSUBACK_FLAG_BITS = 0x00;
|
|
|
|
internal const Byte MQTT_MSG_PINGREQ_FLAG_BITS = 0x00;
|
|
|
|
internal const Byte MQTT_MSG_PINGRESP_FLAG_BITS = 0x00;
|
|
|
|
internal const Byte MQTT_MSG_DISCONNECT_FLAG_BITS = 0x00;
|
|
|
|
|
|
|
|
// QOS levels
|
|
|
|
public const Byte QOS_LEVEL_AT_MOST_ONCE = 0x00;
|
|
|
|
public const Byte QOS_LEVEL_AT_LEAST_ONCE = 0x01;
|
|
|
|
public const Byte QOS_LEVEL_EXACTLY_ONCE = 0x02;
|
|
|
|
|
|
|
|
// SUBSCRIBE QoS level granted failure [v3.1.1]
|
|
|
|
public const Byte QOS_LEVEL_GRANTED_FAILURE = 0x80;
|
|
|
|
|
|
|
|
internal const UInt16 MAX_TOPIC_LENGTH = 65535;
|
|
|
|
internal const UInt16 MIN_TOPIC_LENGTH = 1;
|
|
|
|
internal const Byte MESSAGE_ID_SIZE = 2;
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Properties...
|
|
|
|
|
2018-03-31 22:42:28 +02:00
|
|
|
/// <summary>
|
2019-11-26 15:34:16 +01:00
|
|
|
/// Message type
|
2018-03-31 22:42:28 +02:00
|
|
|
/// </summary>
|
2019-11-26 15:34:16 +01:00
|
|
|
public Byte Type { get; set; }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Duplicate message flag
|
|
|
|
/// </summary>
|
|
|
|
public Boolean DupFlag { get; set; }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Quality of Service level
|
|
|
|
/// </summary>
|
|
|
|
public Byte QosLevel { get; set; }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Retain message flag
|
|
|
|
/// </summary>
|
|
|
|
public Boolean Retain { get; set; }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Message identifier for the message
|
|
|
|
/// </summary>
|
|
|
|
public UInt16 MessageId { get; set; }
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
// message type
|
|
|
|
// duplicate delivery
|
|
|
|
// quality of service level
|
|
|
|
// retain flag
|
|
|
|
// message identifier
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Returns message bytes rapresentation
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="protocolVersion">Protocol version</param>
|
|
|
|
/// <returns>Bytes rapresentation</returns>
|
|
|
|
public abstract Byte[] GetBytes(Byte protocolVersion);
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Encode remaining length and insert it into message buffer
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="remainingLength">Remaining length value to encode</param>
|
|
|
|
/// <param name="buffer">Message buffer for inserting encoded value</param>
|
|
|
|
/// <param name="index">Index from which insert encoded value into buffer</param>
|
|
|
|
/// <returns>Index updated</returns>
|
|
|
|
protected Int32 EncodeRemainingLength(Int32 remainingLength, Byte[] buffer, Int32 index) {
|
|
|
|
do {
|
|
|
|
Int32 digit = remainingLength % 128;
|
|
|
|
remainingLength /= 128;
|
|
|
|
if (remainingLength > 0) {
|
|
|
|
digit |= 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer[index++] = (Byte)digit;
|
|
|
|
} while (remainingLength > 0);
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Decode remaining length reading bytes from socket
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="channel">Channel from reading bytes</param>
|
|
|
|
/// <returns>Decoded remaining length</returns>
|
|
|
|
protected static Int32 DecodeRemainingLength(IMqttNetworkChannel channel) {
|
|
|
|
Int32 multiplier = 1;
|
|
|
|
Int32 value = 0;
|
|
|
|
Byte[] nextByte = new Byte[1];
|
|
|
|
Int32 digit;
|
|
|
|
do {
|
|
|
|
// next digit from stream
|
|
|
|
_ = channel.Receive(nextByte);
|
|
|
|
digit = nextByte[0];
|
|
|
|
value += (digit & 127) * multiplier;
|
|
|
|
multiplier *= 128;
|
|
|
|
} while ((digit & 128) != 0);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2018-04-01 19:31:18 +02:00
|
|
|
#if TRACE
|
2019-11-26 15:34:16 +01:00
|
|
|
/// <summary>
|
|
|
|
/// Returns a string representation of the message for tracing
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="name">Message name</param>
|
|
|
|
/// <param name="fieldNames">Message fields name</param>
|
|
|
|
/// <param name="fieldValues">Message fields value</param>
|
|
|
|
/// <returns>String representation of the message</returns>
|
|
|
|
protected String GetTraceString(String name, Object[] fieldNames, Object[] fieldValues) {
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
_ = sb.Append(name);
|
|
|
|
|
|
|
|
if (fieldNames != null && fieldValues != null) {
|
|
|
|
_ = sb.Append("(");
|
|
|
|
Boolean addComma = false;
|
|
|
|
for (Int32 i = 0; i < fieldValues.Length; i++) {
|
|
|
|
if (fieldValues[i] != null) {
|
|
|
|
if (addComma) {
|
|
|
|
_ = sb.Append(",");
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = sb.Append(fieldNames[i]);
|
|
|
|
_ = sb.Append(":");
|
|
|
|
_ = sb.Append(this.GetStringObject(fieldValues[i]));
|
|
|
|
addComma = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ = sb.Append(")");
|
|
|
|
}
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
Object GetStringObject(Object value) {
|
|
|
|
if (value is Byte[] binary) {
|
|
|
|
String hexChars = "0123456789ABCDEF";
|
|
|
|
StringBuilder sb = new StringBuilder(binary.Length * 2);
|
|
|
|
for (Int32 i = 0; i < binary.Length; ++i) {
|
|
|
|
_ = sb.Append(hexChars[binary[i] >> 4]);
|
|
|
|
_ = sb.Append(hexChars[binary[i] & 0x0F]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value is Object[] list) {
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
_ = sb.Append('[');
|
|
|
|
for (Int32 i = 0; i < list.Length; ++i) {
|
|
|
|
if (i > 0) {
|
|
|
|
_ = sb.Append(',');
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = sb.Append(list[i]);
|
|
|
|
}
|
|
|
|
_ = sb.Append(']');
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
2018-04-01 19:31:18 +02:00
|
|
|
#endif
|
2019-11-26 15:34:16 +01:00
|
|
|
}
|
2018-03-31 22:42:28 +02:00
|
|
|
}
|