diff --git a/Lora-Map/Lora-Map.csproj b/Lora-Map/Lora-Map.csproj index f851340..fc60e19 100644 --- a/Lora-Map/Lora-Map.csproj +++ b/Lora-Map/Lora-Map.csproj @@ -50,6 +50,7 @@ + @@ -64,7 +65,7 @@ - + PreserveNewest diff --git a/Lora-Map/Model/EnviromentData.cs b/Lora-Map/Model/EnviromentData.cs new file mode 100644 index 0000000..920563f --- /dev/null +++ b/Lora-Map/Model/EnviromentData.cs @@ -0,0 +1,41 @@ +using System; +using System.Globalization; + +using LitJson; + +namespace Fraunhofer.Fit.IoT.LoraMap.Model { + class EnviromentData { + public String Name { get; private set; } + public Double Rssi { get; private set; } + public Double Snr { get; private set; } + public Double Temperature { get; private set; } + public Double Humidity { get; private set; } + public Double Windspeed { get; private set; } + public DateTime Lorarecievedtime { get; private set; } + + public EnviromentData(JsonData json) => this.Update(json); + + public void Update(JsonData json) { + this.Name = GetId(json); + this.Rssi = json["Rssi"].IsInt ? (Int32)json["Rssi"] : (Double)json["Rssi"]; + this.Snr = json["Snr"].IsInt ? (Int32)json["Snr"] : (Double)json["Snr"]; + this.Temperature = json["Temperature"].IsInt ? (Int32)json["Temperature"] : (Double)json["Temperature"]; + this.Humidity = json["Humidity"].IsInt ? (Int32)json["Humidity"] : (Double)json["Humidity"]; + this.Windspeed = json["Windspeed"].IsInt ? (Int32)json["Windspeed"] : (Double)json["Windspeed"]; + if(DateTime.TryParse((String)json["Receivedtime"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) { + this.Lorarecievedtime = updatetime.ToUniversalTime(); + } + } + + public static Boolean CheckJson(JsonData json) => + json.ContainsKey("Name") && json["Name"].IsString && + json.ContainsKey("Rssi") && (json["Rssi"].IsDouble || json["Rssi"].IsInt) && + json.ContainsKey("Snr") && (json["Snr"].IsDouble || json["Snr"].IsInt) && + json.ContainsKey("Temperature") && (json["Temperature"].IsDouble || json["Temperature"].IsInt) && + json.ContainsKey("Humidity") && (json["Humidity"].IsDouble || json["Humidity"].IsInt) && + json.ContainsKey("Windspeed") && (json["Windspeed"].IsDouble || json["Windspeed"].IsInt) && + json.ContainsKey("Receivedtime") && json["Receivedtime"].IsString; + + public static String GetId(JsonData json) => (String)json["Name"]; + } +} diff --git a/Lora-Map/Model/Settings.cs b/Lora-Map/Model/Settings.cs index b448814..b2b7001 100644 --- a/Lora-Map/Model/Settings.cs +++ b/Lora-Map/Model/Settings.cs @@ -14,6 +14,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model { public Dictionary>>> Grid { get; private set; } public Dictionary FightDedection { get; private set; } public Dictionary DensityArea { get; private set; } + public Dictionary Sensors { get; private set; } public Settings() => this.ParseJson(); @@ -21,9 +22,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model { private void ParseJson() { JsonData json = JsonMapper.ToObject(File.ReadAllText("json/settings.json")); - if(json.ContainsKey("StartPos") && json["StartPos"].IsObject && - json["StartPos"].ContainsKey("lat") && json["StartPos"]["lat"].IsDouble && - json["StartPos"].ContainsKey("lon") && json["StartPos"]["lon"].IsDouble) { + if(json.ContainsKey("StartPos") && json["StartPos"].IsObject && json["StartPos"].ContainsKey("lat") && json["StartPos"]["lat"].IsDouble && json["StartPos"].ContainsKey("lon") && json["StartPos"]["lon"].IsDouble) { this.Startloclat = (Double)json["StartPos"]["lat"]; this.Startloclon = (Double)json["StartPos"]["lon"]; } else { @@ -31,7 +30,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model { this.Startloclon = 0; } this.weatherCellIDs.Clear(); - if (json.ContainsKey("CellIds") && json["CellIds"].IsArray && json["CellIds"].Count > 0) { + if(json.ContainsKey("CellIds") && json["CellIds"].IsArray && json["CellIds"].Count > 0) { foreach (JsonData item in json["CellIds"]) { if(Int32.TryParse(item.ToString(), out Int32 cellid)) { this.weatherCellIDs.Add(cellid); @@ -54,7 +53,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model { fight.Polygon.Add(coords); } } - if (entry.Value.ContainsKey("Level") && (entry.Value["Level"].IsDouble)) { + if (entry.Value.ContainsKey("Level") && entry.Value["Level"].IsDouble) { fight.Level = (Double)entry.Value["Level"]; } if (entry.Value.ContainsKey("Alias") && entry.Value["Alias"].IsString) { @@ -64,7 +63,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model { } this.FightDedection = fights; } - if (json.ContainsKey("CrwodDensity") && json["CrwodDensity"].IsObject) { + if(json.ContainsKey("CrwodDensity") && json["CrwodDensity"].IsObject) { Dictionary densitys = new Dictionary(); foreach (KeyValuePair entry in json["CrwodDensity"]) { Density density = new Density { @@ -90,6 +89,31 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model { } this.DensityArea = densitys; } + if(json.ContainsKey("Sensors") && json["Sensors"].IsObject) { + Dictionary sensors = new Dictionary(); + foreach(KeyValuePair entry in json["Sensors"]) { + Sensor sensor = new Sensor { + Coordinates = new List() + }; + if(entry.Value.ContainsKey("Poly") && entry.Value["Poly"].IsObject) { + JsonData coord = entry.Value["Poly"]; + List coords = new List(); + if(coord.ContainsKey("Lat") && coord["Lat"].IsDouble && coord.ContainsKey("Lon") && coord["Lon"].IsDouble) { + coords.Add((Double)coord["Lat"]); + coords.Add((Double)coord["Lon"]); + } + sensor.Coordinates = coords; + if(entry.Value.ContainsKey("Level") && (entry.Value["Level"].IsInt || entry.Value["Level"].IsDouble)) { + sensor.Level = entry.Value["Level"].IsInt ? (Int32)entry.Value["Level"] : (Double)entry.Value["Level"]; + } + if(entry.Value.ContainsKey("Alias") && entry.Value["Alias"].IsString) { + sensor.Alias = (String)entry.Value["Alias"]; + } + sensors.Add(entry.Key, sensor); + } + } + this.Sensors = sensors; + } this.gridradius = json.ContainsKey("GridRadius") && json["GridRadius"].IsInt && this.Startloclat != 0 && this.Startloclon != 0 ? (Int32)json["GridRadius"] : 0; this.GenerateGrid(); } @@ -185,5 +209,11 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model { public Double Level { get; set; } public String Alias { get; set; } } + + public struct Sensor { + public List Coordinates { get; set; } + public Double Level { get; set; } + public String Alias { get; set; } + } } } diff --git a/Lora-Map/Server.cs b/Lora-Map/Server.cs index 567b6cc..0383c3c 100644 --- a/Lora-Map/Server.cs +++ b/Lora-Map/Server.cs @@ -18,6 +18,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap { private readonly SortedDictionary counter = new SortedDictionary(); private readonly SortedDictionary density = new SortedDictionary(); private readonly SortedDictionary fights = new SortedDictionary(); + private readonly SortedDictionary sensors = new SortedDictionary(); private JsonData marker; private readonly Settings settings; private readonly WeatherWarnings weather; @@ -28,6 +29,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap { private readonly Object lockFight = new Object(); private readonly Object lockCount = new Object(); private readonly Object lockDensy = new Object(); + private readonly Object lockSensor = new Object(); public Server(ADataBackend backend, Dictionary settings, InIReader requests) : base(backend, settings, requests) { this.logger.SetPath(settings["loggingpath"]); @@ -98,18 +100,17 @@ namespace Fraunhofer.Fit.IoT.LoraMap { Console.WriteLine("PANIC erhalten!"); } else if(Camera.CheckJson(d) && ((String)mqtt.From).Contains("camera/count")) { String cameraid = Camera.GetId(d); - lock (this.lockCount) { - if (this.counter.ContainsKey(cameraid)) { + lock(this.lockCount) { + if(this.counter.ContainsKey(cameraid)) { this.counter[cameraid].Update(d); } else { this.counter.Add(cameraid, new Camera(d)); } } - } else if(((((String)mqtt.From).Contains("sfn/crowd_density_local") || ((String)mqtt.From).Contains("camera/crowd")) && Crowd.CheckJsonCrowdDensityLocal(d)) || - (((String)mqtt.From).Contains("sfn/flow") && Crowd.CheckJsonFlow(d))) { + } else if((((String)mqtt.From).Contains("sfn/crowd_density_local") || ((String)mqtt.From).Contains("camera/crowd")) && Crowd.CheckJsonCrowdDensityLocal(d) || ((String)mqtt.From).Contains("sfn/flow") && Crowd.CheckJsonFlow(d)) { String cameraid = Crowd.GetId(d); - lock (this.lockDensy) { - if (this.density.ContainsKey(cameraid)) { + lock(this.lockDensy) { + if(this.density.ContainsKey(cameraid)) { this.density[cameraid].Update(d); } else { this.density.Add(cameraid, new Crowd(d)); @@ -117,13 +118,23 @@ namespace Fraunhofer.Fit.IoT.LoraMap { } } else if((((String)mqtt.From).Contains("camera/fighting_detection") || ((String)mqtt.From).Contains("sfn/fighting_detection")) && Fight.CheckJsonFightingDetection(d)) { String cameraid = Fight.GetId(d); - lock (this.lockFight) { - if (this.fights.ContainsKey(cameraid)) { + lock(this.lockFight) { + if(this.fights.ContainsKey(cameraid)) { this.fights[cameraid].Update(d); } else { this.fights.Add(cameraid, new Fight(d)); } } + } else if(((String)mqtt.From).Contains("lora/sensor") && EnviromentData.CheckJson(d)) { + String sensorid = EnviromentData.GetId(d); + lock(this.lockSensor) { + if(this.sensors.ContainsKey(sensorid)) { + this.sensors[sensorid].Update(d); + } else { + this.sensors.Add(sensorid, new EnviromentData(d)); + } + } + Console.WriteLine("Umweltdaten erhalten!"); } } catch(Exception e) { Helper.WriteError("Backend_MessageIncomming(): "+e.Message + "\n\n" + e.StackTrace); @@ -139,7 +150,8 @@ namespace Fraunhofer.Fit.IoT.LoraMap { { "cameracount", this.counter }, { "crowdcount", this.density }, { "fightdedect", this.fights }, - { "weatherwarnings", this.weather.Warnungen } + { "weatherwarnings", this.weather.Warnungen }, + { "sensors", this.sensors } }, cont); } else if (cont.Request.Url.PathAndQuery.StartsWith("/get60000")) { return SendJsonResponse(new Dictionary() { @@ -147,7 +159,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap { }, cont); } else if (cont.Request.Url.PathAndQuery.StartsWith("/getonce")) { return SendJsonResponse(new Dictionary() { - { "getlayer", this.FindMapLayer(cont.Request) }, + { "getlayer", this.FindMapLayer() }, { "getgeo", JsonMapper.ToObject(File.ReadAllText("json/geo.json")) }, { "startup", this.settings } }, cont); @@ -175,7 +187,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap { return SendFileResponse(cont); } - private Dictionary> FindMapLayer(HttpListenerRequest request) { + private Dictionary> FindMapLayer() { Dictionary> ret = new Dictionary> { { "online", new Dictionary() { { "title", "Online Map" }, diff --git a/Lora-Map/resources/admin/js/menu.js b/Lora-Map/resources/admin/js/menu.js index 434ab2c..704d969 100644 --- a/Lora-Map/resources/admin/js/menu.js +++ b/Lora-Map/resources/admin/js/menu.js @@ -375,12 +375,16 @@ var Settings = { if (typeof jsonsettings.Counting === "undefined") { jsonsettings.Counting = []; } + if (typeof jsonsettings.Sensors === "undefined") { + jsonsettings.Sensors = []; + } var html = "
Einstellungen
"; html += "
Startpunkt: Lat, Lon
"; html += "
CellId's für DWD-Wetterwarnungen: (Trennen durch \";\", cap_warncellids_csv)
"; html += "
Radius für das Grid um den Startpunkt: m
"; html += "
Fight Dedection Kameras:
" + this._renderFightDedection(jsonsettings.FightDedection) + "
"; html += "
Crowd Density Kameras:
" + this._renderCrowdDensity(jsonsettings.CrwodDensity) + "
"; + html += "
Sensors:
" + this._renderSensorSettings(jsonsettings.Sensors) + "
"; html += "
"; document.getElementById("content").innerHTML = html + "
"; }, @@ -434,6 +438,26 @@ var Settings = { } ret.CrwodDensity = crowdjson; + var rowss = document.getElementById("sensortable").children[1].children; + var sensorjson = {}; + for (i = 0; i < rowss.length; i++) { + if (rowss[i].children[0].children.length === 1) { + alert("Bitte zuerst alle Zeilen speichern oder Löschen!"); + return; + } + id = rowss[i].children[0].innerText; + coord = rowss[i].children[2].innerHTML.split(";"); + sensorjson[id] = { + "Poly": { + "Lat": this._filterFloat(coord[0]), + "Lon": this._filterFloat(coord[1]) + }, + "Level": this._filterFloat(rowss[i].children[3].innerText), + "Alias": rowss[i].children[1].innerText + }; + } + ret.Sensors = sensorjson; + var savesettings = new XMLHttpRequest(); savesettings.onreadystatechange = function () { if (savesettings.readyState === 4) { @@ -447,6 +471,27 @@ var Settings = { savesettings.open("POST", "/admin/set_json_settings", true); savesettings.send(JSON.stringify(ret)); }, + _renderSensorSettings: function (json) { + var ret = ""; + ret += ""; + ret += ""; + + ret += ""; + for (var id in json) { + ret += "" + + "" + + "" + + "" + + "" + + "" + + ""; + } + ret += ""; + + ret += ""; + ret += "
IDAliasKoordinatenWarn above
" + id + "" + json[id].Alias + "" + json[id].Poly.Lat + ";" + json[id].Poly.Lon + "" + json[id].Level + "
"; + return ret; + }, _renderFightDedection: function (json) { var ret = ""; ret += ""; @@ -493,12 +538,21 @@ var Settings = { ret += "
"; return ret; }, + AddSensor: function () { + var newrow = document.createElement("tr"); + newrow.innerHTML = ""; + newrow.innerHTML += ""; + newrow.innerHTML += ""; + newrow.innerHTML += ""; + newrow.innerHTML += " "; + document.getElementById("sensortable").children[1].appendChild(newrow); + }, AddFight: function () { var newrow = document.createElement("tr"); newrow.innerHTML = ""; newrow.innerHTML += ""; - newrow.innerHTML = ""; - newrow.innerHTML = ""; + newrow.innerHTML += ""; + newrow.innerHTML += ""; newrow.innerHTML += " "; document.getElementById("fighttable").children[1].appendChild(newrow); }, @@ -507,13 +561,36 @@ var Settings = { newrow.innerHTML = ""; newrow.innerHTML += ""; newrow.innerHTML += ""; - newrow.innerHTML = ""; + newrow.innerHTML += ""; newrow.innerHTML += " "; document.getElementById("crowdtable").children[1].appendChild(newrow); }, Abort: function (el) { el.parentNode.removeChild(el); }, + SaveRowSensor: function (el) { + var coords = el.children[2].children[0].value; + var coord = coords.split(";"); + var fail = false; + if (coord.length !== 2) { + fail = true; + } else if (isNaN(this._filterFloat(coord[0])) || isNaN(this._filterFloat(coord[1]))) { + fail = true; + } + if (isNaN(this._filterFloat(el.children[3].children[0].value))) { + alert("Die Eingabe des Alertlevel erwartet einen Float"); + return; + } + if (fail) { + alert("Die Eingabe der Koordinaten ist nicht Korrekt!\n\nBeispiel:\n50.7;7.8"); + return; + } + el.innerHTML = "" + el.children[0].children[0].value + "" + + "" + el.children[1].children[0].value + "" + + "" + coords + "" + + "" + this._filterFloat(el.children[3].children[0].value) + "" + + " "; + }, SaveRowfight: function (el) { var coords = el.children[1].children[0].value.replace(/\n/gi, "
"); var coordscheck = coords.split("
"); @@ -541,7 +618,7 @@ var Settings = { "" + coords + "" + "" + el.children[2].children[0].value + "" + "" + this._filterFloat(el.children[3].children[0].value) + "" + - " "; + " "; }, SaveRowdensity: function (el) { var coords = el.children[2].children[0].value.replace(/\n/gi, "
"); @@ -570,7 +647,7 @@ var Settings = { "" + el.children[1].children[0].value + "" + "" + coords + "" + "" + el.children[3].children[0].value + "" + - " "; + " "; }, Delete: function (el) { var answ = window.prompt("Wollen sie den Eintrag für \"" + el.firstChild.innerHTML + "\" wirklich löschen?", ""); @@ -578,6 +655,13 @@ var Settings = { el.parentNode.removeChild(el); } }, + EditSensor: function (el) { + el.innerHTML = "" + + "" + + "" + + "" + + " "; + }, EditFight: function (el) { el.innerHTML = "" + "" + diff --git a/Lora-Map/resources/css/global.css b/Lora-Map/resources/css/global.css index 45019bb..69b38ff 100644 --- a/Lora-Map/resources/css/global.css +++ b/Lora-Map/resources/css/global.css @@ -21,6 +21,22 @@ color: #ffffff; font-weight: bold; } +#bigmap .leaflet-map-pane .leaflet-marker-pane .mapsensor { + background-color: white; + border: 2px solid black; + padding: 5px; + border-radius: 10px; +} +#bigmap .leaflet-map-pane .leaflet-marker-pane .mapsensor span { + display: block; + text-align: center; +} +#bigmap .leaflet-map-pane .leaflet-marker-pane .mapsensor .name { + font-weight: bold; +} +#bigmap .leaflet-map-pane .leaflet-marker-pane .mapsensor .wind { + font-size: 18px; +} /* Optional: Makes the sample page fill the window. */ html, body { @@ -318,22 +334,22 @@ object { } #overlays #cameracount { - position: absolute; - top: 10px; - left: 61px; - z-index: 50000; - font-size: 11px; - font-family: "Verdana"; - padding: 3px; - display: inline-flex; + position: absolute; + top: 10px; + left: 61px; + z-index: 50000; + font-size: 11px; + font-family: "Verdana"; + padding: 3px; + display: inline-flex; +} +#overlays #cameracount .camera { + background-color: white; + border: rgba(0,0,0,0.3) solid 2px; + border-radius: 5px; + padding: 4px; + margin-right: 5px; } - #overlays #cameracount .camera { - background-color: white; - border: rgba(0,0,0,0.3) solid 2px; - border-radius: 5px; - padding: 4px; - margin-right: 5px; - } #overlays #cameracount .camera span { display: block; text-align: center; diff --git a/Lora-Map/resources/js/functions.js b/Lora-Map/resources/js/functions.js index af4a7f5..61c211d 100644 --- a/Lora-Map/resources/js/functions.js +++ b/Lora-Map/resources/js/functions.js @@ -28,6 +28,7 @@ MarkerObject._ParseAJAXPanic(json["panic"]); OverlayObject._ParseAJAXCount(json["cameracount"]); OverlayObject._ParseAJAXDensity(json["crowdcount"]); + MarkerObject._ParseAJAXSensors(json["sensors"]); MenuObject._ParseAJAXWeatherAlerts(json["weatherwarnings"]); MapObject._ParseAJAXFightDedection(json["fightdedect"]); MapObject._ParseAJAXDensity(json["crowdcount"]); @@ -45,6 +46,7 @@ MapObject._ParseAJAXGeo(json["getgeo"]); MapObject._ParseAJAXSettings(json["startup"]); OverlayObject._ParseAJAXSettings(json["startup"]); + MarkerObject._ParseAJAXSettings(json["startup"]); } }; getonce.open("GET", "/getonce", true); diff --git a/Lora-Map/resources/js/marker.js b/Lora-Map/resources/js/marker.js index 44dd496..ac833f9 100644 --- a/Lora-Map/resources/js/marker.js +++ b/Lora-Map/resources/js/marker.js @@ -3,6 +3,8 @@ PanicData: {}, LocationData: {}, VisibleMarkers: {}, + _Sensors: {}, + _SensorSettings: {}, Start: function () { return this; }, @@ -90,6 +92,36 @@ } } }, + _ParseAJAXSensors: function (sensorjson) { + for (var sensorid in sensorjson) { + if (sensorjson.hasOwnProperty(sensorid)) { + if (this._SensorSettings.hasOwnProperty(sensorid)) { + var sensordata = sensorjson[sensorid]; + var sensorsettings = this._SensorSettings[sensorid]; + + if (!this._Sensors.hasOwnProperty(sensorid)) { //Sensor is not drawn until now + var sensor = null; + var sensorIcon = L.divIcon({ + className: 'sensoricon', + iconSize: [60, 120], + iconAnchor: [30, 60], + html: '
' + sensorsettings.Alias + '' + + '' + sensordata.Temperature + ' °C' + + '' + sensordata.Windspeed + ' m/s' + + '' + sensordata.Humidity + ' %rl
' + }); + sensor = L.marker(sensorsettings.Coordinates, { 'title': sensorsettings.Alias, 'icon': sensorIcon }); + this._Sensors[sensorid] = sensor.addTo(MapObject.Map); + } else { //Sensor refresh! + + } + } + } + } + }, + _ParseAJAXSettings: function(json) { + this._SensorSettings = json["Sensors"]; + }, ChangeFilter: function (select) { this.VisibleMarkers = {}; if (select.selectedOptions.length > 0) {