提交 4d7b739a 编写于 作者: 麦壳饼's avatar 麦壳饼

add CoAP for upload Attributes and Telemetry

上级 da2d7707
<?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>{EE801BD6-5757-4A55-BC94-7FBA6E98F393}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>CoAPClient</RootNamespace>
<AssemblyName>CoAPClient</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="CoAP.NET, Version=1.1.0.0, Culture=neutral, PublicKeyToken=e2c10a743037308b, processorArchitecture=MSIL">
<HintPath>..\packages\CoAP.1.1.0\lib\net40\CoAP.NET.dll</HintPath>
</Reference>
<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.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="readme.md" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
\ No newline at end of file
using CoAP;
using CoAP.Util;
using System;
using System.Collections.Generic;
namespace CoAPClient
{
internal class Program
{
private static void Main(string[] args)
{
string method = null;
Uri uri = null;
string payload = null;
bool loop = false;
bool byEvent = true;
if (args.Length == 0)
PrintUsage();
Int32 index = 0;
foreach (String arg in args)
{
if (arg[0] == '-')
{
if (arg.Equals("-l"))
loop = true;
if (arg.Equals("-e"))
byEvent = true;
else
Console.WriteLine("Unknown option: " + arg);
}
else
{
switch (index)
{
case 0:
method = arg.ToUpper();
break;
case 1:
try
{
uri = new Uri(arg);
}
catch (Exception ex)
{
Console.WriteLine("Failed parsing URI: " + ex.Message);
Environment.Exit(1);
}
break;
case 2:
payload = arg;
break;
default:
Console.WriteLine("Unexpected argument: " + arg);
break;
}
index++;
}
}
if (method == null || uri == null)
PrintUsage();
Request request = NewRequest(method);
if (request == null)
{
Console.WriteLine("Unknown method: " + method);
Environment.Exit(1);
}
if ("OBSERVE".Equals(method))
{
request.MarkObserve();
loop = true;
}
else if ("DISCOVER".Equals(method) &&
(String.IsNullOrEmpty(uri.AbsolutePath) || uri.AbsolutePath.Equals("/")))
{
uri = new Uri(uri, "/.well-known/core");
}
request.URI = uri;
request.SetPayload(payload, MediaType.TextPlain);
// uncomment the next line if you want to specify a draft to use
// request.EndPoint = CoAP.Net.EndPointManager.Draft13;
Console.WriteLine(Utils.ToString(request));
try
{
if (byEvent)
{
request.Respond += delegate (Object sender, ResponseEventArgs e)
{
Response response = e.Response;
if (response == null)
{
Console.WriteLine("Request timeout");
}
else
{
Console.WriteLine(Utils.ToString(response));
Console.WriteLine("Time (ms): " + response.RTT);
}
if (!loop)
Environment.Exit(0);
};
request.Send();
while (true)
{
Console.ReadKey();
}
}
else
{
// uncomment the next line if you need retransmission disabled.
// request.AckTimeout = -1;
request.Send();
do
{
Console.WriteLine("Receiving response...");
Response response = null;
response = request.WaitForResponse();
if (response == null)
{
Console.WriteLine("Request timeout");
break;
}
else
{
Console.WriteLine(Utils.ToString(response));
Console.WriteLine("Time elapsed (ms): " + response.RTT);
if (response.ContentType == MediaType.ApplicationLinkFormat)
{
IEnumerable<WebLink> links = LinkFormat.Parse(response.PayloadString);
if (links == null)
{
Console.WriteLine("Failed parsing link format");
Environment.Exit(1);
}
else
{
Console.WriteLine("Discovered resources:");
foreach (var link in links)
{
Console.WriteLine(link);
}
}
}
}
} while (loop);
}
}
catch (Exception ex)
{
Console.WriteLine("Failed executing request: " + ex.Message);
Console.WriteLine(ex);
Environment.Exit(1);
}
}
private static Request NewRequest(String method)
{
switch (method)
{
case "POST":
return Request.NewPost();
case "PUT":
return Request.NewPut();
case "DELETE":
return Request.NewDelete();
case "GET":
case "DISCOVER":
case "OBSERVE":
return Request.NewGet();
default:
return null;
}
}
private static void PrintUsage()
{
Console.WriteLine("CoAP.NET Client");
Console.WriteLine();
Console.WriteLine("Usage: CoAPClient [-e] [-l] method uri [payload]");
Console.WriteLine(" method : { GET, POST, PUT, DELETE, DISCOVER, OBSERVE }");
Console.WriteLine(" uri : The CoAP URI of the remote endpoint or resource.");
Console.WriteLine(" payload : The data to send with the request.");
Console.WriteLine("Options:");
Console.WriteLine(" -e : Receives responses by the Responded event.");
Console.WriteLine(" -l : Loops for multiple responses.");
Console.WriteLine(" (automatic for OBSERVE and separate responses)");
Console.WriteLine();
Console.WriteLine("Examples:");
Console.WriteLine(" CoAPClient DISCOVER coap://localhost");
Console.WriteLine(" CoAPClient POST coap://localhost/storage data");
Environment.Exit(0);
}
}
}
\ No newline at end of file
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("CoAPClient")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CoAPClient")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("ee801bd6-5757-4a55-bc94-7fba6e98f393")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CoAP" version="1.1.0" targetFramework="net40" />
</packages>
\ No newline at end of file
Example:
Telemetry:
CoAPClient.exe POST coap://<_server_>/Telemetry?<_token_> <_playload_>
CoAPClient.exe POST coap://localhost/Telemetry?3cb97cd31fbc40b08d12ec47a6fad622 {\"aaa\":\"bbb\"}
Attributes:
CoAPClient.exe POST coap://<_server_>/Attributes?<_token_> <_playload_>
CoAPClient.exe POST coap://localhost/Attributes?3cb97cd31fbc40b08d12ec47a6fad622 {\"aaa\":\"bbb\"}
URI format:
Json:
POST coap://localhost/Attributes?3cb97cd31fbc40b08d12ec47a6fad622
Xml document:
POST coap://localhost/Attributes?3cb97cd31fbc40b08d12ec47a6fad622&xml&keyname
binary:
POST coap://localhost/Attributes?3cb97cd31fbc40b08d12ec47a6fad622&binary&keyname
\ No newline at end of file
......@@ -15,6 +15,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IoTSharp.Test", "IoTSharp.T
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IoTSharp", "IoTSharp\IoTSharp.csproj", "{CCD2C255-72D2-4A25-B0E9-E30FBEF774C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoAPClient", "CoAPClient\CoAPClient.csproj", "{EE801BD6-5757-4A55-BC94-7FBA6E98F393}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Clients", "Clients", "{897B15C4-C504-4B7C-B01F-E6162959B665}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -29,10 +33,17 @@ Global
{CCD2C255-72D2-4A25-B0E9-E30FBEF774C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCD2C255-72D2-4A25-B0E9-E30FBEF774C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCD2C255-72D2-4A25-B0E9-E30FBEF774C7}.Release|Any CPU.Build.0 = Release|Any CPU
{EE801BD6-5757-4A55-BC94-7FBA6E98F393}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE801BD6-5757-4A55-BC94-7FBA6E98F393}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE801BD6-5757-4A55-BC94-7FBA6E98F393}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE801BD6-5757-4A55-BC94-7FBA6E98F393}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{EE801BD6-5757-4A55-BC94-7FBA6E98F393} = {897B15C4-C504-4B7C-B01F-E6162959B665}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CD7ADD62-E3BD-453A-B653-BC70DB9FF817}
EndGlobalSection
......
......@@ -98,4 +98,9 @@ namespace IoTSharp.Data
Device =0,
Gateway =1
}
public enum CoApRes
{
Attributes,
Telemetry,
}
}
\ No newline at end of file
......@@ -179,7 +179,11 @@ namespace IoTSharp.Extensions
}
public static Dictionary<string, object> ConvertPayloadToDictionary(this MqttApplicationMessage msg)
{
var jojb = JToken.Parse(msg.ConvertPayloadToString());
return JToken.Parse(msg.ConvertPayloadToString())?.JsonToDictionary();
}
public static Dictionary<string, object> JsonToDictionary(this JToken jojb)
{
Dictionary<string, object> keyValues = new Dictionary<string, object>();
if (jojb.Type != JTokenType.Array)
{
......
using CoAP;
using CoAP.Server.Resources;
using IoTSharp.Data;
using IoTSharp.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IoTSharp.Handlers
{
public class CoApResource : Resource
{
private readonly CoApRes _res;
private readonly ApplicationDbContext _dbContext;
private Int32[] _supported = new Int32[] {
MediaType.ApplicationJson,
MediaType.TextPlain,
MediaType.TextXml,
MediaType.ApplicationOctetStream
};
private readonly ILogger _logger;
public CoApResource(string name, ApplicationDbContext dbContext, ILogger logger)
: base(name)
{
Attributes.Title = name;
_res = (CoApRes)Enum.Parse(typeof(CoApRes), name);
_dbContext = dbContext;
foreach (Int32 item in _supported)
{
Attributes.AddContentType(item);
}
_logger = logger;
_logger.LogInformation($"CoApResource {name} is created.");
}
protected override void DoPost(CoapExchange exchange)
{
Task.Run(async () =>
{
try
{
Int32 ct = MediaType.TextPlain;
Dictionary<string, object> keyValues = new Dictionary<string, object>();
if ((ct = MediaType.NegotiationContent(ct, _supported, exchange.Request.GetOptions(OptionType.Accept))) == MediaType.Undefined)
{
exchange.Respond(StatusCode.NotAcceptable, "supported list: ApplicationJson,TextPlain,TextXml,ApplicationOctetStream");
exchange.Reject();
}
else
{
if (!exchange.Request.UriQueries.Any())
{
exchange.Respond(StatusCode.BadRequest, "Forgot the parameters?");
exchange.Reject();
}
else
{
var querys = exchange.Request.UriQueries.ToArray();
var acctoken = exchange.Request.UriQueries.FirstOrDefault();
switch (ct)
{
case MediaType.ApplicationJson:
case MediaType.TextPlain:
keyValues = JToken.Parse(exchange.Request.PayloadString)?.JsonToDictionary();
break;
case MediaType.TextXml:
if (querys.Length >= 2)
{
var xml = new System.Xml.XmlDocument();
try
{
xml.LoadXml(exchange.Request.PayloadString);
}
catch (Exception ex)
{
exchange.Respond(StatusCode.BadRequest, $"Can't load xml ,{ex.Message}");
}
keyValues.Add(querys[1], xml);
}
else
{
exchange.Respond(StatusCode.BadRequest, "You did not specify key name for xml.");
exchange.Reject();
}
break;
case MediaType.ApplicationOctetStream:
if (querys.Length >= 2)
{
keyValues.Add(querys[1], exchange.Request.Payload);
}
else
{
exchange.Respond(StatusCode.BadRequest, "You did not specify key name for binary.");
exchange.Reject();
}
break;
default:
break;
}
var mcr = await _dbContext.DeviceIdentities.Include(d => d.Device).FirstOrDefaultAsync(di => di.IdentityType == IdentityType.AccessToken && di.IdentityId == acctoken);
var dev = mcr?.Device;
if (mcr != null && dev != null)
{
switch (_res)
{
case CoApRes.Attributes:
var result1 = await _dbContext.SaveAsync<AttributeLatest, AttributeData>(keyValues, dev, DataSide.ClientSide);
exchange.Respond(StatusCode.Changed, $"{result1.ret}");
break;
case CoApRes.Telemetry:
var result2 = await _dbContext.SaveAsync<TelemetryLatest, TelemetryData>(keyValues, dev, DataSide.ClientSide);
exchange.Respond(StatusCode.Created, $"{result2.ret}");
break;
default:
break;
}
exchange.Accept();
}
else
{
exchange.Respond(StatusCode.NotFound, "Can't found device.");
exchange.Reject();
}
}
}
}
catch (Exception ex)
{
exchange.Respond(StatusCode.BadRequest, ex.Message);
exchange.Reject();
}
});
}
protected override void DoGet(CoapExchange exchange)
{
}
}
}
......@@ -45,6 +45,7 @@
<RpmDependency Include="libicu" Version="50.1.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CoAP.NET.Core" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="2.2.0" />
<PackageReference Include="Packaging.Targets" Version="0.1.78" />
......@@ -76,8 +77,8 @@
<PackageReference Include="MQTTnet.AspNetCore" Version="3.0.2" />
<PackageReference Include="MQTTnet.AspNetCoreEx" Version="3.0.2" />
<PackageReference Include="MQTTnet.Extensions.Rpc" Version="3.0.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.0" />
<PackageReference Include="NSwag.AspNetCore" Version="12.2.5" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.4" />
<PackageReference Include="NSwag.AspNetCore" Version="12.3.1" />
<PackageReference Include="QuartzHostedService" Version="0.0.4" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
</ItemGroup>
......
using CoAP.Server;
using IoTSharp.Data;
using IoTSharp.Handlers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Receiving;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace IoTSharp.Services
{
public class CoAPService : IHostedService
{
private readonly ILogger _logger;
private ApplicationDbContext _dbContext;
private IServiceScope _serviceScope;
private CoapServer server;
public CoAPService(ILogger<CoAPService> logger, IServiceScopeFactory scopeFactor)
{
server = new CoapServer();
_logger = logger;
_serviceScope = scopeFactor.CreateScope();
_dbContext = _serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
Enum.GetNames(typeof(CoApRes)).ToList().ForEach(n => server.Add(new CoApResource(n, _serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>(), _logger)));
}
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
server.Start();
});
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
server.Stop();
});
}
}
}
\ No newline at end of file
using IoTSharp.Data;
using IoTSharp.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
......@@ -88,6 +89,7 @@ namespace IoTSharp
services.AddIoTSharpMqttServer(AppSettings.MqttBroker);
services.AddMqttClient(AppSettings.MqttClient);
services.AddHostedService<CoAPService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册