From 5e1a38fd9eaef77b5e9360ee3947eb493162fbb9 Mon Sep 17 00:00:00 2001 From: BlubbFish Date: Wed, 17 Apr 2019 16:07:23 +0200 Subject: [PATCH] Init --- .gitignore | 3 + MonicaScral.sln | 61 ++++++++++ MonicaScral/MonicaScral.csproj | 74 ++++++++++++ MonicaScral/MqttEvents.cs | 19 +++ MonicaScral/MqttListener.cs | 66 ++++++++++ MonicaScral/Program.cs | 61 ++++++++++ MonicaScral/Properties/AssemblyInfo.cs | 36 ++++++ MonicaScral/ScralPusher.cs | 160 +++++++++++++++++++++++++ 8 files changed, 480 insertions(+) create mode 100644 .gitignore create mode 100644 MonicaScral.sln create mode 100644 MonicaScral/MonicaScral.csproj create mode 100644 MonicaScral/MqttEvents.cs create mode 100644 MonicaScral/MqttListener.cs create mode 100644 MonicaScral/Program.cs create mode 100644 MonicaScral/Properties/AssemblyInfo.cs create mode 100644 MonicaScral/ScralPusher.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76a8772 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.vs +/MonicaScral/bin +/MonicaScral/obj diff --git a/MonicaScral.sln b/MonicaScral.sln new file mode 100644 index 0000000..d8cd815 --- /dev/null +++ b/MonicaScral.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonicaScral", "MonicaScral\MonicaScral.csproj", "{AF25B9BC-7D93-4CEC-B5E2-8A8383679787}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils-IoT", "..\Utils\Utils-IoT\Utils-IoT\Utils-IoT.csproj", "{B870E4D5-6806-4A0B-B233-8907EEDC5AFC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConnectorDataMqtt", "..\Utils\ConnectorDataMqtt\ConnectorDataMqtt\ConnectorDataMqtt.csproj", "{EE6C8F68-ED46-4C1C-ABDD-CFCDF75104F2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "litjson_4.7.1", "..\Librarys\litjson\litjson\litjson_4.7.1.csproj", "{91A14CD2-2940-4500-8193-56D37EDDDBAA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt_4.7.1", "..\Librarys\mqtt\M2Mqtt\M2Mqtt_4.7.1.csproj", "{A11AEF5A-B246-4FE8-8330-06DB73CC8074}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils", "..\Utils\Utils\Utils\Utils.csproj", "{FAC8CE64-BF13-4ECE-8097-AEB5DD060098}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Posix", "..\Librarys\Mono.Posix\Mono.Posix\Mono.Posix.csproj", "{E2CA132E-E85C-40AD-BE94-B138AA68772B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AF25B9BC-7D93-4CEC-B5E2-8A8383679787}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF25B9BC-7D93-4CEC-B5E2-8A8383679787}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF25B9BC-7D93-4CEC-B5E2-8A8383679787}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF25B9BC-7D93-4CEC-B5E2-8A8383679787}.Release|Any CPU.Build.0 = Release|Any CPU + {B870E4D5-6806-4A0B-B233-8907EEDC5AFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B870E4D5-6806-4A0B-B233-8907EEDC5AFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B870E4D5-6806-4A0B-B233-8907EEDC5AFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B870E4D5-6806-4A0B-B233-8907EEDC5AFC}.Release|Any CPU.Build.0 = Release|Any CPU + {EE6C8F68-ED46-4C1C-ABDD-CFCDF75104F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE6C8F68-ED46-4C1C-ABDD-CFCDF75104F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE6C8F68-ED46-4C1C-ABDD-CFCDF75104F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE6C8F68-ED46-4C1C-ABDD-CFCDF75104F2}.Release|Any CPU.Build.0 = Release|Any CPU + {91A14CD2-2940-4500-8193-56D37EDDDBAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91A14CD2-2940-4500-8193-56D37EDDDBAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91A14CD2-2940-4500-8193-56D37EDDDBAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91A14CD2-2940-4500-8193-56D37EDDDBAA}.Release|Any CPU.Build.0 = Release|Any CPU + {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 + {FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Release|Any CPU.Build.0 = Release|Any CPU + {E2CA132E-E85C-40AD-BE94-B138AA68772B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2CA132E-E85C-40AD-BE94-B138AA68772B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2CA132E-E85C-40AD-BE94-B138AA68772B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2CA132E-E85C-40AD-BE94-B138AA68772B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CE98D3EF-81C6-4C8D-B8EF-E1C6A72FDD29} + EndGlobalSection +EndGlobal diff --git a/MonicaScral/MonicaScral.csproj b/MonicaScral/MonicaScral.csproj new file mode 100644 index 0000000..1027fd8 --- /dev/null +++ b/MonicaScral/MonicaScral.csproj @@ -0,0 +1,74 @@ + + + + + Debug + AnyCPU + {AF25B9BC-7D93-4CEC-B5E2-8A8383679787} + Exe + Fraunhofer.Fit.IoT.MonicaScral + MonicaScral + v4.7.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + {91a14cd2-2940-4500-8193-56d37edddbaa} + litjson_4.7.1 + + + {e2ca132e-e85c-40ad-be94-b138aa68772b} + Mono.Posix + + + {ee6c8f68-ed46-4c1c-abdd-cfcdf75104f2} + ConnectorDataMqtt + + + {b870e4d5-6806-4a0b-b233-8907eedc5afc} + Utils-IoT + + + {fac8ce64-bf13-4ece-8097-aeb5dd060098} + Utils + + + + \ No newline at end of file diff --git a/MonicaScral/MqttEvents.cs b/MonicaScral/MqttEvents.cs new file mode 100644 index 0000000..9ff2c25 --- /dev/null +++ b/MonicaScral/MqttEvents.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Fraunhofer.Fit.IoT.MonicaScral { + class MqttEvents : EventArgs { + public MqttEvents(String topic, String message, DateTime date) { + this.Topic = topic; + this.Message = message; + this.Date = date; + } + + public String Topic { get; } + public String Message { get; } + public DateTime Date { get; } + } +} diff --git a/MonicaScral/MqttListener.cs b/MonicaScral/MqttListener.cs new file mode 100644 index 0000000..e3d06de --- /dev/null +++ b/MonicaScral/MqttListener.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using BlubbFish.Utils.IoT.Connector; +using BlubbFish.Utils.IoT.Events; + +namespace Fraunhofer.Fit.IoT.MonicaScral { + class MqttListener { + protected readonly Thread connectionWatcher; + protected ABackend mqtt; + private readonly Dictionary config; + + public delegate void InputData(Object sender, MqttEvents e); + public event InputData Update; + + public MqttListener(Dictionary settings) { + this.config = settings; + if(this.config.ContainsKey("type")) { + this.connectionWatcher = new Thread(this.ConnectionWatcherRunner); + this.connectionWatcher.Start(); + } else { + throw new ArgumentException("Setting section [mqtt] is missing or wrong!"); + } + } + + protected void ConnectionWatcherRunner() { + while(true) { + try { + if(this.mqtt == null || !this.mqtt.IsConnected) { + this.Reconnect(); + } + Thread.Sleep(10000); + } catch(Exception) { } + } + } + + protected void Reconnect() { + Console.WriteLine("Fraunhofer.Fit.IoT.MonicaScral.Reconnect()"); + this.Disconnect(); + this.Connect(); + } + + protected void Connect() { + this.mqtt = ABackend.GetInstance(this.config, ABackend.BackendType.Data); + this.mqtt.MessageIncomming += this.MqttData; + Console.WriteLine("Fraunhofer.Fit.IoT.MonicaScral.Connect()"); + } + + private void MqttData(Object sender, BackendEvent e) => this.Update?.Invoke(this, new MqttEvents(e.From.ToString(), e.Message, e.Date)); + + protected void Disconnect() { + if(this.mqtt != null) { + this.mqtt.MessageIncomming -= this.MqttData; + this.mqtt.Dispose(); + } + this.mqtt = null; + Console.WriteLine("Fraunhofer.Fit.IoT.MonicaScral.Disconnect()"); + } + + public void Dispose() { + this.connectionWatcher.Abort(); + while(this.connectionWatcher.ThreadState == ThreadState.Running) { Thread.Sleep(10); } + this.Disconnect(); + } + } +} diff --git a/MonicaScral/Program.cs b/MonicaScral/Program.cs new file mode 100644 index 0000000..46af264 --- /dev/null +++ b/MonicaScral/Program.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using BlubbFish.Utils; + +namespace Fraunhofer.Fit.IoT.MonicaScral { + class Program { + private Thread sig_thread; + private Boolean RunningProcess = true; + protected ProgramLogger logger = new ProgramLogger(); + + static void Main(String[] args) => new Program(args); + + public Program(String[] args) { + MqttListener m = new MqttListener(new Dictionary() { { "type", "mqtt" }, { "server", "10.100.0.20" }, { "topic", "lora/data/+;lora/panic/+" } }); + ScralPusher s = new ScralPusher(new Dictionary() { + { "server", "http://monappdwp3.monica-cloud.eu:8250" }, + { "register_addr", "/scral/v1.0/gps-tracker-gw/gps-tag" }, + { "register_method", "post" }, + { "update_addr", "/scral/v1.0/gps-tracker-gw/gps-tag/localization" }, + { "update_method", "put" }, + { "panic_addr", "/scral/v1.0/gps-tracker-gw/gps-tag/alert" }, + { "panic_method", "put" },}); + m.Update += s.DataInput; + this.WaitForShutdown(); + m.Dispose(); + s.Dispose(); + } + + protected void WaitForShutdown() { + if(Type.GetType("Mono.Runtime") != null) { + this.sig_thread = new Thread(delegate () { + Mono.Unix.UnixSignal[] signals = new Mono.Unix.UnixSignal[] { + new Mono.Unix.UnixSignal(Mono.Unix.Native.Signum.SIGTERM), + new Mono.Unix.UnixSignal(Mono.Unix.Native.Signum.SIGINT) + }; + Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Signalhandler Mono attached."); + while(true) { + Int32 i = Mono.Unix.UnixSignal.WaitAny(signals, -1); + Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Signalhandler Mono INT recieved " + i + "."); + this.RunningProcess = false; + break; + } + }); + this.sig_thread.Start(); + } else { + Console.CancelKeyPress += new ConsoleCancelEventHandler(this.SetupShutdown); + Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.WaitForShutdown: Signalhandler Windows attached."); + } + while(this.RunningProcess) { + Thread.Sleep(100); + } + } + + private void SetupShutdown(Object sender, ConsoleCancelEventArgs e) { + e.Cancel = true; + Console.WriteLine("BlubbFish.Utils.IoT.Bots.Bot.SetupShutdown: Signalhandler Windows INT recieved."); + this.RunningProcess = false; + } + } +} diff --git a/MonicaScral/Properties/AssemblyInfo.cs b/MonicaScral/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7f2220f --- /dev/null +++ b/MonicaScral/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("MonicaScral")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MonicaScral")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly +// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("af25b9bc-7d93-4cec-b5e2-8a8383679787")] + +// 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, +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MonicaScral/ScralPusher.cs b/MonicaScral/ScralPusher.cs new file mode 100644 index 0000000..bb76cde --- /dev/null +++ b/MonicaScral/ScralPusher.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using BlubbFish.Utils; +using LitJson; + +namespace Fraunhofer.Fit.IoT.MonicaScral { + class ScralPusher { + private readonly List nodes = new List(); + private readonly Dictionary config; + private readonly Object getLock = new Object(); + private readonly Boolean authRequired = false; + private readonly String auth = ""; + + public ScralPusher(Dictionary settings) => this.config = settings; + + internal async void DataInput(Object sender, MqttEvents e) => await Task.Run(() => { + try { + JsonData data = JsonMapper.ToObject(e.Message); + if(this.CheckRegister(data)) { + if(e.Topic.StartsWith("lora/data")) { + this.SendUpdate(data); + } else if(e.Topic.StartsWith("lora/panic")) { + this.SendPanic(data); + } + } + } catch(Exception ex) { + Helper.WriteError("Something is wrong: " + ex.Message); + } + }); + + private Boolean CheckRegister(JsonData data) { + if(data.ContainsKey("Name") && data["Name"].IsString) { + if(!this.nodes.Contains((String)data["Name"])) { + this.SendRegister(data); + this.nodes.Add((String)data["Name"]); + } + return true; + } + return false; + } + + private void SendRegister(JsonData data) { + Dictionary d = new Dictionary { + { "device", "wearable" }, + { "sensor", "tag" }, + { "type", "uwb" }, + { "tagId", (String)data["Name"] }, + { "timestamp", DateTime.Now.ToString("o") }, + { "unitOfMeasurements", "meters" }, + { "observationType", "propietary" }, + { "state", "active" } + }; + try { + String addr = this.config["register_addr"]; + if(Enum.TryParse(this.config["register_method"], true, out RequestMethod meth)) { + this.RequestString(addr, JsonMapper.ToJson(d), false, meth); + Console.WriteLine(meth.ToString() + " " + this.config["register_addr"] + ": " + JsonMapper.ToJson(d)); + } + } catch(Exception e) { + Helper.WriteError("Fraunhofer.Fit.IoT.MonicaScral.SendRegister: " + e.Message); + } + } + + private void SendUpdate(JsonData data) { + if((Boolean)data["Gps"]["Fix"]) { + Dictionary d = new Dictionary { + { "type", "uwb" }, + { "tagId", (String)data["Name"] }, + { "timestamp", DateTime.Now.ToString("o") }, + { "lat", (Double)data["Gps"]["Latitude"] }, + { "lon", (Double)data["Gps"]["Longitude"] }, + { "height", (Double)data["Gps"]["Height"] }, + { "hdop", (Double)data["Gps"]["Hdop"] }, + { "snr", (Double)data["Snr"] }, + { "battery_level", (Double)data["BatteryLevel"] }, + { "host", (String)data["Host"]} + }; + try { + String addr = this.config["update_addr"]; + if(Enum.TryParse(this.config["update_method"], true, out RequestMethod meth)) { + this.RequestString(addr, JsonMapper.ToJson(d), false, meth); + Console.WriteLine(meth.ToString() + " " + this.config["update_addr"] + ": " + JsonMapper.ToJson(d)); + } + } catch(Exception e) { + Helper.WriteError("Fraunhofer.Fit.IoT.MonicaScral.SendUpdate: " + e.Message); + } + } + } + + private void SendPanic(JsonData data) { + Dictionary d = new Dictionary { + { "type", "uwb" }, + { "tagId", (String)data["Name"] }, + { "timestamp", DateTime.Now.ToString("o") }, + { "last_known_lat", (Double)data["Gps"]["LastLatitude"] }, + { "last_known_lon", (Double)data["Gps"]["LastLongitude"] }, + { "last_known_gps", DateTime.Parse((String)data["Gps"]["LastGPSPos"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal).ToLocalTime().ToString("o") } + }; + try { + String addr = this.config["panic_addr"]; + if(Enum.TryParse(this.config["panic_method"], true, out RequestMethod meth)) { + this.RequestString(addr, JsonMapper.ToJson(d), false, meth); + Console.WriteLine(meth.ToString() + " " + this.config["panic_addr"] + ": " + JsonMapper.ToJson(d)); + } + } catch(Exception e) { + Helper.WriteError("Fraunhofer.Fit.IoT.MonicaScral.SendRegister: " + e.Message); + } + } + + #region HTTP Request + private String RequestString(String address, String json = "", Boolean withoutput = true, RequestMethod method = RequestMethod.GET) { + String ret = null; + lock(this.getLock) { + HttpWebRequest request = WebRequest.CreateHttp(this.config["server"] + address); + request.Timeout = 2000; + if(this.authRequired) { + request.Headers.Add(HttpRequestHeader.Authorization, this.auth); + } + if(method == RequestMethod.POST || method == RequestMethod.PUT) { + Byte[] requestdata = Encoding.ASCII.GetBytes(json); + request.ContentLength = requestdata.Length; + request.Method = method.ToString(); + request.ContentType = "application/json"; + using(Stream stream = request.GetRequestStream()) { + stream.Write(requestdata, 0, requestdata.Length); + } + } + try { + using(HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { + if(response.StatusCode == HttpStatusCode.Unauthorized) { + Console.Error.WriteLine("Benutzer oder Passwort falsch!"); + throw new Exception("Benutzer oder Passwort falsch!"); + } + if(withoutput) { + StreamReader reader = new StreamReader(response.GetResponseStream()); + ret = reader.ReadToEnd(); + } + } + } catch(Exception e) { + throw new WebException("Error while uploading to Scal. Resource: \"" + this.config["server"] + address + "\" Method: " + method + " Data: " + json + " Fehler: " + e.Message); + } + } + return ret; + } + private enum RequestMethod { + GET, + POST, + PUT + } + #endregion + + internal void Dispose() { } + } +}