Compare commits
16
Commits
fa9f0cb17e
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c4e2d17cc | ||
|
|
0287850d13 | ||
|
|
f9f9ff5129 | ||
|
|
daaeca5bf2 | ||
|
|
c6d02a0a71 | ||
|
|
edae4170e3 | ||
|
|
40b5328739 | ||
|
|
e73ef2504c | ||
|
|
831c3dfc54 | ||
|
|
491ed23867 | ||
|
|
6e68241722 | ||
|
|
82755ce75d | ||
|
|
1d66da260b | ||
|
|
48eb1a6974 | ||
|
|
3cda8ca417 | ||
|
|
a103b9f491 |
+33
@@ -0,0 +1,33 @@
|
||||
[submodule "litjson"]
|
||||
path = litjson
|
||||
url = http://git.blubbfish.net/vs_librarys/litjson.git
|
||||
[submodule "mqtt"]
|
||||
path = mqtt
|
||||
url = http://git.blubbfish.net/vs_librarys/mqtt.git
|
||||
[submodule "RaspberryIO"]
|
||||
path = RaspberryIO
|
||||
url = http://git.blubbfish.net/vs_librarys/RaspberryIO.git
|
||||
[submodule "Mono.Posix"]
|
||||
path = Mono.Posix
|
||||
url = http://git.blubbfish.net/vs_librarys/Mono.Posix.git
|
||||
[submodule "RaspberryIO_26"]
|
||||
path = RaspberryIO_26
|
||||
url = http://git.blubbfish.net/vs_librarys/RaspberryIO_26.git
|
||||
[submodule "Coordinates"]
|
||||
path = Coordinates
|
||||
url = https://git.blubbfish.net/vs_librarys/Coordinates.git
|
||||
[submodule "DataMatrix.net"]
|
||||
path = DataMatrix.net
|
||||
url = https://git.blubbfish.net/vs_librarys/DataMatrix.net.git
|
||||
[submodule "GPSDLib"]
|
||||
path = GPSDLib
|
||||
url = https://git.blubbfish.net/vs_librarys/GPSDLib.git
|
||||
[submodule "ImageSharp"]
|
||||
path = ImageSharp
|
||||
url = https://git.blubbfish.net/vs_librarys/ImageSharp.git
|
||||
[submodule "StreamDeckSharp"]
|
||||
path = StreamDeckSharp
|
||||
url = https://git.blubbfish.net/vs_librarys/StreamDeckSharp.git
|
||||
[submodule "TelegramBot"]
|
||||
path = TelegramBot
|
||||
url = https://git.blubbfish.net/vs_librarys/TelegramBot.git
|
||||
Submodule
+1
Submodule Coordinates added at 86f0509c9a
Submodule
+1
Submodule DataMatrix.net added at ed37b7ba01
Submodule
+1
Submodule GPSDLib added at 0ff9e48e2e
Submodule
+1
Submodule ImageSharp added at 7e8cddf208
Submodule
+1
Submodule Mono.Posix added at 2f0ae844d0
Submodule
+1
Submodule RaspberryIO added at 2f74732924
Submodule
+1
Submodule RaspberryIO_26 added at 97ca122560
Submodule
+1
Submodule StreamDeckSharp added at 308ba282fb
Submodule
+1
Submodule TelegramBot added at 17393a47d3
Submodule
+1
Submodule litjson added at 63561dc90d
@@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "litjson_4.7.1", "litjson\litjson_4.7.1.csproj", "{91A14CD2-2940-4500-8193-56D37EDDDBAA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{91A14CD2-2940-4500-8193-56D37EDDDBAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{91A14CD2-2940-4500-8193-56D37EDDDBAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{91A14CD2-2940-4500-8193-56D37EDDDBAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{91A14CD2-2940-4500-8193-56D37EDDDBAA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {AF2398AC-1EAA-4615-B21A-33C68CD44849}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,60 +0,0 @@
|
||||
#region Header
|
||||
/**
|
||||
* IJsonWrapper.cs
|
||||
* Interface that represents a type capable of handling all kinds of JSON
|
||||
* data. This is mainly used when mapping objects through JsonMapper, and
|
||||
* it's implemented by JsonData.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
|
||||
namespace LitJson
|
||||
{
|
||||
public enum JsonType
|
||||
{
|
||||
None,
|
||||
|
||||
Object,
|
||||
Array,
|
||||
String,
|
||||
Int,
|
||||
Long,
|
||||
Double,
|
||||
Boolean
|
||||
}
|
||||
|
||||
public interface IJsonWrapper : IList, IOrderedDictionary
|
||||
{
|
||||
bool IsArray { get; }
|
||||
bool IsBoolean { get; }
|
||||
bool IsDouble { get; }
|
||||
bool IsInt { get; }
|
||||
bool IsLong { get; }
|
||||
bool IsObject { get; }
|
||||
bool IsString { get; }
|
||||
|
||||
bool GetBoolean ();
|
||||
double GetDouble ();
|
||||
int GetInt ();
|
||||
JsonType GetJsonType ();
|
||||
long GetLong ();
|
||||
string GetString ();
|
||||
|
||||
void SetBoolean (bool val);
|
||||
void SetDouble (double val);
|
||||
void SetInt (int val);
|
||||
void SetJsonType (JsonType type);
|
||||
void SetLong (long val);
|
||||
void SetString (string val);
|
||||
|
||||
string ToJson ();
|
||||
void ToJson (JsonWriter writer);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,65 +0,0 @@
|
||||
#region Header
|
||||
/**
|
||||
* JsonException.cs
|
||||
* Base class throwed by LitJSON when a parsing error occurs.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
namespace LitJson
|
||||
{
|
||||
public class JsonException :
|
||||
#if NETSTANDARD1_5
|
||||
Exception
|
||||
#else
|
||||
ApplicationException
|
||||
#endif
|
||||
{
|
||||
public JsonException () : base ()
|
||||
{
|
||||
}
|
||||
|
||||
internal JsonException (ParserToken token) :
|
||||
base (String.Format (
|
||||
"Invalid token '{0}' in input string", token))
|
||||
{
|
||||
}
|
||||
|
||||
internal JsonException (ParserToken token,
|
||||
Exception inner_exception) :
|
||||
base (String.Format (
|
||||
"Invalid token '{0}' in input string", token),
|
||||
inner_exception)
|
||||
{
|
||||
}
|
||||
|
||||
internal JsonException (int c) :
|
||||
base (String.Format (
|
||||
"Invalid character '{0}' in input string", (char) c))
|
||||
{
|
||||
}
|
||||
|
||||
internal JsonException (int c, Exception inner_exception) :
|
||||
base (String.Format (
|
||||
"Invalid character '{0}' in input string", (char) c),
|
||||
inner_exception)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public JsonException (string message) : base (message)
|
||||
{
|
||||
}
|
||||
|
||||
public JsonException (string message, Exception inner_exception) :
|
||||
base (message, inner_exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,857 +0,0 @@
|
||||
#region Header
|
||||
/**
|
||||
* JsonMapper.cs
|
||||
* JSON to .Net object and object to JSON conversions.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
namespace LitJson {
|
||||
internal struct PropertyMetadata {
|
||||
public MemberInfo Info;
|
||||
public Boolean IsField;
|
||||
public Type Type;
|
||||
}
|
||||
|
||||
internal struct ArrayMetadata {
|
||||
private Type element_type;
|
||||
|
||||
public Type ElementType {
|
||||
get {
|
||||
if (this.element_type == null) {
|
||||
return typeof(JsonData);
|
||||
}
|
||||
|
||||
return this.element_type;
|
||||
}
|
||||
|
||||
set { this.element_type = value; }
|
||||
}
|
||||
|
||||
public Boolean IsArray { get; set; }
|
||||
|
||||
public Boolean IsList { get; set; }
|
||||
}
|
||||
|
||||
internal struct ObjectMetadata {
|
||||
private Type element_type;
|
||||
|
||||
public Type ElementType {
|
||||
get {
|
||||
if (this.element_type == null) {
|
||||
return typeof(JsonData);
|
||||
}
|
||||
|
||||
return this.element_type;
|
||||
}
|
||||
|
||||
set { this.element_type = value; }
|
||||
}
|
||||
|
||||
public Boolean IsDictionary { get; set; }
|
||||
|
||||
public IDictionary<String, PropertyMetadata> Properties { get; set; }
|
||||
}
|
||||
|
||||
|
||||
internal delegate void ExporterFunc(Object obj, JsonWriter writer);
|
||||
public delegate void ExporterFunc<T>(T obj, JsonWriter writer);
|
||||
|
||||
internal delegate Object ImporterFunc(Object input);
|
||||
public delegate TValue ImporterFunc<TJson, TValue>(TJson input);
|
||||
|
||||
public delegate IJsonWrapper WrapperFactory();
|
||||
|
||||
public class JsonMapper {
|
||||
#region Fields
|
||||
private static readonly Int32 max_nesting_depth;
|
||||
|
||||
private static readonly IFormatProvider datetime_format;
|
||||
|
||||
private static readonly IDictionary<Type, ExporterFunc> base_exporters_table;
|
||||
private static readonly IDictionary<Type, ExporterFunc> custom_exporters_table;
|
||||
|
||||
private static readonly IDictionary<Type, IDictionary<Type, ImporterFunc>> base_importers_table;
|
||||
private static readonly IDictionary<Type, IDictionary<Type, ImporterFunc>> custom_importers_table;
|
||||
|
||||
private static readonly IDictionary<Type, ArrayMetadata> array_metadata;
|
||||
private static readonly Object array_metadata_lock = new Object();
|
||||
|
||||
private static readonly IDictionary<Type, IDictionary<Type, MethodInfo>> conv_ops;
|
||||
private static readonly Object conv_ops_lock = new Object();
|
||||
|
||||
private static readonly IDictionary<Type, ObjectMetadata> object_metadata;
|
||||
private static readonly Object object_metadata_lock = new Object();
|
||||
|
||||
private static readonly IDictionary<Type, IList<PropertyMetadata>> type_properties;
|
||||
private static readonly Object type_properties_lock = new Object();
|
||||
|
||||
private static readonly JsonWriter static_writer;
|
||||
private static readonly Object static_writer_lock = new Object();
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
static JsonMapper() {
|
||||
max_nesting_depth = 100;
|
||||
|
||||
array_metadata = new Dictionary<Type, ArrayMetadata>();
|
||||
conv_ops = new Dictionary<Type, IDictionary<Type, MethodInfo>>();
|
||||
object_metadata = new Dictionary<Type, ObjectMetadata>();
|
||||
type_properties = new Dictionary<Type, IList<PropertyMetadata>>();
|
||||
|
||||
static_writer = new JsonWriter();
|
||||
|
||||
datetime_format = DateTimeFormatInfo.InvariantInfo;
|
||||
|
||||
base_exporters_table = new Dictionary<Type, ExporterFunc>();
|
||||
custom_exporters_table = new Dictionary<Type, ExporterFunc>();
|
||||
|
||||
base_importers_table = new Dictionary<Type, IDictionary<Type, ImporterFunc>>();
|
||||
custom_importers_table = new Dictionary<Type, IDictionary<Type, ImporterFunc>>();
|
||||
|
||||
RegisterBaseExporters();
|
||||
RegisterBaseImporters();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private static void AddArrayMetadata(Type type) {
|
||||
if (array_metadata.ContainsKey(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayMetadata data = new ArrayMetadata {
|
||||
IsArray = type.IsArray
|
||||
};
|
||||
|
||||
if (type.GetInterface("System.Collections.IList") != null) {
|
||||
data.IsList = true;
|
||||
}
|
||||
|
||||
foreach (PropertyInfo p_info in type.GetProperties()) {
|
||||
if (p_info.Name != "Item") {
|
||||
continue;
|
||||
}
|
||||
|
||||
ParameterInfo[] parameters = p_info.GetIndexParameters();
|
||||
|
||||
if (parameters.Length != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parameters[0].ParameterType == typeof(Int32)) {
|
||||
data.ElementType = p_info.PropertyType;
|
||||
}
|
||||
}
|
||||
|
||||
lock (array_metadata_lock) {
|
||||
try {
|
||||
array_metadata.Add(type, data);
|
||||
} catch (ArgumentException) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddObjectMetadata(Type type) {
|
||||
if (object_metadata.ContainsKey(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectMetadata data = new ObjectMetadata();
|
||||
|
||||
if (type.GetInterface("System.Collections.IDictionary") != null) {
|
||||
data.IsDictionary = true;
|
||||
}
|
||||
|
||||
data.Properties = new Dictionary<String, PropertyMetadata>();
|
||||
|
||||
foreach (PropertyInfo p_info in type.GetProperties()) {
|
||||
if (p_info.Name == "Item") {
|
||||
ParameterInfo[] parameters = p_info.GetIndexParameters();
|
||||
|
||||
if (parameters.Length != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parameters[0].ParameterType == typeof(String)) {
|
||||
data.ElementType = p_info.PropertyType;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
PropertyMetadata p_data = new PropertyMetadata {
|
||||
Info = p_info,
|
||||
Type = p_info.PropertyType
|
||||
};
|
||||
|
||||
data.Properties.Add(p_info.Name, p_data);
|
||||
}
|
||||
|
||||
foreach (FieldInfo f_info in type.GetFields()) {
|
||||
PropertyMetadata p_data = new PropertyMetadata {
|
||||
Info = f_info,
|
||||
IsField = true,
|
||||
Type = f_info.FieldType
|
||||
};
|
||||
|
||||
data.Properties.Add(f_info.Name, p_data);
|
||||
}
|
||||
|
||||
lock (object_metadata_lock) {
|
||||
try {
|
||||
object_metadata.Add(type, data);
|
||||
} catch (ArgumentException) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddTypeProperties(Type type) {
|
||||
if (type_properties.ContainsKey(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IList<PropertyMetadata> props = new List<PropertyMetadata>();
|
||||
|
||||
foreach (PropertyInfo p_info in type.GetProperties()) {
|
||||
if (p_info.Name == "Item") {
|
||||
continue;
|
||||
}
|
||||
|
||||
PropertyMetadata p_data = new PropertyMetadata {
|
||||
Info = p_info,
|
||||
IsField = false
|
||||
};
|
||||
props.Add(p_data);
|
||||
}
|
||||
|
||||
foreach (FieldInfo f_info in type.GetFields()) {
|
||||
PropertyMetadata p_data = new PropertyMetadata {
|
||||
Info = f_info,
|
||||
IsField = true
|
||||
};
|
||||
|
||||
props.Add(p_data);
|
||||
}
|
||||
|
||||
lock (type_properties_lock) {
|
||||
try {
|
||||
type_properties.Add(type, props);
|
||||
} catch (ArgumentException) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodInfo GetConvOp(Type t1, Type t2) {
|
||||
lock (conv_ops_lock) {
|
||||
if (!conv_ops.ContainsKey(t1)) {
|
||||
conv_ops.Add(t1, new Dictionary<Type, MethodInfo>());
|
||||
}
|
||||
}
|
||||
|
||||
if (conv_ops[t1].ContainsKey(t2)) {
|
||||
return conv_ops[t1][t2];
|
||||
}
|
||||
|
||||
MethodInfo op = t1.GetMethod("op_Implicit", new Type[] { t2 });
|
||||
|
||||
lock (conv_ops_lock) {
|
||||
try {
|
||||
conv_ops[t1].Add(t2, op);
|
||||
} catch (ArgumentException) {
|
||||
return conv_ops[t1][t2];
|
||||
}
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
private static Object ReadValue(Type inst_type, JsonReader reader) {
|
||||
reader.Read();
|
||||
|
||||
if (reader.Token == JsonToken.ArrayEnd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Type underlying_type = Nullable.GetUnderlyingType(inst_type);
|
||||
Type value_type = underlying_type ?? inst_type;
|
||||
|
||||
if (reader.Token == JsonToken.Null) {
|
||||
#if NETSTANDARD1_5
|
||||
if (inst_type.IsClass() || underlying_type != null) {
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
if (inst_type.IsClass || underlying_type != null) {
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
throw new JsonException(String.Format("Can't assign null to an instance of type {0}", inst_type));
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.Double ||
|
||||
reader.Token == JsonToken.Int ||
|
||||
reader.Token == JsonToken.Long ||
|
||||
reader.Token == JsonToken.String ||
|
||||
reader.Token == JsonToken.Boolean) {
|
||||
|
||||
Type json_type = reader.Value.GetType();
|
||||
|
||||
if (value_type.IsAssignableFrom(json_type)) {
|
||||
return reader.Value;
|
||||
}
|
||||
|
||||
// If there's a custom importer that fits, use it
|
||||
if (custom_importers_table.ContainsKey(json_type) &&
|
||||
custom_importers_table[json_type].ContainsKey(
|
||||
value_type)) {
|
||||
|
||||
ImporterFunc importer =
|
||||
custom_importers_table[json_type][value_type];
|
||||
|
||||
return importer(reader.Value);
|
||||
}
|
||||
|
||||
// Maybe there's a base importer that works
|
||||
if (base_importers_table.ContainsKey(json_type) &&
|
||||
base_importers_table[json_type].ContainsKey(
|
||||
value_type)) {
|
||||
|
||||
ImporterFunc importer =
|
||||
base_importers_table[json_type][value_type];
|
||||
|
||||
return importer(reader.Value);
|
||||
}
|
||||
|
||||
// Maybe it's an enum
|
||||
#if NETSTANDARD1_5
|
||||
if (value_type.IsEnum())
|
||||
return Enum.ToObject (value_type, reader.Value);
|
||||
#else
|
||||
if (value_type.IsEnum) {
|
||||
return Enum.ToObject(value_type, reader.Value);
|
||||
}
|
||||
#endif
|
||||
// Try using an implicit conversion operator
|
||||
MethodInfo conv_op = GetConvOp(value_type, json_type);
|
||||
|
||||
if (conv_op != null) {
|
||||
return conv_op.Invoke(null, new Object[] { reader.Value });
|
||||
}
|
||||
|
||||
// No luck
|
||||
throw new JsonException(String.Format("Can't assign value '{0}' (type {1}) to type {2}", reader.Value, json_type, inst_type));
|
||||
}
|
||||
|
||||
Object instance = null;
|
||||
|
||||
if (reader.Token == JsonToken.ArrayStart) {
|
||||
|
||||
AddArrayMetadata(inst_type);
|
||||
ArrayMetadata t_data = array_metadata[inst_type];
|
||||
|
||||
if (!t_data.IsArray && !t_data.IsList) {
|
||||
throw new JsonException(String.Format("Type {0} can't act as an array", inst_type));
|
||||
}
|
||||
|
||||
IList list;
|
||||
Type elem_type;
|
||||
|
||||
if (!t_data.IsArray) {
|
||||
list = (IList)Activator.CreateInstance(inst_type);
|
||||
elem_type = t_data.ElementType;
|
||||
} else {
|
||||
list = new ArrayList();
|
||||
elem_type = inst_type.GetElementType();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
Object item = ReadValue(elem_type, reader);
|
||||
if (item == null && reader.Token == JsonToken.ArrayEnd) {
|
||||
break;
|
||||
}
|
||||
|
||||
list.Add(item);
|
||||
}
|
||||
|
||||
if (t_data.IsArray) {
|
||||
Int32 n = list.Count;
|
||||
instance = Array.CreateInstance(elem_type, n);
|
||||
|
||||
for (Int32 i = 0; i < n; i++) {
|
||||
((Array)instance).SetValue(list[i], i);
|
||||
}
|
||||
} else {
|
||||
instance = list;
|
||||
}
|
||||
} else if (reader.Token == JsonToken.ObjectStart) {
|
||||
AddObjectMetadata(value_type);
|
||||
ObjectMetadata t_data = object_metadata[value_type];
|
||||
|
||||
instance = Activator.CreateInstance(value_type);
|
||||
|
||||
while (true) {
|
||||
reader.Read();
|
||||
|
||||
if (reader.Token == JsonToken.ObjectEnd) {
|
||||
break;
|
||||
}
|
||||
|
||||
String property = (String)reader.Value;
|
||||
|
||||
if (t_data.Properties.ContainsKey(property)) {
|
||||
PropertyMetadata prop_data = t_data.Properties[property];
|
||||
|
||||
if (prop_data.IsField) {
|
||||
((FieldInfo)prop_data.Info).SetValue(instance, ReadValue(prop_data.Type, reader));
|
||||
} else {
|
||||
PropertyInfo p_info = (PropertyInfo)prop_data.Info;
|
||||
|
||||
if (p_info.CanWrite) {
|
||||
p_info.SetValue(instance, ReadValue(prop_data.Type, reader), null);
|
||||
} else {
|
||||
ReadValue(prop_data.Type, reader);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!t_data.IsDictionary) {
|
||||
|
||||
if (!reader.SkipNonMembers) {
|
||||
throw new JsonException(String.Format("The type {0} doesn't have the property '{1}'", inst_type, property));
|
||||
} else {
|
||||
ReadSkip(reader);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
((IDictionary)instance).Add(property, ReadValue(t_data.ElementType, reader));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static IJsonWrapper ReadValue(WrapperFactory factory, JsonReader reader) {
|
||||
reader.Read();
|
||||
|
||||
if (reader.Token == JsonToken.ArrayEnd ||
|
||||
reader.Token == JsonToken.Null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IJsonWrapper instance = factory();
|
||||
|
||||
if (reader.Token == JsonToken.String) {
|
||||
instance.SetString((String)reader.Value);
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.Double) {
|
||||
instance.SetDouble((Double)reader.Value);
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.Int) {
|
||||
instance.SetInt((Int32)reader.Value);
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.Long) {
|
||||
instance.SetLong((Int64)reader.Value);
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.Boolean) {
|
||||
instance.SetBoolean((Boolean)reader.Value);
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.ArrayStart) {
|
||||
instance.SetJsonType(JsonType.Array);
|
||||
|
||||
while (true) {
|
||||
IJsonWrapper item = ReadValue(factory, reader);
|
||||
if (item == null && reader.Token == JsonToken.ArrayEnd) {
|
||||
break;
|
||||
}
|
||||
instance.Add(item);
|
||||
}
|
||||
} else if (reader.Token == JsonToken.ObjectStart) {
|
||||
instance.SetJsonType(JsonType.Object);
|
||||
|
||||
while (true) {
|
||||
reader.Read();
|
||||
|
||||
if (reader.Token == JsonToken.ObjectEnd) {
|
||||
break;
|
||||
}
|
||||
|
||||
String property = (String)reader.Value;
|
||||
|
||||
instance[property] = ReadValue(factory, reader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static void ReadSkip(JsonReader reader) {
|
||||
ToWrapper(delegate { return new JsonMockWrapper(); }, reader);
|
||||
}
|
||||
|
||||
private static void RegisterBaseExporters() {
|
||||
base_exporters_table[typeof(Byte)] = delegate (Object obj, JsonWriter writer) {
|
||||
writer.Write(Convert.ToInt32((Byte)obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof(Char)] = delegate (Object obj, JsonWriter writer) {
|
||||
writer.Write(Convert.ToString((Char)obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof(DateTime)] = delegate (Object obj, JsonWriter writer) {
|
||||
writer.Write(Convert.ToString((DateTime)obj, datetime_format));
|
||||
};
|
||||
base_exporters_table[typeof(TimeSpan)] = delegate (Object obj, JsonWriter writer) {
|
||||
writer.Write(Convert.ToString((TimeSpan)obj, datetime_format));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof(Decimal)] = delegate (Object obj, JsonWriter writer) {
|
||||
writer.Write((Decimal)obj);
|
||||
};
|
||||
|
||||
base_exporters_table[typeof(SByte)] = delegate (Object obj, JsonWriter writer) {
|
||||
writer.Write(Convert.ToInt32((SByte)obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof(Int16)] = delegate (Object obj, JsonWriter writer) {
|
||||
writer.Write(Convert.ToInt32((Int16)obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof(UInt16)] = delegate (Object obj, JsonWriter writer) {
|
||||
writer.Write(Convert.ToInt32((UInt16)obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof(UInt32)] = delegate (Object obj, JsonWriter writer) {
|
||||
writer.Write(Convert.ToUInt64((UInt32)obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof(UInt64)] = delegate (Object obj, JsonWriter writer) {
|
||||
writer.Write((UInt64)obj);
|
||||
};
|
||||
}
|
||||
|
||||
private static void RegisterBaseImporters() {
|
||||
ImporterFunc importer;
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToByte((Int32)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(Int32), typeof(Byte), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToUInt64((Int32)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(Int32), typeof(UInt64), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToSByte((Int32)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(Int32), typeof(SByte), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToInt16((Int32)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(Int32), typeof(Int16), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToUInt16((Int32)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(Int32), typeof(UInt16), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToUInt32((Int32)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(Int32), typeof(UInt32), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToSingle((Int32)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(Int32), typeof(Single), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToDouble((Int32)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(Int32), typeof(Double), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToDecimal((Double)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(Double), typeof(Decimal), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToUInt32((Int64)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(Int64), typeof(UInt32), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToChar((String)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(String), typeof(Char), importer);
|
||||
|
||||
importer = delegate (Object input) {
|
||||
return Convert.ToDateTime((String)input, datetime_format);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(String), typeof(DateTime), importer);
|
||||
}
|
||||
|
||||
private static void RegisterImporter(IDictionary<Type, IDictionary<Type, ImporterFunc>> table, Type json_type, Type value_type, ImporterFunc importer) {
|
||||
if (!table.ContainsKey(json_type)) {
|
||||
table.Add(json_type, new Dictionary<Type, ImporterFunc>());
|
||||
}
|
||||
|
||||
table[json_type][value_type] = importer;
|
||||
}
|
||||
|
||||
private static void WriteValue(Object obj, JsonWriter writer, Boolean writer_is_private, Int32 depth) {
|
||||
if (depth > max_nesting_depth) {
|
||||
throw new JsonException(String.Format("Max allowed object depth reached while trying to export from type {0}", obj.GetType()));
|
||||
}
|
||||
|
||||
if (obj == null) {
|
||||
writer.Write(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is IJsonWrapper) {
|
||||
if (writer_is_private) {
|
||||
writer.TextWriter.Write(((IJsonWrapper)obj).ToJson());
|
||||
} else {
|
||||
((IJsonWrapper)obj).ToJson(writer);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is String) {
|
||||
writer.Write((String)obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Double) {
|
||||
writer.Write((Double)obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Int32) {
|
||||
writer.Write((Int32)obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Boolean) {
|
||||
writer.Write((Boolean)obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Int64) {
|
||||
writer.Write((Int64)obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Array) {
|
||||
writer.WriteArrayStart();
|
||||
|
||||
foreach (Object elem in (Array)obj) {
|
||||
WriteValue(elem, writer, writer_is_private, depth + 1);
|
||||
}
|
||||
|
||||
writer.WriteArrayEnd();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is IList) {
|
||||
writer.WriteArrayStart();
|
||||
foreach (Object elem in (IList)obj) {
|
||||
WriteValue(elem, writer, writer_is_private, depth + 1);
|
||||
}
|
||||
|
||||
writer.WriteArrayEnd();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is IDictionary) {
|
||||
writer.WriteObjectStart();
|
||||
foreach (DictionaryEntry entry in (IDictionary)obj) {
|
||||
writer.WritePropertyName((String)entry.Key);
|
||||
WriteValue(entry.Value, writer, writer_is_private,
|
||||
depth + 1);
|
||||
}
|
||||
writer.WriteObjectEnd();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Type obj_type = obj.GetType();
|
||||
|
||||
// See if there's a custom exporter for the object
|
||||
if (custom_exporters_table.ContainsKey(obj_type)) {
|
||||
ExporterFunc exporter = custom_exporters_table[obj_type];
|
||||
exporter(obj, writer);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If not, maybe there's a base exporter
|
||||
if (base_exporters_table.ContainsKey(obj_type)) {
|
||||
ExporterFunc exporter = base_exporters_table[obj_type];
|
||||
exporter(obj, writer);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Last option, let's see if it's an enum
|
||||
if (obj is Enum) {
|
||||
Type e_type = Enum.GetUnderlyingType(obj_type);
|
||||
|
||||
if (e_type == typeof(Int64)
|
||||
|| e_type == typeof(UInt32)
|
||||
|| e_type == typeof(UInt64)) {
|
||||
writer.Write((UInt64)obj);
|
||||
} else {
|
||||
writer.Write((Int32)obj);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Okay, so it looks like the input should be exported as an
|
||||
// object
|
||||
AddTypeProperties(obj_type);
|
||||
IList<PropertyMetadata> props = type_properties[obj_type];
|
||||
|
||||
writer.WriteObjectStart();
|
||||
foreach (PropertyMetadata p_data in props) {
|
||||
if (p_data.IsField) {
|
||||
writer.WritePropertyName(p_data.Info.Name);
|
||||
WriteValue(((FieldInfo)p_data.Info).GetValue(obj),
|
||||
writer, writer_is_private, depth + 1);
|
||||
} else {
|
||||
PropertyInfo p_info = (PropertyInfo)p_data.Info;
|
||||
|
||||
if (p_info.CanRead) {
|
||||
writer.WritePropertyName(p_data.Info.Name);
|
||||
WriteValue(p_info.GetValue(obj, null),
|
||||
writer, writer_is_private, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.WriteObjectEnd();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public static String ToJson(Object obj) {
|
||||
lock (static_writer_lock) {
|
||||
static_writer.Reset();
|
||||
|
||||
WriteValue(obj, static_writer, true, 0);
|
||||
|
||||
return static_writer.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ToJson(Object obj, JsonWriter writer) {
|
||||
WriteValue(obj, writer, false, 0);
|
||||
}
|
||||
|
||||
public static JsonData ToObject(JsonReader reader) {
|
||||
return (JsonData)ToWrapper(delegate { return new JsonData(); }, reader);
|
||||
}
|
||||
|
||||
public static JsonData ToObject(TextReader reader) {
|
||||
JsonReader json_reader = new JsonReader(reader);
|
||||
|
||||
return (JsonData)ToWrapper(delegate { return new JsonData(); }, json_reader);
|
||||
}
|
||||
|
||||
public static JsonData ToObject(String json) {
|
||||
return (JsonData)ToWrapper(delegate { return new JsonData(); }, json);
|
||||
}
|
||||
|
||||
public static T ToObject<T>(JsonReader reader) {
|
||||
return (T)ReadValue(typeof(T), reader);
|
||||
}
|
||||
|
||||
public static T ToObject<T>(TextReader reader) {
|
||||
JsonReader json_reader = new JsonReader(reader);
|
||||
|
||||
return (T)ReadValue(typeof(T), json_reader);
|
||||
}
|
||||
|
||||
public static T ToObject<T>(String json) {
|
||||
JsonReader reader = new JsonReader(json);
|
||||
|
||||
return (T)ReadValue(typeof(T), reader);
|
||||
}
|
||||
|
||||
public static Object ToObject(String json, Type ConvertType) {
|
||||
JsonReader reader = new JsonReader(json);
|
||||
|
||||
return ReadValue(ConvertType, reader);
|
||||
}
|
||||
|
||||
public static IJsonWrapper ToWrapper(WrapperFactory factory, JsonReader reader) {
|
||||
return ReadValue(factory, reader);
|
||||
}
|
||||
|
||||
public static IJsonWrapper ToWrapper(WrapperFactory factory, String json) {
|
||||
JsonReader reader = new JsonReader(json);
|
||||
|
||||
return ReadValue(factory, reader);
|
||||
}
|
||||
|
||||
public static void RegisterExporter<T>(ExporterFunc<T> exporter) {
|
||||
ExporterFunc exporter_wrapper = delegate (Object obj, JsonWriter writer) {
|
||||
exporter((T)obj, writer);
|
||||
};
|
||||
|
||||
custom_exporters_table[typeof(T)] = exporter_wrapper;
|
||||
}
|
||||
|
||||
public static void RegisterImporter<TJson, TValue>(ImporterFunc<TJson, TValue> importer) {
|
||||
ImporterFunc importer_wrapper = delegate (Object input) {
|
||||
return importer((TJson)input);
|
||||
};
|
||||
|
||||
RegisterImporter(custom_importers_table, typeof(TJson), typeof(TValue), importer_wrapper);
|
||||
}
|
||||
|
||||
public static void UnregisterExporters() {
|
||||
custom_exporters_table.Clear();
|
||||
}
|
||||
|
||||
public static void UnregisterImporters() {
|
||||
custom_importers_table.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
#region Header
|
||||
/**
|
||||
* JsonMockWrapper.cs
|
||||
* Mock object implementing IJsonWrapper, to facilitate actions like
|
||||
* skipping data more efficiently.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
|
||||
namespace LitJson
|
||||
{
|
||||
public class JsonMockWrapper : IJsonWrapper
|
||||
{
|
||||
public bool IsArray { get { return false; } }
|
||||
public bool IsBoolean { get { return false; } }
|
||||
public bool IsDouble { get { return false; } }
|
||||
public bool IsInt { get { return false; } }
|
||||
public bool IsLong { get { return false; } }
|
||||
public bool IsObject { get { return false; } }
|
||||
public bool IsString { get { return false; } }
|
||||
|
||||
public bool GetBoolean () { return false; }
|
||||
public double GetDouble () { return 0.0; }
|
||||
public int GetInt () { return 0; }
|
||||
public JsonType GetJsonType () { return JsonType.None; }
|
||||
public long GetLong () { return 0L; }
|
||||
public string GetString () { return ""; }
|
||||
|
||||
public void SetBoolean (bool val) {}
|
||||
public void SetDouble (double val) {}
|
||||
public void SetInt (int val) {}
|
||||
public void SetJsonType (JsonType type) {}
|
||||
public void SetLong (long val) {}
|
||||
public void SetString (string val) {}
|
||||
|
||||
public string ToJson () { return ""; }
|
||||
public void ToJson (JsonWriter writer) {}
|
||||
|
||||
|
||||
bool IList.IsFixedSize { get { return true; } }
|
||||
bool IList.IsReadOnly { get { return true; } }
|
||||
|
||||
object IList.this[int index] {
|
||||
get { return null; }
|
||||
set {}
|
||||
}
|
||||
|
||||
int IList.Add (object value) { return 0; }
|
||||
void IList.Clear () {}
|
||||
bool IList.Contains (object value) { return false; }
|
||||
int IList.IndexOf (object value) { return -1; }
|
||||
void IList.Insert (int i, object v) {}
|
||||
void IList.Remove (object value) {}
|
||||
void IList.RemoveAt (int index) {}
|
||||
|
||||
|
||||
int ICollection.Count { get { return 0; } }
|
||||
bool ICollection.IsSynchronized { get { return false; } }
|
||||
object ICollection.SyncRoot { get { return null; } }
|
||||
|
||||
void ICollection.CopyTo (Array array, int index) {}
|
||||
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator () { return null; }
|
||||
|
||||
|
||||
bool IDictionary.IsFixedSize { get { return true; } }
|
||||
bool IDictionary.IsReadOnly { get { return true; } }
|
||||
|
||||
ICollection IDictionary.Keys { get { return null; } }
|
||||
ICollection IDictionary.Values { get { return null; } }
|
||||
|
||||
object IDictionary.this[object key] {
|
||||
get { return null; }
|
||||
set {}
|
||||
}
|
||||
|
||||
void IDictionary.Add (object k, object v) {}
|
||||
void IDictionary.Clear () {}
|
||||
bool IDictionary.Contains (object key) { return false; }
|
||||
void IDictionary.Remove (object key) {}
|
||||
|
||||
IDictionaryEnumerator IDictionary.GetEnumerator () { return null; }
|
||||
|
||||
|
||||
object IOrderedDictionary.this[int idx] {
|
||||
get { return null; }
|
||||
set {}
|
||||
}
|
||||
|
||||
IDictionaryEnumerator IOrderedDictionary.GetEnumerator () {
|
||||
return null;
|
||||
}
|
||||
void IOrderedDictionary.Insert (int i, object k, object v) {}
|
||||
void IOrderedDictionary.RemoveAt (int i) {}
|
||||
}
|
||||
}
|
||||
@@ -1,478 +0,0 @@
|
||||
#region Header
|
||||
/**
|
||||
* JsonReader.cs
|
||||
* Stream-like access to JSON text.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace LitJson
|
||||
{
|
||||
public enum JsonToken
|
||||
{
|
||||
None,
|
||||
|
||||
ObjectStart,
|
||||
PropertyName,
|
||||
ObjectEnd,
|
||||
|
||||
ArrayStart,
|
||||
ArrayEnd,
|
||||
|
||||
Int,
|
||||
Long,
|
||||
Double,
|
||||
|
||||
String,
|
||||
|
||||
Boolean,
|
||||
Null
|
||||
}
|
||||
|
||||
|
||||
public class JsonReader
|
||||
{
|
||||
#region Fields
|
||||
private static readonly IDictionary<int, IDictionary<int, int[]>> parse_table;
|
||||
|
||||
private Stack<int> automaton_stack;
|
||||
private int current_input;
|
||||
private int current_symbol;
|
||||
private bool end_of_json;
|
||||
private bool end_of_input;
|
||||
private Lexer lexer;
|
||||
private bool parser_in_string;
|
||||
private bool parser_return;
|
||||
private bool read_started;
|
||||
private TextReader reader;
|
||||
private bool reader_is_owned;
|
||||
private bool skip_non_members;
|
||||
private object token_value;
|
||||
private JsonToken token;
|
||||
#endregion
|
||||
|
||||
|
||||
#region Public Properties
|
||||
public bool AllowComments {
|
||||
get { return lexer.AllowComments; }
|
||||
set { lexer.AllowComments = value; }
|
||||
}
|
||||
|
||||
public bool AllowSingleQuotedStrings {
|
||||
get { return lexer.AllowSingleQuotedStrings; }
|
||||
set { lexer.AllowSingleQuotedStrings = value; }
|
||||
}
|
||||
|
||||
public bool SkipNonMembers {
|
||||
get { return skip_non_members; }
|
||||
set { skip_non_members = value; }
|
||||
}
|
||||
|
||||
public bool EndOfInput {
|
||||
get { return end_of_input; }
|
||||
}
|
||||
|
||||
public bool EndOfJson {
|
||||
get { return end_of_json; }
|
||||
}
|
||||
|
||||
public JsonToken Token {
|
||||
get { return token; }
|
||||
}
|
||||
|
||||
public object Value {
|
||||
get { return token_value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructors
|
||||
static JsonReader ()
|
||||
{
|
||||
parse_table = PopulateParseTable ();
|
||||
}
|
||||
|
||||
public JsonReader (string json_text) :
|
||||
this (new StringReader (json_text), true)
|
||||
{
|
||||
}
|
||||
|
||||
public JsonReader (TextReader reader) :
|
||||
this (reader, false)
|
||||
{
|
||||
}
|
||||
|
||||
private JsonReader (TextReader reader, bool owned)
|
||||
{
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException ("reader");
|
||||
|
||||
parser_in_string = false;
|
||||
parser_return = false;
|
||||
|
||||
read_started = false;
|
||||
automaton_stack = new Stack<int> ();
|
||||
automaton_stack.Push ((int) ParserToken.End);
|
||||
automaton_stack.Push ((int) ParserToken.Text);
|
||||
|
||||
lexer = new Lexer (reader);
|
||||
|
||||
end_of_input = false;
|
||||
end_of_json = false;
|
||||
|
||||
skip_non_members = true;
|
||||
|
||||
this.reader = reader;
|
||||
reader_is_owned = owned;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Static Methods
|
||||
private static IDictionary<int, IDictionary<int, int[]>> PopulateParseTable ()
|
||||
{
|
||||
// See section A.2. of the manual for details
|
||||
IDictionary<int, IDictionary<int, int[]>> parse_table = new Dictionary<int, IDictionary<int, int[]>> ();
|
||||
|
||||
TableAddRow (parse_table, ParserToken.Array);
|
||||
TableAddCol (parse_table, ParserToken.Array, '[',
|
||||
'[',
|
||||
(int) ParserToken.ArrayPrime);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.ArrayPrime);
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, '"',
|
||||
(int) ParserToken.Value,
|
||||
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, '[',
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, ']',
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, '{',
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Number,
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.True,
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.False,
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Null,
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
|
||||
TableAddRow (parse_table, ParserToken.Object);
|
||||
TableAddCol (parse_table, ParserToken.Object, '{',
|
||||
'{',
|
||||
(int) ParserToken.ObjectPrime);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.ObjectPrime);
|
||||
TableAddCol (parse_table, ParserToken.ObjectPrime, '"',
|
||||
(int) ParserToken.Pair,
|
||||
(int) ParserToken.PairRest,
|
||||
'}');
|
||||
TableAddCol (parse_table, ParserToken.ObjectPrime, '}',
|
||||
'}');
|
||||
|
||||
TableAddRow (parse_table, ParserToken.Pair);
|
||||
TableAddCol (parse_table, ParserToken.Pair, '"',
|
||||
(int) ParserToken.String,
|
||||
':',
|
||||
(int) ParserToken.Value);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.PairRest);
|
||||
TableAddCol (parse_table, ParserToken.PairRest, ',',
|
||||
',',
|
||||
(int) ParserToken.Pair,
|
||||
(int) ParserToken.PairRest);
|
||||
TableAddCol (parse_table, ParserToken.PairRest, '}',
|
||||
(int) ParserToken.Epsilon);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.String);
|
||||
TableAddCol (parse_table, ParserToken.String, '"',
|
||||
'"',
|
||||
(int) ParserToken.CharSeq,
|
||||
'"');
|
||||
|
||||
TableAddRow (parse_table, ParserToken.Text);
|
||||
TableAddCol (parse_table, ParserToken.Text, '[',
|
||||
(int) ParserToken.Array);
|
||||
TableAddCol (parse_table, ParserToken.Text, '{',
|
||||
(int) ParserToken.Object);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.Value);
|
||||
TableAddCol (parse_table, ParserToken.Value, '"',
|
||||
(int) ParserToken.String);
|
||||
TableAddCol (parse_table, ParserToken.Value, '[',
|
||||
(int) ParserToken.Array);
|
||||
TableAddCol (parse_table, ParserToken.Value, '{',
|
||||
(int) ParserToken.Object);
|
||||
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Number,
|
||||
(int) ParserToken.Number);
|
||||
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.True,
|
||||
(int) ParserToken.True);
|
||||
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.False,
|
||||
(int) ParserToken.False);
|
||||
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Null,
|
||||
(int) ParserToken.Null);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.ValueRest);
|
||||
TableAddCol (parse_table, ParserToken.ValueRest, ',',
|
||||
',',
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest);
|
||||
TableAddCol (parse_table, ParserToken.ValueRest, ']',
|
||||
(int) ParserToken.Epsilon);
|
||||
|
||||
return parse_table;
|
||||
}
|
||||
|
||||
private static void TableAddCol (IDictionary<int, IDictionary<int, int[]>> parse_table, ParserToken row, int col,
|
||||
params int[] symbols)
|
||||
{
|
||||
parse_table[(int) row].Add (col, symbols);
|
||||
}
|
||||
|
||||
private static void TableAddRow (IDictionary<int, IDictionary<int, int[]>> parse_table, ParserToken rule)
|
||||
{
|
||||
parse_table.Add ((int) rule, new Dictionary<int, int[]> ());
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Private Methods
|
||||
private void ProcessNumber (string number)
|
||||
{
|
||||
if (number.IndexOf ('.') != -1 ||
|
||||
number.IndexOf ('e') != -1 ||
|
||||
number.IndexOf ('E') != -1) {
|
||||
|
||||
double n_double;
|
||||
if (double.TryParse (number, NumberStyles.Any, CultureInfo.InvariantCulture, out n_double)) {
|
||||
token = JsonToken.Double;
|
||||
token_value = n_double;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int n_int32;
|
||||
if (int.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int32)) {
|
||||
token = JsonToken.Int;
|
||||
token_value = n_int32;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
long n_int64;
|
||||
if (long.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int64)) {
|
||||
token = JsonToken.Long;
|
||||
token_value = n_int64;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ulong n_uint64;
|
||||
if (ulong.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_uint64))
|
||||
{
|
||||
token = JsonToken.Long;
|
||||
token_value = n_uint64;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Shouldn't happen, but just in case, return something
|
||||
token = JsonToken.Int;
|
||||
token_value = 0;
|
||||
}
|
||||
|
||||
private void ProcessSymbol ()
|
||||
{
|
||||
if (current_symbol == '[') {
|
||||
token = JsonToken.ArrayStart;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == ']') {
|
||||
token = JsonToken.ArrayEnd;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == '{') {
|
||||
token = JsonToken.ObjectStart;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == '}') {
|
||||
token = JsonToken.ObjectEnd;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == '"') {
|
||||
if (parser_in_string) {
|
||||
parser_in_string = false;
|
||||
|
||||
parser_return = true;
|
||||
|
||||
} else {
|
||||
if (token == JsonToken.None)
|
||||
token = JsonToken.String;
|
||||
|
||||
parser_in_string = true;
|
||||
}
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.CharSeq) {
|
||||
token_value = lexer.StringValue;
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.False) {
|
||||
token = JsonToken.Boolean;
|
||||
token_value = false;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.Null) {
|
||||
token = JsonToken.Null;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.Number) {
|
||||
ProcessNumber (lexer.StringValue);
|
||||
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.Pair) {
|
||||
token = JsonToken.PropertyName;
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.True) {
|
||||
token = JsonToken.Boolean;
|
||||
token_value = true;
|
||||
parser_return = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private bool ReadToken ()
|
||||
{
|
||||
if (end_of_input)
|
||||
return false;
|
||||
|
||||
lexer.NextToken ();
|
||||
|
||||
if (lexer.EndOfInput) {
|
||||
Close ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
current_input = lexer.Token;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public void Close ()
|
||||
{
|
||||
if (end_of_input)
|
||||
return;
|
||||
|
||||
end_of_input = true;
|
||||
end_of_json = true;
|
||||
|
||||
if (reader_is_owned)
|
||||
{
|
||||
using(reader){}
|
||||
}
|
||||
|
||||
reader = null;
|
||||
}
|
||||
|
||||
public bool Read ()
|
||||
{
|
||||
if (end_of_input)
|
||||
return false;
|
||||
|
||||
if (end_of_json) {
|
||||
end_of_json = false;
|
||||
automaton_stack.Clear ();
|
||||
automaton_stack.Push ((int) ParserToken.End);
|
||||
automaton_stack.Push ((int) ParserToken.Text);
|
||||
}
|
||||
|
||||
parser_in_string = false;
|
||||
parser_return = false;
|
||||
|
||||
token = JsonToken.None;
|
||||
token_value = null;
|
||||
|
||||
if (! read_started) {
|
||||
read_started = true;
|
||||
|
||||
if (! ReadToken ())
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int[] entry_symbols;
|
||||
|
||||
while (true) {
|
||||
if (parser_return) {
|
||||
if (automaton_stack.Peek () == (int) ParserToken.End)
|
||||
end_of_json = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
current_symbol = automaton_stack.Pop ();
|
||||
|
||||
ProcessSymbol ();
|
||||
|
||||
if (current_symbol == current_input) {
|
||||
if (! ReadToken ()) {
|
||||
if (automaton_stack.Peek () != (int) ParserToken.End)
|
||||
throw new JsonException (
|
||||
"Input doesn't evaluate to proper JSON text");
|
||||
|
||||
if (parser_return)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
entry_symbols =
|
||||
parse_table[current_symbol][current_input];
|
||||
|
||||
} catch (KeyNotFoundException e) {
|
||||
throw new JsonException ((ParserToken) current_input, e);
|
||||
}
|
||||
|
||||
if (entry_symbols[0] == (int) ParserToken.Epsilon)
|
||||
continue;
|
||||
|
||||
for (int i = entry_symbols.Length - 1; i >= 0; i--)
|
||||
automaton_stack.Push (entry_symbols[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,465 +0,0 @@
|
||||
#region Header
|
||||
/**
|
||||
* JsonWriter.cs
|
||||
* Stream-like facility to output JSON text.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace LitJson
|
||||
{
|
||||
internal enum Condition
|
||||
{
|
||||
InArray,
|
||||
InObject,
|
||||
NotAProperty,
|
||||
Property,
|
||||
Value
|
||||
}
|
||||
|
||||
internal class WriterContext
|
||||
{
|
||||
public Int32 Count;
|
||||
public Boolean InArray;
|
||||
public Boolean InObject;
|
||||
public Boolean ExpectingValue;
|
||||
public Int32 Padding;
|
||||
}
|
||||
|
||||
public class JsonWriter {
|
||||
#region Fields
|
||||
private static readonly NumberFormatInfo number_format;
|
||||
|
||||
private WriterContext context;
|
||||
private Stack<WriterContext> ctx_stack;
|
||||
private Boolean has_reached_end;
|
||||
private Char[] hex_seq;
|
||||
private Int32 indentation;
|
||||
private Int32 indent_value;
|
||||
private StringBuilder inst_string_builder;
|
||||
private Boolean pretty_print;
|
||||
private Boolean validate;
|
||||
private Boolean lower_case_properties;
|
||||
private TextWriter writer;
|
||||
#endregion
|
||||
|
||||
|
||||
#region Properties
|
||||
public Int32 IndentValue {
|
||||
get { return this.indent_value; }
|
||||
set {
|
||||
this.indentation = (this.indentation / this.indent_value) * value;
|
||||
this.indent_value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean PrettyPrint {
|
||||
get { return this.pretty_print; }
|
||||
set { this.pretty_print = value; }
|
||||
}
|
||||
|
||||
public TextWriter TextWriter {
|
||||
get { return this.writer; }
|
||||
}
|
||||
|
||||
public Boolean Validate {
|
||||
get { return this.validate; }
|
||||
set { this.validate = value; }
|
||||
}
|
||||
|
||||
public Boolean LowerCaseProperties {
|
||||
get { return this.lower_case_properties; }
|
||||
set { this.lower_case_properties = value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructors
|
||||
static JsonWriter() {
|
||||
number_format = NumberFormatInfo.InvariantInfo;
|
||||
}
|
||||
|
||||
public JsonWriter() {
|
||||
this.inst_string_builder = new StringBuilder();
|
||||
this.writer = new StringWriter(this.inst_string_builder);
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
public JsonWriter(StringBuilder sb) :
|
||||
this(new StringWriter(sb)) {
|
||||
}
|
||||
|
||||
public JsonWriter(TextWriter writer) {
|
||||
this.writer = writer ?? throw new ArgumentNullException("writer");
|
||||
|
||||
Init();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Private Methods
|
||||
private void DoValidation(Condition cond) {
|
||||
if (!this.context.ExpectingValue) {
|
||||
this.context.Count++;
|
||||
}
|
||||
|
||||
if (!this.validate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.has_reached_end) {
|
||||
throw new JsonException(
|
||||
"A complete JSON symbol has already been written");
|
||||
}
|
||||
|
||||
switch (cond) {
|
||||
case Condition.InArray:
|
||||
if (!this.context.InArray) {
|
||||
throw new JsonException(
|
||||
"Can't close an array here");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Condition.InObject:
|
||||
if (!this.context.InObject || this.context.ExpectingValue) {
|
||||
throw new JsonException(
|
||||
"Can't close an object here");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Condition.NotAProperty:
|
||||
if (this.context.InObject && !this.context.ExpectingValue) {
|
||||
throw new JsonException(
|
||||
"Expected a property");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Condition.Property:
|
||||
if (!this.context.InObject || this.context.ExpectingValue) {
|
||||
throw new JsonException(
|
||||
"Can't add a property here");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Condition.Value:
|
||||
if (!this.context.InArray &&
|
||||
(!this.context.InObject || !this.context.ExpectingValue)) {
|
||||
throw new JsonException(
|
||||
"Can't add a value here");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Init() {
|
||||
this.has_reached_end = false;
|
||||
this.hex_seq = new Char[4];
|
||||
this.indentation = 0;
|
||||
this.indent_value = 4;
|
||||
this.pretty_print = false;
|
||||
this.validate = true;
|
||||
this.lower_case_properties = false;
|
||||
|
||||
this.ctx_stack = new Stack<WriterContext>();
|
||||
this.context = new WriterContext();
|
||||
this.ctx_stack.Push(this.context);
|
||||
}
|
||||
|
||||
private static void IntToHex(Int32 n, Char[] hex) {
|
||||
Int32 num;
|
||||
|
||||
for (Int32 i = 0; i < 4; i++) {
|
||||
num = n % 16;
|
||||
|
||||
if (num < 10) {
|
||||
hex[3 - i] = (Char)('0' + num);
|
||||
} else {
|
||||
hex[3 - i] = (Char)('A' + (num - 10));
|
||||
}
|
||||
|
||||
n >>= 4;
|
||||
}
|
||||
}
|
||||
|
||||
private void Indent() {
|
||||
if (this.pretty_print) {
|
||||
this.indentation += this.indent_value;
|
||||
}
|
||||
}
|
||||
|
||||
private void Put(String str) {
|
||||
if (this.pretty_print && !this.context.ExpectingValue) {
|
||||
for (Int32 i = 0; i < this.indentation; i++) {
|
||||
this.writer.Write(' ');
|
||||
}
|
||||
}
|
||||
|
||||
this.writer.Write(str);
|
||||
}
|
||||
|
||||
private void PutNewline() {
|
||||
PutNewline(true);
|
||||
}
|
||||
|
||||
private void PutNewline(Boolean add_comma) {
|
||||
if (add_comma && !this.context.ExpectingValue &&
|
||||
this.context.Count > 1) {
|
||||
this.writer.Write(',');
|
||||
}
|
||||
|
||||
if (this.pretty_print && !this.context.ExpectingValue) {
|
||||
this.writer.Write(Environment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
private void PutString(String str) {
|
||||
Put(String.Empty);
|
||||
|
||||
this.writer.Write('"');
|
||||
|
||||
Int32 n = str.Length;
|
||||
for (Int32 i = 0; i < n; i++) {
|
||||
switch (str[i]) {
|
||||
case '\n':
|
||||
this.writer.Write("\\n");
|
||||
continue;
|
||||
|
||||
case '\r':
|
||||
this.writer.Write("\\r");
|
||||
continue;
|
||||
|
||||
case '\t':
|
||||
this.writer.Write("\\t");
|
||||
continue;
|
||||
|
||||
case '"':
|
||||
case '\\':
|
||||
this.writer.Write('\\');
|
||||
this.writer.Write(str[i]);
|
||||
continue;
|
||||
|
||||
case '\f':
|
||||
this.writer.Write("\\f");
|
||||
continue;
|
||||
|
||||
case '\b':
|
||||
this.writer.Write("\\b");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str[i] >= 32 && str[i] <= 126) {
|
||||
this.writer.Write(str[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Default, turn into a \uXXXX sequence
|
||||
IntToHex(str[i], this.hex_seq);
|
||||
this.writer.Write("\\u");
|
||||
this.writer.Write(this.hex_seq);
|
||||
}
|
||||
|
||||
this.writer.Write('"');
|
||||
}
|
||||
|
||||
private void Unindent() {
|
||||
if (this.pretty_print) {
|
||||
this.indentation -= this.indent_value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public override String ToString() {
|
||||
if (this.inst_string_builder == null) {
|
||||
return String.Empty;
|
||||
}
|
||||
return this.inst_string_builder.ToString();
|
||||
}
|
||||
|
||||
public void Reset() {
|
||||
this.has_reached_end = false;
|
||||
|
||||
this.ctx_stack.Clear();
|
||||
this.context = new WriterContext();
|
||||
this.ctx_stack.Push(this.context);
|
||||
|
||||
if (this.inst_string_builder != null) {
|
||||
this.inst_string_builder.Remove(0, this.inst_string_builder.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(Boolean boolean) {
|
||||
DoValidation(Condition.Value);
|
||||
PutNewline();
|
||||
|
||||
Put(boolean ? "true" : "false");
|
||||
|
||||
this.context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write(Decimal number) {
|
||||
DoValidation(Condition.Value);
|
||||
PutNewline();
|
||||
|
||||
Put(Convert.ToString(number, number_format));
|
||||
|
||||
this.context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write(Double number) {
|
||||
DoValidation(Condition.Value);
|
||||
PutNewline();
|
||||
|
||||
String str = Convert.ToString(number, number_format);
|
||||
Put(str);
|
||||
|
||||
if (str.IndexOf('.') == -1 && str.IndexOf('E') == -1) {
|
||||
this.writer.Write(".0");
|
||||
}
|
||||
|
||||
this.context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write(Int32 number) {
|
||||
DoValidation(Condition.Value);
|
||||
PutNewline();
|
||||
|
||||
Put(Convert.ToString(number, number_format));
|
||||
|
||||
this.context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write(Int64 number) {
|
||||
DoValidation(Condition.Value);
|
||||
PutNewline();
|
||||
|
||||
Put(Convert.ToString(number, number_format));
|
||||
|
||||
this.context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write(String str) {
|
||||
DoValidation(Condition.Value);
|
||||
PutNewline();
|
||||
|
||||
if (str == null) {
|
||||
Put("null");
|
||||
} else {
|
||||
PutString(str);
|
||||
}
|
||||
|
||||
this.context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
// [CLSCompliant(false)]
|
||||
public void Write(UInt64 number) {
|
||||
DoValidation(Condition.Value);
|
||||
PutNewline();
|
||||
|
||||
Put(Convert.ToString(number, number_format));
|
||||
|
||||
this.context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void WriteArrayEnd() {
|
||||
DoValidation(Condition.InArray);
|
||||
PutNewline(false);
|
||||
|
||||
this.ctx_stack.Pop();
|
||||
if (this.ctx_stack.Count == 1) {
|
||||
this.has_reached_end = true;
|
||||
} else {
|
||||
this.context = this.ctx_stack.Peek();
|
||||
this.context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
Unindent();
|
||||
Put("]");
|
||||
}
|
||||
|
||||
public void WriteArrayStart() {
|
||||
DoValidation(Condition.NotAProperty);
|
||||
PutNewline();
|
||||
|
||||
Put("[");
|
||||
|
||||
this.context = new WriterContext {
|
||||
InArray = true
|
||||
};
|
||||
this.ctx_stack.Push(this.context);
|
||||
|
||||
Indent();
|
||||
}
|
||||
|
||||
public void WriteObjectEnd() {
|
||||
DoValidation(Condition.InObject);
|
||||
PutNewline(false);
|
||||
|
||||
this.ctx_stack.Pop();
|
||||
if (this.ctx_stack.Count == 1) {
|
||||
this.has_reached_end = true;
|
||||
} else {
|
||||
this.context = this.ctx_stack.Peek();
|
||||
this.context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
Unindent();
|
||||
Put("}");
|
||||
}
|
||||
|
||||
public void WriteObjectStart() {
|
||||
DoValidation(Condition.NotAProperty);
|
||||
PutNewline();
|
||||
|
||||
Put("{");
|
||||
|
||||
this.context = new WriterContext {
|
||||
InObject = true
|
||||
};
|
||||
this.ctx_stack.Push(this.context);
|
||||
|
||||
Indent();
|
||||
}
|
||||
|
||||
public void WritePropertyName(String property_name) {
|
||||
DoValidation(Condition.Property);
|
||||
PutNewline();
|
||||
String propertyName = (property_name == null || !this.lower_case_properties) ? property_name : property_name.ToLowerInvariant();
|
||||
|
||||
PutString(propertyName);
|
||||
|
||||
if (this.pretty_print) {
|
||||
if (propertyName.Length > this.context.Padding) {
|
||||
this.context.Padding = propertyName.Length;
|
||||
}
|
||||
|
||||
for (Int32 i = this.context.Padding - propertyName.Length;
|
||||
i >= 0; i--) {
|
||||
this.writer.Write(' ');
|
||||
}
|
||||
|
||||
this.writer.Write(": ");
|
||||
} else {
|
||||
this.writer.Write(':');
|
||||
}
|
||||
|
||||
this.context.ExpectingValue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,912 +0,0 @@
|
||||
#region Header
|
||||
/**
|
||||
* Lexer.cs
|
||||
* JSON lexer implementation based on a finite state machine.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace LitJson
|
||||
{
|
||||
internal class FsmContext
|
||||
{
|
||||
public bool Return;
|
||||
public int NextState;
|
||||
public Lexer L;
|
||||
public int StateStack;
|
||||
}
|
||||
|
||||
|
||||
internal class Lexer
|
||||
{
|
||||
#region Fields
|
||||
private delegate bool StateHandler (FsmContext ctx);
|
||||
|
||||
private static readonly int[] fsm_return_table;
|
||||
private static readonly StateHandler[] fsm_handler_table;
|
||||
|
||||
private bool allow_comments;
|
||||
private bool allow_single_quoted_strings;
|
||||
private bool end_of_input;
|
||||
private FsmContext fsm_context;
|
||||
private int input_buffer;
|
||||
private int input_char;
|
||||
private TextReader reader;
|
||||
private int state;
|
||||
private StringBuilder string_buffer;
|
||||
private string string_value;
|
||||
private int token;
|
||||
private int unichar;
|
||||
#endregion
|
||||
|
||||
|
||||
#region Properties
|
||||
public bool AllowComments {
|
||||
get { return allow_comments; }
|
||||
set { allow_comments = value; }
|
||||
}
|
||||
|
||||
public bool AllowSingleQuotedStrings {
|
||||
get { return allow_single_quoted_strings; }
|
||||
set { allow_single_quoted_strings = value; }
|
||||
}
|
||||
|
||||
public bool EndOfInput {
|
||||
get { return end_of_input; }
|
||||
}
|
||||
|
||||
public int Token {
|
||||
get { return token; }
|
||||
}
|
||||
|
||||
public string StringValue {
|
||||
get { return string_value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructors
|
||||
static Lexer ()
|
||||
{
|
||||
PopulateFsmTables (out fsm_handler_table, out fsm_return_table);
|
||||
}
|
||||
|
||||
public Lexer (TextReader reader)
|
||||
{
|
||||
allow_comments = true;
|
||||
allow_single_quoted_strings = true;
|
||||
|
||||
input_buffer = 0;
|
||||
string_buffer = new StringBuilder (128);
|
||||
state = 1;
|
||||
end_of_input = false;
|
||||
this.reader = reader;
|
||||
|
||||
fsm_context = new FsmContext ();
|
||||
fsm_context.L = this;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Static Methods
|
||||
private static int HexValue (int digit)
|
||||
{
|
||||
switch (digit) {
|
||||
case 'a':
|
||||
case 'A':
|
||||
return 10;
|
||||
|
||||
case 'b':
|
||||
case 'B':
|
||||
return 11;
|
||||
|
||||
case 'c':
|
||||
case 'C':
|
||||
return 12;
|
||||
|
||||
case 'd':
|
||||
case 'D':
|
||||
return 13;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
return 14;
|
||||
|
||||
case 'f':
|
||||
case 'F':
|
||||
return 15;
|
||||
|
||||
default:
|
||||
return digit - '0';
|
||||
}
|
||||
}
|
||||
|
||||
private static void PopulateFsmTables (out StateHandler[] fsm_handler_table, out int[] fsm_return_table)
|
||||
{
|
||||
// See section A.1. of the manual for details of the finite
|
||||
// state machine.
|
||||
fsm_handler_table = new StateHandler[28] {
|
||||
State1,
|
||||
State2,
|
||||
State3,
|
||||
State4,
|
||||
State5,
|
||||
State6,
|
||||
State7,
|
||||
State8,
|
||||
State9,
|
||||
State10,
|
||||
State11,
|
||||
State12,
|
||||
State13,
|
||||
State14,
|
||||
State15,
|
||||
State16,
|
||||
State17,
|
||||
State18,
|
||||
State19,
|
||||
State20,
|
||||
State21,
|
||||
State22,
|
||||
State23,
|
||||
State24,
|
||||
State25,
|
||||
State26,
|
||||
State27,
|
||||
State28
|
||||
};
|
||||
|
||||
fsm_return_table = new int[28] {
|
||||
(int) ParserToken.Char,
|
||||
0,
|
||||
(int) ParserToken.Number,
|
||||
(int) ParserToken.Number,
|
||||
0,
|
||||
(int) ParserToken.Number,
|
||||
0,
|
||||
(int) ParserToken.Number,
|
||||
0,
|
||||
0,
|
||||
(int) ParserToken.True,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
(int) ParserToken.False,
|
||||
0,
|
||||
0,
|
||||
(int) ParserToken.Null,
|
||||
(int) ParserToken.CharSeq,
|
||||
(int) ParserToken.Char,
|
||||
0,
|
||||
0,
|
||||
(int) ParserToken.CharSeq,
|
||||
(int) ParserToken.Char,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
private static char ProcessEscChar (int esc_char)
|
||||
{
|
||||
switch (esc_char) {
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
return Convert.ToChar (esc_char);
|
||||
|
||||
case 'n':
|
||||
return '\n';
|
||||
|
||||
case 't':
|
||||
return '\t';
|
||||
|
||||
case 'r':
|
||||
return '\r';
|
||||
|
||||
case 'b':
|
||||
return '\b';
|
||||
|
||||
case 'f':
|
||||
return '\f';
|
||||
|
||||
default:
|
||||
// Unreachable
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State1 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char == ' ' ||
|
||||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r')
|
||||
continue;
|
||||
|
||||
if (ctx.L.input_char >= '1' && ctx.L.input_char <= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '"':
|
||||
ctx.NextState = 19;
|
||||
ctx.Return = true;
|
||||
return true;
|
||||
|
||||
case ',':
|
||||
case ':':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
ctx.NextState = 1;
|
||||
ctx.Return = true;
|
||||
return true;
|
||||
|
||||
case '-':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 2;
|
||||
return true;
|
||||
|
||||
case '0':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 4;
|
||||
return true;
|
||||
|
||||
case 'f':
|
||||
ctx.NextState = 12;
|
||||
return true;
|
||||
|
||||
case 'n':
|
||||
ctx.NextState = 16;
|
||||
return true;
|
||||
|
||||
case 't':
|
||||
ctx.NextState = 9;
|
||||
return true;
|
||||
|
||||
case '\'':
|
||||
if (! ctx.L.allow_single_quoted_strings)
|
||||
return false;
|
||||
|
||||
ctx.L.input_char = '"';
|
||||
ctx.NextState = 23;
|
||||
ctx.Return = true;
|
||||
return true;
|
||||
|
||||
case '/':
|
||||
if (! ctx.L.allow_comments)
|
||||
return false;
|
||||
|
||||
ctx.NextState = 25;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State2 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
if (ctx.L.input_char >= '1' && ctx.L.input_char<= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '0':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 4;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State3 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx.L.input_char == ' ' ||
|
||||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') {
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case ',':
|
||||
case ']':
|
||||
case '}':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
case '.':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 5;
|
||||
return true;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 7;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State4 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
if (ctx.L.input_char == ' ' ||
|
||||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') {
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case ',':
|
||||
case ']':
|
||||
case '}':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
case '.':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 5;
|
||||
return true;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 7;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State5 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 6;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State6 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx.L.input_char == ' ' ||
|
||||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') {
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case ',':
|
||||
case ']':
|
||||
case '}':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 7;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State7 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '+':
|
||||
case '-':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 8;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State8 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx.L.input_char == ' ' ||
|
||||
ctx.L.input_char >= '\t' && ctx.L.input_char<= '\r') {
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case ',':
|
||||
case ']':
|
||||
case '}':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State9 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'r':
|
||||
ctx.NextState = 10;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State10 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'u':
|
||||
ctx.NextState = 11;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State11 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'e':
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State12 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'a':
|
||||
ctx.NextState = 13;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State13 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'l':
|
||||
ctx.NextState = 14;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State14 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 's':
|
||||
ctx.NextState = 15;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State15 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'e':
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State16 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'u':
|
||||
ctx.NextState = 17;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State17 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'l':
|
||||
ctx.NextState = 18;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State18 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'l':
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State19 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
switch (ctx.L.input_char) {
|
||||
case '"':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 20;
|
||||
return true;
|
||||
|
||||
case '\\':
|
||||
ctx.StateStack = 19;
|
||||
ctx.NextState = 21;
|
||||
return true;
|
||||
|
||||
default:
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State20 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '"':
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State21 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'u':
|
||||
ctx.NextState = 22;
|
||||
return true;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
case '/':
|
||||
case '\\':
|
||||
case 'b':
|
||||
case 'f':
|
||||
case 'n':
|
||||
case 'r':
|
||||
case 't':
|
||||
ctx.L.string_buffer.Append (
|
||||
ProcessEscChar (ctx.L.input_char));
|
||||
ctx.NextState = ctx.StateStack;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State22 (FsmContext ctx)
|
||||
{
|
||||
int counter = 0;
|
||||
int mult = 4096;
|
||||
|
||||
ctx.L.unichar = 0;
|
||||
|
||||
while (ctx.L.GetChar ()) {
|
||||
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9' ||
|
||||
ctx.L.input_char >= 'A' && ctx.L.input_char <= 'F' ||
|
||||
ctx.L.input_char >= 'a' && ctx.L.input_char <= 'f') {
|
||||
|
||||
ctx.L.unichar += HexValue (ctx.L.input_char) * mult;
|
||||
|
||||
counter++;
|
||||
mult /= 16;
|
||||
|
||||
if (counter == 4) {
|
||||
ctx.L.string_buffer.Append (
|
||||
Convert.ToChar (ctx.L.unichar));
|
||||
ctx.NextState = ctx.StateStack;
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State23 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
switch (ctx.L.input_char) {
|
||||
case '\'':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 24;
|
||||
return true;
|
||||
|
||||
case '\\':
|
||||
ctx.StateStack = 23;
|
||||
ctx.NextState = 21;
|
||||
return true;
|
||||
|
||||
default:
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State24 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '\'':
|
||||
ctx.L.input_char = '"';
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State25 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '*':
|
||||
ctx.NextState = 27;
|
||||
return true;
|
||||
|
||||
case '/':
|
||||
ctx.NextState = 26;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State26 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char == '\n') {
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State27 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char == '*') {
|
||||
ctx.NextState = 28;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State28 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char == '*')
|
||||
continue;
|
||||
|
||||
if (ctx.L.input_char == '/') {
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
ctx.NextState = 27;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
private bool GetChar ()
|
||||
{
|
||||
if ((input_char = NextChar ()) != -1)
|
||||
return true;
|
||||
|
||||
end_of_input = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private int NextChar ()
|
||||
{
|
||||
if (input_buffer != 0) {
|
||||
int tmp = input_buffer;
|
||||
input_buffer = 0;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
return reader.Read ();
|
||||
}
|
||||
|
||||
public bool NextToken ()
|
||||
{
|
||||
StateHandler handler;
|
||||
fsm_context.Return = false;
|
||||
|
||||
while (true) {
|
||||
handler = fsm_handler_table[state - 1];
|
||||
|
||||
if (! handler (fsm_context))
|
||||
throw new JsonException (input_char);
|
||||
|
||||
if (end_of_input)
|
||||
return false;
|
||||
|
||||
if (fsm_context.Return) {
|
||||
string_value = string_buffer.ToString ();
|
||||
string_buffer.Remove (0, string_buffer.Length);
|
||||
token = fsm_return_table[state - 1];
|
||||
|
||||
if (token == (int) ParserToken.Char)
|
||||
token = input_char;
|
||||
|
||||
state = fsm_context.NextState;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
state = fsm_context.NextState;
|
||||
}
|
||||
}
|
||||
|
||||
private void UngetChar ()
|
||||
{
|
||||
input_buffer = input_char;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#if NETSTANDARD1_5
|
||||
using System;
|
||||
using System.Reflection;
|
||||
namespace LitJson
|
||||
{
|
||||
internal static class Netstandard15Polyfill
|
||||
{
|
||||
internal static Type GetInterface(this Type type, string name)
|
||||
{
|
||||
return type.GetTypeInfo().GetInterface(name);
|
||||
}
|
||||
|
||||
internal static bool IsClass(this Type type)
|
||||
{
|
||||
return type.GetTypeInfo().IsClass;
|
||||
}
|
||||
|
||||
internal static bool IsEnum(this Type type)
|
||||
{
|
||||
return type.GetTypeInfo().IsEnum;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,44 +0,0 @@
|
||||
#region Header
|
||||
/**
|
||||
* ParserToken.cs
|
||||
* Internal representation of the tokens used by the lexer and the parser.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
namespace LitJson
|
||||
{
|
||||
internal enum ParserToken
|
||||
{
|
||||
// Lexer tokens (see section A.1.1. of the manual)
|
||||
None = System.Char.MaxValue + 1,
|
||||
Number,
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
CharSeq,
|
||||
// Single char
|
||||
Char,
|
||||
|
||||
// Parser Rules (see section A.2.1 of the manual)
|
||||
Text,
|
||||
Object,
|
||||
ObjectPrime,
|
||||
Pair,
|
||||
PairRest,
|
||||
Array,
|
||||
ArrayPrime,
|
||||
Value,
|
||||
ValueRest,
|
||||
String,
|
||||
|
||||
// End of input
|
||||
End,
|
||||
|
||||
// The empty rule
|
||||
Epsilon
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||
// die einer Assembly zugeordnet sind.
|
||||
[assembly: AssemblyTitle("LitJson")]
|
||||
[assembly: AssemblyDescription("LitJSON library")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("LitJSON")]
|
||||
[assembly: AssemblyCopyright("The authors disclaim copyright to this source code")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
|
||||
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
|
||||
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||
[assembly: Guid("91a14cd2-2940-4500-8193-56d37edddbaa")]
|
||||
|
||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||
//
|
||||
// Hauptversion
|
||||
// Nebenversion
|
||||
// Buildnummer
|
||||
// Revision
|
||||
//
|
||||
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
|
||||
// indem Sie "*" wie unten gezeigt eingeben:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.12.0")]
|
||||
[assembly: AssemblyFileVersion("0.12.0")]
|
||||
@@ -1,57 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{91A14CD2-2940-4500-8193-56D37EDDDBAA}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>LitJson</RootNamespace>
|
||||
<AssemblyName>litjson</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\4.7.1\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\4.7.1\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="IJsonWrapper.cs" />
|
||||
<Compile Include="JsonData.cs" />
|
||||
<Compile Include="JsonException.cs" />
|
||||
<Compile Include="JsonMapper.cs" />
|
||||
<Compile Include="JsonMockWrapper.cs" />
|
||||
<Compile Include="JsonReader.cs" />
|
||||
<Compile Include="JsonWriter.cs" />
|
||||
<Compile Include="Lexer.cs" />
|
||||
<Compile Include="Netstandard15Polyfill.cs" />
|
||||
<Compile Include="ParserToken.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
Submodule
+1
Submodule mqtt added at 0a913efc4a
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT client exception
|
||||
/// </summary>
|
||||
public class MqttClientException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="code">Error code</param>
|
||||
public MqttClientException(MqttClientErrorCode errorCode)
|
||||
{
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
// error code
|
||||
private MqttClientErrorCode errorCode;
|
||||
|
||||
/// <summary>
|
||||
/// Error code
|
||||
/// </summary>
|
||||
public MqttClientErrorCode ErrorCode
|
||||
{
|
||||
get { return this.errorCode; }
|
||||
set { this.errorCode = value; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MQTT client erroro code
|
||||
/// </summary>
|
||||
public enum MqttClientErrorCode
|
||||
{
|
||||
/// <summary>
|
||||
/// Will error (topic, message or QoS level)
|
||||
/// </summary>
|
||||
WillWrong = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Keep alive period too large
|
||||
/// </summary>
|
||||
KeepAliveWrong,
|
||||
|
||||
/// <summary>
|
||||
/// Topic contains wildcards
|
||||
/// </summary>
|
||||
TopicWildcard,
|
||||
|
||||
/// <summary>
|
||||
/// Topic length wrong
|
||||
/// </summary>
|
||||
TopicLength,
|
||||
|
||||
/// <summary>
|
||||
/// QoS level not allowed
|
||||
/// </summary>
|
||||
QosNotAllowed,
|
||||
|
||||
/// <summary>
|
||||
/// Topics list empty for subscribe
|
||||
/// </summary>
|
||||
TopicsEmpty,
|
||||
|
||||
/// <summary>
|
||||
/// Qos levels list empty for subscribe
|
||||
/// </summary>
|
||||
QosLevelsEmpty,
|
||||
|
||||
/// <summary>
|
||||
/// Topics / Qos Levels not match in subscribe
|
||||
/// </summary>
|
||||
TopicsQosLevelsNotMatch,
|
||||
|
||||
/// <summary>
|
||||
/// Wrong message from broker
|
||||
/// </summary>
|
||||
WrongBrokerMessage,
|
||||
|
||||
/// <summary>
|
||||
/// Wrong Message Id
|
||||
/// </summary>
|
||||
WrongMessageId,
|
||||
|
||||
/// <summary>
|
||||
/// Inflight queue is full
|
||||
/// </summary>
|
||||
InflightQueueFull,
|
||||
|
||||
// [v3.1.1]
|
||||
/// <summary>
|
||||
/// Invalid flag bits received
|
||||
/// </summary>
|
||||
InvalidFlagBits,
|
||||
|
||||
// [v3.1.1]
|
||||
/// <summary>
|
||||
/// Invalid connect flags received
|
||||
/// </summary>
|
||||
InvalidConnectFlags,
|
||||
|
||||
// [v3.1.1]
|
||||
/// <summary>
|
||||
/// Invalid client id
|
||||
/// </summary>
|
||||
InvalidClientId,
|
||||
|
||||
// [v3.1.1]
|
||||
/// <summary>
|
||||
/// Invalid protocol name
|
||||
/// </summary>
|
||||
InvalidProtocolName
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception due to error communication with broker on socket
|
||||
/// </summary>
|
||||
public class MqttCommunicationException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public MqttCommunicationException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="e">Inner Exception</param>
|
||||
public MqttCommunicationException(Exception e)
|
||||
: base(String.Empty, e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Connection to the broker exception
|
||||
/// </summary>
|
||||
public class MqttConnectionException : Exception
|
||||
{
|
||||
public MqttConnectionException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Timeout on receiving from broker exception
|
||||
/// </summary>
|
||||
public class MqttTimeoutException : Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for channel under MQTT library
|
||||
/// </summary>
|
||||
public interface IMqttNetworkChannel
|
||||
{
|
||||
/// <summary>
|
||||
/// Data available on channel
|
||||
/// </summary>
|
||||
bool DataAvailable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Receive data from the network channel
|
||||
/// </summary>
|
||||
/// <param name="buffer">Data buffer for receiving data</param>
|
||||
/// <returns>Number of bytes received</returns>
|
||||
int Receive(byte[] buffer);
|
||||
|
||||
/// <summary>
|
||||
/// Receive data from the network channel with a specified timeout
|
||||
/// </summary>
|
||||
/// <param name="buffer">Data buffer for receiving data</param>
|
||||
/// <param name="timeout">Timeout on receiving (in milliseconds)</param>
|
||||
/// <returns>Number of bytes received</returns>
|
||||
int Receive(byte[] buffer, int timeout);
|
||||
|
||||
/// <summary>
|
||||
/// Send data on the network channel to the broker
|
||||
/// </summary>
|
||||
/// <param name="buffer">Data buffer to send</param>
|
||||
/// <returns>Number of byte sent</returns>
|
||||
int Send(byte[] buffer);
|
||||
|
||||
/// <summary>
|
||||
/// Close the network channel
|
||||
/// </summary>
|
||||
void Close();
|
||||
|
||||
/// <summary>
|
||||
/// Connect to remote server
|
||||
/// </summary>
|
||||
void Connect();
|
||||
|
||||
/// <summary>
|
||||
/// Accept client connection
|
||||
/// </summary>
|
||||
void Accept();
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic internal event for dispatching
|
||||
/// </summary>
|
||||
public abstract class InternalEvent
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using uPLibrary.Networking.M2Mqtt.Messages;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal event with a message
|
||||
/// </summary>
|
||||
public class MsgInternalEvent : InternalEvent
|
||||
{
|
||||
#region Properties ...
|
||||
|
||||
/// <summary>
|
||||
/// Related message
|
||||
/// </summary>
|
||||
public MqttMsgBase Message
|
||||
{
|
||||
get { return this.msg; }
|
||||
set { this.msg = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// related message
|
||||
protected MqttMsgBase msg;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="msg">Related message</param>
|
||||
public MsgInternalEvent(MqttMsgBase msg)
|
||||
{
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using uPLibrary.Networking.M2Mqtt.Messages;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal event for a published message
|
||||
/// </summary>
|
||||
public class MsgPublishedInternalEvent : MsgInternalEvent
|
||||
{
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// Message published (or failed due to retries)
|
||||
/// </summary>
|
||||
public bool IsPublished
|
||||
{
|
||||
get { return this.isPublished; }
|
||||
internal set { this.isPublished = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// published flag
|
||||
bool isPublished;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="msg">Message published</param>
|
||||
/// <param name="isPublished">Publish flag</param>
|
||||
public MsgPublishedInternalEvent(MqttMsgBase msg, bool isPublished)
|
||||
: base(msg)
|
||||
{
|
||||
this.isPublished = isPublished;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{A11AEF5A-B246-4FE8-8330-06DB73CC8074}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>uPLibrary.Networking.M2Mqtt</RootNamespace>
|
||||
<AssemblyName>M2Mqtt</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\2.0\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\2.0\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Exceptions\MqttClientException.cs" />
|
||||
<Compile Include="Exceptions\MqttCommunicationException.cs" />
|
||||
<Compile Include="Exceptions\MqttConnectionException.cs" />
|
||||
<Compile Include="Exceptions\MqttTimeoutException.cs" />
|
||||
<Compile Include="IMqttNetworkChannel.cs" />
|
||||
<Compile Include="Internal\InternalEvent.cs" />
|
||||
<Compile Include="Internal\MsgInternalEvent.cs" />
|
||||
<Compile Include="Internal\MsgPublishedInternalEvent.cs" />
|
||||
<Compile Include="Messages\MqttMsgBase.cs" />
|
||||
<Compile Include="Messages\MqttMsgConnack.cs" />
|
||||
<Compile Include="Messages\MqttMsgConnect.cs" />
|
||||
<Compile Include="Messages\MqttMsgConnectEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgContext.cs" />
|
||||
<Compile Include="Messages\MqttMsgDisconnect.cs" />
|
||||
<Compile Include="Messages\MqttMsgPingReq.cs" />
|
||||
<Compile Include="Messages\MqttMsgPingResp.cs" />
|
||||
<Compile Include="Messages\MqttMsgPuback.cs" />
|
||||
<Compile Include="Messages\MqttMsgPubcomp.cs" />
|
||||
<Compile Include="Messages\MqttMsgPublish.cs" />
|
||||
<Compile Include="Messages\MqttMsgPublishedEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgPublishEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgPubrec.cs" />
|
||||
<Compile Include="Messages\MqttMsgPubrel.cs" />
|
||||
<Compile Include="Messages\MqttMsgSuback.cs" />
|
||||
<Compile Include="Messages\MqttMsgSubscribe.cs" />
|
||||
<Compile Include="Messages\MqttMsgSubscribedEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgSubscribeEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgUnsuback.cs" />
|
||||
<Compile Include="Messages\MqttMsgUnsubscribe.cs" />
|
||||
<Compile Include="Messages\MqttMsgUnsubscribedEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgUnsubscribeEventArgs.cs" />
|
||||
<Compile Include="MqttClient.cs" />
|
||||
<Compile Include="MqttSecurity.cs" />
|
||||
<Compile Include="Net\Fx.cs" />
|
||||
<Compile Include="Net\MqttNetworkChannel.cs" />
|
||||
<Compile Include="MqttSettings.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Session\MqttBrokerSession.cs" />
|
||||
<Compile Include="Session\MqttClientSession.cs" />
|
||||
<Compile Include="Session\MqttSession.cs" />
|
||||
<Compile Include="Utility\Trace.cs" />
|
||||
<Compile Include="Utility\QueueExtension.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -1,93 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{A11AEF5A-B246-4FE8-8330-06DB73CC8074}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>uPLibrary.Networking.M2Mqtt</RootNamespace>
|
||||
<AssemblyName>M2Mqtt</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\4.7.1\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;SSL</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\4.7.1\</OutputPath>
|
||||
<DefineConstants>TRACE;SSL</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Exceptions\MqttClientException.cs" />
|
||||
<Compile Include="Exceptions\MqttCommunicationException.cs" />
|
||||
<Compile Include="Exceptions\MqttConnectionException.cs" />
|
||||
<Compile Include="Exceptions\MqttTimeoutException.cs" />
|
||||
<Compile Include="IMqttNetworkChannel.cs" />
|
||||
<Compile Include="Internal\InternalEvent.cs" />
|
||||
<Compile Include="Internal\MsgInternalEvent.cs" />
|
||||
<Compile Include="Internal\MsgPublishedInternalEvent.cs" />
|
||||
<Compile Include="Messages\MqttMsgBase.cs" />
|
||||
<Compile Include="Messages\MqttMsgConnack.cs" />
|
||||
<Compile Include="Messages\MqttMsgConnect.cs" />
|
||||
<Compile Include="Messages\MqttMsgConnectEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgContext.cs" />
|
||||
<Compile Include="Messages\MqttMsgDisconnect.cs" />
|
||||
<Compile Include="Messages\MqttMsgPingReq.cs" />
|
||||
<Compile Include="Messages\MqttMsgPingResp.cs" />
|
||||
<Compile Include="Messages\MqttMsgPuback.cs" />
|
||||
<Compile Include="Messages\MqttMsgPubcomp.cs" />
|
||||
<Compile Include="Messages\MqttMsgPublish.cs" />
|
||||
<Compile Include="Messages\MqttMsgPublishedEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgPublishEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgPubrec.cs" />
|
||||
<Compile Include="Messages\MqttMsgPubrel.cs" />
|
||||
<Compile Include="Messages\MqttMsgSuback.cs" />
|
||||
<Compile Include="Messages\MqttMsgSubscribe.cs" />
|
||||
<Compile Include="Messages\MqttMsgSubscribedEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgSubscribeEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgUnsuback.cs" />
|
||||
<Compile Include="Messages\MqttMsgUnsubscribe.cs" />
|
||||
<Compile Include="Messages\MqttMsgUnsubscribedEventArgs.cs" />
|
||||
<Compile Include="Messages\MqttMsgUnsubscribeEventArgs.cs" />
|
||||
<Compile Include="MqttClient.cs" />
|
||||
<Compile Include="MqttSecurity.cs" />
|
||||
<Compile Include="Net\Fx.cs" />
|
||||
<Compile Include="Net\MqttNetworkChannel.cs" />
|
||||
<Compile Include="MqttSettings.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Session\MqttBrokerSession.cs" />
|
||||
<Compile Include="Session\MqttClientSession.cs" />
|
||||
<Compile Include="Session\MqttSession.cs" />
|
||||
<Compile Include="Utility\Trace.cs" />
|
||||
<Compile Include="Utility\QueueExtension.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
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 ushort MAX_TOPIC_LENGTH = 65535;
|
||||
internal const ushort MIN_TOPIC_LENGTH = 1;
|
||||
internal const byte MESSAGE_ID_SIZE = 2;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// Message type
|
||||
/// </summary>
|
||||
public byte Type
|
||||
{
|
||||
get { return this.type; }
|
||||
set { this.type = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Duplicate message flag
|
||||
/// </summary>
|
||||
public bool DupFlag
|
||||
{
|
||||
get { return this.dupFlag; }
|
||||
set { this.dupFlag = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quality of Service level
|
||||
/// </summary>
|
||||
public byte QosLevel
|
||||
{
|
||||
get { return this.qosLevel; }
|
||||
set { this.qosLevel = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retain message flag
|
||||
/// </summary>
|
||||
public bool Retain
|
||||
{
|
||||
get { return this.retain; }
|
||||
set { this.retain = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message identifier for the message
|
||||
/// </summary>
|
||||
public ushort MessageId
|
||||
{
|
||||
get { return this.messageId; }
|
||||
set { this.messageId = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// message type
|
||||
protected byte type;
|
||||
// duplicate delivery
|
||||
protected bool dupFlag;
|
||||
// quality of service level
|
||||
protected byte qosLevel;
|
||||
// retain flag
|
||||
protected bool retain;
|
||||
// message identifier
|
||||
protected ushort messageId;
|
||||
|
||||
/// <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 int encodeRemainingLength(int remainingLength, byte[] buffer, int index)
|
||||
{
|
||||
int digit = 0;
|
||||
do
|
||||
{
|
||||
digit = remainingLength % 128;
|
||||
remainingLength /= 128;
|
||||
if (remainingLength > 0)
|
||||
digit = 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 int decodeRemainingLength(IMqttNetworkChannel channel)
|
||||
{
|
||||
int multiplier = 1;
|
||||
int value = 0;
|
||||
int digit = 0;
|
||||
byte[] nextByte = new byte[1];
|
||||
do
|
||||
{
|
||||
// next digit from stream
|
||||
channel.Receive(nextByte);
|
||||
digit = nextByte[0];
|
||||
value += ((digit & 127) * multiplier);
|
||||
multiplier *= 128;
|
||||
} while ((digit & 128) != 0);
|
||||
return value;
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
/// <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("(");
|
||||
bool addComma = false;
|
||||
for (int i = 0; i < fieldValues.Length; i++)
|
||||
{
|
||||
if (fieldValues[i] != null)
|
||||
{
|
||||
if (addComma)
|
||||
{
|
||||
sb.Append(",");
|
||||
}
|
||||
|
||||
sb.Append(fieldNames[i]);
|
||||
sb.Append(":");
|
||||
sb.Append(GetStringObject(fieldValues[i]));
|
||||
addComma = true;
|
||||
}
|
||||
}
|
||||
sb.Append(")");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
object GetStringObject(object value)
|
||||
{
|
||||
byte[] binary = value as byte[];
|
||||
if (binary != null)
|
||||
{
|
||||
string hexChars = "0123456789ABCDEF";
|
||||
StringBuilder sb = new StringBuilder(binary.Length * 2);
|
||||
for (int i = 0; i < binary.Length; ++i)
|
||||
{
|
||||
sb.Append(hexChars[binary[i] >> 4]);
|
||||
sb.Append(hexChars[binary[i] & 0x0F]);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
object[] list = value as object[];
|
||||
if (list != null)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
for (int i = 0; i < list.Length; ++i)
|
||||
{
|
||||
if (i > 0) sb.Append(',');
|
||||
sb.Append(list[i]);
|
||||
}
|
||||
sb.Append(']');
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for CONNACK message from broker to client
|
||||
/// </summary>
|
||||
public class MqttMsgConnack : MqttMsgBase
|
||||
{
|
||||
#region Constants...
|
||||
|
||||
// return codes for CONNACK message
|
||||
public const byte CONN_ACCEPTED = 0x00;
|
||||
public const byte CONN_REFUSED_PROT_VERS = 0x01;
|
||||
public const byte CONN_REFUSED_IDENT_REJECTED = 0x02;
|
||||
public const byte CONN_REFUSED_SERVER_UNAVAILABLE = 0x03;
|
||||
public const byte CONN_REFUSED_USERNAME_PASSWORD = 0x04;
|
||||
public const byte CONN_REFUSED_NOT_AUTHORIZED = 0x05;
|
||||
|
||||
private const byte TOPIC_NAME_COMP_RESP_BYTE_OFFSET = 0;
|
||||
private const byte TOPIC_NAME_COMP_RESP_BYTE_SIZE = 1;
|
||||
// [v3.1.1] connect acknowledge flags replace "old" topic name compression respone (not used in 3.1)
|
||||
private const byte CONN_ACK_FLAGS_BYTE_OFFSET = 0;
|
||||
private const byte CONN_ACK_FLAGS_BYTE_SIZE = 1;
|
||||
// [v3.1.1] session present flag
|
||||
private const byte SESSION_PRESENT_FLAG_MASK = 0x01;
|
||||
private const byte SESSION_PRESENT_FLAG_OFFSET = 0x00;
|
||||
private const byte SESSION_PRESENT_FLAG_SIZE = 0x01;
|
||||
private const byte CONN_RETURN_CODE_BYTE_OFFSET = 1;
|
||||
private const byte CONN_RETURN_CODE_BYTE_SIZE = 1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties...
|
||||
|
||||
// [v3.1.1] session present flag
|
||||
/// <summary>
|
||||
/// Session present flag
|
||||
/// </summary>
|
||||
public bool SessionPresent
|
||||
{
|
||||
get { return this.sessionPresent; }
|
||||
set { this.sessionPresent = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return Code
|
||||
/// </summary>
|
||||
public byte ReturnCode
|
||||
{
|
||||
get { return this.returnCode; }
|
||||
set { this.returnCode = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// [v3.1.1] session present flag
|
||||
private bool sessionPresent;
|
||||
|
||||
// return code for CONNACK message
|
||||
private byte returnCode;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgConnack()
|
||||
{
|
||||
this.type = MQTT_MSG_CONNACK_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a CONNACK message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>CONNACK message instance</returns>
|
||||
public static MqttMsgConnack Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
byte[] buffer;
|
||||
MqttMsgConnack msg = new MqttMsgConnack();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_CONNACK_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
buffer = new byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
channel.Receive(buffer);
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] ... set session present flag ...
|
||||
msg.sessionPresent = (buffer[CONN_ACK_FLAGS_BYTE_OFFSET] & SESSION_PRESENT_FLAG_MASK) != 0x00;
|
||||
}
|
||||
// ...and set return code from broker
|
||||
msg.returnCode = buffer[CONN_RETURN_CODE_BYTE_OFFSET];
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte ProtocolVersion)
|
||||
{
|
||||
int fixedHeaderSize = 0;
|
||||
int varHeaderSize = 0;
|
||||
int payloadSize = 0;
|
||||
int remainingLength = 0;
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
|
||||
if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
// flags byte and connect return code
|
||||
varHeaderSize += (CONN_ACK_FLAGS_BYTE_SIZE + CONN_RETURN_CODE_BYTE_SIZE);
|
||||
else
|
||||
// topic name compression response and connect return code
|
||||
varHeaderSize += (TOPIC_NAME_COMP_RESP_BYTE_SIZE + CONN_RETURN_CODE_BYTE_SIZE);
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
int temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do
|
||||
{
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_CONNACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_CONNACK_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
buffer[index++] = (byte)(MQTT_MSG_CONNACK_TYPE << MSG_TYPE_OFFSET);
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
// [v3.1.1] session present flag
|
||||
buffer[index++] = this.sessionPresent ? (byte)(1 << SESSION_PRESENT_FLAG_OFFSET) : (byte)0x00;
|
||||
else
|
||||
// topic name compression response (reserved values. not used);
|
||||
buffer[index++] = 0x00;
|
||||
|
||||
// connect return code
|
||||
buffer[index++] = this.returnCode;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"CONNACK",
|
||||
new object[] { "returnCode" },
|
||||
new object[] { this.returnCode });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,582 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for CONNECT message from client to broker
|
||||
/// </summary>
|
||||
public class MqttMsgConnect : MqttMsgBase
|
||||
{
|
||||
#region Constants...
|
||||
|
||||
// protocol name supported
|
||||
internal const string PROTOCOL_NAME_V3_1 = "MQIsdp";
|
||||
internal const string PROTOCOL_NAME_V3_1_1 = "MQTT"; // [v.3.1.1]
|
||||
|
||||
// max length for client id (removed in 3.1.1)
|
||||
internal const int CLIENT_ID_MAX_LENGTH = 23;
|
||||
|
||||
// variable header fields
|
||||
internal const byte PROTOCOL_NAME_LEN_SIZE = 2;
|
||||
internal const byte PROTOCOL_NAME_V3_1_SIZE = 6;
|
||||
internal const byte PROTOCOL_NAME_V3_1_1_SIZE = 4; // [v.3.1.1]
|
||||
internal const byte PROTOCOL_VERSION_SIZE = 1;
|
||||
internal const byte CONNECT_FLAGS_SIZE = 1;
|
||||
internal const byte KEEP_ALIVE_TIME_SIZE = 2;
|
||||
|
||||
internal const byte PROTOCOL_VERSION_V3_1 = 0x03;
|
||||
internal const byte PROTOCOL_VERSION_V3_1_1 = 0x04; // [v.3.1.1]
|
||||
internal const ushort KEEP_ALIVE_PERIOD_DEFAULT = 60; // seconds
|
||||
internal const ushort MAX_KEEP_ALIVE = 65535; // 16 bit
|
||||
|
||||
// connect flags
|
||||
internal const byte USERNAME_FLAG_MASK = 0x80;
|
||||
internal const byte USERNAME_FLAG_OFFSET = 0x07;
|
||||
internal const byte USERNAME_FLAG_SIZE = 0x01;
|
||||
internal const byte PASSWORD_FLAG_MASK = 0x40;
|
||||
internal const byte PASSWORD_FLAG_OFFSET = 0x06;
|
||||
internal const byte PASSWORD_FLAG_SIZE = 0x01;
|
||||
internal const byte WILL_RETAIN_FLAG_MASK = 0x20;
|
||||
internal const byte WILL_RETAIN_FLAG_OFFSET = 0x05;
|
||||
internal const byte WILL_RETAIN_FLAG_SIZE = 0x01;
|
||||
internal const byte WILL_QOS_FLAG_MASK = 0x18;
|
||||
internal const byte WILL_QOS_FLAG_OFFSET = 0x03;
|
||||
internal const byte WILL_QOS_FLAG_SIZE = 0x02;
|
||||
internal const byte WILL_FLAG_MASK = 0x04;
|
||||
internal const byte WILL_FLAG_OFFSET = 0x02;
|
||||
internal const byte WILL_FLAG_SIZE = 0x01;
|
||||
internal const byte CLEAN_SESSION_FLAG_MASK = 0x02;
|
||||
internal const byte CLEAN_SESSION_FLAG_OFFSET = 0x01;
|
||||
internal const byte CLEAN_SESSION_FLAG_SIZE = 0x01;
|
||||
// [v.3.1.1] lsb (reserved) must be now 0
|
||||
internal const byte RESERVED_FLAG_MASK = 0x01;
|
||||
internal const byte RESERVED_FLAG_OFFSET = 0x00;
|
||||
internal const byte RESERVED_FLAG_SIZE = 0x01;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// Protocol name
|
||||
/// </summary>
|
||||
public string ProtocolName
|
||||
{
|
||||
get { return this.protocolName; }
|
||||
set { this.protocolName = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protocol version
|
||||
/// </summary>
|
||||
public byte ProtocolVersion
|
||||
{
|
||||
get { return this.protocolVersion; }
|
||||
set { this.protocolVersion = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client identifier
|
||||
/// </summary>
|
||||
public string ClientId
|
||||
{
|
||||
get { return this.clientId; }
|
||||
set { this.clientId = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will retain flag
|
||||
/// </summary>
|
||||
public bool WillRetain
|
||||
{
|
||||
get { return this.willRetain; }
|
||||
set { this.willRetain = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will QOS level
|
||||
/// </summary>
|
||||
public byte WillQosLevel
|
||||
{
|
||||
get { return this.willQosLevel; }
|
||||
set { this.willQosLevel = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will flag
|
||||
/// </summary>
|
||||
public bool WillFlag
|
||||
{
|
||||
get { return this.willFlag; }
|
||||
set { this.willFlag = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will topic
|
||||
/// </summary>
|
||||
public string WillTopic
|
||||
{
|
||||
get { return this.willTopic; }
|
||||
set { this.willTopic = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will message
|
||||
/// </summary>
|
||||
public string WillMessage
|
||||
{
|
||||
get { return this.willMessage; }
|
||||
set { this.willMessage = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Username
|
||||
/// </summary>
|
||||
public string Username
|
||||
{
|
||||
get { return this.username; }
|
||||
set { this.username = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Password
|
||||
/// </summary>
|
||||
public string Password
|
||||
{
|
||||
get { return this.password; }
|
||||
set { this.password = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean session flag
|
||||
/// </summary>
|
||||
public bool CleanSession
|
||||
{
|
||||
get { return this.cleanSession; }
|
||||
set { this.cleanSession = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keep alive period
|
||||
/// </summary>
|
||||
public ushort KeepAlivePeriod
|
||||
{
|
||||
get { return this.keepAlivePeriod; }
|
||||
set { this.keepAlivePeriod = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// protocol name
|
||||
private string protocolName;
|
||||
// protocol version
|
||||
private byte protocolVersion;
|
||||
// client identifier
|
||||
private string clientId;
|
||||
// will retain flag
|
||||
protected bool willRetain;
|
||||
// will quality of service level
|
||||
protected byte willQosLevel;
|
||||
// will flag
|
||||
private bool willFlag;
|
||||
// will topic
|
||||
private string willTopic;
|
||||
// will message
|
||||
private string willMessage;
|
||||
// username
|
||||
private string username;
|
||||
// password
|
||||
private string password;
|
||||
// clean session flag
|
||||
private bool cleanSession;
|
||||
// keep alive period (in sec)
|
||||
private ushort keepAlivePeriod;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgConnect()
|
||||
{
|
||||
this.type = MQTT_MSG_CONNECT_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="clientId">Client identifier</param>
|
||||
public MqttMsgConnect(string clientId) :
|
||||
this(clientId, null, null, false, QOS_LEVEL_AT_LEAST_ONCE, false, null, null, true, KEEP_ALIVE_PERIOD_DEFAULT, PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="clientId">Client identifier</param>
|
||||
/// <param name="username">Username</param>
|
||||
/// <param name="password">Password</param>
|
||||
/// <param name="willRetain">Will retain flag</param>
|
||||
/// <param name="willQosLevel">Will QOS level</param>
|
||||
/// <param name="willFlag">Will flag</param>
|
||||
/// <param name="willTopic">Will topic</param>
|
||||
/// <param name="willMessage">Will message</param>
|
||||
/// <param name="cleanSession">Clean sessione flag</param>
|
||||
/// <param name="keepAlivePeriod">Keep alive period</param>
|
||||
/// <param name="protocolVersion">Protocol version</param>
|
||||
public MqttMsgConnect(string clientId,
|
||||
string username,
|
||||
string password,
|
||||
bool willRetain,
|
||||
byte willQosLevel,
|
||||
bool willFlag,
|
||||
string willTopic,
|
||||
string willMessage,
|
||||
bool cleanSession,
|
||||
ushort keepAlivePeriod,
|
||||
byte protocolVersion
|
||||
)
|
||||
{
|
||||
this.type = MQTT_MSG_CONNECT_TYPE;
|
||||
|
||||
this.clientId = clientId;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.willRetain = willRetain;
|
||||
this.willQosLevel = willQosLevel;
|
||||
this.willFlag = willFlag;
|
||||
this.willTopic = willTopic;
|
||||
this.willMessage = willMessage;
|
||||
this.cleanSession = cleanSession;
|
||||
this.keepAlivePeriod = keepAlivePeriod;
|
||||
// [v.3.1.1] added new protocol name and version
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.protocolName = (this.protocolVersion == PROTOCOL_VERSION_V3_1_1) ? PROTOCOL_NAME_V3_1_1 : PROTOCOL_NAME_V3_1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a CONNECT message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>CONNECT message instance</returns>
|
||||
public static MqttMsgConnect Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
int protNameUtf8Length;
|
||||
byte[] protNameUtf8;
|
||||
bool isUsernameFlag;
|
||||
bool isPasswordFlag;
|
||||
int clientIdUtf8Length;
|
||||
byte[] clientIdUtf8;
|
||||
int willTopicUtf8Length;
|
||||
byte[] willTopicUtf8;
|
||||
int willMessageUtf8Length;
|
||||
byte[] willMessageUtf8;
|
||||
int usernameUtf8Length;
|
||||
byte[] usernameUtf8;
|
||||
int passwordUtf8Length;
|
||||
byte[] passwordUtf8;
|
||||
MqttMsgConnect msg = new MqttMsgConnect();
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
buffer = new byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
channel.Receive(buffer);
|
||||
|
||||
// protocol name
|
||||
protNameUtf8Length = ((buffer[index++] << 8) & 0xFF00);
|
||||
protNameUtf8Length |= buffer[index++];
|
||||
protNameUtf8 = new byte[protNameUtf8Length];
|
||||
Array.Copy(buffer, index, protNameUtf8, 0, protNameUtf8Length);
|
||||
index += protNameUtf8Length;
|
||||
msg.protocolName = new String(Encoding.UTF8.GetChars(protNameUtf8));
|
||||
|
||||
// [v3.1.1] wrong protocol name
|
||||
if (!msg.protocolName.Equals(PROTOCOL_NAME_V3_1) && !msg.protocolName.Equals(PROTOCOL_NAME_V3_1_1))
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidProtocolName);
|
||||
|
||||
// protocol version
|
||||
msg.protocolVersion = buffer[index];
|
||||
index += PROTOCOL_VERSION_SIZE;
|
||||
|
||||
// connect flags
|
||||
// [v3.1.1] check lsb (reserved) must be 0
|
||||
if ((msg.protocolVersion == PROTOCOL_VERSION_V3_1_1) &&
|
||||
((buffer[index] & RESERVED_FLAG_MASK) != 0x00))
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidConnectFlags);
|
||||
|
||||
isUsernameFlag = (buffer[index] & USERNAME_FLAG_MASK) != 0x00;
|
||||
isPasswordFlag = (buffer[index] & PASSWORD_FLAG_MASK) != 0x00;
|
||||
msg.willRetain = (buffer[index] & WILL_RETAIN_FLAG_MASK) != 0x00;
|
||||
msg.willQosLevel = (byte)((buffer[index] & WILL_QOS_FLAG_MASK) >> WILL_QOS_FLAG_OFFSET);
|
||||
msg.willFlag = (buffer[index] & WILL_FLAG_MASK) != 0x00;
|
||||
msg.cleanSession = (buffer[index] & CLEAN_SESSION_FLAG_MASK) != 0x00;
|
||||
index += CONNECT_FLAGS_SIZE;
|
||||
|
||||
// keep alive timer
|
||||
msg.keepAlivePeriod = (ushort)((buffer[index++] << 8) & 0xFF00);
|
||||
msg.keepAlivePeriod |= buffer[index++];
|
||||
|
||||
// client identifier [v3.1.1] it may be zero bytes long (empty string)
|
||||
clientIdUtf8Length = ((buffer[index++] << 8) & 0xFF00);
|
||||
clientIdUtf8Length |= buffer[index++];
|
||||
clientIdUtf8 = new byte[clientIdUtf8Length];
|
||||
Array.Copy(buffer, index, clientIdUtf8, 0, clientIdUtf8Length);
|
||||
index += clientIdUtf8Length;
|
||||
msg.clientId = new String(Encoding.UTF8.GetChars(clientIdUtf8));
|
||||
// [v3.1.1] if client identifier is zero bytes long, clean session must be true
|
||||
if ((msg.protocolVersion == PROTOCOL_VERSION_V3_1_1) && (clientIdUtf8Length == 0) && (!msg.cleanSession))
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidClientId);
|
||||
|
||||
// will topic and will message
|
||||
if (msg.willFlag)
|
||||
{
|
||||
willTopicUtf8Length = ((buffer[index++] << 8) & 0xFF00);
|
||||
willTopicUtf8Length |= buffer[index++];
|
||||
willTopicUtf8 = new byte[willTopicUtf8Length];
|
||||
Array.Copy(buffer, index, willTopicUtf8, 0, willTopicUtf8Length);
|
||||
index += willTopicUtf8Length;
|
||||
msg.willTopic = new String(Encoding.UTF8.GetChars(willTopicUtf8));
|
||||
|
||||
willMessageUtf8Length = ((buffer[index++] << 8) & 0xFF00);
|
||||
willMessageUtf8Length |= buffer[index++];
|
||||
willMessageUtf8 = new byte[willMessageUtf8Length];
|
||||
Array.Copy(buffer, index, willMessageUtf8, 0, willMessageUtf8Length);
|
||||
index += willMessageUtf8Length;
|
||||
msg.willMessage = new String(Encoding.UTF8.GetChars(willMessageUtf8));
|
||||
}
|
||||
|
||||
// username
|
||||
if (isUsernameFlag)
|
||||
{
|
||||
usernameUtf8Length = ((buffer[index++] << 8) & 0xFF00);
|
||||
usernameUtf8Length |= buffer[index++];
|
||||
usernameUtf8 = new byte[usernameUtf8Length];
|
||||
Array.Copy(buffer, index, usernameUtf8, 0, usernameUtf8Length);
|
||||
index += usernameUtf8Length;
|
||||
msg.username = new String(Encoding.UTF8.GetChars(usernameUtf8));
|
||||
}
|
||||
|
||||
// password
|
||||
if (isPasswordFlag)
|
||||
{
|
||||
passwordUtf8Length = ((buffer[index++] << 8) & 0xFF00);
|
||||
passwordUtf8Length |= buffer[index++];
|
||||
passwordUtf8 = new byte[passwordUtf8Length];
|
||||
Array.Copy(buffer, index, passwordUtf8, 0, passwordUtf8Length);
|
||||
index += passwordUtf8Length;
|
||||
msg.password = new String(Encoding.UTF8.GetChars(passwordUtf8));
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
int fixedHeaderSize = 0;
|
||||
int varHeaderSize = 0;
|
||||
int payloadSize = 0;
|
||||
int remainingLength = 0;
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
|
||||
byte[] clientIdUtf8 = Encoding.UTF8.GetBytes(this.clientId);
|
||||
byte[] willTopicUtf8 = (this.willFlag && (this.willTopic != null)) ? Encoding.UTF8.GetBytes(this.willTopic) : null;
|
||||
byte[] willMessageUtf8 = (this.willFlag && (this.willMessage != null)) ? Encoding.UTF8.GetBytes(this.willMessage) : null;
|
||||
byte[] usernameUtf8 = ((this.username != null) && (this.username.Length > 0)) ? Encoding.UTF8.GetBytes(this.username) : null;
|
||||
byte[] passwordUtf8 = ((this.password != null) && (this.password.Length > 0)) ? Encoding.UTF8.GetBytes(this.password) : null;
|
||||
|
||||
// [v3.1.1]
|
||||
if (this.protocolVersion == PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// will flag set, will topic and will message MUST be present
|
||||
if (this.willFlag && ((this.willQosLevel >= 0x03) ||
|
||||
(willTopicUtf8 == null) || (willMessageUtf8 == null) ||
|
||||
((willTopicUtf8 != null) && (willTopicUtf8.Length == 0)) ||
|
||||
((willMessageUtf8 != null) && (willMessageUtf8.Length == 0))))
|
||||
throw new MqttClientException(MqttClientErrorCode.WillWrong);
|
||||
// willflag not set, retain must be 0 and will topic and message MUST NOT be present
|
||||
else if (!this.willFlag && ((this.willRetain) ||
|
||||
(willTopicUtf8 != null) || (willMessageUtf8 != null) ||
|
||||
((willTopicUtf8 != null) && (willTopicUtf8.Length != 0)) ||
|
||||
((willMessageUtf8 != null) && (willMessageUtf8.Length != 0))))
|
||||
throw new MqttClientException(MqttClientErrorCode.WillWrong);
|
||||
}
|
||||
|
||||
if (this.keepAlivePeriod > MAX_KEEP_ALIVE)
|
||||
throw new MqttClientException(MqttClientErrorCode.KeepAliveWrong);
|
||||
|
||||
// check on will QoS Level
|
||||
if ((this.willQosLevel < MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE) ||
|
||||
(this.willQosLevel > MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE))
|
||||
throw new MqttClientException(MqttClientErrorCode.WillWrong);
|
||||
|
||||
// protocol name field size
|
||||
// MQTT version 3.1
|
||||
if (this.protocolVersion == PROTOCOL_VERSION_V3_1)
|
||||
{
|
||||
varHeaderSize += (PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_V3_1_SIZE);
|
||||
}
|
||||
// MQTT version 3.1.1
|
||||
else
|
||||
{
|
||||
varHeaderSize += (PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_V3_1_1_SIZE);
|
||||
}
|
||||
// protocol level field size
|
||||
varHeaderSize += PROTOCOL_VERSION_SIZE;
|
||||
// connect flags field size
|
||||
varHeaderSize += CONNECT_FLAGS_SIZE;
|
||||
// keep alive timer field size
|
||||
varHeaderSize += KEEP_ALIVE_TIME_SIZE;
|
||||
|
||||
// client identifier field size
|
||||
payloadSize += clientIdUtf8.Length + 2;
|
||||
// will topic field size
|
||||
payloadSize += (willTopicUtf8 != null) ? (willTopicUtf8.Length + 2) : 0;
|
||||
// will message field size
|
||||
payloadSize += (willMessageUtf8 != null) ? (willMessageUtf8.Length + 2) : 0;
|
||||
// username field size
|
||||
payloadSize += (usernameUtf8 != null) ? (usernameUtf8.Length + 2) : 0;
|
||||
// password field size
|
||||
payloadSize += (passwordUtf8 != null) ? (passwordUtf8.Length + 2) : 0;
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
int temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do
|
||||
{
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
buffer[index++] = (MQTT_MSG_CONNECT_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_CONNECT_FLAG_BITS; // [v.3.1.1]
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
// protocol name
|
||||
buffer[index++] = 0; // MSB protocol name size
|
||||
// MQTT version 3.1
|
||||
if (this.protocolVersion == PROTOCOL_VERSION_V3_1)
|
||||
{
|
||||
buffer[index++] = PROTOCOL_NAME_V3_1_SIZE; // LSB protocol name size
|
||||
Array.Copy(Encoding.UTF8.GetBytes(PROTOCOL_NAME_V3_1), 0, buffer, index, PROTOCOL_NAME_V3_1_SIZE);
|
||||
index += PROTOCOL_NAME_V3_1_SIZE;
|
||||
// protocol version
|
||||
buffer[index++] = PROTOCOL_VERSION_V3_1;
|
||||
}
|
||||
// MQTT version 3.1.1
|
||||
else
|
||||
{
|
||||
buffer[index++] = PROTOCOL_NAME_V3_1_1_SIZE; // LSB protocol name size
|
||||
Array.Copy(Encoding.UTF8.GetBytes(PROTOCOL_NAME_V3_1_1), 0, buffer, index, PROTOCOL_NAME_V3_1_1_SIZE);
|
||||
index += PROTOCOL_NAME_V3_1_1_SIZE;
|
||||
// protocol version
|
||||
buffer[index++] = PROTOCOL_VERSION_V3_1_1;
|
||||
}
|
||||
|
||||
// connect flags
|
||||
byte connectFlags = 0x00;
|
||||
connectFlags |= (usernameUtf8 != null) ? (byte)(1 << USERNAME_FLAG_OFFSET) : (byte)0x00;
|
||||
connectFlags |= (passwordUtf8 != null) ? (byte)(1 << PASSWORD_FLAG_OFFSET) : (byte)0x00;
|
||||
connectFlags |= (this.willRetain) ? (byte)(1 << WILL_RETAIN_FLAG_OFFSET) : (byte)0x00;
|
||||
// only if will flag is set, we have to use will QoS level (otherwise is MUST be 0)
|
||||
if (this.willFlag)
|
||||
connectFlags |= (byte)(this.willQosLevel << WILL_QOS_FLAG_OFFSET);
|
||||
connectFlags |= (this.willFlag) ? (byte)(1 << WILL_FLAG_OFFSET) : (byte)0x00;
|
||||
connectFlags |= (this.cleanSession) ? (byte)(1 << CLEAN_SESSION_FLAG_OFFSET) : (byte)0x00;
|
||||
buffer[index++] = connectFlags;
|
||||
|
||||
// keep alive period
|
||||
buffer[index++] = (byte)((this.keepAlivePeriod >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(this.keepAlivePeriod & 0x00FF); // LSB
|
||||
|
||||
// client identifier
|
||||
buffer[index++] = (byte)((clientIdUtf8.Length >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(clientIdUtf8.Length & 0x00FF); // LSB
|
||||
Array.Copy(clientIdUtf8, 0, buffer, index, clientIdUtf8.Length);
|
||||
index += clientIdUtf8.Length;
|
||||
|
||||
// will topic
|
||||
if (this.willFlag && (willTopicUtf8 != null))
|
||||
{
|
||||
buffer[index++] = (byte)((willTopicUtf8.Length >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(willTopicUtf8.Length & 0x00FF); // LSB
|
||||
Array.Copy(willTopicUtf8, 0, buffer, index, willTopicUtf8.Length);
|
||||
index += willTopicUtf8.Length;
|
||||
}
|
||||
|
||||
// will message
|
||||
if (this.willFlag && (willMessageUtf8 != null))
|
||||
{
|
||||
buffer[index++] = (byte)((willMessageUtf8.Length >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(willMessageUtf8.Length & 0x00FF); // LSB
|
||||
Array.Copy(willMessageUtf8, 0, buffer, index, willMessageUtf8.Length);
|
||||
index += willMessageUtf8.Length;
|
||||
}
|
||||
|
||||
// username
|
||||
if (usernameUtf8 != null)
|
||||
{
|
||||
buffer[index++] = (byte)((usernameUtf8.Length >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(usernameUtf8.Length & 0x00FF); // LSB
|
||||
Array.Copy(usernameUtf8, 0, buffer, index, usernameUtf8.Length);
|
||||
index += usernameUtf8.Length;
|
||||
}
|
||||
|
||||
// password
|
||||
if (passwordUtf8 != null)
|
||||
{
|
||||
buffer[index++] = (byte)((passwordUtf8.Length >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(passwordUtf8.Length & 0x00FF); // LSB
|
||||
Array.Copy(passwordUtf8, 0, buffer, index, passwordUtf8.Length);
|
||||
index += passwordUtf8.Length;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"CONNECT",
|
||||
new object[] { "protocolName", "protocolVersion", "clientId", "willFlag", "willRetain", "willQosLevel", "willTopic", "willMessage", "username", "password", "cleanSession", "keepAlivePeriod" },
|
||||
new object[] { this.protocolName, this.protocolVersion, this.clientId, this.willFlag, this.willRetain, this.willQosLevel, this.willTopic, this.willMessage, this.username, this.password, this.cleanSession, this.keepAlivePeriod });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
using System;
|
||||
#else
|
||||
using Microsoft.SPOT;
|
||||
#endif
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Event Args class for CONNECT message received from client
|
||||
/// </summary>
|
||||
public class MqttMsgConnectEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Message received from client
|
||||
/// </summary>
|
||||
public MqttMsgConnect Message { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="msg">CONNECT message received from client</param>
|
||||
public MqttMsgConnectEventArgs(MqttMsgConnect connect)
|
||||
{
|
||||
this.Message = connect;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Context for MQTT message
|
||||
/// </summary>
|
||||
public class MqttMsgContext
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT message
|
||||
/// </summary>
|
||||
public MqttMsgBase Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MQTT message state
|
||||
/// </summary>
|
||||
public MqttMsgState State { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flow of the message
|
||||
/// </summary>
|
||||
public MqttMsgFlow Flow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp in ticks (for retry)
|
||||
/// </summary>
|
||||
public int Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Attempt (for retry)
|
||||
/// </summary>
|
||||
public int Attempt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique key
|
||||
/// </summary>
|
||||
public string Key
|
||||
{
|
||||
get { return this.Flow + "_" + this.Message.MessageId; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flow of the message
|
||||
/// </summary>
|
||||
public enum MqttMsgFlow
|
||||
{
|
||||
/// <summary>
|
||||
/// To publish to subscribers
|
||||
/// </summary>
|
||||
ToPublish,
|
||||
|
||||
/// <summary>
|
||||
/// To acknowledge to publisher
|
||||
/// </summary>
|
||||
ToAcknowledge
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MQTT message state
|
||||
/// </summary>
|
||||
public enum MqttMsgState
|
||||
{
|
||||
/// <summary>
|
||||
/// QOS = 0, Message queued
|
||||
/// </summary>
|
||||
QueuedQos0,
|
||||
|
||||
/// <summary>
|
||||
/// QOS = 1, Message queued
|
||||
/// </summary>
|
||||
QueuedQos1,
|
||||
|
||||
/// <summary>
|
||||
/// QOS = 2, Message queued
|
||||
/// </summary>
|
||||
QueuedQos2,
|
||||
|
||||
/// <summary>
|
||||
/// QOS = 1, PUBLISH sent, wait for PUBACK
|
||||
/// </summary>
|
||||
WaitForPuback,
|
||||
|
||||
/// <summary>
|
||||
/// QOS = 2, PUBLISH sent, wait for PUBREC
|
||||
/// </summary>
|
||||
WaitForPubrec,
|
||||
|
||||
/// <summary>
|
||||
/// QOS = 2, PUBREC sent, wait for PUBREL
|
||||
/// </summary>
|
||||
WaitForPubrel,
|
||||
|
||||
/// <summary>
|
||||
/// QOS = 2, PUBREL sent, wait for PUBCOMP
|
||||
/// </summary>
|
||||
WaitForPubcomp,
|
||||
|
||||
/// <summary>
|
||||
/// QOS = 2, start first phase handshake send PUBREC
|
||||
/// </summary>
|
||||
SendPubrec,
|
||||
|
||||
/// <summary>
|
||||
/// QOS = 2, start second phase handshake send PUBREL
|
||||
/// </summary>
|
||||
SendPubrel,
|
||||
|
||||
/// <summary>
|
||||
/// QOS = 2, end second phase handshake send PUBCOMP
|
||||
/// </summary>
|
||||
SendPubcomp,
|
||||
|
||||
/// <summary>
|
||||
/// QOS = 1, PUBLISH received, send PUBACK
|
||||
/// </summary>
|
||||
SendPuback,
|
||||
|
||||
// [v3.1.1] SUBSCRIBE isn't "officially" QOS = 1
|
||||
/// <summary>
|
||||
/// Send SUBSCRIBE message
|
||||
/// </summary>
|
||||
SendSubscribe,
|
||||
|
||||
// [v3.1.1] UNSUBSCRIBE isn't "officially" QOS = 1
|
||||
/// <summary>
|
||||
/// Send UNSUBSCRIBE message
|
||||
/// </summary>
|
||||
SendUnsubscribe,
|
||||
|
||||
/// <summary>
|
||||
/// (QOS = 1), SUBSCRIBE sent, wait for SUBACK
|
||||
/// </summary>
|
||||
WaitForSuback,
|
||||
|
||||
/// <summary>
|
||||
/// (QOS = 1), UNSUBSCRIBE sent, wait for UNSUBACK
|
||||
/// </summary>
|
||||
WaitForUnsuback
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for DISCONNECT message from client to broker
|
||||
/// </summary>
|
||||
public class MqttMsgDisconnect : MqttMsgBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgDisconnect()
|
||||
{
|
||||
this.type = MQTT_MSG_DISCONNECT_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a DISCONNECT message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>DISCONNECT message instance</returns>
|
||||
public static MqttMsgDisconnect Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
MqttMsgDisconnect msg = new MqttMsgDisconnect();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_DISCONNECT_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
// NOTE : remainingLength must be 0
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
byte[] buffer = new byte[2];
|
||||
int index = 0;
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_DISCONNECT_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_DISCONNECT_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
buffer[index++] = (MQTT_MSG_DISCONNECT_TYPE << MSG_TYPE_OFFSET);
|
||||
buffer[index++] = 0x00;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"DISCONNECT",
|
||||
null,
|
||||
null);
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for PINGREQ message from client to broker
|
||||
/// </summary>
|
||||
public class MqttMsgPingReq : MqttMsgBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgPingReq()
|
||||
{
|
||||
this.type = MQTT_MSG_PINGREQ_TYPE;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
byte[] buffer = new byte[2];
|
||||
int index = 0;
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_PINGREQ_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PINGREQ_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
buffer[index++] = (MQTT_MSG_PINGREQ_TYPE << MSG_TYPE_OFFSET);
|
||||
buffer[index++] = 0x00;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a PINGREQ message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>PINGREQ message instance</returns>
|
||||
public static MqttMsgPingReq Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
MqttMsgPingReq msg = new MqttMsgPingReq();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PINGREQ_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// already know remaininglength is zero (MQTT specification),
|
||||
// so it isn't necessary to read other data from socket
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"PINGREQ",
|
||||
null,
|
||||
null);
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for PINGRESP message from client to broker
|
||||
/// </summary>
|
||||
public class MqttMsgPingResp : MqttMsgBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgPingResp()
|
||||
{
|
||||
this.type = MQTT_MSG_PINGRESP_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a PINGRESP message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>PINGRESP message instance</returns>
|
||||
public static MqttMsgPingResp Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
MqttMsgPingResp msg = new MqttMsgPingResp();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PINGRESP_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// already know remaininglength is zero (MQTT specification),
|
||||
// so it isn't necessary to read other data from socket
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
byte[] buffer = new byte[2];
|
||||
int index = 0;
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_PINGRESP_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PINGRESP_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
buffer[index++] = (MQTT_MSG_PINGRESP_TYPE << MSG_TYPE_OFFSET);
|
||||
buffer[index++] = 0x00;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"PINGRESP",
|
||||
null,
|
||||
null);
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for PUBACK message from broker to client
|
||||
/// </summary>
|
||||
public class MqttMsgPuback : MqttMsgBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgPuback()
|
||||
{
|
||||
this.type = MQTT_MSG_PUBACK_TYPE;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
int fixedHeaderSize = 0;
|
||||
int varHeaderSize = 0;
|
||||
int payloadSize = 0;
|
||||
int remainingLength = 0;
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
|
||||
// message identifier
|
||||
varHeaderSize += MESSAGE_ID_SIZE;
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
int temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do
|
||||
{
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_PUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBACK_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
buffer[index++] = (MQTT_MSG_PUBACK_TYPE << MSG_TYPE_OFFSET);
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
// get message identifier
|
||||
buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a PUBACK message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>PUBACK message instance</returns>
|
||||
public static MqttMsgPuback Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
MqttMsgPuback msg = new MqttMsgPuback();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBACK_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
buffer = new byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
channel.Receive(buffer);
|
||||
|
||||
// message id
|
||||
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
|
||||
msg.messageId |= (buffer[index++]);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"PUBACK",
|
||||
new object[] { "messageId" },
|
||||
new object[] { this.messageId });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for PUBCOMP message from broker to client
|
||||
/// </summary>
|
||||
public class MqttMsgPubcomp : MqttMsgBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgPubcomp()
|
||||
{
|
||||
this.type = MQTT_MSG_PUBCOMP_TYPE;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
int fixedHeaderSize = 0;
|
||||
int varHeaderSize = 0;
|
||||
int payloadSize = 0;
|
||||
int remainingLength = 0;
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
|
||||
// message identifier
|
||||
varHeaderSize += MESSAGE_ID_SIZE;
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
int temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do
|
||||
{
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_PUBCOMP_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBCOMP_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
buffer[index++] = (MQTT_MSG_PUBCOMP_TYPE << MSG_TYPE_OFFSET);
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
// get message identifier
|
||||
buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a PUBCOMP message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>PUBCOMP message instance</returns>
|
||||
public static MqttMsgPubcomp Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
MqttMsgPubcomp msg = new MqttMsgPubcomp();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBCOMP_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
buffer = new byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
channel.Receive(buffer);
|
||||
|
||||
// message id
|
||||
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
|
||||
msg.messageId |= (buffer[index++]);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"PUBCOMP",
|
||||
new object[] { "messageId" },
|
||||
new object[] { this.messageId });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages {
|
||||
/// <summary>
|
||||
/// Class for PUBLISH message from client to broker
|
||||
/// </summary>
|
||||
public class MqttMsgPublish : MqttMsgBase {
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// Message topic
|
||||
/// </summary>
|
||||
public String Topic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Message data
|
||||
/// </summary>
|
||||
public Byte[] Message { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgPublish() {
|
||||
this.type = MQTT_MSG_PUBLISH_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="topic">Message topic</param>
|
||||
/// <param name="message">Message data</param>
|
||||
public MqttMsgPublish(String topic, Byte[] message) : this(topic, message, false, QOS_LEVEL_AT_MOST_ONCE, false) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="topic">Message topic</param>
|
||||
/// <param name="message">Message data</param>
|
||||
/// <param name="dupFlag">Duplicate flag</param>
|
||||
/// <param name="qosLevel">Quality of Service level</param>
|
||||
/// <param name="retain">Retain flag</param>
|
||||
public MqttMsgPublish(String topic, Byte[] message, Boolean dupFlag, Byte qosLevel, Boolean retain) : base() {
|
||||
this.type = MQTT_MSG_PUBLISH_TYPE;
|
||||
this.Topic = topic;
|
||||
this.Message = message;
|
||||
this.dupFlag = dupFlag;
|
||||
this.qosLevel = qosLevel;
|
||||
this.retain = retain;
|
||||
this.messageId = 0;
|
||||
}
|
||||
|
||||
public override Byte[] GetBytes(Byte protocolVersion) {
|
||||
Int32 fixedHeaderSize = 0;
|
||||
Int32 varHeaderSize = 0;
|
||||
Int32 payloadSize = 0;
|
||||
Int32 remainingLength = 0;
|
||||
Byte[] buffer;
|
||||
Int32 index = 0;
|
||||
|
||||
// topic can't contain wildcards
|
||||
if ((this.Topic.IndexOf('#') != -1) || (this.Topic.IndexOf('+') != -1)) {
|
||||
throw new MqttClientException(MqttClientErrorCode.TopicWildcard);
|
||||
}
|
||||
|
||||
// check topic length
|
||||
if ((this.Topic.Length < MIN_TOPIC_LENGTH) || (this.Topic.Length > MAX_TOPIC_LENGTH)) {
|
||||
throw new MqttClientException(MqttClientErrorCode.TopicLength);
|
||||
}
|
||||
|
||||
// check wrong QoS level (both bits can't be set 1)
|
||||
if (this.qosLevel > QOS_LEVEL_EXACTLY_ONCE) {
|
||||
throw new MqttClientException(MqttClientErrorCode.QosNotAllowed);
|
||||
}
|
||||
|
||||
Byte[] topicUtf8 = Encoding.UTF8.GetBytes(this.Topic);
|
||||
|
||||
// topic name
|
||||
varHeaderSize += topicUtf8.Length + 2;
|
||||
|
||||
// message id is valid only with QOS level 1 or QOS level 2
|
||||
if ((this.qosLevel == QOS_LEVEL_AT_LEAST_ONCE) || (this.qosLevel == QOS_LEVEL_EXACTLY_ONCE)) {
|
||||
varHeaderSize += MESSAGE_ID_SIZE;
|
||||
}
|
||||
|
||||
// check on message with zero length
|
||||
if (this.Message != null) {
|
||||
// message data
|
||||
payloadSize += this.Message.Length;
|
||||
}
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
Int32 temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do {
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
buffer[index] = (Byte)((MQTT_MSG_PUBLISH_TYPE << MSG_TYPE_OFFSET) | (this.qosLevel << QOS_LEVEL_OFFSET));
|
||||
buffer[index] |= this.dupFlag ? (Byte)(1 << DUP_FLAG_OFFSET) : (Byte)0x00;
|
||||
buffer[index] |= this.retain ? (Byte)(1 << RETAIN_FLAG_OFFSET) : (Byte)0x00;
|
||||
index++;
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
// topic name
|
||||
buffer[index++] = (Byte)((topicUtf8.Length >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (Byte)(topicUtf8.Length & 0x00FF); // LSB
|
||||
Array.Copy(topicUtf8, 0, buffer, index, topicUtf8.Length);
|
||||
index += topicUtf8.Length;
|
||||
|
||||
// message id is valid only with QOS level 1 or QOS level 2
|
||||
if ((this.qosLevel == QOS_LEVEL_AT_LEAST_ONCE) ||
|
||||
(this.qosLevel == QOS_LEVEL_EXACTLY_ONCE)) {
|
||||
// check message identifier assigned
|
||||
if (this.messageId == 0) {
|
||||
throw new MqttClientException(MqttClientErrorCode.WrongMessageId);
|
||||
}
|
||||
|
||||
buffer[index++] = (Byte)((this.messageId >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (Byte)(this.messageId & 0x00FF); // LSB
|
||||
}
|
||||
|
||||
// check on message with zero length
|
||||
if (this.Message != null) {
|
||||
// message data
|
||||
Array.Copy(this.Message, 0, buffer, index, this.Message.Length);
|
||||
index += this.Message.Length;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a PUBLISH message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>PUBLISH message instance</returns>
|
||||
public static MqttMsgPublish Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) {
|
||||
Byte[] buffer;
|
||||
Int32 index = 0;
|
||||
Byte[] topicUtf8;
|
||||
Int32 topicUtf8Length;
|
||||
MqttMsgPublish msg = new MqttMsgPublish();
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
Int32 remainingLength = decodeRemainingLength(channel);
|
||||
buffer = new Byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
Int32 received = channel.Receive(buffer);
|
||||
|
||||
// topic name
|
||||
topicUtf8Length = ((buffer[index++] << 8) & 0xFF00);
|
||||
topicUtf8Length |= buffer[index++];
|
||||
topicUtf8 = new Byte[topicUtf8Length];
|
||||
Array.Copy(buffer, index, topicUtf8, 0, topicUtf8Length);
|
||||
index += topicUtf8Length;
|
||||
msg.Topic = new String(Encoding.UTF8.GetChars(topicUtf8));
|
||||
|
||||
// read QoS level from fixed header
|
||||
msg.qosLevel = (Byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET);
|
||||
// check wrong QoS level (both bits can't be set 1)
|
||||
if (msg.qosLevel > QOS_LEVEL_EXACTLY_ONCE) {
|
||||
throw new MqttClientException(MqttClientErrorCode.QosNotAllowed);
|
||||
}
|
||||
// read DUP flag from fixed header
|
||||
msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01);
|
||||
// read retain flag from fixed header
|
||||
msg.retain = (((fixedHeaderFirstByte & RETAIN_FLAG_MASK) >> RETAIN_FLAG_OFFSET) == 0x01);
|
||||
|
||||
// message id is valid only with QOS level 1 or QOS level 2
|
||||
if ((msg.qosLevel == QOS_LEVEL_AT_LEAST_ONCE) ||
|
||||
(msg.qosLevel == QOS_LEVEL_EXACTLY_ONCE)) {
|
||||
// message id
|
||||
msg.messageId = (UInt16)((buffer[index++] << 8) & 0xFF00);
|
||||
msg.messageId |= (buffer[index++]);
|
||||
}
|
||||
|
||||
// get payload with message data
|
||||
Int32 messageSize = remainingLength - index;
|
||||
Int32 remaining = messageSize;
|
||||
Int32 messageOffset = 0;
|
||||
msg.Message = new Byte[messageSize];
|
||||
|
||||
// BUG FIX 26/07/2013 : receiving large payload
|
||||
|
||||
// copy first part of payload data received
|
||||
Array.Copy(buffer, index, msg.Message, messageOffset, received - index);
|
||||
remaining -= (received - index);
|
||||
messageOffset += (received - index);
|
||||
|
||||
// if payload isn't finished
|
||||
while (remaining > 0) {
|
||||
// receive other payload data
|
||||
received = channel.Receive(buffer);
|
||||
Array.Copy(buffer, 0, msg.Message, messageOffset, received);
|
||||
remaining -= received;
|
||||
messageOffset += received;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override String ToString() {
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"PUBLISH",
|
||||
new Object[] { "messageId", "topic", "message" },
|
||||
new Object[] { this.messageId, this.Topic, this.Message });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
using System;
|
||||
#else
|
||||
using Microsoft.SPOT;
|
||||
#endif
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Event Args class for PUBLISH message received from broker
|
||||
/// </summary>
|
||||
public class MqttMsgPublishEventArgs : EventArgs
|
||||
{
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// Message topic
|
||||
/// </summary>
|
||||
public string Topic
|
||||
{
|
||||
get { return this.topic; }
|
||||
internal set { this.topic = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message data
|
||||
/// </summary>
|
||||
public byte[] Message
|
||||
{
|
||||
get { return this.message; }
|
||||
internal set { this.message = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Duplicate message flag
|
||||
/// </summary>
|
||||
public bool DupFlag
|
||||
{
|
||||
get { return this.dupFlag; }
|
||||
set { this.dupFlag = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quality of Service level
|
||||
/// </summary>
|
||||
public byte QosLevel
|
||||
{
|
||||
get { return this.qosLevel; }
|
||||
internal set { this.qosLevel = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retain message flag
|
||||
/// </summary>
|
||||
public bool Retain
|
||||
{
|
||||
get { return this.retain; }
|
||||
internal set { this.retain = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// message topic
|
||||
private string topic;
|
||||
// message data
|
||||
private byte[] message;
|
||||
// duplicate delivery
|
||||
private bool dupFlag;
|
||||
// quality of service level
|
||||
private byte qosLevel;
|
||||
// retain flag
|
||||
private bool retain;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="topic">Message topic</param>
|
||||
/// <param name="message">Message data</param>
|
||||
/// <param name="dupFlag">Duplicate delivery flag</param>
|
||||
/// <param name="qosLevel">Quality of Service level</param>
|
||||
/// <param name="retain">Retain flag</param>
|
||||
public MqttMsgPublishEventArgs(string topic,
|
||||
byte[] message,
|
||||
bool dupFlag,
|
||||
byte qosLevel,
|
||||
bool retain)
|
||||
{
|
||||
this.topic = topic;
|
||||
this.message = message;
|
||||
this.dupFlag = dupFlag;
|
||||
this.qosLevel = qosLevel;
|
||||
this.retain = retain;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
using System;
|
||||
#else
|
||||
using Microsoft.SPOT;
|
||||
#endif
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Event Args class for published message
|
||||
/// </summary>
|
||||
public class MqttMsgPublishedEventArgs : EventArgs
|
||||
{
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// Message identifier
|
||||
/// </summary>
|
||||
public ushort MessageId
|
||||
{
|
||||
get { return this.messageId; }
|
||||
internal set { this.messageId = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message published (or failed due to retries)
|
||||
/// </summary>
|
||||
public bool IsPublished
|
||||
{
|
||||
get { return this.isPublished; }
|
||||
internal set { this.isPublished = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// message identifier
|
||||
ushort messageId;
|
||||
|
||||
// published flag
|
||||
bool isPublished;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor (published message)
|
||||
/// </summary>
|
||||
/// <param name="messageId">Message identifier published</param>
|
||||
public MqttMsgPublishedEventArgs(ushort messageId)
|
||||
: this(messageId, true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="messageId">Message identifier</param>
|
||||
/// <param name="isPublished">Publish flag</param>
|
||||
public MqttMsgPublishedEventArgs(ushort messageId, bool isPublished)
|
||||
{
|
||||
this.messageId = messageId;
|
||||
this.isPublished = isPublished;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for PUBREC message from broker to client
|
||||
/// </summary>
|
||||
public class MqttMsgPubrec : MqttMsgBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgPubrec()
|
||||
{
|
||||
this.type = MQTT_MSG_PUBREC_TYPE;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
int fixedHeaderSize = 0;
|
||||
int varHeaderSize = 0;
|
||||
int payloadSize = 0;
|
||||
int remainingLength = 0;
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
|
||||
// message identifier
|
||||
varHeaderSize += MESSAGE_ID_SIZE;
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
int temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do
|
||||
{
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_PUBREC_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBREC_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
buffer[index++] = (MQTT_MSG_PUBREC_TYPE << MSG_TYPE_OFFSET);
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
// get message identifier
|
||||
buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a PUBREC message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>PUBREC message instance</returns>
|
||||
public static MqttMsgPubrec Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
MqttMsgPubrec msg = new MqttMsgPubrec();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBREC_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
buffer = new byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
channel.Receive(buffer);
|
||||
|
||||
// message id
|
||||
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
|
||||
msg.messageId |= (buffer[index++]);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"PUBREC",
|
||||
new object[] { "messageId" },
|
||||
new object[] { this.messageId });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for PUBREL message from client top broker
|
||||
/// </summary>
|
||||
public class MqttMsgPubrel : MqttMsgBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgPubrel()
|
||||
{
|
||||
this.type = MQTT_MSG_PUBREL_TYPE;
|
||||
// PUBREL message use QoS Level 1 (not "officially" in 3.1.1)
|
||||
this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
int fixedHeaderSize = 0;
|
||||
int varHeaderSize = 0;
|
||||
int payloadSize = 0;
|
||||
int remainingLength = 0;
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
|
||||
// message identifier
|
||||
varHeaderSize += MESSAGE_ID_SIZE;
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
int temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do
|
||||
{
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_PUBREL_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBREL_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
{
|
||||
buffer[index] = (byte)((MQTT_MSG_PUBREL_TYPE << MSG_TYPE_OFFSET) |
|
||||
(this.qosLevel << QOS_LEVEL_OFFSET));
|
||||
buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00;
|
||||
index++;
|
||||
}
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
// get next message identifier
|
||||
buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a PUBREL message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>PUBREL message instance</returns>
|
||||
public static MqttMsgPubrel Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
MqttMsgPubrel msg = new MqttMsgPubrel();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBREL_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
buffer = new byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
channel.Receive(buffer);
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1)
|
||||
{
|
||||
// only 3.1.0
|
||||
|
||||
// read QoS level from fixed header (would be QoS Level 1)
|
||||
msg.qosLevel = (byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET);
|
||||
// read DUP flag from fixed header
|
||||
msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01);
|
||||
}
|
||||
|
||||
// message id
|
||||
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
|
||||
msg.messageId |= (buffer[index++]);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"PUBREL",
|
||||
new object[] { "messageId" },
|
||||
new object[] { this.messageId });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for SUBACK message from broker to client
|
||||
/// </summary>
|
||||
public class MqttMsgSuback : MqttMsgBase
|
||||
{
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// List of granted QOS Levels
|
||||
/// </summary>
|
||||
public byte[] GrantedQoSLevels
|
||||
{
|
||||
get { return this.grantedQosLevels; }
|
||||
set { this.grantedQosLevels = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// granted QOS levels
|
||||
byte[] grantedQosLevels;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgSuback()
|
||||
{
|
||||
this.type = MQTT_MSG_SUBACK_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a SUBACK message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>SUBACK message instance</returns>
|
||||
public static MqttMsgSuback Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
MqttMsgSuback msg = new MqttMsgSuback();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_SUBACK_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
buffer = new byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
channel.Receive(buffer);
|
||||
|
||||
// message id
|
||||
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
|
||||
msg.messageId |= (buffer[index++]);
|
||||
|
||||
// payload contains QoS levels granted
|
||||
msg.grantedQosLevels = new byte[remainingLength - MESSAGE_ID_SIZE];
|
||||
int qosIdx = 0;
|
||||
do
|
||||
{
|
||||
msg.grantedQosLevels[qosIdx++] = buffer[index++];
|
||||
} while (index < remainingLength);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
int fixedHeaderSize = 0;
|
||||
int varHeaderSize = 0;
|
||||
int payloadSize = 0;
|
||||
int remainingLength = 0;
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
|
||||
// message identifier
|
||||
varHeaderSize += MESSAGE_ID_SIZE;
|
||||
|
||||
int grantedQosIdx = 0;
|
||||
for (grantedQosIdx = 0; grantedQosIdx < this.grantedQosLevels.Length; grantedQosIdx++)
|
||||
{
|
||||
payloadSize++;
|
||||
}
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
int temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do
|
||||
{
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_SUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_SUBACK_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
buffer[index++] = (byte)(MQTT_MSG_SUBACK_TYPE << MSG_TYPE_OFFSET);
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
// message id
|
||||
buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB
|
||||
|
||||
// payload contains QoS levels granted
|
||||
for (grantedQosIdx = 0; grantedQosIdx < this.grantedQosLevels.Length; grantedQosIdx++)
|
||||
{
|
||||
buffer[index++] = this.grantedQosLevels[grantedQosIdx];
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"SUBACK",
|
||||
new object[] { "messageId", "grantedQosLevels" },
|
||||
new object[] { this.messageId, this.grantedQosLevels });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
// if NOT .Net Micro Framework
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
using System.Collections.Generic;
|
||||
#endif
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for SUBSCRIBE message from client to broker
|
||||
/// </summary>
|
||||
public class MqttMsgSubscribe : MqttMsgBase
|
||||
{
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// List of topics to subscribe
|
||||
/// </summary>
|
||||
public string[] Topics
|
||||
{
|
||||
get { return this.topics; }
|
||||
set { this.topics = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of QOS Levels related to topics
|
||||
/// </summary>
|
||||
public byte[] QoSLevels
|
||||
{
|
||||
get { return this.qosLevels; }
|
||||
set { this.qosLevels = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// topics to subscribe
|
||||
string[] topics;
|
||||
// QOS levels related to topics
|
||||
byte[] qosLevels;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgSubscribe()
|
||||
{
|
||||
this.type = MQTT_MSG_SUBSCRIBE_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="topics">List of topics to subscribe</param>
|
||||
/// <param name="qosLevels">List of QOS Levels related to topics</param>
|
||||
public MqttMsgSubscribe(string[] topics, byte[] qosLevels)
|
||||
{
|
||||
this.type = MQTT_MSG_SUBSCRIBE_TYPE;
|
||||
|
||||
this.topics = topics;
|
||||
this.qosLevels = qosLevels;
|
||||
|
||||
// SUBSCRIBE message uses QoS Level 1 (not "officially" in 3.1.1)
|
||||
this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a SUBSCRIBE message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>SUBSCRIBE message instance</returns>
|
||||
public static MqttMsgSubscribe Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
byte[] topicUtf8;
|
||||
int topicUtf8Length;
|
||||
MqttMsgSubscribe msg = new MqttMsgSubscribe();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_SUBSCRIBE_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
buffer = new byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
int received = channel.Receive(buffer);
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1)
|
||||
{
|
||||
// only 3.1.0
|
||||
|
||||
// read QoS level from fixed header
|
||||
msg.qosLevel = (byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET);
|
||||
// read DUP flag from fixed header
|
||||
msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01);
|
||||
// retain flag not used
|
||||
msg.retain = false;
|
||||
}
|
||||
|
||||
// message id
|
||||
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
|
||||
msg.messageId |= (buffer[index++]);
|
||||
|
||||
// payload contains topics and QoS levels
|
||||
// NOTE : before, I don't know how many topics will be in the payload (so use List)
|
||||
|
||||
// if .Net Micro Framework
|
||||
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
|
||||
IList tmpTopics = new ArrayList();
|
||||
IList tmpQosLevels = new ArrayList();
|
||||
// else other frameworks (.Net, .Net Compact, Mono, Windows Phone)
|
||||
#else
|
||||
IList<String> tmpTopics = new List<String>();
|
||||
IList<byte> tmpQosLevels = new List<byte>();
|
||||
#endif
|
||||
do
|
||||
{
|
||||
// topic name
|
||||
topicUtf8Length = ((buffer[index++] << 8) & 0xFF00);
|
||||
topicUtf8Length |= buffer[index++];
|
||||
topicUtf8 = new byte[topicUtf8Length];
|
||||
Array.Copy(buffer, index, topicUtf8, 0, topicUtf8Length);
|
||||
index += topicUtf8Length;
|
||||
tmpTopics.Add(new String(Encoding.UTF8.GetChars(topicUtf8)));
|
||||
|
||||
// QoS level
|
||||
tmpQosLevels.Add(buffer[index++]);
|
||||
|
||||
} while (index < remainingLength);
|
||||
|
||||
// copy from list to array
|
||||
msg.topics = new string[tmpTopics.Count];
|
||||
msg.qosLevels = new byte[tmpQosLevels.Count];
|
||||
for (int i = 0; i < tmpTopics.Count; i++)
|
||||
{
|
||||
msg.topics[i] = (string)tmpTopics[i];
|
||||
msg.qosLevels[i] = (byte)tmpQosLevels[i];
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
int fixedHeaderSize = 0;
|
||||
int varHeaderSize = 0;
|
||||
int payloadSize = 0;
|
||||
int remainingLength = 0;
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
|
||||
// topics list empty
|
||||
if ((this.topics == null) || (this.topics.Length == 0))
|
||||
throw new MqttClientException(MqttClientErrorCode.TopicsEmpty);
|
||||
|
||||
// qos levels list empty
|
||||
if ((this.qosLevels == null) || (this.qosLevels.Length == 0))
|
||||
throw new MqttClientException(MqttClientErrorCode.QosLevelsEmpty);
|
||||
|
||||
// topics and qos levels lists length don't match
|
||||
if (this.topics.Length != this.qosLevels.Length)
|
||||
throw new MqttClientException(MqttClientErrorCode.TopicsQosLevelsNotMatch);
|
||||
|
||||
// message identifier
|
||||
varHeaderSize += MESSAGE_ID_SIZE;
|
||||
|
||||
int topicIdx = 0;
|
||||
byte[][] topicsUtf8 = new byte[this.topics.Length][];
|
||||
|
||||
for (topicIdx = 0; topicIdx < this.topics.Length; topicIdx++)
|
||||
{
|
||||
// check topic length
|
||||
if ((this.topics[topicIdx].Length < MIN_TOPIC_LENGTH) || (this.topics[topicIdx].Length > MAX_TOPIC_LENGTH))
|
||||
throw new MqttClientException(MqttClientErrorCode.TopicLength);
|
||||
|
||||
topicsUtf8[topicIdx] = Encoding.UTF8.GetBytes(this.topics[topicIdx]);
|
||||
payloadSize += 2; // topic size (MSB, LSB)
|
||||
payloadSize += topicsUtf8[topicIdx].Length;
|
||||
payloadSize++; // byte for QoS
|
||||
}
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
int temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do
|
||||
{
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_SUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_SUBSCRIBE_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
{
|
||||
buffer[index] = (byte)((MQTT_MSG_SUBSCRIBE_TYPE << MSG_TYPE_OFFSET) |
|
||||
(this.qosLevel << QOS_LEVEL_OFFSET));
|
||||
buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00;
|
||||
index++;
|
||||
}
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
// check message identifier assigned (SUBSCRIBE uses QoS Level 1, so message id is mandatory)
|
||||
if (this.messageId == 0)
|
||||
throw new MqttClientException(MqttClientErrorCode.WrongMessageId);
|
||||
buffer[index++] = (byte)((messageId >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(messageId & 0x00FF); // LSB
|
||||
|
||||
topicIdx = 0;
|
||||
for (topicIdx = 0; topicIdx < this.topics.Length; topicIdx++)
|
||||
{
|
||||
// topic name
|
||||
buffer[index++] = (byte)((topicsUtf8[topicIdx].Length >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(topicsUtf8[topicIdx].Length & 0x00FF); // LSB
|
||||
Array.Copy(topicsUtf8[topicIdx], 0, buffer, index, topicsUtf8[topicIdx].Length);
|
||||
index += topicsUtf8[topicIdx].Length;
|
||||
|
||||
// requested QoS
|
||||
buffer[index++] = this.qosLevels[topicIdx];
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"SUBSCRIBE",
|
||||
new object[] { "messageId", "topics", "qosLevels" },
|
||||
new object[] { this.messageId, this.topics, this.qosLevels });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
using System;
|
||||
#else
|
||||
using Microsoft.SPOT;
|
||||
#endif
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Event Args class for subscribe request on topics
|
||||
/// </summary>
|
||||
public class MqttMsgSubscribeEventArgs : EventArgs
|
||||
{
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// Message identifier
|
||||
/// </summary>
|
||||
public ushort MessageId
|
||||
{
|
||||
get { return this.messageId; }
|
||||
internal set { this.messageId = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Topics requested to subscribe
|
||||
/// </summary>
|
||||
public string[] Topics
|
||||
{
|
||||
get { return this.topics; }
|
||||
internal set { this.topics = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of QOS Levels requested
|
||||
/// </summary>
|
||||
public byte[] QoSLevels
|
||||
{
|
||||
get { return this.qosLevels; }
|
||||
internal set { this.qosLevels = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// message identifier
|
||||
ushort messageId;
|
||||
// topics requested to subscribe
|
||||
string[] topics;
|
||||
// QoS levels requested
|
||||
byte[] qosLevels;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="messageId">Message identifier for subscribe topics request</param>
|
||||
/// <param name="topics">Topics requested to subscribe</param>
|
||||
/// <param name="qosLevels">List of QOS Levels requested</param>
|
||||
public MqttMsgSubscribeEventArgs(ushort messageId, string[] topics, byte[] qosLevels)
|
||||
{
|
||||
this.messageId = messageId;
|
||||
this.topics = topics;
|
||||
this.qosLevels = qosLevels;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
using System;
|
||||
#else
|
||||
using Microsoft.SPOT;
|
||||
#endif
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Event Args class for subscribed topics
|
||||
/// </summary>
|
||||
public class MqttMsgSubscribedEventArgs : EventArgs
|
||||
{
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// Message identifier
|
||||
/// </summary>
|
||||
public ushort MessageId
|
||||
{
|
||||
get { return this.messageId; }
|
||||
internal set { this.messageId = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of granted QOS Levels
|
||||
/// </summary>
|
||||
public byte[] GrantedQoSLevels
|
||||
{
|
||||
get { return this.grantedQosLevels; }
|
||||
internal set { this.grantedQosLevels = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// message identifier
|
||||
ushort messageId;
|
||||
// granted QOS levels
|
||||
byte[] grantedQosLevels;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="messageId">Message identifier for subscribed topics</param>
|
||||
/// <param name="grantedQosLevels">List of granted QOS Levels</param>
|
||||
public MqttMsgSubscribedEventArgs(ushort messageId, byte[] grantedQosLevels)
|
||||
{
|
||||
this.messageId = messageId;
|
||||
this.grantedQosLevels = grantedQosLevels;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for UNSUBACK message from broker to client
|
||||
/// </summary>
|
||||
public class MqttMsgUnsuback : MqttMsgBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgUnsuback()
|
||||
{
|
||||
this.type = MQTT_MSG_UNSUBACK_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a UNSUBACK message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>UNSUBACK message instance</returns>
|
||||
public static MqttMsgUnsuback Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
MqttMsgUnsuback msg = new MqttMsgUnsuback();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_UNSUBACK_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
buffer = new byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
channel.Receive(buffer);
|
||||
|
||||
// message id
|
||||
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
|
||||
msg.messageId |= (buffer[index++]);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
int fixedHeaderSize = 0;
|
||||
int varHeaderSize = 0;
|
||||
int payloadSize = 0;
|
||||
int remainingLength = 0;
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
|
||||
// message identifier
|
||||
varHeaderSize += MESSAGE_ID_SIZE;
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
int temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do
|
||||
{
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_UNSUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_UNSUBACK_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
buffer[index++] = (byte)(MQTT_MSG_UNSUBACK_TYPE << MSG_TYPE_OFFSET);
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
// message id
|
||||
buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"UNSUBACK",
|
||||
new object[] { "messageId" },
|
||||
new object[] { this.messageId });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
// if NOT .Net Micro Framework
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
using System.Collections.Generic;
|
||||
#endif
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using uPLibrary.Networking.M2Mqtt.Exceptions;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for UNSUBSCRIBE message from client to broker
|
||||
/// </summary>
|
||||
public class MqttMsgUnsubscribe : MqttMsgBase
|
||||
{
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// List of topics to unsubscribe
|
||||
/// </summary>
|
||||
public string[] Topics
|
||||
{
|
||||
get { return this.topics; }
|
||||
set { this.topics = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// topics to unsubscribe
|
||||
string[] topics;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttMsgUnsubscribe()
|
||||
{
|
||||
this.type = MQTT_MSG_UNSUBSCRIBE_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="topics">List of topics to unsubscribe</param>
|
||||
public MqttMsgUnsubscribe(string[] topics)
|
||||
{
|
||||
this.type = MQTT_MSG_UNSUBSCRIBE_TYPE;
|
||||
|
||||
this.topics = topics;
|
||||
|
||||
// UNSUBSCRIBE message uses QoS Level 1 (not "officially" in 3.1.1)
|
||||
this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse bytes for a UNSUBSCRIBE message
|
||||
/// </summary>
|
||||
/// <param name="fixedHeaderFirstByte">First fixed header byte</param>
|
||||
/// <param name="protocolVersion">Protocol Version</param>
|
||||
/// <param name="channel">Channel connected to the broker</param>
|
||||
/// <returns>UNSUBSCRIBE message instance</returns>
|
||||
public static MqttMsgUnsubscribe Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
|
||||
{
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
byte[] topicUtf8;
|
||||
int topicUtf8Length;
|
||||
MqttMsgUnsubscribe msg = new MqttMsgUnsubscribe();
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
{
|
||||
// [v3.1.1] check flag bits
|
||||
if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_UNSUBSCRIBE_FLAG_BITS)
|
||||
throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
|
||||
}
|
||||
|
||||
// get remaining length and allocate buffer
|
||||
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
|
||||
buffer = new byte[remainingLength];
|
||||
|
||||
// read bytes from socket...
|
||||
int received = channel.Receive(buffer);
|
||||
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1)
|
||||
{
|
||||
// only 3.1.0
|
||||
|
||||
// read QoS level from fixed header
|
||||
msg.qosLevel = (byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET);
|
||||
// read DUP flag from fixed header
|
||||
msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01);
|
||||
// retain flag not used
|
||||
msg.retain = false;
|
||||
}
|
||||
|
||||
// message id
|
||||
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
|
||||
msg.messageId |= (buffer[index++]);
|
||||
|
||||
// payload contains topics
|
||||
// NOTE : before, I don't know how many topics will be in the payload (so use List)
|
||||
|
||||
// if .Net Micro Framework
|
||||
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
|
||||
IList tmpTopics = new ArrayList();
|
||||
// else other frameworks (.Net, .Net Compact, Mono, Windows Phone)
|
||||
#else
|
||||
IList<String> tmpTopics = new List<String>();
|
||||
#endif
|
||||
do
|
||||
{
|
||||
// topic name
|
||||
topicUtf8Length = ((buffer[index++] << 8) & 0xFF00);
|
||||
topicUtf8Length |= buffer[index++];
|
||||
topicUtf8 = new byte[topicUtf8Length];
|
||||
Array.Copy(buffer, index, topicUtf8, 0, topicUtf8Length);
|
||||
index += topicUtf8Length;
|
||||
tmpTopics.Add(new String(Encoding.UTF8.GetChars(topicUtf8)));
|
||||
} while (index < remainingLength);
|
||||
|
||||
// copy from list to array
|
||||
msg.topics = new string[tmpTopics.Count];
|
||||
for (int i = 0; i < tmpTopics.Count; i++)
|
||||
{
|
||||
msg.topics[i] = (string)tmpTopics[i];
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override byte[] GetBytes(byte protocolVersion)
|
||||
{
|
||||
int fixedHeaderSize = 0;
|
||||
int varHeaderSize = 0;
|
||||
int payloadSize = 0;
|
||||
int remainingLength = 0;
|
||||
byte[] buffer;
|
||||
int index = 0;
|
||||
|
||||
// topics list empty
|
||||
if ((this.topics == null) || (this.topics.Length == 0))
|
||||
throw new MqttClientException(MqttClientErrorCode.TopicsEmpty);
|
||||
|
||||
// message identifier
|
||||
varHeaderSize += MESSAGE_ID_SIZE;
|
||||
|
||||
int topicIdx = 0;
|
||||
byte[][] topicsUtf8 = new byte[this.topics.Length][];
|
||||
|
||||
for (topicIdx = 0; topicIdx < this.topics.Length; topicIdx++)
|
||||
{
|
||||
// check topic length
|
||||
if ((this.topics[topicIdx].Length < MIN_TOPIC_LENGTH) || (this.topics[topicIdx].Length > MAX_TOPIC_LENGTH))
|
||||
throw new MqttClientException(MqttClientErrorCode.TopicLength);
|
||||
|
||||
topicsUtf8[topicIdx] = Encoding.UTF8.GetBytes(this.topics[topicIdx]);
|
||||
payloadSize += 2; // topic size (MSB, LSB)
|
||||
payloadSize += topicsUtf8[topicIdx].Length;
|
||||
}
|
||||
|
||||
remainingLength += (varHeaderSize + payloadSize);
|
||||
|
||||
// first byte of fixed header
|
||||
fixedHeaderSize = 1;
|
||||
|
||||
int temp = remainingLength;
|
||||
// increase fixed header size based on remaining length
|
||||
// (each remaining length byte can encode until 128)
|
||||
do
|
||||
{
|
||||
fixedHeaderSize++;
|
||||
temp = temp / 128;
|
||||
} while (temp > 0);
|
||||
|
||||
// allocate buffer for message
|
||||
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
|
||||
|
||||
// first fixed header byte
|
||||
if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
|
||||
buffer[index++] = (MQTT_MSG_UNSUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_UNSUBSCRIBE_FLAG_BITS; // [v.3.1.1]
|
||||
else
|
||||
{
|
||||
buffer[index] = (byte)((MQTT_MSG_UNSUBSCRIBE_TYPE << MSG_TYPE_OFFSET) |
|
||||
(this.qosLevel << QOS_LEVEL_OFFSET));
|
||||
buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00;
|
||||
index++;
|
||||
}
|
||||
|
||||
// encode remaining length
|
||||
index = this.encodeRemainingLength(remainingLength, buffer, index);
|
||||
|
||||
// check message identifier assigned
|
||||
if (this.messageId == 0)
|
||||
throw new MqttClientException(MqttClientErrorCode.WrongMessageId);
|
||||
buffer[index++] = (byte)((messageId >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(messageId & 0x00FF); // LSB
|
||||
|
||||
topicIdx = 0;
|
||||
for (topicIdx = 0; topicIdx < this.topics.Length; topicIdx++)
|
||||
{
|
||||
// topic name
|
||||
buffer[index++] = (byte)((topicsUtf8[topicIdx].Length >> 8) & 0x00FF); // MSB
|
||||
buffer[index++] = (byte)(topicsUtf8[topicIdx].Length & 0x00FF); // LSB
|
||||
Array.Copy(topicsUtf8[topicIdx], 0, buffer, index, topicsUtf8[topicIdx].Length);
|
||||
index += topicsUtf8[topicIdx].Length;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if TRACE
|
||||
return this.GetTraceString(
|
||||
"UNSUBSCRIBE",
|
||||
new object[] { "messageId", "topics" },
|
||||
new object[] { this.messageId, this.topics });
|
||||
#else
|
||||
return base.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
using System;
|
||||
#else
|
||||
using Microsoft.SPOT;
|
||||
#endif
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Event Args class for unsubscribe request on topics
|
||||
/// </summary>
|
||||
public class MqttMsgUnsubscribeEventArgs : EventArgs
|
||||
{
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// Message identifier
|
||||
/// </summary>
|
||||
public ushort MessageId
|
||||
{
|
||||
get { return this.messageId; }
|
||||
internal set { this.messageId = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Topics requested to subscribe
|
||||
/// </summary>
|
||||
public string[] Topics
|
||||
{
|
||||
get { return this.topics; }
|
||||
internal set { this.topics = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// message identifier
|
||||
ushort messageId;
|
||||
// topics requested to unsubscribe
|
||||
string[] topics;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="messageId">Message identifier for subscribed topics</param>
|
||||
/// <param name="topics">Topics requested to subscribe</param>
|
||||
public MqttMsgUnsubscribeEventArgs(ushort messageId, string[] topics)
|
||||
{
|
||||
this.messageId = messageId;
|
||||
this.topics = topics;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
using System;
|
||||
#else
|
||||
using Microsoft.SPOT;
|
||||
#endif
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Event Args class for unsubscribed topic
|
||||
/// </summary>
|
||||
public class MqttMsgUnsubscribedEventArgs : EventArgs
|
||||
{
|
||||
#region Properties...
|
||||
|
||||
/// <summary>
|
||||
/// Message identifier
|
||||
/// </summary>
|
||||
public ushort MessageId
|
||||
{
|
||||
get { return this.messageId; }
|
||||
internal set { this.messageId = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// message identifier
|
||||
ushort messageId;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="messageId">Message identifier for unsubscribed topic</param>
|
||||
public MqttMsgUnsubscribedEventArgs(ushort messageId)
|
||||
{
|
||||
this.messageId = messageId;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// Supported SSL/TLS protocol versions
|
||||
/// </summary>
|
||||
public enum MqttSslProtocols
|
||||
{
|
||||
None,
|
||||
SSLv3,
|
||||
TLSv1_0,
|
||||
TLSv1_1,
|
||||
TLSv1_2
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings class for the MQTT broker
|
||||
/// </summary>
|
||||
public class MqttSettings
|
||||
{
|
||||
// default port for MQTT protocol
|
||||
public const int MQTT_BROKER_DEFAULT_PORT = 1883;
|
||||
public const int MQTT_BROKER_DEFAULT_SSL_PORT = 8883;
|
||||
// default timeout on receiving from client
|
||||
public const int MQTT_DEFAULT_TIMEOUT = 30000;
|
||||
// max publish, subscribe and unsubscribe retry for QoS Level 1 or 2
|
||||
public const int MQTT_ATTEMPTS_RETRY = 3;
|
||||
// delay for retry publish, subscribe and unsubscribe for QoS Level 1 or 2
|
||||
public const int MQTT_DELAY_RETRY = 10000;
|
||||
// broker need to receive the first message (CONNECT)
|
||||
// within a reasonable amount of time after TCP/IP connection
|
||||
public const int MQTT_CONNECT_TIMEOUT = 30000;
|
||||
// default inflight queue size
|
||||
public const int MQTT_MAX_INFLIGHT_QUEUE_SIZE = int.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// Listening connection port
|
||||
/// </summary>
|
||||
public int Port { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Listening connection SSL port
|
||||
/// </summary>
|
||||
public int SslPort { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timeout on client connection (before receiving CONNECT message)
|
||||
/// </summary>
|
||||
public int TimeoutOnConnection { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timeout on receiving
|
||||
/// </summary>
|
||||
public int TimeoutOnReceiving { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Attempts on retry
|
||||
/// </summary>
|
||||
public int AttemptsOnRetry { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Delay on retry
|
||||
/// </summary>
|
||||
public int DelayOnRetry { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Inflight queue size
|
||||
/// </summary>
|
||||
public int InflightQueueSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of settings
|
||||
/// </summary>
|
||||
public static MqttSettings Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new MqttSettings();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
// singleton instance
|
||||
private static MqttSettings instance;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
private MqttSettings()
|
||||
{
|
||||
this.Port = MQTT_BROKER_DEFAULT_PORT;
|
||||
this.SslPort = MQTT_BROKER_DEFAULT_SSL_PORT;
|
||||
this.TimeoutOnReceiving = MQTT_DEFAULT_TIMEOUT;
|
||||
this.AttemptsOnRetry = MQTT_ATTEMPTS_RETRY;
|
||||
this.DelayOnRetry = MQTT_DELAY_RETRY;
|
||||
this.TimeoutOnConnection = MQTT_CONNECT_TIMEOUT;
|
||||
this.InflightQueueSize = MQTT_MAX_INFLIGHT_QUEUE_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System.Threading;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// Support methods fos specific framework
|
||||
/// </summary>
|
||||
public class Fx
|
||||
{
|
||||
public static void StartThread(ThreadStart threadStart)
|
||||
{
|
||||
new Thread(threadStart).Start();
|
||||
}
|
||||
|
||||
public static void SleepThread(int millisecondsTimeout)
|
||||
{
|
||||
Thread.Sleep(millisecondsTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,472 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
#if SSL
|
||||
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
|
||||
using Microsoft.SPOT.Net.Security;
|
||||
#else
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
#endif
|
||||
#endif
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System;
|
||||
//using System.Security.Authentication;
|
||||
//using System.Net.Security;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// Channel to communicate over the network
|
||||
/// </summary>
|
||||
public class MqttNetworkChannel : IMqttNetworkChannel
|
||||
{
|
||||
#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
|
||||
private readonly RemoteCertificateValidationCallback userCertificateValidationCallback;
|
||||
private readonly LocalCertificateSelectionCallback userCertificateSelectionCallback;
|
||||
#endif
|
||||
// remote host information
|
||||
private string remoteHostName;
|
||||
private IPAddress remoteIpAddress;
|
||||
private int remotePort;
|
||||
|
||||
// socket for communication
|
||||
private Socket socket;
|
||||
// using SSL
|
||||
private bool secure;
|
||||
|
||||
// CA certificate (on client)
|
||||
private X509Certificate caCert;
|
||||
// Server certificate (on broker)
|
||||
private X509Certificate serverCert;
|
||||
// client certificate (on client)
|
||||
private X509Certificate clientCert;
|
||||
|
||||
// SSL/TLS protocol version
|
||||
private MqttSslProtocols sslProtocol;
|
||||
|
||||
/// <summary>
|
||||
/// Remote host name
|
||||
/// </summary>
|
||||
public string RemoteHostName { get { return this.remoteHostName; } }
|
||||
|
||||
/// <summary>
|
||||
/// Remote IP address
|
||||
/// </summary>
|
||||
public IPAddress RemoteIpAddress { get { return this.remoteIpAddress; } }
|
||||
|
||||
/// <summary>
|
||||
/// Remote port
|
||||
/// </summary>
|
||||
public int RemotePort { get { return this.remotePort; } }
|
||||
|
||||
#if SSL
|
||||
// SSL stream
|
||||
private SslStream sslStream;
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
private NetworkStream netStream;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Data available on the channel
|
||||
/// </summary>
|
||||
public bool DataAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
#if SSL
|
||||
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
|
||||
if (secure)
|
||||
return this.sslStream.DataAvailable;
|
||||
else
|
||||
return (this.socket.Available > 0);
|
||||
#else
|
||||
if (secure)
|
||||
return this.netStream.DataAvailable;
|
||||
else
|
||||
return (this.socket.Available > 0);
|
||||
#endif
|
||||
#else
|
||||
return (this.socket.Available > 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="socket">Socket opened with the client</param>
|
||||
public MqttNetworkChannel(Socket socket)
|
||||
#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
|
||||
: this(socket, false, null, MqttSslProtocols.None, null, null)
|
||||
#else
|
||||
: this(socket, false, null, MqttSslProtocols.None)
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="socket">Socket opened with the client</param>
|
||||
/// <param name="secure">Secure connection (SSL/TLS)</param>
|
||||
/// <param name="serverCert">Server X509 certificate for secure connection</param>
|
||||
/// <param name="sslProtocol">SSL/TLS protocol version</param>
|
||||
#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
|
||||
/// <param name="userCertificateSelectionCallback">A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party</param>
|
||||
/// <param name="userCertificateValidationCallback">A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication</param>
|
||||
public MqttNetworkChannel(Socket socket, bool secure, X509Certificate serverCert, MqttSslProtocols sslProtocol,
|
||||
RemoteCertificateValidationCallback userCertificateValidationCallback,
|
||||
LocalCertificateSelectionCallback userCertificateSelectionCallback)
|
||||
#else
|
||||
public MqttNetworkChannel(Socket socket, bool secure, X509Certificate serverCert, MqttSslProtocols sslProtocol)
|
||||
#endif
|
||||
{
|
||||
this.socket = socket;
|
||||
this.secure = secure;
|
||||
this.serverCert = serverCert;
|
||||
this.sslProtocol = sslProtocol;
|
||||
#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
|
||||
this.userCertificateValidationCallback = userCertificateValidationCallback;
|
||||
this.userCertificateSelectionCallback = userCertificateSelectionCallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="remoteHostName">Remote Host name</param>
|
||||
/// <param name="remotePort">Remote port</param>
|
||||
public MqttNetworkChannel(string remoteHostName, int remotePort)
|
||||
#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
|
||||
: this(remoteHostName, remotePort, false, null, null, MqttSslProtocols.None, null, null)
|
||||
#else
|
||||
: this(remoteHostName, remotePort, false, null, null, MqttSslProtocols.None)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="remoteHostName">Remote Host name</param>
|
||||
/// <param name="remotePort">Remote port</param>
|
||||
/// <param name="secure">Using SSL</param>
|
||||
/// <param name="caCert">CA certificate</param>
|
||||
/// <param name="clientCert">Client certificate</param>
|
||||
/// <param name="sslProtocol">SSL/TLS protocol version</param>
|
||||
#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
|
||||
/// <param name="userCertificateSelectionCallback">A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party</param>
|
||||
/// <param name="userCertificateValidationCallback">A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication</param>
|
||||
public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol,
|
||||
RemoteCertificateValidationCallback userCertificateValidationCallback,
|
||||
LocalCertificateSelectionCallback userCertificateSelectionCallback)
|
||||
#else
|
||||
public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol)
|
||||
#endif
|
||||
{
|
||||
IPAddress remoteIpAddress = null;
|
||||
try
|
||||
{
|
||||
// check if remoteHostName is a valid IP address and get it
|
||||
remoteIpAddress = IPAddress.Parse(remoteHostName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// in this case the parameter remoteHostName isn't a valid IP address
|
||||
if (remoteIpAddress == null)
|
||||
{
|
||||
IPHostEntry hostEntry = Dns.GetHostEntry(remoteHostName);
|
||||
if ((hostEntry != null) && (hostEntry.AddressList.Length > 0))
|
||||
{
|
||||
// check for the first address not null
|
||||
// it seems that with .Net Micro Framework, the IPV6 addresses aren't supported and return "null"
|
||||
int i = 0;
|
||||
while (hostEntry.AddressList[i] == null) i++;
|
||||
remoteIpAddress = hostEntry.AddressList[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("No address found for the remote host name");
|
||||
}
|
||||
}
|
||||
|
||||
this.remoteHostName = remoteHostName;
|
||||
this.remoteIpAddress = remoteIpAddress;
|
||||
this.remotePort = remotePort;
|
||||
this.secure = secure;
|
||||
this.caCert = caCert;
|
||||
this.clientCert = clientCert;
|
||||
this.sslProtocol = sslProtocol;
|
||||
#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
|
||||
this.userCertificateValidationCallback = userCertificateValidationCallback;
|
||||
this.userCertificateSelectionCallback = userCertificateSelectionCallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect to remote server
|
||||
/// </summary>
|
||||
public void Connect()
|
||||
{
|
||||
this.socket = new Socket(IPAddressUtility.GetAddressFamily(this.remoteIpAddress), SocketType.Stream, ProtocolType.Tcp);
|
||||
// try connection to the broker
|
||||
this.socket.Connect(new IPEndPoint(this.remoteIpAddress, this.remotePort));
|
||||
|
||||
#if SSL
|
||||
// secure channel requested
|
||||
if (secure)
|
||||
{
|
||||
// create SSL stream
|
||||
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
|
||||
this.sslStream = new SslStream(this.socket);
|
||||
#else
|
||||
this.netStream = new NetworkStream(this.socket);
|
||||
this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback);
|
||||
#endif
|
||||
|
||||
// server authentication (SSL/TLS handshake)
|
||||
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
|
||||
this.sslStream.AuthenticateAsClient(this.remoteHostName,
|
||||
this.clientCert,
|
||||
new X509Certificate[] { this.caCert },
|
||||
SslVerification.CertificateRequired,
|
||||
MqttSslUtility.ToSslPlatformEnum(this.sslProtocol));
|
||||
#else
|
||||
X509CertificateCollection clientCertificates = null;
|
||||
// check if there is a client certificate to add to the collection, otherwise it's null (as empty)
|
||||
if (this.clientCert != null)
|
||||
clientCertificates = new X509CertificateCollection(new X509Certificate[] { this.clientCert });
|
||||
|
||||
this.sslStream.AuthenticateAsClient(this.remoteHostName,
|
||||
clientCertificates,
|
||||
MqttSslUtility.ToSslPlatformEnum(this.sslProtocol),
|
||||
false);
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send data on the network channel
|
||||
/// </summary>
|
||||
/// <param name="buffer">Data buffer to send</param>
|
||||
/// <returns>Number of byte sent</returns>
|
||||
public int Send(byte[] buffer)
|
||||
{
|
||||
#if SSL
|
||||
if (this.secure)
|
||||
{
|
||||
this.sslStream.Write(buffer, 0, buffer.Length);
|
||||
this.sslStream.Flush();
|
||||
return buffer.Length;
|
||||
}
|
||||
else
|
||||
return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
|
||||
#else
|
||||
return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receive data from the network
|
||||
/// </summary>
|
||||
/// <param name="buffer">Data buffer for receiving data</param>
|
||||
/// <returns>Number of bytes received</returns>
|
||||
public int Receive(byte[] buffer)
|
||||
{
|
||||
#if SSL
|
||||
if (this.secure)
|
||||
{
|
||||
// read all data needed (until fill buffer)
|
||||
int idx = 0, read = 0;
|
||||
while (idx < buffer.Length)
|
||||
{
|
||||
// fixed scenario with socket closed gracefully by peer/broker and
|
||||
// Read return 0. Avoid infinite loop.
|
||||
read = this.sslStream.Read(buffer, idx, buffer.Length - idx);
|
||||
if (read == 0)
|
||||
return 0;
|
||||
idx += read;
|
||||
}
|
||||
return buffer.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
// read all data needed (until fill buffer)
|
||||
int idx = 0, read = 0;
|
||||
while (idx < buffer.Length)
|
||||
{
|
||||
// fixed scenario with socket closed gracefully by peer/broker and
|
||||
// Read return 0. Avoid infinite loop.
|
||||
read = this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
|
||||
if (read == 0)
|
||||
return 0;
|
||||
idx += read;
|
||||
}
|
||||
return buffer.Length;
|
||||
}
|
||||
#else
|
||||
// read all data needed (until fill buffer)
|
||||
int idx = 0, read = 0;
|
||||
while (idx < buffer.Length)
|
||||
{
|
||||
// fixed scenario with socket closed gracefully by peer/broker and
|
||||
// Read return 0. Avoid infinite loop.
|
||||
read = this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
|
||||
if (read == 0)
|
||||
return 0;
|
||||
idx += read;
|
||||
}
|
||||
return buffer.Length;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receive data from the network channel with a specified timeout
|
||||
/// </summary>
|
||||
/// <param name="buffer">Data buffer for receiving data</param>
|
||||
/// <param name="timeout">Timeout on receiving (in milliseconds)</param>
|
||||
/// <returns>Number of bytes received</returns>
|
||||
public int Receive(byte[] buffer, int timeout)
|
||||
{
|
||||
// check data availability (timeout is in microseconds)
|
||||
if (this.socket.Poll(timeout * 1000, SelectMode.SelectRead))
|
||||
{
|
||||
return this.Receive(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the network channel
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
#if SSL
|
||||
if (this.secure)
|
||||
{
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
this.netStream.Close();
|
||||
#endif
|
||||
this.sslStream.Close();
|
||||
}
|
||||
this.socket.Close();
|
||||
#else
|
||||
this.socket.Close();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accept connection from a remote client
|
||||
/// </summary>
|
||||
public void Accept()
|
||||
{
|
||||
#if SSL
|
||||
// secure channel requested
|
||||
if (secure)
|
||||
{
|
||||
#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
|
||||
|
||||
this.netStream = new NetworkStream(this.socket);
|
||||
this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback);
|
||||
|
||||
this.sslStream.AuthenticateAsServer(this.serverCert, false, MqttSslUtility.ToSslPlatformEnum(this.sslProtocol), false);
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IPAddress Utility class
|
||||
/// </summary>
|
||||
public static class IPAddressUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Return AddressFamily for the IP address
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">IP address to check</param>
|
||||
/// <returns>Address family</returns>
|
||||
public static AddressFamily GetAddressFamily(IPAddress ipAddress)
|
||||
{
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
|
||||
return ipAddress.AddressFamily;
|
||||
#else
|
||||
return (ipAddress.ToString().IndexOf(':') != -1) ?
|
||||
AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MQTT SSL utility class
|
||||
/// </summary>
|
||||
public static class MqttSslUtility
|
||||
{
|
||||
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 && !COMPACT_FRAMEWORK)
|
||||
public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol)
|
||||
{
|
||||
switch (mqttSslProtocol)
|
||||
{
|
||||
case MqttSslProtocols.None:
|
||||
return SslProtocols.None;
|
||||
case MqttSslProtocols.SSLv3:
|
||||
return SslProtocols.Ssl3;
|
||||
case MqttSslProtocols.TLSv1_0:
|
||||
return SslProtocols.Tls;
|
||||
/*case MqttSslProtocols.TLSv1_1:
|
||||
return SslProtocols.Tls11;
|
||||
case MqttSslProtocols.TLSv1_2:
|
||||
return SslProtocols.Tls12;*/
|
||||
default:
|
||||
throw new ArgumentException("SSL/TLS protocol version not supported");
|
||||
}
|
||||
}
|
||||
#elif (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
|
||||
public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol)
|
||||
{
|
||||
switch (mqttSslProtocol)
|
||||
{
|
||||
case MqttSslProtocols.None:
|
||||
return SslProtocols.None;
|
||||
case MqttSslProtocols.SSLv3:
|
||||
return SslProtocols.SSLv3;
|
||||
case MqttSslProtocols.TLSv1_0:
|
||||
return SslProtocols.TLSv1;
|
||||
case MqttSslProtocols.TLSv1_1:
|
||||
case MqttSslProtocols.TLSv1_2:
|
||||
default:
|
||||
throw new ArgumentException("SSL/TLS protocol version not supported");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("M2Mqtt")]
|
||||
[assembly: AssemblyDescription("MQTT Client Library for M2M communication")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Paolo Patierno")]
|
||||
[assembly: AssemblyProduct("M2Mqtt")]
|
||||
[assembly: AssemblyCopyright("Copyright © Paolo Patierno 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("4.3.0.0")]
|
||||
// to avoid compilation error (AssemblyFileVersionAttribute doesn't exist) under .Net CF 3.5
|
||||
#if !WindowsCE
|
||||
[assembly: AssemblyFileVersion("4.3.0.0")]
|
||||
#endif
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
#if BROKER
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using uPLibrary.Networking.M2Mqtt.Managers;
|
||||
using uPLibrary.Networking.M2Mqtt.Messages;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Session
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT Broker Session
|
||||
/// </summary>
|
||||
public class MqttBrokerSession : MqttSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Client related to the subscription
|
||||
/// </summary>
|
||||
public MqttClient Client { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Subscriptions for the client session
|
||||
/// </summary>
|
||||
public List<MqttSubscription> Subscriptions;
|
||||
|
||||
/// <summary>
|
||||
/// Outgoing messages to publish
|
||||
/// </summary>
|
||||
public Queue<MqttMsgPublish> OutgoingMessages;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttBrokerSession()
|
||||
: base()
|
||||
{
|
||||
this.Client = null;
|
||||
this.Subscriptions = new List<MqttSubscription>();
|
||||
this.OutgoingMessages = new Queue<MqttMsgPublish>();
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
this.Client = null;
|
||||
this.Subscriptions.Clear();
|
||||
this.OutgoingMessages.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Session
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT Client Session
|
||||
/// </summary>
|
||||
public class MqttClientSession : MqttSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="clientId">Client Id to create session</param>
|
||||
public MqttClientSession(string clientId)
|
||||
: base(clientId)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System.Collections;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Session
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT Session base class
|
||||
/// </summary>
|
||||
public abstract class MqttSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Client Id
|
||||
/// </summary>
|
||||
public string ClientId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Messages inflight during session
|
||||
/// </summary>
|
||||
public Hashtable InflightMessages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MqttSession()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="clientId">Client Id to create session</param>
|
||||
public MqttSession(string clientId)
|
||||
{
|
||||
this.ClientId = clientId;
|
||||
this.InflightMessages = new Hashtable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean session
|
||||
/// </summary>
|
||||
public virtual void Clear()
|
||||
{
|
||||
this.ClientId = null;
|
||||
this.InflightMessages.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension class for a Queue
|
||||
/// </summary>
|
||||
internal static class QueueExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Predicate for searching inside a queue
|
||||
/// </summary>
|
||||
/// <param name="item">Item of the queue</param>
|
||||
/// <returns>Result of predicate</returns>
|
||||
internal delegate bool QueuePredicate(object item);
|
||||
|
||||
/// <summary>
|
||||
/// Get (without removing) an item from queue based on predicate
|
||||
/// </summary>
|
||||
/// <param name="queue">Queue in which to search</param>
|
||||
/// <param name="predicate">Predicate to verify to get item</param>
|
||||
/// <returns>Item matches the predicate</returns>
|
||||
internal static object Get(Queue queue, QueuePredicate predicate)
|
||||
{
|
||||
foreach (var item in queue)
|
||||
{
|
||||
if (predicate(item))
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracing levels
|
||||
/// </summary>
|
||||
public enum TraceLevel
|
||||
{
|
||||
Error = 0x01,
|
||||
Warning = 0x02,
|
||||
Information = 0x04,
|
||||
Verbose = 0x0F,
|
||||
Frame = 0x10,
|
||||
Queuing = 0x20
|
||||
}
|
||||
|
||||
// delegate for writing trace
|
||||
public delegate void WriteTrace(string format, params object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Tracing class
|
||||
/// </summary>
|
||||
public static class Trace
|
||||
{
|
||||
public static TraceLevel TraceLevel;
|
||||
public static WriteTrace TraceListener;
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public static void Debug(string format, params object[] args)
|
||||
{
|
||||
if (TraceListener != null)
|
||||
{
|
||||
TraceListener(format, args);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteLine(TraceLevel level, string format)
|
||||
{
|
||||
if (TraceListener != null && (level & TraceLevel) > 0)
|
||||
{
|
||||
TraceListener(format);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteLine(TraceLevel level, string format, object arg1)
|
||||
{
|
||||
if (TraceListener != null && (level & TraceLevel) > 0)
|
||||
{
|
||||
TraceListener(format, arg1);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteLine(TraceLevel level, string format, object arg1, object arg2)
|
||||
{
|
||||
if (TraceListener != null && (level & TraceLevel) > 0)
|
||||
{
|
||||
TraceListener(format, arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteLine(TraceLevel level, string format, object arg1, object arg2, object arg3)
|
||||
{
|
||||
if (TraceListener != null && (level & TraceLevel) > 0)
|
||||
{
|
||||
TraceListener(format, arg1, arg2, arg3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// Support methods fos specific framework
|
||||
/// </summary>
|
||||
public class Fx
|
||||
{
|
||||
|
||||
public delegate void ThreadStart();
|
||||
public static void StartThread(ThreadStart threadStart)
|
||||
{
|
||||
Task.Factory.StartNew(o => ((ThreadStart)o)(), threadStart);
|
||||
}
|
||||
|
||||
public static void SleepThread(int millisecondsTimeout) { Task.Delay(millisecondsTimeout).RunSynchronously(); }
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper Hashtable class for generic Dictionary<TKey,TValue> (the only available in WinRT)
|
||||
/// </summary>
|
||||
public class Hashtable : Dictionary<object, object>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Networking;
|
||||
using Windows.Networking.Sockets;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Storage.Streams;
|
||||
using System.Threading;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt
|
||||
{
|
||||
public class MqttNetworkChannel : IMqttNetworkChannel
|
||||
{
|
||||
// stream socket for communication
|
||||
private StreamSocket socket;
|
||||
|
||||
// remote host information
|
||||
private HostName remoteHostName;
|
||||
private int remotePort;
|
||||
|
||||
// using SSL
|
||||
private bool secure;
|
||||
|
||||
// SSL/TLS protocol version
|
||||
private MqttSslProtocols sslProtocol;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="socket">Socket opened with the client</param>
|
||||
public MqttNetworkChannel(StreamSocket socket)
|
||||
{
|
||||
this.socket = socket;
|
||||
this.sslProtocol = MqttSslProtocols.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="remoteHostName">Remote Host name</param>
|
||||
/// <param name="remotePort">Remote port</param>
|
||||
/// <param name="secure">Using SSL</param>
|
||||
/// <param name="sslProtocol">SSL/TLS protocol version</param>
|
||||
public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, MqttSslProtocols sslProtocol)
|
||||
{
|
||||
this.remoteHostName = new HostName(remoteHostName);
|
||||
this.remotePort = remotePort;
|
||||
this.secure = secure;
|
||||
this.sslProtocol = sslProtocol;
|
||||
|
||||
if (secure && (sslProtocol == MqttSslProtocols.None))
|
||||
throw new ArgumentException("For secure connection, an SSL/TLS protocol version is needed");
|
||||
}
|
||||
|
||||
public bool DataAvailable
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public int Receive(byte[] buffer)
|
||||
{
|
||||
IBuffer result;
|
||||
|
||||
// read all data needed (until fill buffer)
|
||||
int idx = 0;
|
||||
while (idx < buffer.Length)
|
||||
{
|
||||
// fixed scenario with socket closed gracefully by peer/broker and
|
||||
// Read return 0. Avoid infinite loop.
|
||||
|
||||
// read is executed synchronously
|
||||
result = this.socket.InputStream.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None).AsTask().Result;
|
||||
if (result.Length == 0)
|
||||
return 0;
|
||||
idx += (int)result.Length;
|
||||
}
|
||||
return buffer.Length;
|
||||
}
|
||||
|
||||
public int Receive(byte[] buffer, int timeout)
|
||||
{
|
||||
CancellationTokenSource cts = new CancellationTokenSource(timeout);
|
||||
|
||||
try
|
||||
{
|
||||
IBuffer result;
|
||||
|
||||
// read all data needed (until fill buffer)
|
||||
int idx = 0;
|
||||
while (idx < buffer.Length)
|
||||
{
|
||||
// fixed scenario with socket closed gracefully by peer/broker and
|
||||
// Read return 0. Avoid infinite loop.
|
||||
|
||||
// read is executed synchronously
|
||||
result = this.socket.InputStream.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None).AsTask(cts.Token).Result;
|
||||
if (result.Length == 0)
|
||||
return 0;
|
||||
idx += (int)result.Length;
|
||||
}
|
||||
return buffer.Length;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int Send(byte[] buffer)
|
||||
{
|
||||
// send is executed synchronously
|
||||
return (int)this.socket.OutputStream.WriteAsync(buffer.AsBuffer()).AsTask().Result;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
this.socket.Dispose();
|
||||
}
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
this.socket = new StreamSocket();
|
||||
|
||||
// connection is executed synchronously
|
||||
this.socket.ConnectAsync(this.remoteHostName,
|
||||
this.remotePort.ToString(),
|
||||
MqttSslUtility.ToSslPlatformEnum(this.sslProtocol)).AsTask().Wait();
|
||||
}
|
||||
|
||||
public void Accept()
|
||||
{
|
||||
// TODO : SSL support with StreamSocket / StreamSocketListener seems to be NOT supported
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MQTT SSL utility class
|
||||
/// </summary>
|
||||
public static class MqttSslUtility
|
||||
{
|
||||
public static SocketProtectionLevel ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol)
|
||||
{
|
||||
switch (mqttSslProtocol)
|
||||
{
|
||||
case MqttSslProtocols.None:
|
||||
return SocketProtectionLevel.PlainSocket;
|
||||
case MqttSslProtocols.SSLv3:
|
||||
return SocketProtectionLevel.SslAllowNullEncryption;
|
||||
case MqttSslProtocols.TLSv1_0:
|
||||
return SocketProtectionLevel.Tls10;
|
||||
case MqttSslProtocols.TLSv1_1:
|
||||
return SocketProtectionLevel.Tls11;
|
||||
case MqttSslProtocols.TLSv1_2:
|
||||
return SocketProtectionLevel.Tls12;
|
||||
default:
|
||||
throw new ArgumentException("SSL/TLS protocol version not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, 2014 Paolo Patierno
|
||||
|
||||
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:
|
||||
Paolo Patierno - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace uPLibrary.Networking.M2Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper Queue class for generic Queue<T> (the only available in WinRT)
|
||||
/// </summary>
|
||||
public class Queue : Queue<object>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt_2", "M2Mqtt\M2Mqtt_2.csproj", "{A11AEF5A-B246-4FE8-8330-06DB73CC8074}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {135814CF-A469-4436-9C51-3CFE4B93D2FE}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt", "M2Mqtt\M2Mqtt_4.7.1.csproj", "{A11AEF5A-B246-4FE8-8330-06DB73CC8074}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {135814CF-A469-4436-9C51-3CFE4B93D2FE}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
function look_for_update() {
|
||||
if [[ $1 == ./.git ]] || [[ $1 == "." ]]; then
|
||||
echo "Ignore $1"
|
||||
return
|
||||
fi
|
||||
WithoutPrefix=$(echo -n $1 | cut -c 2-)
|
||||
if grep -qx "$WithoutPrefix" .gitignore; then
|
||||
echo "Ignore $1, because it's in .gitignore"
|
||||
return
|
||||
fi
|
||||
printf -v FolderNameEscaped '%q' "$1"
|
||||
if [[ -f $1/update.sh ]]; then
|
||||
echo "Switch to subdir $1 and run update.sh"
|
||||
cd "$1"
|
||||
git pull
|
||||
bash -c "./update.sh nobreak"
|
||||
else
|
||||
if [[ -f $1/.git ]]
|
||||
then
|
||||
echo "Pull $1"
|
||||
cd "$1"
|
||||
git pull
|
||||
else
|
||||
echo "Checkout master for $1"
|
||||
git submodule update --init -- "$1"
|
||||
cd "$1"
|
||||
git checkout master
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
export -f look_for_update
|
||||
find . -maxdepth 1 -type d -exec bash -c 'look_for_update "$0"' {} \;
|
||||
|
||||
if [ -z $1 ]; then
|
||||
read -p "Press return"
|
||||
fi
|
||||
Reference in New Issue
Block a user