From ffdbb6eaa46d459292efdfb5b9fbe41d3dde20b1 Mon Sep 17 00:00:00 2001 From: BlubbFish Date: Sat, 31 Mar 2018 20:42:28 +0000 Subject: [PATCH] [DW] Flex4Grid deleted [NF] BosMon Mqtt Plugin created --- BosmonMqtt.sln | 31 ++++ BosmonMqtt/BosmonMqtt.csproj | 80 +++++++++ BosmonMqtt/BosmonMqtt.csproj.user | 9 + BosmonMqtt/MqttEventProcessor.cs | 77 +++++++++ BosmonMqtt/MqttPlugin.cs | 89 ++++++++++ BosmonMqtt/Plugin.cs | 85 ++++++++++ BosmonMqtt/PluginConfiguration.cs | 123 ++++++++++++++ BosmonMqtt/Properties/AssemblyInfo.cs | 39 +++++ BosmonMqtt/Views/Config.Designer.cs | 232 ++++++++++++++++++++++++++ BosmonMqtt/Views/Config.cs | 117 +++++++++++++ BosmonMqtt/Views/Config.resx | 120 +++++++++++++ BosmonMqtt/bin/Release/BosmonMqtt.dll | Bin 0 -> 19456 bytes BosmonMqtt/bin/Release/M2Mqtt.dll | Bin 0 -> 38912 bytes 13 files changed, 1002 insertions(+) create mode 100644 BosmonMqtt.sln create mode 100644 BosmonMqtt/BosmonMqtt.csproj create mode 100644 BosmonMqtt/BosmonMqtt.csproj.user create mode 100644 BosmonMqtt/MqttEventProcessor.cs create mode 100644 BosmonMqtt/MqttPlugin.cs create mode 100644 BosmonMqtt/Plugin.cs create mode 100644 BosmonMqtt/PluginConfiguration.cs create mode 100644 BosmonMqtt/Properties/AssemblyInfo.cs create mode 100644 BosmonMqtt/Views/Config.Designer.cs create mode 100644 BosmonMqtt/Views/Config.cs create mode 100644 BosmonMqtt/Views/Config.resx create mode 100644 BosmonMqtt/bin/Release/BosmonMqtt.dll create mode 100644 BosmonMqtt/bin/Release/M2Mqtt.dll diff --git a/BosmonMqtt.sln b/BosmonMqtt.sln new file mode 100644 index 0000000..f654721 --- /dev/null +++ b/BosmonMqtt.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2010 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BosmonMqtt", "BosmonMqtt\BosmonMqtt.csproj", "{EE2F31C4-64D7-4788-AC68-33C5CC8EFCCD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt", "..\Librarys\mqtt\M2Mqtt\M2Mqtt.csproj", "{A11AEF5A-B246-4FE8-8330-06DB73CC8074}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EE2F31C4-64D7-4788-AC68-33C5CC8EFCCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE2F31C4-64D7-4788-AC68-33C5CC8EFCCD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE2F31C4-64D7-4788-AC68-33C5CC8EFCCD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE2F31C4-64D7-4788-AC68-33C5CC8EFCCD}.Release|Any CPU.Build.0 = Release|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {768F1E16-781A-45A6-B11D-6B2D5E0008B1} + EndGlobalSection +EndGlobal diff --git a/BosmonMqtt/BosmonMqtt.csproj b/BosmonMqtt/BosmonMqtt.csproj new file mode 100644 index 0000000..7d7219e --- /dev/null +++ b/BosmonMqtt/BosmonMqtt.csproj @@ -0,0 +1,80 @@ + + + + + Debug + AnyCPU + {EE2F31C4-64D7-4788-AC68-33C5CC8EFCCD} + Library + Properties + BlubbFish.BosmonMqtt + BosmonMqtt + v2.0 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x86 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x86 + + + + D:\Programme\BosMon\BosMonClasses.dll + False + + + + + + + + D:\Programme\BosMon\plugins\TelegramFilter.dll + False + + + + + + + + + + UserControl + + + Config.cs + + + + + Config.cs + + + + + {a11aef5a-b246-4fe8-8330-06db73cc8074} + M2Mqtt + + + + + copy "$(TargetPath)" "D:\Programme\BosMon_dev\plugins\$(TargetFileName)" +copy "$(TargetDir)\M2Mqtt.dll" "D:\Programme\BosMon_dev\plugins\M2Mqtt.dll" +del "D:\Programme\BosMon_dev\config\bosmon.log" + + \ No newline at end of file diff --git a/BosmonMqtt/BosmonMqtt.csproj.user b/BosmonMqtt/BosmonMqtt.csproj.user new file mode 100644 index 0000000..1a284e1 --- /dev/null +++ b/BosmonMqtt/BosmonMqtt.csproj.user @@ -0,0 +1,9 @@ + + + + Program + D:\Programme\BosMon_dev\BosMon.exe + D:\Programme\BosMon_dev\ + -nosplashscreen -debug -configpath "D:\Programme\BosMon_dev\config" + + \ No newline at end of file diff --git a/BosmonMqtt/MqttEventProcessor.cs b/BosmonMqtt/MqttEventProcessor.cs new file mode 100644 index 0000000..ffc0c9e --- /dev/null +++ b/BosmonMqtt/MqttEventProcessor.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Text; +using BosMon.Data; +using BosMon.Data.Telegrams; +using BosMon.Prefs; +using TelegramFilter.Filter; +using uPLibrary.Networking.M2Mqtt; +using uPLibrary.Networking.M2Mqtt.Messages; + +namespace BlubbFish.BosmonMqtt { + class MqttEventProcessor : IDisposable { + private PluginConfiguration pluginconfig; + private MqttClient client; + private FilterList filter; + private List pfilter = new List(); + + public MqttEventProcessor(PluginConfiguration pluginconfiguration) { + this.pluginconfig = pluginconfiguration; + this.Connect(); + } + + public void Connect() { + try { + this.client = new MqttClient(this.pluginconfig.Server, this.pluginconfig.Port, false, null); + if (this.pluginconfig.User == "" || this.pluginconfig.Password == "") { + this.client.Connect("bosmon-" + Guid.NewGuid().ToString()); + } else { + this.client.Connect("bosmon-" + Guid.NewGuid().ToString(), this.pluginconfig.User, this.pluginconfig.Password); + } + this.client.Subscribe(new String[] { this.pluginconfig.Topic }, new Byte[] { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE }); + } catch (Exception) { + + } + this.filter = new FilterList(this.pluginconfig.PluginStorage.GetSection("filter")); + this.pfilter.Clear(); + foreach (FilterItem item in this.filter) { + if(item.GetType() == typeof(PocsagFilterItem)) { + this.pfilter.Add(item as PocsagFilterItem); + } + } + } + + internal void TelegramEvent(Object sender, TelegramEventArgs e) { + Telegram t = e.Telegram as Telegram; + if (this.client != null && this.client.IsConnected && this.filter != null) { + if (t.Type == PocsagTelegram.TYPE_POCSAG) { + PocsagTelegram p = t as PocsagTelegram; + foreach (PocsagFilterItem item in this.pfilter) { + if((item.IsMatching(p) && item.Negated)) { + return; + } + } + this.client.Publish(this.pluginconfig.Topic + p.Address+p.Func, Encoding.UTF8.GetBytes(p.Msg)); + } + } + } + + #region IDisposable Support + private Boolean disposedValue = false; + + + protected virtual void Dispose(Boolean disposing) { + if (!this.disposedValue) { + if (disposing) { + // TODO: verwalteten Zustand (verwaltete Objekte) entsorgen. + } + this.disposedValue = true; + } + } + + public void Dispose() { + Dispose(true); + } + #endregion + } +} diff --git a/BosmonMqtt/MqttPlugin.cs b/BosmonMqtt/MqttPlugin.cs new file mode 100644 index 0000000..0910c3a --- /dev/null +++ b/BosmonMqtt/MqttPlugin.cs @@ -0,0 +1,89 @@ +using System; +using BosMon.Exceptions; +using BosMon.Plugins; +using BosMon.Prefs; + +namespace BlubbFish.BosmonMqtt { + public class Class1 : IBosMonPlugin { + private Plugin plugin = new Plugin(); + #region Plugin Eigenschaften + String IBosMonPlugin.PluginName { + get { + return "MQTT-Bridge"; + } + } + + String IBosMonPlugin.PluginDescription { + get { + return "Sendet Telegramme nach MQTT"; + } + } + + Int32 IBosMonPlugin.PluginVersion { + get { + return 1; + } + } + + PluginType IBosMonPlugin.PluginType { + get { + return PluginType.Client; + } + } + + Int32 IBosMonPlugin.PluginFlags { + get { + return 0; + } + } + + String[] IBosMonPlugin.StartPluginAfter { + get { + return null; + } + } + #endregion + #region Plugin Connectoren + public IBosMonHost PluginHost { + get { + return this.plugin.PluginHost; + } + set { + this.plugin.PluginHost = value ?? throw new BosMonArgumentNullException(); + } + } + + IBosMonConfigurationStorage IBosMonPlugin.ConfigurationStorage { + get { + return this.plugin.ConfigurationStorage; + } + set { + if (value == null) { + throw new BosMonArgumentNullException(); + } else if (this.plugin.ConfigurationStorage != null) { + throw new BosMonInternalException("ConfigStorage already set"); + } + this.plugin.ConfigurationStorage = value; + } + } + #endregion + #region Plugin Einstiegspunkte + void IDisposable.Dispose() { + this.plugin.Dispose(true); + GC.SuppressFinalize(this); + } + + void IBosMonPlugin.InitializePlugin() { + this.plugin.Init(); + } + + void IBosMonPlugin.StartPlugin() { + this.plugin.Start(); + } + + void IBosMonPlugin.StopPlugin() { + this.plugin.Stop(); + } + #endregion + } +} diff --git a/BosmonMqtt/Plugin.cs b/BosmonMqtt/Plugin.cs new file mode 100644 index 0000000..f483c25 --- /dev/null +++ b/BosmonMqtt/Plugin.cs @@ -0,0 +1,85 @@ +using System; +using BlubbFish.BosmonMqtt.Views; +using BosMon.Data.Processors; +using BosMon.Exceptions; +using BosMon.Plugins; +using BosMon.Prefs; + +namespace BlubbFish.BosmonMqtt { + class Plugin { + private PluginConfiguration pluginconfiguration; + private Boolean isDisposed = false; + private Boolean isInitialized = false; + private ConfigView _configView; + private MqttEventProcessor _mqtteventprocessor; + private Boolean isStarted = false; + private ITelegramEventProcessor _events; + + internal IBosMonHost PluginHost { get; set; } + internal IBosMonConfigurationStorage ConfigurationStorage { get; set; } + + internal void Init() { + if (this.ConfigurationStorage == null || this.PluginHost == null) { + throw new BosMonArgumentNullException(); + } + if (this.isInitialized) { + throw new BosMonInternalException(); + } + this.pluginconfiguration = new PluginConfiguration(this.ConfigurationStorage); + this.isInitialized = true; + this._configView = new ConfigView(this.pluginconfiguration); + this._mqtteventprocessor = new MqttEventProcessor(this.pluginconfiguration); + } + + internal void Start() { + if (!this.isInitialized) { + throw new BosMonInternalException("Not initialized"); + } + try { + // ... Plugin starten ... + this.PluginHost.RegisterConfigPage("Plugins", "Mqtt", this._configView); + //this.PluginHost.RegisterEventProcessor("MqttEventProcessor", this._mqtteventprocessor); + + this._events = this.PluginHost.GetEventProcessor("TelegramEventProcessor") as ITelegramEventProcessor; + this._events.TelegramEvent += this._mqtteventprocessor.TelegramEvent; + + // Es muss unbedingt darauf geachtet werden, dass das Plugin in dieser + // Methode nicht blockiert, da sonst der Hauptthread von BosMon ebenfalls + // blockiert wird. + + this.isStarted = true; + } catch (Exception) { + + } + } + + internal void Stop() { + if (!this.isInitialized) { + throw new BosMonInternalException("Not initialized"); + } + + try { + if (this.isStarted) { + this._configView.Dispose(); + this.PluginHost.UnRegisterConfigPage(this._configView); + this._events.TelegramEvent -= this._mqtteventprocessor.TelegramEvent; + this._mqtteventprocessor.Dispose(); + this.isStarted = false; + } + } catch (Exception ex) { + BosMon.Gui.BosMonErrorBox.Show(ex, "Fehler"); + } + } + + internal void Dispose(Boolean dispose) { + if (!this.isDisposed) { + if (dispose) { + try { + //DeInitialize(); + } catch (Exception) { } + } + this.isDisposed = true; + } + } + } +} diff --git a/BosmonMqtt/PluginConfiguration.cs b/BosmonMqtt/PluginConfiguration.cs new file mode 100644 index 0000000..b17a422 --- /dev/null +++ b/BosmonMqtt/PluginConfiguration.cs @@ -0,0 +1,123 @@ +using BosMon.Prefs; +using System; +using System.Text; + +namespace BlubbFish.BosmonMqtt { + internal class PluginConfiguration : BosMonConfiguration, IBosMonConfigurationTemplate { + private String _server; + private String _user; + private String _pass; + private String _topic; + private Int32 _port; + + public PluginConfiguration(IBosMonConfigurationStorage storage) : base(storage) { + this.PluginStorage = storage; + } + + public IBosMonConfigurationStorage PluginStorage { get; private set; } + + public String Server { + get { + return this._server; + } + set { + this._server = value; + WriteConfiguration(); + } + } + public String User { + get { + return this._user; + } + set { + this._user = value; + WriteConfiguration(); + } + } + public String Password { + get { + return Encoding.UTF8.GetString(Convert.FromBase64String(this._pass)); + } + set { + this._pass = Convert.ToBase64String(Encoding.UTF8.GetBytes(value)); + ; + WriteConfiguration(); + } + } + public String Topic { + get { + return this._topic; + } + set { + this._topic = value; + if (this._topic.Length > 0 && this._topic[0] != '/') { + this._topic = "/" + this._topic; + } + if (this._topic.Length > 1 && this._topic[this._topic.Length-1] != '/') { + this._topic = this._topic + "/"; + } + WriteConfiguration(); + } + } + public Int32 Port { + get { + return this._port; + } + set { + this._port = value; + WriteConfiguration(); + } + } + + #region IBosMonConfigurationTemplate Config-Variablen + public override void ReadConfiguration() { + base.ReadConfiguration(); + this._server = this.Storage.ReadString("server", ""); + this._user = this.Storage.ReadString("user", ""); + this._pass = this.Storage.ReadString("pass", ""); + this._topic = this.Storage.ReadString("topic", ""); + this._port = this.Storage.ReadInteger("port", 1883); + } + public override void WriteConfiguration() { + this.Storage.Write("server", this._server); + this.Storage.Write("user", this._user); + this.Storage.Write("pass", this._pass); + this.Storage.Write("topic", this._topic); + this.Storage.Write("port", this._port); + base.WriteConfiguration(); + } + #endregion + #region IBosMonConfigurationTemplate Member + public override TKeyVal[] StringVariables { + get { + return Merge( + new TKeyVal[] { + new TKeyVal("server", "localhost"), + new TKeyVal("user", ""), + new TKeyVal("pass", ""), + new TKeyVal("topic", "/bosmon/messages/") + }, + base.StringVariables); + } + } + public override TKeyVal[] IntVariables { + get { + return Merge(new TKeyVal[] { + new TKeyVal("port",1883) + }, base.IntVariables); + } + } + public override TKeyVal[] Sections { + get { + return new TKeyVal[] { + new TKeyVal("filter", true) + }; + } + } + + + + + #endregion + } +} \ No newline at end of file diff --git a/BosmonMqtt/Properties/AssemblyInfo.cs b/BosmonMqtt/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f551efa --- /dev/null +++ b/BosmonMqtt/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System.Resources; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("BosmonMqtt")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BosmonMqtt")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("BlubbFish")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly +// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("ee2f31c4-64d7-4788-ac68-33c5cc8efccd")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, +// indem Sie "*" wie unten gezeigt eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: NeutralResourcesLanguage("de")] + diff --git a/BosmonMqtt/Views/Config.Designer.cs b/BosmonMqtt/Views/Config.Designer.cs new file mode 100644 index 0000000..22d5817 --- /dev/null +++ b/BosmonMqtt/Views/Config.Designer.cs @@ -0,0 +1,232 @@ +namespace BlubbFish.BosmonMqtt.Views { + partial class ConfigView { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.label1 = new System.Windows.Forms.Label(); + this.textServer = new System.Windows.Forms.TextBox(); + this.textUser = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.textPass = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textPort = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.labelStatus = new System.Windows.Forms.Label(); + this.testButton = new System.Windows.Forms.Button(); + this.label5 = new System.Windows.Forms.Label(); + this.textTopic = new System.Windows.Forms.TextBox(); + this.filterButton = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(6, 16); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(55, 20); + this.label1.TabIndex = 0; + this.label1.Text = "Server:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textServer + // + this.textServer.Location = new System.Drawing.Point(67, 16); + this.textServer.Name = "textServer"; + this.textServer.Size = new System.Drawing.Size(317, 20); + this.textServer.TabIndex = 1; + this.textServer.Tag = "server"; + this.textServer.Leave += new System.EventHandler(this.Settings_Changed); + // + // textUser + // + this.textUser.Location = new System.Drawing.Point(67, 68); + this.textUser.Name = "textUser"; + this.textUser.Size = new System.Drawing.Size(317, 20); + this.textUser.TabIndex = 4; + this.textUser.Tag = "user"; + this.textUser.Leave += new System.EventHandler(this.Settings_Changed); + // + // label3 + // + this.label3.Location = new System.Drawing.Point(6, 67); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(55, 20); + this.label3.TabIndex = 3; + this.label3.Text = "Benutzer:"; + this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textPass + // + this.textPass.Location = new System.Drawing.Point(67, 94); + this.textPass.Name = "textPass"; + this.textPass.Size = new System.Drawing.Size(317, 20); + this.textPass.TabIndex = 6; + this.textPass.Tag = "pass"; + this.textPass.UseSystemPasswordChar = true; + this.textPass.Leave += new System.EventHandler(this.Settings_Changed); + // + // label4 + // + this.label4.Location = new System.Drawing.Point(6, 94); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(55, 20); + this.label4.TabIndex = 5; + this.label4.Text = "Passwort:"; + this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // groupBox1 + // + this.groupBox1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.groupBox1.Controls.Add(this.textPort); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.label7); + this.groupBox1.Controls.Add(this.labelStatus); + this.groupBox1.Controls.Add(this.testButton); + this.groupBox1.Controls.Add(this.label5); + this.groupBox1.Controls.Add(this.textTopic); + this.groupBox1.Controls.Add(this.filterButton); + this.groupBox1.Controls.Add(this.textServer); + this.groupBox1.Controls.Add(this.label4); + this.groupBox1.Controls.Add(this.textPass); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Controls.Add(this.textUser); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Location = new System.Drawing.Point(3, 3); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(390, 250); + this.groupBox1.TabIndex = 7; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Einstellungen"; + // + // textPort + // + this.textPort.Location = new System.Drawing.Point(67, 42); + this.textPort.MaxLength = 5; + this.textPort.Name = "textPort"; + this.textPort.Size = new System.Drawing.Size(317, 20); + this.textPort.TabIndex = 15; + this.textPort.Tag = "port"; + this.textPort.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.Check_Numbers); + this.textPort.Leave += new System.EventHandler(this.Settings_Changed); + // + // label2 + // + this.label2.Location = new System.Drawing.Point(6, 42); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(55, 20); + this.label2.TabIndex = 14; + this.label2.Text = "Port:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // label7 + // + this.label7.Location = new System.Drawing.Point(141, 151); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(243, 29); + this.label7.TabIndex = 13; + this.label7.Text = "Wurden Filter eingetragen, so werden nur Telegramme gesendet, die explizit erlaub" + + "t sind."; + // + // labelStatus + // + this.labelStatus.AutoSize = true; + this.labelStatus.Location = new System.Drawing.Point(67, 227); + this.labelStatus.Name = "labelStatus"; + this.labelStatus.Size = new System.Drawing.Size(131, 13); + this.labelStatus.TabIndex = 12; + this.labelStatus.Text = "Verbindung nicht getestet."; + // + // testButton + // + this.testButton.Location = new System.Drawing.Point(67, 197); + this.testButton.Name = "testButton"; + this.testButton.Size = new System.Drawing.Size(200, 23); + this.testButton.TabIndex = 11; + this.testButton.Text = "Einstellungen Testen"; + this.testButton.UseVisualStyleBackColor = true; + this.testButton.Click += new System.EventHandler(this.Setting_Test); + // + // label5 + // + this.label5.Location = new System.Drawing.Point(6, 120); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(55, 20); + this.label5.TabIndex = 9; + this.label5.Text = "Topic:"; + this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textTopic + // + this.textTopic.Location = new System.Drawing.Point(67, 120); + this.textTopic.Name = "textTopic"; + this.textTopic.Size = new System.Drawing.Size(317, 20); + this.textTopic.TabIndex = 10; + this.textTopic.Tag = "topic"; + this.textTopic.Leave += new System.EventHandler(this.Settings_Changed); + // + // filterButton + // + this.filterButton.Location = new System.Drawing.Point(9, 153); + this.filterButton.Name = "filterButton"; + this.filterButton.Size = new System.Drawing.Size(125, 23); + this.filterButton.TabIndex = 8; + this.filterButton.Text = "Filter..."; + this.filterButton.UseVisualStyleBackColor = true; + this.filterButton.Click += new System.EventHandler(this.Filter_Click); + // + // ConfigView + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.Controls.Add(this.groupBox1); + this.Name = "ConfigView"; + this.Size = new System.Drawing.Size(396, 256); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textServer; + private System.Windows.Forms.TextBox textUser; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox textPass; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button filterButton; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox textTopic; + private System.Windows.Forms.Label labelStatus; + private System.Windows.Forms.Button testButton; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.TextBox textPort; + private System.Windows.Forms.Label label2; + } +} \ No newline at end of file diff --git a/BosmonMqtt/Views/Config.cs b/BosmonMqtt/Views/Config.cs new file mode 100644 index 0000000..706e00a --- /dev/null +++ b/BosmonMqtt/Views/Config.cs @@ -0,0 +1,117 @@ +using System; +using System.Drawing; +using System.Text; +using System.Threading; +using System.Windows.Forms; +using BosMon.Prefs; +using BosMon.Utils; +using TelegramFilter.Filter; +using TelegramFilter.Gui; +using uPLibrary.Networking.M2Mqtt; +using uPLibrary.Networking.M2Mqtt.Messages; + +namespace BlubbFish.BosmonMqtt.Views { + internal partial class ConfigView : UserControl { + private PluginConfiguration pluginconfig; + private EditFilterListDlg filter; + + internal ConfigView(PluginConfiguration pluginconfiguration) { + InitializeComponent(); + this.pluginconfig = pluginconfiguration; + this.filter = new EditFilterListDlg(new FilterList(this.pluginconfig.PluginStorage.GetSection("filter"))); + this.textServer.Text = this.pluginconfig.Server; + this.textUser.Text = this.pluginconfig.User; + this.textPass.Text = this.pluginconfig.Password; + this.textTopic.Text = this.pluginconfig.Topic; + this.textPort.Text = this.pluginconfig.Port.ToString(); + } + + private void Filter_Click(Object sender, EventArgs e) { + this.filter.ShowDialog(); + } + + private void Settings_Changed(Object sender, EventArgs e) { + TextBox t = (TextBox)sender; + switch (t.Tag) { + case "server": + this.pluginconfig.Server = t.Text; + break; + case "user": + this.pluginconfig.User = t.Text; + break; + case "pass": + this.pluginconfig.Password = t.Text; + break; + case "topic": + this.pluginconfig.Topic = t.Text; + t.BeginInvoke((Action)(() => { + t.Text = this.pluginconfig.Topic; + })); + break; + case "port": + if (Int32.TryParse(t.Text, out Int32 port)) { + this.pluginconfig.Port = port; + } + break; + } + } + + private void Check_Numbers(Object sender, KeyPressEventArgs e) { + e.Handled = !Char.IsDigit(e.KeyChar) && !Char.IsControl(e.KeyChar); + } + + private void Setting_Test(Object sender, EventArgs e) { + try { + this.labelStatus.BeginInvoke((Action)(() => { + this.labelStatus.Text = "Baue Verbindung auf."; + this.labelStatus.ForeColor = Color.DarkOrange; + })); + MqttClient client = new MqttClient(this.pluginconfig.Server, this.pluginconfig.Port, false, null); + if (this.pluginconfig.User == "" || this.pluginconfig.Password == "") { + client.Connect("bosmon-" + Guid.NewGuid().ToString()); + } else { + client.Connect("bosmon-" + Guid.NewGuid().ToString(), this.pluginconfig.User, this.pluginconfig.Password); + } + this.labelStatus.BeginInvoke((Action)(() => { + this.labelStatus.Text = "Subscribe Topic."; + this.labelStatus.ForeColor = Color.DarkOrange; + })); + client.Subscribe(new String[] { this.pluginconfig.Topic }, new Byte[] { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE }); + this.labelStatus.BeginInvoke((Action)(() => { + this.labelStatus.Text = "Schicke Testnachricht."; + this.labelStatus.ForeColor = Color.DarkOrange; + })); + client.MqttMsgPublishReceived += this.Client_MqttMsgPublishReceived; + client.Publish(this.pluginconfig.Topic, Encoding.UTF8.GetBytes("BosMon Connection Test")); + this.labelStatus.BeginInvoke((Action)(() => { + this.labelStatus.Text = "Warte auf Testnachricht."; + this.labelStatus.ForeColor = Color.DarkOrange; + })); + for(Int32 i=0;i<=20;i++) { + Thread.Sleep(100); + if (this._testMessageRecieved) { + break; + } + } + if(!this._testMessageRecieved) { + throw new Exception(); + } + this.labelStatus.BeginInvoke((Action)(() => { + this.labelStatus.Text = "Verbindung erfolgreich!"; + this.labelStatus.ForeColor = Color.DarkGreen; + })); + } catch(Exception) { + this.labelStatus.BeginInvoke((Action)(() => { + this.labelStatus.Text += " Fehler!"; + this.labelStatus.ForeColor = Color.Red; + })); + } + } + private Boolean _testMessageRecieved = false; + private void Client_MqttMsgPublishReceived(Object sender, MqttMsgPublishEventArgs e) { + if(Encoding.UTF8.GetString(e.Message) == "BosMon Connection Test") { + this._testMessageRecieved = true; + } + } + } +} diff --git a/BosmonMqtt/Views/Config.resx b/BosmonMqtt/Views/Config.resx new file mode 100644 index 0000000..5ea0895 --- /dev/null +++ b/BosmonMqtt/Views/Config.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/BosmonMqtt/bin/Release/BosmonMqtt.dll b/BosmonMqtt/bin/Release/BosmonMqtt.dll new file mode 100644 index 0000000000000000000000000000000000000000..79dd32ed0a9c1d7b68cf88081a65ee70283d2bbe GIT binary patch literal 19456 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 literal 0 HcmV?d00001