mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-28 17:22:37 +08:00
Extend MQTTnet.Server application.
This commit is contained in:
+1
-1
@@ -47,7 +47,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedC
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnetServer", "Source\MQTTnetServer\MQTTnetServer.csproj", "{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ namespace MQTTnet.AspNetCore
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
StopAsync().GetAwaiter().GetResult();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace MQTTnet.Server.Configuration
|
||||||
|
{
|
||||||
|
public class EndpointConfiguration
|
||||||
|
{
|
||||||
|
public int Port { get; set; } = 1883;
|
||||||
|
|
||||||
|
public int BacklogSize { get; set; } = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace MQTTnetServer.Settings
|
namespace MQTTnet.Server.Configuration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Listen Entry Settings Model
|
/// Listen Entry Settings Model
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace MQTTnetServer.Settings
|
namespace MQTTnet.Server.Configuration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Listen Protocol Types
|
/// Listen Protocol Types
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace MQTTnet.Server.Configuration
|
||||||
|
{
|
||||||
|
public class MqttNetServerConfiguration
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MQTTnetServer.Settings
|
namespace MQTTnet.Server.Configuration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main Settings Model
|
/// Main Settings Model
|
||||||
+2
-5
@@ -1,10 +1,7 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace MQTTnetServer.Controllers
|
namespace MQTTnet.Server.Controllers
|
||||||
{
|
{
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Christian Kratky
|
||||||
|
|
||||||
|
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,43 @@
|
|||||||
|
using System;
|
||||||
|
using MQTTnet.Diagnostics;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Logging
|
||||||
|
{
|
||||||
|
public class MqttNetChildLoggerWrapper : IMqttNetChildLogger
|
||||||
|
{
|
||||||
|
private readonly MqttNetLoggerWrapper _logger;
|
||||||
|
private readonly string _source;
|
||||||
|
|
||||||
|
public MqttNetChildLoggerWrapper(string source, MqttNetLoggerWrapper logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
_source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMqttNetChildLogger CreateChildLogger(string source = null)
|
||||||
|
{
|
||||||
|
return _logger.CreateChildLogger(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Verbose(string message, params object[] parameters)
|
||||||
|
{
|
||||||
|
_logger.Publish(MqttNetLogLevel.Verbose, _source, message, parameters, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Info(string message, params object[] parameters)
|
||||||
|
{
|
||||||
|
_logger.Publish(MqttNetLogLevel.Info, _source, message, parameters, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Warning(Exception exception, string message, params object[] parameters)
|
||||||
|
{
|
||||||
|
_logger.Publish(MqttNetLogLevel.Warning, _source, message, parameters, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error(Exception exception, string message, params object[] parameters)
|
||||||
|
{
|
||||||
|
_logger.Publish(MqttNetLogLevel.Error, _source, message, parameters, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet.Diagnostics;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Logging
|
||||||
|
{
|
||||||
|
public class MqttNetLoggerWrapper : IMqttNetLogger
|
||||||
|
{
|
||||||
|
private readonly ILogger<MqttServer> _logger;
|
||||||
|
|
||||||
|
public MqttNetLoggerWrapper(ILogger<MqttServer> logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
||||||
|
|
||||||
|
public IMqttNetChildLogger CreateChildLogger(string source = null)
|
||||||
|
{
|
||||||
|
return new MqttNetChildLoggerWrapper(source, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
||||||
|
{
|
||||||
|
var convertedLogLevel = ConvertLogLevel(logLevel);
|
||||||
|
|
||||||
|
if (!_logger.IsEnabled(convertedLogLevel))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Log(convertedLogLevel, exception, message, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LogLevel ConvertLogLevel(MqttNetLogLevel logLevel)
|
||||||
|
{
|
||||||
|
switch (logLevel)
|
||||||
|
{
|
||||||
|
case MqttNetLogLevel.Error: return LogLevel.Error;
|
||||||
|
case MqttNetLogLevel.Warning: return LogLevel.Warning;
|
||||||
|
case MqttNetLogLevel.Info: return LogLevel.Information;
|
||||||
|
case MqttNetLogLevel.Verbose: return LogLevel.Trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LogLevel.Debug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-4
@@ -3,11 +3,11 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||||
<AssemblyName>MQTTnetServer</AssemblyName>
|
<AssemblyName>MQTTnet.Server</AssemblyName>
|
||||||
<RootNamespace>MQTTnetServer</RootNamespace>
|
<RootNamespace>MQTTnet.Server</RootNamespace>
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
<Company />
|
<Company />
|
||||||
<Product />
|
<Product>MQTTnet</Product>
|
||||||
<Description />
|
<Description />
|
||||||
<Authors />
|
<Authors />
|
||||||
<PackageId />
|
<PackageId />
|
||||||
@@ -16,8 +16,12 @@
|
|||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<StartupObject></StartupObject>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DocumentationFile>MQTTnetServer.xml</DocumentationFile>
|
<DocumentationFile></DocumentationFile>
|
||||||
<WarningsAsErrors>NU1605</WarningsAsErrors>
|
<WarningsAsErrors>NU1605</WarningsAsErrors>
|
||||||
<NoWarn>1701;1702</NoWarn>
|
<NoWarn>1701;1702</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -29,8 +33,12 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="IronPython" Version="2.7.9" />
|
||||||
|
<PackageReference Include="IronPython.StdLib" Version="2.7.9" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="4.0.1" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="4.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -38,4 +46,16 @@
|
|||||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="LICENSE">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Scripts\readme.md">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Scripts\00_sample.py">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using IronPython.Runtime;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
using MQTTnet.Server.Scripting;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Mqtt
|
||||||
|
{
|
||||||
|
public class MqttApplicationMessageInterceptor : IMqttServerApplicationMessageInterceptor
|
||||||
|
{
|
||||||
|
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||||
|
private readonly ILogger<MqttApplicationMessageInterceptor> _logger;
|
||||||
|
|
||||||
|
public MqttApplicationMessageInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttApplicationMessageInterceptor> logger)
|
||||||
|
{
|
||||||
|
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InterceptApplicationMessagePublishAsync(MqttApplicationMessageInterceptorContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pythonContext = new PythonDictionary
|
||||||
|
{
|
||||||
|
{ "accept_publish", context.AcceptPublish },
|
||||||
|
{ "close_connection", context.CloseConnection },
|
||||||
|
{ "client_id", context.ClientId },
|
||||||
|
{ "topic", context.ApplicationMessage.Topic },
|
||||||
|
{ "qos", (int)context.ApplicationMessage.QualityOfServiceLevel },
|
||||||
|
{ "retain", context.ApplicationMessage.Retain }
|
||||||
|
};
|
||||||
|
|
||||||
|
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_application_message", pythonContext);
|
||||||
|
|
||||||
|
context.AcceptPublish = (bool)pythonContext.get("accept_publish", context.AcceptPublish);
|
||||||
|
context.CloseConnection = (bool)pythonContext.get("close_connection", context.CloseConnection);
|
||||||
|
context.ApplicationMessage.Topic = (string)pythonContext.get("topic", context.ApplicationMessage.Topic);
|
||||||
|
context.ApplicationMessage.QualityOfServiceLevel = (MqttQualityOfServiceLevel)(int)pythonContext.get("qos", (int)context.ApplicationMessage.QualityOfServiceLevel);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(exception, "Error while intercepting application message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using IronPython.Runtime;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
using MQTTnet.Server.Scripting;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Mqtt
|
||||||
|
{
|
||||||
|
public class MqttConnectionValidator : IMqttServerConnectionValidator
|
||||||
|
{
|
||||||
|
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||||
|
private readonly ILogger<MqttConnectionValidator> _logger;
|
||||||
|
|
||||||
|
public MqttConnectionValidator(PythonScriptHostService pythonScriptHostService, ILogger<MqttConnectionValidator> logger)
|
||||||
|
{
|
||||||
|
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ValidateConnectionAsync(MqttConnectionValidatorContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pythonContext = new PythonDictionary
|
||||||
|
{
|
||||||
|
{ "client_id", context.ClientId },
|
||||||
|
{ "endpoint", context.Endpoint },
|
||||||
|
{ "username", context.Username },
|
||||||
|
{ "password", context.Password },
|
||||||
|
{ "result", PythonConvert.Pythonfy(context.ReturnCode) }
|
||||||
|
};
|
||||||
|
|
||||||
|
_pythonScriptHostService.InvokeOptionalFunction("on_validate_client_connection", pythonContext);
|
||||||
|
|
||||||
|
context.ReturnCode = PythonConvert.ParseEnum<MqttConnectReturnCode>((string)pythonContext["result"]);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(exception, "Error while validating client connection.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using IronPython.Runtime;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet.Adapter;
|
||||||
|
using MQTTnet.AspNetCore;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
using MQTTnet.Server.Logging;
|
||||||
|
using MQTTnet.Server.Scripting;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Mqtt
|
||||||
|
{
|
||||||
|
public class MqttServerService
|
||||||
|
{
|
||||||
|
private readonly ILogger<MqttServerService> _logger;
|
||||||
|
|
||||||
|
private readonly MqttConnectionValidator _mqttConnectionValidator;
|
||||||
|
private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
|
||||||
|
private readonly MqttApplicationMessageInterceptor _mqttApplicationMessageInterceptor;
|
||||||
|
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||||
|
|
||||||
|
private readonly IMqttServer _mqttServer;
|
||||||
|
|
||||||
|
public MqttServerService(
|
||||||
|
IMqttServerFactory mqttServerFactory,
|
||||||
|
MqttWebSocketServerAdapter webSocketServerAdapter,
|
||||||
|
MqttNetLoggerWrapper mqttNetLogger,
|
||||||
|
MqttConnectionValidator mqttConnectionValidator,
|
||||||
|
MqttSubscriptionInterceptor mqttSubscriptionInterceptor,
|
||||||
|
MqttApplicationMessageInterceptor mqttApplicationMessageInterceptor,
|
||||||
|
PythonScriptHostService pythonScriptHostService,
|
||||||
|
ILogger<MqttServerService> logger)
|
||||||
|
{
|
||||||
|
_mqttConnectionValidator = mqttConnectionValidator ?? throw new ArgumentNullException(nameof(mqttConnectionValidator));
|
||||||
|
_mqttSubscriptionInterceptor = mqttSubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttSubscriptionInterceptor));
|
||||||
|
_mqttApplicationMessageInterceptor = mqttApplicationMessageInterceptor ?? throw new ArgumentNullException(nameof(mqttApplicationMessageInterceptor));
|
||||||
|
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
var adapters = new List<IMqttServerAdapter>
|
||||||
|
{
|
||||||
|
new MqttTcpServerAdapter(new MqttNetChildLoggerWrapper(null, mqttNetLogger)),
|
||||||
|
webSocketServerAdapter
|
||||||
|
};
|
||||||
|
|
||||||
|
_mqttServer = mqttServerFactory.CreateMqttServer(adapters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure()
|
||||||
|
{
|
||||||
|
_pythonScriptHostService.RegisterProxyObject("publish", new Action<PythonDictionary>(Publish));
|
||||||
|
|
||||||
|
var options = new MqttServerOptionsBuilder()
|
||||||
|
.WithDefaultEndpoint()
|
||||||
|
.WithDefaultEndpointPort(1883)
|
||||||
|
.WithConnectionValidator(_mqttConnectionValidator)
|
||||||
|
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
|
||||||
|
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_mqttServer.StartAsync(options).GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
_logger.LogInformation("MQTT server started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Publish(PythonDictionary parameters)
|
||||||
|
{
|
||||||
|
var applicationMessageBuilder = new MqttApplicationMessageBuilder()
|
||||||
|
.WithTopic((string)parameters.get("topic", null))
|
||||||
|
.WithRetainFlag((bool)parameters.get("retain", false))
|
||||||
|
.WithQualityOfServiceLevel((MqttQualityOfServiceLevel)(int)parameters.get("qos", 0));
|
||||||
|
|
||||||
|
var payload = parameters.get("payload", null);
|
||||||
|
var binaryPayload = new byte[0];
|
||||||
|
|
||||||
|
if (payload is string stringPayload)
|
||||||
|
{
|
||||||
|
binaryPayload = Encoding.UTF8.GetBytes(stringPayload);
|
||||||
|
}
|
||||||
|
else if (payload is ByteArray byteArray)
|
||||||
|
{
|
||||||
|
binaryPayload = byteArray.ToArray();
|
||||||
|
}
|
||||||
|
else if (payload is IEnumerable<int> intArray)
|
||||||
|
{
|
||||||
|
binaryPayload = intArray.Select(Convert.ToByte).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
applicationMessageBuilder = applicationMessageBuilder
|
||||||
|
.WithPayload(binaryPayload);
|
||||||
|
|
||||||
|
var applicationMessage = applicationMessageBuilder.Build();
|
||||||
|
|
||||||
|
_mqttServer.PublishAsync(applicationMessage).GetAwaiter().GetResult();
|
||||||
|
_logger.LogInformation($"Published topic '{applicationMessage.Topic}' from server.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using IronPython.Runtime;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet.Server.Scripting;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Mqtt
|
||||||
|
{
|
||||||
|
public class MqttSubscriptionInterceptor : IMqttServerSubscriptionInterceptor
|
||||||
|
{
|
||||||
|
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||||
|
private readonly ILogger<MqttSubscriptionInterceptor> _logger;
|
||||||
|
|
||||||
|
public MqttSubscriptionInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttSubscriptionInterceptor> logger)
|
||||||
|
{
|
||||||
|
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InterceptSubscriptionAsync(MqttSubscriptionInterceptorContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pythonContext = new PythonDictionary
|
||||||
|
{
|
||||||
|
{ "accept_subscription", context.AcceptSubscription },
|
||||||
|
{ "close_connection", context.CloseConnection },
|
||||||
|
|
||||||
|
{ "client_id", context.ClientId },
|
||||||
|
{ "topic", context.TopicFilter.Topic },
|
||||||
|
{ "qos", (int)context.TopicFilter.QualityOfServiceLevel }
|
||||||
|
};
|
||||||
|
|
||||||
|
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_subscription", pythonContext);
|
||||||
|
|
||||||
|
context.AcceptSubscription = (bool)pythonContext["accept_subscription"];
|
||||||
|
context.CloseConnection = (bool)pythonContext["close_connection"];
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(exception, "Error while intercepting subscription.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using MQTTnet.Server.Configuration;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
public static int Main(string[] args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PrintLogo();
|
||||||
|
|
||||||
|
CreateWebHostBuilder(args).Build().Run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Console.WriteLine(exception);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintLogo()
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
|
||||||
|
Console.WriteLine(@"
|
||||||
|
__ __ ____ _______ _______ _ _____
|
||||||
|
| \/ |/ __ \__ __|__ __| | | / ____|
|
||||||
|
| \ / | | | | | | | |_ __ ___| |_ | (___ ___ _ ____ _____ _ __
|
||||||
|
| |\/| | | | | | | | | '_ \ / _ \ __| \___ \ / _ \ '__\ \ / / _ \ '__|
|
||||||
|
| | | | |__| | | | | | | | | __/ |_ ____) | __/ | \ V / __/ |
|
||||||
|
|_| |_|\___\_\ |_| |_|_| |_|\___|\__| |_____/ \___|_| \_/ \___|_|");
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
|
Console.WriteLine(@"
|
||||||
|
-- The official MQTT server implementation of MQTTnet --
|
||||||
|
Copyright (c) 2017-2019 The MQTTnet Team");
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.Blue;
|
||||||
|
Console.WriteLine(@"
|
||||||
|
https://github.com/chkr1011/MQTTnet");
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
|
Console.WriteLine(@"
|
||||||
|
Version: 1.0.0-alpha1
|
||||||
|
License: MIT (read LICENSE file)
|
||||||
|
");
|
||||||
|
|
||||||
|
Console.BackgroundColor = ConsoleColor.White;
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.WriteLine(" ! THIS IS AN ALPHA VERSION! IT IS NOT RECOMMENDED TO USE IT FOR ANY DIFFERENT PURPOSE THAN TESTING OR EVALUATING!");
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IWebHostBuilder CreateWebHostBuilder(string[] args)
|
||||||
|
{
|
||||||
|
var webHost = WebHost.CreateDefaultBuilder(args)
|
||||||
|
.UseKestrel(kestrelOptions =>
|
||||||
|
{
|
||||||
|
kestrelOptions.ListenAnyIP(80, listenOptions =>
|
||||||
|
{
|
||||||
|
listenOptions.NoDelay = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//var listen = ReadListenSettings();
|
||||||
|
//webHost
|
||||||
|
// .UseKestrel(o =>
|
||||||
|
// {
|
||||||
|
// if (listen?.Length > 0)
|
||||||
|
// {
|
||||||
|
// foreach (var item in listen)
|
||||||
|
// {
|
||||||
|
// if (item.Address?.Trim() == "*")
|
||||||
|
// {
|
||||||
|
// if (item.Protocol == ListenProtocolTypes.MQTT)
|
||||||
|
// {
|
||||||
|
// o.ListenAnyIP(item.Port, c => c.UseMqtt());
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// o.ListenAnyIP(item.Port);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else if (item.Address?.Trim() == "localhost")
|
||||||
|
// {
|
||||||
|
// if (item.Protocol == ListenProtocolTypes.MQTT)
|
||||||
|
// {
|
||||||
|
// o.ListenLocalhost(item.Port, c => c.UseMqtt());
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// o.ListenLocalhost(item.Port);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// if (IPAddress.TryParse(item.Address, out var ip))
|
||||||
|
// {
|
||||||
|
// if (item.Protocol == ListenProtocolTypes.MQTT)
|
||||||
|
// {
|
||||||
|
// o.Listen(ip, item.Port, c => c.UseMqtt());
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// o.Listen(ip, item.Port);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// o.ListenAnyIP(1883, l => l.UseMqtt());
|
||||||
|
// o.ListenAnyIP(5000);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
webHost.UseStartup<Startup>();
|
||||||
|
|
||||||
|
return webHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ListenModel[] ReadListenSettings()
|
||||||
|
{
|
||||||
|
var builder = new ConfigurationBuilder()
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var listen = new List<ListenModel>();
|
||||||
|
builder.Bind("MQTTnetServer:Listen", listen);
|
||||||
|
|
||||||
|
return listen.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Scripting.DataSharing
|
||||||
|
{
|
||||||
|
public class DataSharingService
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, object> _storage = new Dictionary<string, object>();
|
||||||
|
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||||
|
private readonly ILogger<DataSharingService> _logger;
|
||||||
|
|
||||||
|
public DataSharingService(PythonScriptHostService pythonScriptHostService, ILogger<DataSharingService> logger)
|
||||||
|
{
|
||||||
|
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure()
|
||||||
|
{
|
||||||
|
_pythonScriptHostService.RegisterProxyObject("write_shared_data", new Action<string, object>(Write));
|
||||||
|
_pythonScriptHostService.RegisterProxyObject("read_shared_data", new Func<string, object, object>(Read));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string key, object value)
|
||||||
|
{
|
||||||
|
lock (_storage)
|
||||||
|
{
|
||||||
|
_storage[key] = value;
|
||||||
|
_logger.LogInformation($"Shared data with key '{key}' updated.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Read(string key, object defaultValue)
|
||||||
|
{
|
||||||
|
lock (_storage)
|
||||||
|
{
|
||||||
|
if (!_storage.TryGetValue(key, out var value))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Text;
|
||||||
|
using IronPython.Runtime;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Scripting
|
||||||
|
{
|
||||||
|
public static class PythonConvert
|
||||||
|
{
|
||||||
|
public static object ToPython(object value)
|
||||||
|
{
|
||||||
|
if (value is PythonDictionary)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is string)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is int)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is float)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is bool)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is IDictionary dictionary)
|
||||||
|
{
|
||||||
|
var pythonDictionary = new PythonDictionary();
|
||||||
|
foreach (DictionaryEntry dictionaryEntry in dictionary)
|
||||||
|
{
|
||||||
|
pythonDictionary.Add(dictionaryEntry.Key, ToPython(dictionaryEntry.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pythonDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is IEnumerable enumerable)
|
||||||
|
{
|
||||||
|
var pythonList = new List();
|
||||||
|
foreach (var item in enumerable)
|
||||||
|
{
|
||||||
|
pythonList.Add(ToPython(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pythonList;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Pythonfy(Enum value)
|
||||||
|
{
|
||||||
|
return Pythonfy(value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Pythonfy(string value)
|
||||||
|
{
|
||||||
|
var result = new StringBuilder();
|
||||||
|
foreach (var @char in value)
|
||||||
|
{
|
||||||
|
if (char.IsUpper(@char) && result.Length > 0)
|
||||||
|
{
|
||||||
|
result.Append('_');
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Append(char.ToLowerInvariant(@char));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TEnum ParseEnum<TEnum>(string value) where TEnum : Enum
|
||||||
|
{
|
||||||
|
return (TEnum)Enum.Parse(typeof(TEnum), value.Replace("_", string.Empty), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Scripting
|
||||||
|
{
|
||||||
|
public class PythonIOStream : Stream
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly Encoding _encoder = Encoding.UTF8;
|
||||||
|
|
||||||
|
public PythonIOStream(ILogger<PythonIOStream> logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = _encoder.GetString(buffer, offset, count);
|
||||||
|
if (text.Equals(Environment.NewLine))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanRead { get; } = false;
|
||||||
|
public override bool CanSeek { get; } = false;
|
||||||
|
public override bool CanWrite { get; } = true;
|
||||||
|
public override long Length { get; } = 0L;
|
||||||
|
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => throw new NotSupportedException();
|
||||||
|
set => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Dynamic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Scripting;
|
||||||
|
using Microsoft.Scripting.Hosting;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Scripting
|
||||||
|
{
|
||||||
|
public class PythonScriptHostService
|
||||||
|
{
|
||||||
|
private readonly IDictionary<string, object> _proxyObjects = new ExpandoObject();
|
||||||
|
private readonly List<PythonScriptInstance> _scriptInstances = new List<PythonScriptInstance>();
|
||||||
|
private readonly ILogger<PythonScriptHostService> _logger;
|
||||||
|
private readonly ScriptEngine _scriptEngine;
|
||||||
|
|
||||||
|
public PythonScriptHostService(PythonIOStream pythonIOStream, ILogger<PythonScriptHostService> logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
_scriptEngine = IronPython.Hosting.Python.CreateEngine();
|
||||||
|
_scriptEngine.Runtime.IO.SetOutput(pythonIOStream, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure()
|
||||||
|
{
|
||||||
|
AddSearchPaths(_scriptEngine);
|
||||||
|
|
||||||
|
var scriptsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Scripts");
|
||||||
|
foreach (var filename in Directory.GetFiles(scriptsDirectory, "*.py", SearchOption.AllDirectories).OrderBy(file => file))
|
||||||
|
{
|
||||||
|
TryInitializeScript(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterProxyObject(string name, object action)
|
||||||
|
{
|
||||||
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
|
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||||
|
|
||||||
|
_proxyObjects.Add(name, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InvokeOptionalFunction(string name, object parameters)
|
||||||
|
{
|
||||||
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
|
|
||||||
|
lock (_scriptInstances)
|
||||||
|
{
|
||||||
|
foreach (var pythonScriptInstance in _scriptInstances)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pythonScriptInstance.InvokeOptionalFunction(name, parameters);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(exception, $"Error while invoking function '{name}' at script '{pythonScriptInstance.Name}'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryInitializeScript(string filename)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var scriptName = new FileInfo(filename).Name;
|
||||||
|
|
||||||
|
_logger.LogTrace($"Initializing Python script '{scriptName}'...");
|
||||||
|
var code = File.ReadAllText(filename);
|
||||||
|
|
||||||
|
var scriptInstance = CreateScriptInstance(scriptName, code);
|
||||||
|
scriptInstance.InvokeOptionalFunction("initialize");
|
||||||
|
|
||||||
|
_scriptInstances.Add(scriptInstance);
|
||||||
|
|
||||||
|
_logger.LogInformation($"Initialized script '{scriptName}'.");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(exception, $"Error while initializing script '{new FileInfo(filename).Name}'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PythonScriptInstance CreateScriptInstance(string name, string code)
|
||||||
|
{
|
||||||
|
var scriptScope = _scriptEngine.CreateScope();
|
||||||
|
|
||||||
|
var source = scriptScope.Engine.CreateScriptSourceFromString(code, SourceCodeKind.File);
|
||||||
|
var compiledCode = source.Compile();
|
||||||
|
|
||||||
|
scriptScope.SetVariable("mqtt_net_server", _proxyObjects);
|
||||||
|
compiledCode.Execute(scriptScope);
|
||||||
|
|
||||||
|
return new PythonScriptInstance(name, scriptScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddSearchPaths(ScriptEngine scriptEngine)
|
||||||
|
{
|
||||||
|
var paths = new List<string>
|
||||||
|
{
|
||||||
|
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Lib"),
|
||||||
|
"/usr/lib/python2.7",
|
||||||
|
@"C:\Python27\Lib"
|
||||||
|
};
|
||||||
|
|
||||||
|
AddSearchPaths(scriptEngine, paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddSearchPaths(ScriptEngine scriptEngine, IEnumerable<string> paths)
|
||||||
|
{
|
||||||
|
var searchPaths = scriptEngine.GetSearchPaths();
|
||||||
|
|
||||||
|
foreach (var path in paths)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(path))
|
||||||
|
{
|
||||||
|
searchPaths.Add(path);
|
||||||
|
_logger.LogInformation($"Added Python lib path: {path}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptEngine.SetSearchPaths(searchPaths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using IronPython.Runtime;
|
||||||
|
using Microsoft.Scripting.Hosting;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Scripting
|
||||||
|
{
|
||||||
|
public class PythonScriptInstance
|
||||||
|
{
|
||||||
|
private readonly ScriptScope _scriptScope;
|
||||||
|
|
||||||
|
public PythonScriptInstance(string name, ScriptScope scriptScope)
|
||||||
|
{
|
||||||
|
_scriptScope = scriptScope;
|
||||||
|
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public bool InvokeOptionalFunction(string name, params object[] parameters)
|
||||||
|
{
|
||||||
|
return InvokeOptionalFunction(name, parameters, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool InvokeOptionalFunction(string name, object[] parameters, out object result)
|
||||||
|
{
|
||||||
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
|
|
||||||
|
lock (_scriptScope)
|
||||||
|
{
|
||||||
|
if (!_scriptScope.Engine.Operations.TryGetMember(_scriptScope, name, out var member))
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(member is PythonFunction function))
|
||||||
|
{
|
||||||
|
throw new Exception($"Member '{name}' is no Python function.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = _scriptScope.Engine.Operations.Invoke(function, parameters);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
var details = _scriptScope.Engine.GetService<ExceptionOperations>().FormatException(exception);
|
||||||
|
var message = $"Error while invoking function '{name}'. " + Environment.NewLine + details;
|
||||||
|
|
||||||
|
throw new Exception(message, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def initialize():
|
||||||
|
"""
|
||||||
|
This function is invoked after the script file has been loaded.
|
||||||
|
It will be executed only one time.
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("Hello World from Sample script.")
|
||||||
|
|
||||||
|
|
||||||
|
def on_validate_client_connection(context):
|
||||||
|
"""
|
||||||
|
This function is invoked whenever a client wants to connect. It can be used to validate the connection.
|
||||||
|
"""
|
||||||
|
|
||||||
|
print(context)
|
||||||
|
|
||||||
|
mqtt_net_server.write_shared_data(context["client_id"], {"custom_value_1": 1, "custom_value_2": True})
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
if context["client_id"] != "test_client":
|
||||||
|
context["result"] = "connection_refused_not_authorized"
|
||||||
|
return
|
||||||
|
|
||||||
|
if context["username"] != "bud spencer":
|
||||||
|
context["result"] = "connection_refused_not_authorized"
|
||||||
|
return
|
||||||
|
|
||||||
|
if context["password"] != "secret":
|
||||||
|
context["result"] = "connection_refused_not_authorized"
|
||||||
|
|
||||||
|
print(context)
|
||||||
|
|
||||||
|
|
||||||
|
def on_intercept_subscription(context):
|
||||||
|
"""
|
||||||
|
This function is invoked whenever a client wants to subscribe to a topic.
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("Client '{client_id}' want's to subscribe to topic '{topic}'.".format(client_id=context["client_id"], topic=context["topic"]))
|
||||||
|
|
||||||
|
|
||||||
|
def on_intercept_application_message(context):
|
||||||
|
"""
|
||||||
|
This function is invoked for every processed application message. It also allows modifying
|
||||||
|
the message or cancel processing at all.
|
||||||
|
"""
|
||||||
|
client_id = context["client_id"]
|
||||||
|
|
||||||
|
if client_id != None:
|
||||||
|
shared_data = mqtt_net_server.read_shared_data(context["client_id"], {})
|
||||||
|
print(shared_data)
|
||||||
|
|
||||||
|
if context["topic"] == "topic_with_response":
|
||||||
|
|
||||||
|
json_payload = {
|
||||||
|
"hello": "world",
|
||||||
|
"x": 1,
|
||||||
|
"y": True,
|
||||||
|
"z": None
|
||||||
|
}
|
||||||
|
|
||||||
|
application_message = {
|
||||||
|
"retain": False,
|
||||||
|
"topic": "reply",
|
||||||
|
"payload": json.dumps(json_payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
mqtt_net_server.publish(application_message)
|
||||||
|
|
||||||
|
print("Client '{client_id}' published topic '{topic}'.".format(client_id=context["client_id"], topic=context["topic"]))
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
This directory contains scripts which are loaded by the server and can be used to perform the following tasks.
|
||||||
|
|
||||||
|
1. Validation of client connections via credentials, client IDs etc.
|
||||||
|
2. Manipulation of every processed message.
|
||||||
|
3. Validation of subscription attempts.
|
||||||
|
4. Publishing of custom application messages.
|
||||||
|
|
||||||
|
All scripts are loaded and _MQTTnet Server_ will invoke functions according to predefined naming conventions.
|
||||||
|
If a function is implemented in multiple scripts the context will be moved throug all instances. This allows overriding of results or passing data to other (following) scripts.
|
||||||
|
|
||||||
|
The Python starndard library ships with _MQTTnet Server_. But it is possible to add custom paths with Python libraries.
|
||||||
|
|
||||||
|
```
|
||||||
|
import sys
|
||||||
|
sys.path.append(PATH_TO_LIBRARY)
|
||||||
|
```
|
||||||
|
|
||||||
|
* All scripts must have the file extension _.py_.
|
||||||
|
* All scripts are sorted alphabetically (A to Z) before being loaded and parsed.
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using MQTTnet.AspNetCore;
|
||||||
|
using MQTTnet.Server.Logging;
|
||||||
|
using MQTTnet.Server.Mqtt;
|
||||||
|
using MQTTnet.Server.Scripting;
|
||||||
|
using MQTTnet.Server.Scripting.DataSharing;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
//var builder = new ConfigurationBuilder()
|
||||||
|
// .AddJsonFile("appsettings.json")
|
||||||
|
// .AddEnvironmentVariables();
|
||||||
|
|
||||||
|
//Configuration = builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfigurationRoot Configuration { get; }
|
||||||
|
|
||||||
|
public void Configure(
|
||||||
|
IApplicationBuilder application,
|
||||||
|
IHostingEnvironment environment,
|
||||||
|
MqttServerService mqttServerService,
|
||||||
|
PythonScriptHostService pythonScriptHostService,
|
||||||
|
DataSharingService dataSharingService)
|
||||||
|
{
|
||||||
|
if (environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
application.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
application.UseHsts();
|
||||||
|
}
|
||||||
|
|
||||||
|
application.UseHttpsRedirection();
|
||||||
|
application.UseMvc();
|
||||||
|
|
||||||
|
dataSharingService.Configure();
|
||||||
|
pythonScriptHostService.Configure();
|
||||||
|
mqttServerService.Configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<MqttNetLoggerWrapper>();
|
||||||
|
|
||||||
|
services.AddSingleton<PythonIOStream>();
|
||||||
|
services.AddSingleton<PythonScriptHostService>();
|
||||||
|
services.AddSingleton<DataSharingService>();
|
||||||
|
|
||||||
|
services.AddSingleton<MqttWebSocketServerAdapter>();
|
||||||
|
services.AddSingleton<IMqttServerFactory, MqttFactory>();
|
||||||
|
services.AddSingleton<MqttServerService>();
|
||||||
|
services.AddSingleton<MqttConnectionValidator>();
|
||||||
|
services.AddSingleton<MqttSubscriptionInterceptor>();
|
||||||
|
services.AddSingleton<MqttApplicationMessageInterceptor>();
|
||||||
|
|
||||||
|
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,17 @@ namespace MQTTnet
|
|||||||
{
|
{
|
||||||
public class MqttFactory : IMqttClientFactory, IMqttServerFactory
|
public class MqttFactory : IMqttClientFactory, IMqttServerFactory
|
||||||
{
|
{
|
||||||
|
private readonly IMqttNetLogger _logger;
|
||||||
|
|
||||||
|
public MqttFactory() : this(new MqttNetLogger())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttFactory(IMqttNetLogger logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public IMqttClient CreateMqttClient()
|
public IMqttClient CreateMqttClient()
|
||||||
{
|
{
|
||||||
return CreateMqttClient(new MqttNetLogger());
|
return CreateMqttClient(new MqttNetLogger());
|
||||||
@@ -57,5 +68,12 @@ namespace MQTTnet
|
|||||||
|
|
||||||
return new MqttServer(adapters, logger.CreateChildLogger());
|
return new MqttServer(adapters, logger.CreateChildLogger());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> adapters)
|
||||||
|
{
|
||||||
|
if (adapters == null) throw new ArgumentNullException(nameof(adapters));
|
||||||
|
|
||||||
|
return new MqttServer(adapters, _logger.CreateChildLogger());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<doc>
|
|
||||||
<assembly>
|
|
||||||
<name>MQTTnetServer</name>
|
|
||||||
</assembly>
|
|
||||||
<members>
|
|
||||||
<member name="T:MQTTnetServer.Program">
|
|
||||||
<summary>
|
|
||||||
Main Entry point
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:MQTTnetServer.Program.Main(System.String[])">
|
|
||||||
<summary>
|
|
||||||
Main
|
|
||||||
</summary>
|
|
||||||
<param name="args"></param>
|
|
||||||
</member>
|
|
||||||
<member name="M:MQTTnetServer.Program.CreateWebHostBuilder(System.String[])">
|
|
||||||
<summary>
|
|
||||||
Configure and Start Kestrel
|
|
||||||
</summary>
|
|
||||||
<param name="args"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:MQTTnetServer.Program.ReadListenSettings">
|
|
||||||
<summary>
|
|
||||||
Read Application Settings
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="T:MQTTnetServer.Settings.ListenModel">
|
|
||||||
<summary>
|
|
||||||
Listen Entry Settings Model
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:MQTTnetServer.Settings.ListenModel.#ctor">
|
|
||||||
<summary>
|
|
||||||
Constructor
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:MQTTnetServer.Settings.ListenModel.Address">
|
|
||||||
<summary>
|
|
||||||
Listen Address
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:MQTTnetServer.Settings.ListenModel.Port">
|
|
||||||
<summary>
|
|
||||||
Listen Port
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:MQTTnetServer.Settings.ListenModel.Protocol">
|
|
||||||
<summary>
|
|
||||||
Protocol Type
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:MQTTnetServer.Settings.ListenProtocolTypes">
|
|
||||||
<summary>
|
|
||||||
Listen Protocol Types
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:MQTTnetServer.Settings.ListenProtocolTypes.HTTP">
|
|
||||||
<summary>
|
|
||||||
HTTP
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:MQTTnetServer.Settings.ListenProtocolTypes.HTTPS">
|
|
||||||
<summary>
|
|
||||||
HTTPS
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:MQTTnetServer.Settings.ListenProtocolTypes.MQTT">
|
|
||||||
<summary>
|
|
||||||
MQTT
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:MQTTnetServer.Settings.SettingsModel">
|
|
||||||
<summary>
|
|
||||||
Main Settings Model
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:MQTTnetServer.Settings.SettingsModel.Listen">
|
|
||||||
<summary>
|
|
||||||
Listen Settings
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:MQTTnetServer.Startup">
|
|
||||||
<summary>
|
|
||||||
Web App Startup
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:MQTTnetServer.Startup.#ctor(Microsoft.Extensions.Configuration.IConfiguration)">
|
|
||||||
<summary>
|
|
||||||
Constructor
|
|
||||||
</summary>
|
|
||||||
<param name="configuration"></param>
|
|
||||||
</member>
|
|
||||||
<member name="P:MQTTnetServer.Startup.Configuration">
|
|
||||||
<summary>
|
|
||||||
Application Settings
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:MQTTnetServer.Startup.Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder,Microsoft.AspNetCore.Hosting.IHostingEnvironment)">
|
|
||||||
<summary>
|
|
||||||
This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|
||||||
</summary>
|
|
||||||
<param name="app"></param>
|
|
||||||
<param name="env"></param>
|
|
||||||
</member>
|
|
||||||
<member name="M:MQTTnetServer.Startup.ConfigureServices(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
|
|
||||||
<summary>
|
|
||||||
This method gets called by the runtime. Use this method to add services to the container.
|
|
||||||
</summary>
|
|
||||||
<param name="services"></param>
|
|
||||||
</member>
|
|
||||||
</members>
|
|
||||||
</doc>
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
using Microsoft.AspNetCore;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using MQTTnet.AspNetCore;
|
|
||||||
using MQTTnetServer.Settings;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace MQTTnetServer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Main Entry point
|
|
||||||
/// </summary>
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Main
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public static void Main(string[] args)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CreateWebHostBuilder(args).Build().Run();
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException e)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Could not find application settings file in: " + e.FileName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Configure and Start Kestrel
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
|
|
||||||
{
|
|
||||||
var webHost = WebHost.CreateDefaultBuilder(args);
|
|
||||||
var listen = ReadListenSettings();
|
|
||||||
webHost
|
|
||||||
.UseKestrel(o =>
|
|
||||||
{
|
|
||||||
if (listen?.Length > 0)
|
|
||||||
{
|
|
||||||
foreach (var item in listen)
|
|
||||||
{
|
|
||||||
if (item.Address?.Trim() == "*")
|
|
||||||
{
|
|
||||||
if (item.Protocol == ListenProtocolTypes.MQTT)
|
|
||||||
{
|
|
||||||
o.ListenAnyIP(item.Port, c => c.UseMqtt());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
o.ListenAnyIP(item.Port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (item.Address?.Trim() == "localhost")
|
|
||||||
{
|
|
||||||
if (item.Protocol == ListenProtocolTypes.MQTT)
|
|
||||||
{
|
|
||||||
o.ListenLocalhost(item.Port, c => c.UseMqtt());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
o.ListenLocalhost(item.Port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (IPAddress.TryParse(item.Address, out var ip))
|
|
||||||
{
|
|
||||||
if (item.Protocol == ListenProtocolTypes.MQTT)
|
|
||||||
{
|
|
||||||
o.Listen(ip, item.Port, c => c.UseMqtt());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
o.Listen(ip, item.Port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
o.ListenAnyIP(1883, l => l.UseMqtt());
|
|
||||||
o.ListenAnyIP(5000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
webHost.UseStartup<Startup>();
|
|
||||||
|
|
||||||
return webHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read Application Settings
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static ListenModel[] ReadListenSettings()
|
|
||||||
{
|
|
||||||
var builder = new ConfigurationBuilder()
|
|
||||||
.AddJsonFile("appsettings.json")
|
|
||||||
.AddEnvironmentVariables()
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var listen = new List<ListenModel>();
|
|
||||||
builder.Bind("MQTTnetServer:Listen", listen);
|
|
||||||
|
|
||||||
return listen.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace MQTTnetServer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Web App Startup
|
|
||||||
/// </summary>
|
|
||||||
public class Startup
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configuration"></param>
|
|
||||||
public Startup(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
var builder = new ConfigurationBuilder()
|
|
||||||
.AddJsonFile("appsettings.json")
|
|
||||||
.AddEnvironmentVariables();
|
|
||||||
Configuration = builder.Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Application Settings
|
|
||||||
/// </summary>
|
|
||||||
public IConfigurationRoot Configuration { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="app"></param>
|
|
||||||
/// <param name="env"></param>
|
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
|
||||||
{
|
|
||||||
if (env.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseDeveloperExceptionPage();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
||||||
app.UseHsts();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
app.UseMvc();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method gets called by the runtime. Use this method to add services to the container.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="services"></param>
|
|
||||||
public void ConfigureServices(IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user