Add Csharp and Bash/Curl/Node.js versions of Http device telemetry
This commit is contained in:
Родитель
4eb2cbf38f
Коммит
55ed4b98c6
|
@ -0,0 +1,124 @@
|
|||
### Connecting to Azure IoT Central via HTTP (bash/curl/node)
|
||||
|
||||
If you want to jump directly to example script without the explanations,
|
||||
[click here](dobash.sh)
|
||||
|
||||
```
|
||||
DEVICE_ID="ENTER DEVICE ID HERE"
|
||||
DEVICE_KEY="ENTER DEVICE SYM KEY HERE"
|
||||
SCOPE="ENTER SCOPE ID HERE"
|
||||
MESSAGE='{"temp":15}' # <=== your telemetry goes here
|
||||
```
|
||||
|
||||
Open [apps.azureiotcentral.com](apps.azureiotcentral.com) `>` Device Explorer (explorer)
|
||||
`>` Select one of the templates (i.e. MXCHIP) `>` Click to `+` button on the tab `>`
|
||||
Select `Real device`
|
||||
|
||||
You should see a `Connect` link on top-right of the device page. Once you click
|
||||
to that button, you will find all the `DEVICE_ID`, `DEVICE_KEY` (Primary_key) and
|
||||
`SCOPE_ID` there.
|
||||
|
||||
Now, we need to generate the authentication header for our requests. Please take
|
||||
a look at the basic BASH function below. It executes a node script and uses the
|
||||
arguments we collected from the device page.
|
||||
|
||||
```
|
||||
function getAuth {
|
||||
# A node script that prints an auth signature using SCOPE, DEVICE_ID and DEVICE_KEY
|
||||
AUTH=`node -e "\
|
||||
const crypto = require('crypto');\
|
||||
|
||||
function computeDrivedSymmetricKey(masterKey, regId) {\
|
||||
return crypto.createHmac('SHA256', Buffer.from(masterKey, 'base64'))\
|
||||
.update(regId, 'utf8')\
|
||||
.digest('base64');\
|
||||
}\
|
||||
\
|
||||
var expires = parseInt((Date.now() + (7200 * 1000)) / 1000);\
|
||||
var sr = '${SCOPE}%2f${TARGET}%2f${DEVICE_ID}';\
|
||||
var sigNoEncode = computeDrivedSymmetricKey('${DEVICE_KEY}', sr + '\n' + expires);\
|
||||
var sigEncoded = encodeURIComponent(sigNoEncode);\
|
||||
console.log('SharedAccessSignature sr=' + sr + '&sig=' + sigEncoded + '&se=' + expires)\
|
||||
"`
|
||||
}
|
||||
|
||||
SCOPEID="$SCOPE"
|
||||
TARGET="registrations"
|
||||
# get auth for Azure IoT DPS service
|
||||
getAuth
|
||||
```
|
||||
|
||||
We will make the first DPS call. This call will provide us the `operationId`
|
||||
|
||||
```
|
||||
# use the Auth and make a PUT request to Azure IoT DPS service
|
||||
curl \
|
||||
-H "authorization: ${AUTH}&skn=registration" \
|
||||
-H 'content-type: application/json; charset=utf-8' -H 'content-length: 25' \
|
||||
-s -o output.txt \
|
||||
--request PUT --data "{\"registrationId\":\"$DEVICE_ID\"}" "https://global.azure-devices-provisioning.net/$SCOPEID/registrations/$DEVICE_ID/register?api-version=2018-11-01"
|
||||
|
||||
OPERATION=`node -e "fs=require('fs');c=fs.readFileSync('./output.txt')+'';c=JSON.parse(c);if(c.errorCode){console.log(c);process.exit(1);}console.log(c.operationId)"`
|
||||
|
||||
if [[ $? != 0 ]]; then
|
||||
# if there was an error, print the stdout part and exit
|
||||
echo "$OPERATION"
|
||||
exit
|
||||
else
|
||||
echo "Authenticating.."
|
||||
# wait 2 secs before making the GET request to Azure IoT DPS service
|
||||
# the second call will bring us the Azure IoT Hub endpoint we are supposed to talk
|
||||
sleep 2
|
||||
```
|
||||
|
||||
Now, we are going to make the second DPS call. This call will give us the Azure IoT hub
|
||||
hostname. Using that hostname and our device key, we will create the authentication
|
||||
key for our iothub telemetry message.
|
||||
|
||||
```
|
||||
OUT=`curl -s \
|
||||
-H "authorization: ${AUTH}&skn=registration" \
|
||||
-H "content-type: application/json; charset=utf-8" \
|
||||
--request GET "https://global.azure-devices-provisioning.net/$SCOPEID/registrations/$DEVICE_ID/operations/$OPERATION?api-version=2018-11-01"`
|
||||
|
||||
# parse the return value from the DPS host and try to grab the assigned hub
|
||||
OUT=`node -pe "a=JSON.parse('$OUT');if(a.errorCode){a}else{a.registrationState.assignedHub}"`
|
||||
|
||||
if [[ $OUT =~ 'errorCode' ]]; then
|
||||
# if there was an error, print the stdout part and exit
|
||||
echo "$OUT"
|
||||
exit
|
||||
fi
|
||||
```
|
||||
|
||||
Finally generate the Azure IoT hub authentication key using the hostname that
|
||||
we received from DPS.
|
||||
|
||||
```
|
||||
TARGET="devices"
|
||||
SCOPE="$OUT"
|
||||
# get Auth for Azure IoThub service
|
||||
getAuth
|
||||
|
||||
echo "OK"
|
||||
echo
|
||||
```
|
||||
|
||||
It's time to send our telemetry to Azure IoT Hub
|
||||
|
||||
```
|
||||
echo "SENDING => " $MESSAGE
|
||||
|
||||
# send a telemetry to Azure IoT hub service
|
||||
curl -s \
|
||||
-H "authorization: ${AUTH}" \
|
||||
-H "iothub-to: /devices/$DEVICE_ID/messages/events" \
|
||||
--request POST --data "$MESSAGE" "https://$SCOPE/devices/$DEVICE_ID/messages/events/?api-version=2016-11-14"
|
||||
|
||||
echo "DONE"
|
||||
fi
|
||||
|
||||
echo
|
||||
```
|
||||
|
||||
Turn back to Azure IoT central and browse the device page. You should see `temperature` telemetry soon.
|
|
@ -0,0 +1,85 @@
|
|||
#/bin/bash
|
||||
|
||||
DEVICE_ID="ENTER DEVICE ID HERE"
|
||||
DEVICE_KEY="ENTER DEVICE SYM KEY HERE"
|
||||
SCOPE="ENTER SCOPE ID HERE"
|
||||
|
||||
MESSAGE='{"temp":15}'
|
||||
|
||||
function getAuth {
|
||||
# A node script that prints an auth signature using SCOPE, DEVICE_ID and DEVICE_KEY
|
||||
AUTH=`node -e "\
|
||||
const crypto = require('crypto');\
|
||||
|
||||
function computeDrivedSymmetricKey(masterKey, regId) {\
|
||||
return crypto.createHmac('SHA256', Buffer.from(masterKey, 'base64'))\
|
||||
.update(regId, 'utf8')\
|
||||
.digest('base64');\
|
||||
}\
|
||||
\
|
||||
var expires = parseInt((Date.now() + (7200 * 1000)) / 1000);\
|
||||
var sr = '${SCOPE}%2f${TARGET}%2f${DEVICE_ID}';\
|
||||
var sigNoEncode = computeDrivedSymmetricKey('${DEVICE_KEY}', sr + '\n' + expires);\
|
||||
var sigEncoded = encodeURIComponent(sigNoEncode);\
|
||||
console.log('SharedAccessSignature sr=' + sr + '&sig=' + sigEncoded + '&se=' + expires)\
|
||||
"`
|
||||
}
|
||||
|
||||
SCOPEID="$SCOPE"
|
||||
TARGET="registrations"
|
||||
# get auth for Azure IoT DPS service
|
||||
getAuth
|
||||
|
||||
# use the Auth and make a PUT request to Azure IoT DPS service
|
||||
OUT=`curl \
|
||||
-H "authorization: ${AUTH}&skn=registration" \
|
||||
-H 'content-type: application/json; charset=utf-8' -H 'content-length: 25' \
|
||||
-s \
|
||||
--request PUT --data "{\"registrationId\":\"$DEVICE_ID\"}" "https://global.azure-devices-provisioning.net/$SCOPEID/registrations/$DEVICE_ID/register?api-version=2018-11-01"`
|
||||
|
||||
OPERATION=`node -e "c=JSON.parse('$OUT');if(c.errorCode){console.log(c);process.exit(1);}console.log(c.operationId)"`
|
||||
|
||||
if [[ $? != 0 ]]; then
|
||||
# if there was an error, print the stdout part and exit
|
||||
echo "$OPERATION"
|
||||
exit
|
||||
else
|
||||
echo "Authenticating.."
|
||||
# wait 2 secs before making the GET request to Azure IoT DPS service
|
||||
# the second call will bring us the Azure IoT Hub endpoint we are supposed to talk
|
||||
sleep 2
|
||||
|
||||
OUT=`curl -s \
|
||||
-H "authorization: ${AUTH}&skn=registration" \
|
||||
-H "content-type: application/json; charset=utf-8" \
|
||||
--request GET "https://global.azure-devices-provisioning.net/$SCOPEID/registrations/$DEVICE_ID/operations/$OPERATION?api-version=2018-11-01"`
|
||||
|
||||
# parse the return value from the DPS host and try to grab the assigned hub
|
||||
OUT=`node -pe "a=JSON.parse('$OUT');if(a.errorCode){a}else{a.registrationState.assignedHub}"`
|
||||
|
||||
if [[ $OUT =~ 'errorCode' ]]; then
|
||||
# if there was an error, print the stdout part and exit
|
||||
echo "$OUT"
|
||||
exit
|
||||
fi
|
||||
|
||||
TARGET="devices"
|
||||
SCOPE="$OUT"
|
||||
# get Auth for Azure IoThub service
|
||||
getAuth
|
||||
|
||||
echo "OK"
|
||||
echo
|
||||
|
||||
echo "SENDING => " $MESSAGE
|
||||
|
||||
# send a telemetry to Azure IoT hub service
|
||||
curl -s \
|
||||
-H "authorization: ${AUTH}" \
|
||||
-H "iothub-to: /devices/$DEVICE_ID/messages/events" \
|
||||
--request POST --data "$MESSAGE" "https://$SCOPE/devices/$DEVICE_ID/messages/events/?api-version=2016-11-14"
|
||||
|
||||
echo "DONE"
|
||||
fi
|
||||
|
||||
echo
|
|
@ -0,0 +1 @@
|
|||
app.exe
|
|
@ -0,0 +1,371 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace TinyJson
|
||||
{
|
||||
// Really simple JSON parser in ~300 lines
|
||||
// - Attempts to parse JSON files with minimal GC allocation
|
||||
// - Nice and simple "[1,2,3]".FromJson<List<int>>() API
|
||||
// - Classes and structs can be parsed too!
|
||||
// class Foo { public int Value; }
|
||||
// "{\"Value\":10}".FromJson<Foo>()
|
||||
// - Can parse JSON without type information into Dictionary<string,object> and List<object> e.g.
|
||||
// "[1,2,3]".FromJson<object>().GetType() == typeof(List<object>)
|
||||
// "{\"Value\":10}".FromJson<object>().GetType() == typeof(Dictionary<string,object>)
|
||||
// - No JIT Emit support to support AOT compilation on iOS
|
||||
// - Attempts are made to NOT throw an exception if the JSON is corrupted or invalid: returns null instead.
|
||||
// - Only public fields and property setters on classes/structs will be written to
|
||||
//
|
||||
// Limitations:
|
||||
// - No JIT Emit support to parse structures quickly
|
||||
// - Limited to parsing <2GB JSON files (due to int.MaxValue)
|
||||
// - Parsing of abstract classes or interfaces is NOT supported and will throw an exception.
|
||||
public static class JSONParser
|
||||
{
|
||||
[ThreadStatic] static Stack<List<string>> splitArrayPool;
|
||||
[ThreadStatic] static StringBuilder stringBuilder;
|
||||
[ThreadStatic] static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfoCache;
|
||||
[ThreadStatic] static Dictionary<Type, Dictionary<string, PropertyInfo>> propertyInfoCache;
|
||||
|
||||
public static T FromJson<T>(this string json)
|
||||
{
|
||||
// Initialize, if needed, the ThreadStatic variables
|
||||
if (propertyInfoCache == null) propertyInfoCache = new Dictionary<Type, Dictionary<string, PropertyInfo>>();
|
||||
if (fieldInfoCache == null) fieldInfoCache = new Dictionary<Type, Dictionary<string, FieldInfo>>();
|
||||
if (stringBuilder == null) stringBuilder = new StringBuilder();
|
||||
if (splitArrayPool == null) splitArrayPool = new Stack<List<string>>();
|
||||
|
||||
//Remove all whitespace not within strings to make parsing simpler
|
||||
stringBuilder.Length = 0;
|
||||
for (int i = 0; i < json.Length; i++)
|
||||
{
|
||||
char c = json[i];
|
||||
if (c == '"')
|
||||
{
|
||||
i = AppendUntilStringEnd(true, i, json);
|
||||
continue;
|
||||
}
|
||||
if (char.IsWhiteSpace(c))
|
||||
continue;
|
||||
|
||||
stringBuilder.Append(c);
|
||||
}
|
||||
|
||||
//Parse the thing!
|
||||
return (T)ParseValue(typeof(T), stringBuilder.ToString());
|
||||
}
|
||||
|
||||
static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json)
|
||||
{
|
||||
stringBuilder.Append(json[startIdx]);
|
||||
for (int i = startIdx+1; i<json.Length; i++)
|
||||
{
|
||||
if (json[i] == '\\')
|
||||
{
|
||||
if (appendEscapeCharacter)
|
||||
stringBuilder.Append(json[i]);
|
||||
stringBuilder.Append(json[i + 1]);
|
||||
i++;//Skip next character as it is escaped
|
||||
}
|
||||
else if (json[i] == '"')
|
||||
{
|
||||
stringBuilder.Append(json[i]);
|
||||
return i;
|
||||
}
|
||||
else
|
||||
stringBuilder.Append(json[i]);
|
||||
}
|
||||
return json.Length - 1;
|
||||
}
|
||||
|
||||
//Splits { <value>:<value>, <value>:<value> } and [ <value>, <value> ] into a list of <value> strings
|
||||
static List<string> Split(string json)
|
||||
{
|
||||
List<string> splitArray = splitArrayPool.Count > 0 ? splitArrayPool.Pop() : new List<string>();
|
||||
splitArray.Clear();
|
||||
if(json.Length == 2)
|
||||
return splitArray;
|
||||
int parseDepth = 0;
|
||||
stringBuilder.Length = 0;
|
||||
for (int i = 1; i<json.Length-1; i++)
|
||||
{
|
||||
switch (json[i])
|
||||
{
|
||||
case '[':
|
||||
case '{':
|
||||
parseDepth++;
|
||||
break;
|
||||
case ']':
|
||||
case '}':
|
||||
parseDepth--;
|
||||
break;
|
||||
case '"':
|
||||
i = AppendUntilStringEnd(true, i, json);
|
||||
continue;
|
||||
case ',':
|
||||
case ':':
|
||||
if (parseDepth == 0)
|
||||
{
|
||||
splitArray.Add(stringBuilder.ToString());
|
||||
stringBuilder.Length = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
stringBuilder.Append(json[i]);
|
||||
}
|
||||
|
||||
splitArray.Add(stringBuilder.ToString());
|
||||
|
||||
return splitArray;
|
||||
}
|
||||
|
||||
internal static object ParseValue(Type type, string json)
|
||||
{
|
||||
if (type == typeof(string))
|
||||
{
|
||||
if (json.Length <= 2)
|
||||
return string.Empty;
|
||||
StringBuilder parseStringBuilder = new StringBuilder(json.Length);
|
||||
for (int i = 1; i<json.Length-1; ++i)
|
||||
{
|
||||
if (json[i] == '\\' && i + 1 < json.Length - 1)
|
||||
{
|
||||
int j = "\"\\nrtbf/".IndexOf(json[i + 1]);
|
||||
if (j >= 0)
|
||||
{
|
||||
parseStringBuilder.Append("\"\\\n\r\t\b\f/"[j]);
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (json[i + 1] == 'u' && i + 5 < json.Length - 1)
|
||||
{
|
||||
UInt32 c = 0;
|
||||
if (UInt32.TryParse(json.Substring(i + 2, 4), System.Globalization.NumberStyles.AllowHexSpecifier, null, out c))
|
||||
{
|
||||
parseStringBuilder.Append((char)c);
|
||||
i += 5;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
parseStringBuilder.Append(json[i]);
|
||||
}
|
||||
return parseStringBuilder.ToString();
|
||||
}
|
||||
if (type.IsPrimitive)
|
||||
{
|
||||
var result = Convert.ChangeType(json, type, System.Globalization.CultureInfo.InvariantCulture);
|
||||
return result;
|
||||
}
|
||||
if (type == typeof(decimal))
|
||||
{
|
||||
decimal result;
|
||||
decimal.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out result);
|
||||
return result;
|
||||
}
|
||||
if (json == "null")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (type.IsEnum)
|
||||
{
|
||||
if (json[0] == '"')
|
||||
json = json.Substring(1, json.Length - 2);
|
||||
try
|
||||
{
|
||||
return Enum.Parse(type, json, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (type.IsArray)
|
||||
{
|
||||
Type arrayType = type.GetElementType();
|
||||
if (json[0] != '[' || json[json.Length - 1] != ']')
|
||||
return null;
|
||||
|
||||
List<string> elems = Split(json);
|
||||
Array newArray = Array.CreateInstance(arrayType, elems.Count);
|
||||
for (int i = 0; i < elems.Count; i++)
|
||||
newArray.SetValue(ParseValue(arrayType, elems[i]), i);
|
||||
splitArrayPool.Push(elems);
|
||||
return newArray;
|
||||
}
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
Type listType = type.GetGenericArguments()[0];
|
||||
if (json[0] != '[' || json[json.Length - 1] != ']')
|
||||
return null;
|
||||
|
||||
List<string> elems = Split(json);
|
||||
var list = (IList)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count });
|
||||
for (int i = 0; i < elems.Count; i++)
|
||||
list.Add(ParseValue(listType, elems[i]));
|
||||
splitArrayPool.Push(elems);
|
||||
return list;
|
||||
}
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
{
|
||||
Type keyType, valueType;
|
||||
{
|
||||
Type[] args = type.GetGenericArguments();
|
||||
keyType = args[0];
|
||||
valueType = args[1];
|
||||
}
|
||||
|
||||
//Refuse to parse dictionary keys that aren't of type string
|
||||
if (keyType != typeof(string))
|
||||
return null;
|
||||
//Must be a valid dictionary element
|
||||
if (json[0] != '{' || json[json.Length - 1] != '}')
|
||||
return null;
|
||||
//The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
|
||||
List<string> elems = Split(json);
|
||||
if (elems.Count % 2 != 0)
|
||||
return null;
|
||||
|
||||
var dictionary = (IDictionary)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count / 2 });
|
||||
for (int i = 0; i < elems.Count; i += 2)
|
||||
{
|
||||
if (elems[i].Length <= 2)
|
||||
continue;
|
||||
string keyValue = elems[i].Substring(1, elems[i].Length - 2);
|
||||
object val = ParseValue(valueType, elems[i + 1]);
|
||||
dictionary.Add(keyValue, val);
|
||||
}
|
||||
return dictionary;
|
||||
}
|
||||
if (type == typeof(object))
|
||||
{
|
||||
return ParseAnonymousValue(json);
|
||||
}
|
||||
if (json[0] == '{' && json[json.Length - 1] == '}')
|
||||
{
|
||||
return ParseObject(type, json);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static object ParseAnonymousValue(string json)
|
||||
{
|
||||
if (json.Length == 0)
|
||||
return null;
|
||||
if (json[0] == '{' && json[json.Length - 1] == '}')
|
||||
{
|
||||
List<string> elems = Split(json);
|
||||
if (elems.Count % 2 != 0)
|
||||
return null;
|
||||
var dict = new Dictionary<string, object>(elems.Count / 2);
|
||||
for (int i = 0; i < elems.Count; i += 2)
|
||||
dict.Add(elems[i].Substring(1, elems[i].Length - 2), ParseAnonymousValue(elems[i + 1]));
|
||||
return dict;
|
||||
}
|
||||
if (json[0] == '[' && json[json.Length - 1] == ']')
|
||||
{
|
||||
List<string> items = Split(json);
|
||||
var finalList = new List<object>(items.Count);
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
finalList.Add(ParseAnonymousValue(items[i]));
|
||||
return finalList;
|
||||
}
|
||||
if (json[0] == '"' && json[json.Length - 1] == '"')
|
||||
{
|
||||
string str = json.Substring(1, json.Length - 2);
|
||||
return str.Replace("\\", string.Empty);
|
||||
}
|
||||
if (char.IsDigit(json[0]) || json[0] == '-')
|
||||
{
|
||||
if (json.Contains("."))
|
||||
{
|
||||
double result;
|
||||
double.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out result);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
int result;
|
||||
int.TryParse(json, out result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (json == "true")
|
||||
return true;
|
||||
if (json == "false")
|
||||
return false;
|
||||
// handles json == "null" as well as invalid JSON
|
||||
return null;
|
||||
}
|
||||
|
||||
static Dictionary<string, T> CreateMemberNameDictionary<T>(T[] members) where T : MemberInfo
|
||||
{
|
||||
Dictionary<string, T> nameToMember = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
|
||||
for (int i = 0; i < members.Length; i++)
|
||||
{
|
||||
T member = members[i];
|
||||
if (member.IsDefined(typeof(IgnoreDataMemberAttribute), true))
|
||||
continue;
|
||||
|
||||
string name = member.Name;
|
||||
if (member.IsDefined(typeof(DataMemberAttribute), true))
|
||||
{
|
||||
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
|
||||
if (!string.IsNullOrEmpty(dataMemberAttribute.Name))
|
||||
name = dataMemberAttribute.Name;
|
||||
}
|
||||
|
||||
nameToMember.Add(name, member);
|
||||
}
|
||||
|
||||
return nameToMember;
|
||||
}
|
||||
|
||||
static object ParseObject(Type type, string json)
|
||||
{
|
||||
object instance = FormatterServices.GetUninitializedObject(type);
|
||||
|
||||
//The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
|
||||
List<string> elems = Split(json);
|
||||
if (elems.Count % 2 != 0)
|
||||
return instance;
|
||||
|
||||
Dictionary<string, FieldInfo> nameToField;
|
||||
Dictionary<string, PropertyInfo> nameToProperty;
|
||||
if (!fieldInfoCache.TryGetValue(type, out nameToField))
|
||||
{
|
||||
nameToField = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
|
||||
fieldInfoCache.Add(type, nameToField);
|
||||
}
|
||||
if (!propertyInfoCache.TryGetValue(type, out nameToProperty))
|
||||
{
|
||||
nameToProperty = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
|
||||
propertyInfoCache.Add(type, nameToProperty);
|
||||
}
|
||||
|
||||
for (int i = 0; i < elems.Count; i += 2)
|
||||
{
|
||||
if (elems[i].Length <= 2)
|
||||
continue;
|
||||
string key = elems[i].Substring(1, elems[i].Length - 2);
|
||||
string value = elems[i + 1];
|
||||
|
||||
FieldInfo fieldInfo;
|
||||
PropertyInfo propertyInfo;
|
||||
if (nameToField.TryGetValue(key, out fieldInfo))
|
||||
fieldInfo.SetValue(instance, ParseValue(fieldInfo.FieldType, value));
|
||||
else if (nameToProperty.TryGetValue(key, out propertyInfo))
|
||||
propertyInfo.SetValue(instance, ParseValue(propertyInfo.PropertyType, value), null);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Microsoft Corporation
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
- TinyJson
|
||||
|
||||
Copyright (c) 2013 Patrick Hogan
|
||||
|
||||
Based on MiniJSON by Calvin Rien https://gist.github.com/darktable/1411710
|
||||
|
||||
Based on the JSON parser by Patrick van Bergen http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,23 @@
|
|||
### Connecting to Azure IoT Central via HTTP (C#)
|
||||
|
||||
To Run;
|
||||
```
|
||||
csc app.cs JSONParser.cs
|
||||
```
|
||||
|
||||
and then execute `app.exe`
|
||||
|
||||
Don't forget filling the required variables under `app.cs`
|
||||
```
|
||||
string scopeId = "ENTER SCOPE ID HERE";
|
||||
string deviceId = "ENTER DEVICE ID HERE";
|
||||
string deviceKey = "ENTER DEVICE KEY HERE=";
|
||||
```
|
||||
|
||||
Open [apps.azureiotcentral.com](apps.azureiotcentral.com) `>` Device Explorer (explorer)
|
||||
`>` Select one of the templates (i.e. MXCHIP) `>` Click to `+` button on the tab `>`
|
||||
Select `Real device`
|
||||
|
||||
You should see a `Connect` link on top-right of the device page. Once you click
|
||||
to that button, you will find all the `deviceId`, `deviceKey` (Primary_key) and
|
||||
`scopeId` there.
|
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TinyJson;
|
||||
|
||||
public class IoTCentralDemo
|
||||
{
|
||||
string scopeId = "Enter SCOPE ID here";
|
||||
string deviceId = "Enter DEVICE ID here";
|
||||
string deviceKey = "Enter DEVICE KEY here";
|
||||
|
||||
string computeHash(string key, string Id) {
|
||||
HMACSHA256 hmac = new HMACSHA256(System.Convert.FromBase64String(key));
|
||||
return System.Convert.ToBase64String(hmac.ComputeHash(Encoding.ASCII.GetBytes(Id)));
|
||||
}
|
||||
|
||||
string getAuth(string scope, string target) {
|
||||
long utcTimeInMilliseconds = DateTime.UtcNow.Ticks / 10000;
|
||||
long expires = ((utcTimeInMilliseconds + (7200 * 1000)) / 1000);
|
||||
string sr = scope + "%2f" + target + "%2f" + deviceId;
|
||||
string sigNoEncode = computeHash(deviceKey, sr + '\n' + expires.ToString());
|
||||
string sigEncoded = Uri.EscapeDataString(sigNoEncode);
|
||||
return "SharedAccessSignature sr=" + sr + "&sig=" + sigEncoded + "&se=" + expires.ToString();
|
||||
}
|
||||
|
||||
public string MakeRequest(string method, string payload, string authScope, string address, bool isDPS)
|
||||
{
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
|
||||
request.Method = method;
|
||||
request.ContentType = "application/json";
|
||||
string authTarget = "";
|
||||
string reason = "";
|
||||
if (isDPS) {
|
||||
reason = "registrations";
|
||||
authTarget = "&skn=registration";
|
||||
} else {
|
||||
reason = "devices";
|
||||
request.Headers.Add("iothub-to", "/devices/" + deviceId + "/messages/events");
|
||||
}
|
||||
|
||||
request.Headers.Add("Authorization", getAuth(authScope, reason) + authTarget);
|
||||
|
||||
if (payload.Length > 0) {
|
||||
request.ContentLength = payload.Length;
|
||||
Stream dataStream = request.GetRequestStream();
|
||||
dataStream.Write(Encoding.ASCII.GetBytes(payload), 0, payload.Length);
|
||||
dataStream.Close();
|
||||
}
|
||||
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
Stream receiveStream = response.GetResponseStream();
|
||||
Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
|
||||
StreamReader readStream = new StreamReader( receiveStream, encode );
|
||||
return readStream.ReadToEnd(); // bad hack
|
||||
}
|
||||
|
||||
public void Run() {
|
||||
// get operationId from Azure IoT DPS
|
||||
string address = "https://global.azure-devices-provisioning.net/" + scopeId + "/registrations/" + deviceId + "/register?api-version=2018-11-01";
|
||||
string json = MakeRequest("PUT", "{\"registrationId\":\"" + deviceId + "\"}", scopeId, address, true);
|
||||
var obj = json.FromJson<object>();
|
||||
Dictionary<string,object> dict = (Dictionary<string,object>)obj;
|
||||
|
||||
if (dict.ContainsKey("errorCode")) {
|
||||
Console.WriteLine("ERROR: " + json);
|
||||
return;
|
||||
}
|
||||
|
||||
string operationId = dict["operationId"].ToString();
|
||||
|
||||
// Using the operationId and the rest of the credentials, get hostname for the Azure IoT hub
|
||||
// The reason for the loop is that the async op. we had triggered with the operationId request
|
||||
// may take more than 2secs.
|
||||
// So, wait for 2 secs+ each time and then try again
|
||||
for (int i = 0; i < 10; i++) // try 5 times
|
||||
{
|
||||
System.Threading.Thread.Sleep(2500);
|
||||
address = "https://global.azure-devices-provisioning.net/" + scopeId + "/registrations/" + deviceId + "/operations/" + operationId + "?api-version=2018-11-01";
|
||||
json = MakeRequest("GET", "", scopeId, address, true);
|
||||
obj = json.FromJson<object>();
|
||||
dict = (Dictionary<string,object>)obj;
|
||||
|
||||
if (dict.ContainsKey("errorCode")) {
|
||||
Console.WriteLine("ERROR: " + json);
|
||||
return;
|
||||
}
|
||||
if (dict.ContainsKey("registrationState")) {
|
||||
if ( ((Dictionary<string,object>)dict["registrationState"]).ContainsKey("assignedHub"))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string hostName = ((Dictionary<string,object>)dict["registrationState"])["assignedHub"].ToString();
|
||||
|
||||
|
||||
// finally, send our temperature telemetry to Azure IoT Hub
|
||||
address = "https://" + hostName + "/devices/" + deviceId + "/messages/events/?api-version=2016-11-14";
|
||||
json = MakeRequest("POST", "{\"temp\" : 15}", hostName, address, false);
|
||||
|
||||
if (json.Length == 0) {
|
||||
Console.WriteLine("Success!! Check the new temperature telemetry on Azure IoT Central Device window");
|
||||
} else {
|
||||
Console.WriteLine(json);
|
||||
}
|
||||
}
|
||||
|
||||
static void Main() {
|
||||
IoTCentralDemo demo = new IoTCentralDemo();
|
||||
demo.Run();
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче