Compare commits
10 Commits
69bae8feca
...
dc0cbc4903
| Author | SHA1 | Date | |
|---|---|---|---|
| dc0cbc4903 | |||
| 9129878d80 | |||
| 51aa552a0c | |||
| fda19d315e | |||
| 434fc0f9cd | |||
| e4bd3583c5 | |||
| d71a206975 | |||
| 41c3ec8f93 | |||
| 3c9ac046f7 | |||
| 014f5ca8bf |
@ -26,6 +26,10 @@ In der Übersicht nur Daten aus der Datenbank, vorerst keine Daten aus Wetris.
|
||||
2) der Zeitpunkt des letzten Reports innerhalb der Zone
|
||||
3) keine Referenz sondern Kopie der Position/Timestamp?
|
||||
- Alarm / Zuordnung sind unabhängig von der Gruppe. Die Gruppe / der Zulauf ergibt sich erst in der Webanwendung. (Ist das sinnvoll?)
|
||||
- Für die Abfrage in der Gruppe der Zonen Außenweser/Binnenweser: Jeweils "neuester" Alarm:
|
||||
- wenn kein weiterer Alarm: o : unspezifisch
|
||||
- wenn vorh. Alarm in Zone mit niedrigere Seq : <- eingehend
|
||||
- wenn vorh. Alarm in Zone mit höherer Seq : -> abgehend
|
||||
|
||||
## Stand Dez 22
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ namespace bsmd.AIS2Service
|
||||
private static Timer _staleTargetTimer; // cleanup sitrep
|
||||
private static Timer _stalePosReportTimer; // clean db
|
||||
private static IDisposable _restAPISelfHost = null;
|
||||
private static AIS_SQLiteStorage _sqliteStorage = null;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -38,16 +39,17 @@ namespace bsmd.AIS2Service
|
||||
_tasks.Add(new SerialTCPReader(Properties.Settings.Default.DataSourceHost, Properties.Settings.Default.DataSourcePort, _inputLines));
|
||||
_tasks.Add(new AISDecoder(_inputLines, _decodedClasses));
|
||||
_tasks.Add(new SitRep(_decodedClasses, _sitRepList, _dbSaveTargets));
|
||||
AIS_SQLiteStorage sqliteStorage = new AIS_SQLiteStorage(_dbSaveTargets);
|
||||
_tasks.Add(sqliteStorage);
|
||||
_tasks.Add(new AISZoneMonitor(_sitRepList, sqliteStorage));
|
||||
_sqliteStorage = new AIS_SQLiteStorage(_dbSaveTargets);
|
||||
_tasks.Add(_sqliteStorage);
|
||||
_tasks.Add(new AISZoneMonitor(_sitRepList, _sqliteStorage));
|
||||
|
||||
// preload sit rep
|
||||
Dictionary<int, AIS_Target> targets = await sqliteStorage.LoadTargets();
|
||||
Dictionary<int, AIS_Target> targets = await _sqliteStorage.LoadTargets();
|
||||
foreach(int key in targets.Keys)
|
||||
{
|
||||
_sitRepList.TryAdd(key, targets[key]);
|
||||
}
|
||||
_log.InfoFormat("preloaded {0} targets", _sitRepList.Count);
|
||||
|
||||
foreach (var task in _tasks)
|
||||
{
|
||||
@ -58,7 +60,7 @@ namespace bsmd.AIS2Service
|
||||
|
||||
// init timer tasks
|
||||
_staleTargetTimer = new Timer(StaleTargetTimerCheck, null, 0, 60000); // check every minute, start immediately
|
||||
_stalePosReportTimer = new Timer(StalePosReportCheck, sqliteStorage, 0, 60000 * 10); // every ten minutes,
|
||||
_stalePosReportTimer = new Timer(StalePosReportCheck, _sqliteStorage, 0, 60000 * 10); // every ten minutes,
|
||||
|
||||
// if required start self-hosted owin endpoint
|
||||
if(Properties.Settings.Default.EnableRestAPIEndpoint)
|
||||
@ -87,10 +89,9 @@ namespace bsmd.AIS2Service
|
||||
|
||||
#region Properties
|
||||
|
||||
public static ConcurrentDictionary<int, AIS_Target> SitRep
|
||||
{
|
||||
get { return _sitRepList; }
|
||||
}
|
||||
public static ConcurrentDictionary<int, AIS_Target> SitRep { get { return _sitRepList; } }
|
||||
|
||||
public static AIS_SQLiteStorage SQLiteStorage { get { return _sqliteStorage; } }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@ -21,8 +21,8 @@ namespace bsmd.AIS2Service
|
||||
|
||||
#region Fields
|
||||
|
||||
ConcurrentDictionary<int, AIS_Target> _sitRepDict;
|
||||
AIS_SQLiteStorage _storage;
|
||||
private readonly ConcurrentDictionary<int, AIS_Target> _sitRepDict;
|
||||
private readonly AIS_SQLiteStorage _storage;
|
||||
private Thread _thread;
|
||||
private bool _stopFlag = false;
|
||||
private static readonly ILog _log = LogManager.GetLogger(typeof(AISZoneMonitor));
|
||||
@ -45,18 +45,24 @@ namespace bsmd.AIS2Service
|
||||
{
|
||||
// load zones from storage
|
||||
List<MonitorZone> allZones = _storage.LoadMonitorZones();
|
||||
Dictionary<int, List<MonitorZone>> testZones = new Dictionary<int, List<MonitorZone>>();
|
||||
Dictionary<int, List<AlarmAssignmentZone>> testDict = new Dictionary<int, List<AlarmAssignmentZone>>();
|
||||
// letzte Alarme
|
||||
// Dictionary<int, Alarm> currentAlarms = _storage.LoadAlarms();
|
||||
|
||||
foreach(MonitorZone zone in allZones)
|
||||
{
|
||||
// load up Zone list for all assignment mmsi's (to check)
|
||||
foreach(MonitorAssignment ma in zone.Assignments)
|
||||
{
|
||||
if(!testZones.ContainsKey(ma.MMSI)) testZones[ma.MMSI] = new List<MonitorZone>();
|
||||
testZones[ma.MMSI].Add(zone);
|
||||
if(!testDict.ContainsKey(ma.MMSI)) testDict[ma.MMSI] = new List<AlarmAssignmentZone>();
|
||||
ma.Alarms.AddRange(_storage.LoadAlarms(ma));
|
||||
AlarmAssignmentZone aazone = new AlarmAssignmentZone();
|
||||
aazone.Zone = zone;
|
||||
aazone.Assignment = ma;
|
||||
testDict[ma.MMSI].Add(aazone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// loop
|
||||
while(!_stopFlag)
|
||||
{
|
||||
@ -64,13 +70,41 @@ namespace bsmd.AIS2Service
|
||||
|
||||
foreach(int mmsi in _sitRepDict.Keys)
|
||||
{
|
||||
if(testZones.ContainsKey(mmsi))
|
||||
if(testDict.ContainsKey(mmsi))
|
||||
{
|
||||
if (!_sitRepDict.ContainsKey(mmsi)) continue;
|
||||
AIS_Target target = _sitRepDict[mmsi];
|
||||
foreach(MonitorZone zone in testZones[mmsi]) {
|
||||
if(zone.IsPointInPolygon4(target.Position))
|
||||
foreach(AlarmAssignmentZone aazone in testDict[mmsi]) {
|
||||
if(aazone.Zone.IsPointInPolygon4(target.Position))
|
||||
{
|
||||
_log.InfoFormat("{0} is in zone {1}", target.ToString(), zone.Name);
|
||||
_log.DebugFormat("{0} is in zone {1}", target.ToString(), aazone.Zone.Name);
|
||||
Alarm alarm;
|
||||
if (aazone.Assignment.Alarms.Count > 0)
|
||||
{
|
||||
alarm = aazone.Assignment.Alarms[0];
|
||||
if(alarm.Timestamp_Last.HasValue &&
|
||||
((DateTime.Now - alarm.Timestamp_Last.Value).TotalMinutes > Properties.Settings.Default.MinAlarmIntervalMins))
|
||||
{
|
||||
// this "old" alarm will be triggered again (overwritten)
|
||||
alarm.Acknowledged = null;
|
||||
alarm.Timestamp_Last = null;
|
||||
alarm.Timestamp_First = DateTime.Now;
|
||||
}
|
||||
else
|
||||
{
|
||||
// update the last time the target registered in the zone
|
||||
alarm.Timestamp_Last = DateTime.Now;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create new alarm
|
||||
alarm = new Alarm(-1, aazone.Assignment);
|
||||
alarm.Timestamp_First = DateTime.Now;
|
||||
alarm.ZoneMonitorType = aazone.Assignment.MonitorType;
|
||||
aazone.Assignment.Alarms.Add(alarm);
|
||||
}
|
||||
_storage.Save(alarm);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,6 +147,15 @@ namespace bsmd.AIS2Service
|
||||
|
||||
#endregion
|
||||
|
||||
#region class AssignmentZoneAlarm composition
|
||||
|
||||
private class AlarmAssignmentZone
|
||||
{
|
||||
public MonitorZone Zone { get; set; }
|
||||
public MonitorAssignment Assignment { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using log4net;
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using System.Threading;
|
||||
|
||||
using System.Data.SQLite;
|
||||
using log4net;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
@ -16,13 +15,14 @@ namespace bsmd.AIS2Service
|
||||
/// past track. It is just intended to function as a "saving the state" of the AIS situation.
|
||||
/// Attention: Alarm zones / alarms are also stored here. This might or might not be such a great idea.
|
||||
/// </summary>
|
||||
public class AIS_SQLiteStorage : IAISThread
|
||||
public class AIS_SQLiteStorage : IAISThread, IDisposable
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly SQLiteConnection _connection;
|
||||
private Thread _thread;
|
||||
private bool _stopFlag = false;
|
||||
private bool disposedValue;
|
||||
private static readonly ILog _log = LogManager.GetLogger(typeof(AIS_SQLiteStorage));
|
||||
private readonly ConcurrentQueue<AIS_Target> _inputQueue;
|
||||
private readonly Dictionary<int, AIS_Target> _storageTargets = new Dictionary<int, AIS_Target>();
|
||||
@ -54,7 +54,7 @@ namespace bsmd.AIS2Service
|
||||
/// monitor zone loader func for the zone alarm watchdog (doesn't need groups or such)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<MonitorZone> LoadMonitorZones()
|
||||
public List<MonitorZone> LoadMonitorZones(bool loadInnerCollections = true)
|
||||
{
|
||||
List<MonitorZone> monitorZones = new List<MonitorZone>();
|
||||
if ((_connection == null) || (_connection.State != ConnectionState.Open)) return monitorZones; // can't load but return nothing in a friendly way
|
||||
@ -79,51 +79,54 @@ namespace bsmd.AIS2Service
|
||||
reader.Close();
|
||||
lzCmd.Dispose();
|
||||
|
||||
// load vertices for each zone
|
||||
string loadVertexString = "SELECT Id, latitude, longitude FROM zone_vertex WHERE monitor_zone_id = @ID";
|
||||
SQLiteCommand lvCmd = new SQLiteCommand(loadVertexString, _connection);
|
||||
foreach(MonitorZone mz in monitorZones)
|
||||
if (loadInnerCollections)
|
||||
{
|
||||
lvCmd.Parameters.Clear();
|
||||
lvCmd.Parameters.AddWithValue("@ID", mz.Id);
|
||||
reader = lvCmd.ExecuteReader();
|
||||
if(reader.HasRows)
|
||||
// load vertices for each zone
|
||||
string loadVertexString = "SELECT Id, latitude, longitude FROM zone_vertex WHERE monitor_zone_id = @ID";
|
||||
SQLiteCommand lvCmd = new SQLiteCommand(loadVertexString, _connection);
|
||||
foreach (MonitorZone mz in monitorZones)
|
||||
{
|
||||
while(reader.Read())
|
||||
lvCmd.Parameters.Clear();
|
||||
lvCmd.Parameters.AddWithValue("@ID", mz.Id);
|
||||
reader = lvCmd.ExecuteReader();
|
||||
if (reader.HasRows)
|
||||
{
|
||||
int id = reader.GetInt32(0);
|
||||
GeoPoint gp = new GeoPoint(id);
|
||||
gp.Lat = reader.GetDouble(1);
|
||||
gp.Lon = reader.GetDouble(2);
|
||||
mz.Vertices.Add(gp);
|
||||
while (reader.Read())
|
||||
{
|
||||
int id = reader.GetInt32(0);
|
||||
GeoPoint gp = new GeoPoint(id);
|
||||
gp.Lat = reader.GetDouble(1);
|
||||
gp.Lon = reader.GetDouble(2);
|
||||
mz.Vertices.Add(gp);
|
||||
}
|
||||
}
|
||||
reader.Close();
|
||||
}
|
||||
reader.Close();
|
||||
}
|
||||
lvCmd.Dispose();
|
||||
lvCmd.Dispose();
|
||||
|
||||
// load mmsi / zone assignments for each zone
|
||||
string loadAssignmentsString = "SELECT id, mmsi, type FROM zone_assignment WHERE monitor_zone_id = @ID";
|
||||
SQLiteCommand laCmd = new SQLiteCommand(loadAssignmentsString, _connection);
|
||||
foreach (MonitorZone mz in monitorZones)
|
||||
{
|
||||
laCmd.Parameters.Clear();
|
||||
laCmd.Parameters.AddWithValue("@ID", mz.Id);
|
||||
reader = laCmd.ExecuteReader();
|
||||
if (reader.HasRows)
|
||||
// load mmsi / zone assignments for each zone
|
||||
string loadAssignmentsString = "SELECT id, mmsi, type FROM zone_assignment WHERE monitor_zone_id = @ID";
|
||||
SQLiteCommand laCmd = new SQLiteCommand(loadAssignmentsString, _connection);
|
||||
foreach (MonitorZone mz in monitorZones)
|
||||
{
|
||||
while (reader.Read())
|
||||
laCmd.Parameters.Clear();
|
||||
laCmd.Parameters.AddWithValue("@ID", mz.Id);
|
||||
reader = laCmd.ExecuteReader();
|
||||
if (reader.HasRows)
|
||||
{
|
||||
int id = reader.GetInt32(0);
|
||||
MonitorAssignment ma = new MonitorAssignment(id);
|
||||
ma.MMSI = reader.GetInt32(1);
|
||||
ma.MonitorType = (MonitorAssignment.ZoneMonitorType)reader.GetInt32(2);
|
||||
mz.Assignments.Add(ma);
|
||||
while (reader.Read())
|
||||
{
|
||||
int id = reader.GetInt32(0);
|
||||
MonitorAssignment ma = new MonitorAssignment(id);
|
||||
ma.MMSI = reader.GetInt32(1);
|
||||
ma.MonitorType = (MonitorAssignment.ZoneMonitorType)reader.GetInt32(2);
|
||||
mz.Assignments.Add(ma);
|
||||
}
|
||||
}
|
||||
reader.Close();
|
||||
}
|
||||
reader.Close();
|
||||
laCmd.Dispose();
|
||||
}
|
||||
laCmd.Dispose();
|
||||
|
||||
return monitorZones;
|
||||
}
|
||||
@ -180,7 +183,8 @@ namespace bsmd.AIS2Service
|
||||
int id = reader.GetInt32(0);
|
||||
Alarm alarm = new Alarm(id, assignment);
|
||||
alarm.Timestamp_First = reader.GetDateTime(1);
|
||||
alarm.Timestamp_Last = reader.GetDateTime(2);
|
||||
if(!reader.IsDBNull(2))
|
||||
alarm.Timestamp_Last = reader.GetDateTime(2);
|
||||
alarm.ZoneMonitorType = (MonitorAssignment.ZoneMonitorType)reader.GetInt32(3);
|
||||
if(!reader.IsDBNull(4))
|
||||
alarm.Acknowledged = reader.GetDateTime(4);
|
||||
@ -207,8 +211,10 @@ namespace bsmd.AIS2Service
|
||||
if (alarm.Id <= 0)
|
||||
{
|
||||
// insert
|
||||
string saveAlarmString = $"INSERT INTO alarm (zone_assignment_id, timestamp_first, timestamp_last, type, acknowledged) VALUES ({alarm.Assignment.Id}, '{alarm.Timestamp_First}', '{alarm.Timestamp_Last}', {alarm.ZoneMonitorType}, {alarm.Acknowledged})";
|
||||
string saveAlarmString = $"INSERT INTO alarm (zone_assignment_id, timestamp_first, timestamp_last, type) VALUES ({alarm.Assignment.Id}, @TS_FIRST, @TS_LAST, {(int) alarm.ZoneMonitorType})";
|
||||
SQLiteCommand cmd = new SQLiteCommand(saveAlarmString, _connection);
|
||||
cmd.Parameters.AddWithValue("@TS_FIRST", alarm.Timestamp_First);
|
||||
cmd.Parameters.AddWithValue("@TS_LAST", alarm.Timestamp_Last);
|
||||
int insertedRows = cmd.ExecuteNonQuery();
|
||||
cmd.Dispose();
|
||||
alarm.Id = GetLastInsertId();
|
||||
@ -217,8 +223,14 @@ namespace bsmd.AIS2Service
|
||||
else
|
||||
{
|
||||
// update
|
||||
string updateAlarmString = $"UPDATE alarm SET acknowledged = {alarm.Acknowledged}, timestamp_last = '{alarm.Timestamp_Last}' WHERE id = {alarm.Id}";
|
||||
string updateAlarmString = $"UPDATE alarm SET acknowledged = @TS_ACK, timestamp_first = @TS_FIRST, timestamp_last = @TS_LAST WHERE id = {alarm.Id}";
|
||||
SQLiteCommand cmd = new SQLiteCommand(updateAlarmString, _connection);
|
||||
cmd.Parameters.AddWithValue("@TS_FIRST", alarm.Timestamp_First);
|
||||
cmd.Parameters.AddWithValue("@TS_LAST", alarm.Timestamp_Last);
|
||||
if(alarm.Acknowledged.HasValue)
|
||||
cmd.Parameters.AddWithValue("@TS_ACK", alarm.Acknowledged);
|
||||
else
|
||||
cmd.Parameters.AddWithValue("@TS_ACK", DBNull.Value);
|
||||
int updatedRows = cmd.ExecuteNonQuery();
|
||||
cmd.Dispose();
|
||||
return (updatedRows == 1);
|
||||
@ -247,6 +259,8 @@ namespace bsmd.AIS2Service
|
||||
groups.Add(mGroup);
|
||||
}
|
||||
}
|
||||
reader.Close();
|
||||
laCmd.Dispose();
|
||||
return groups;
|
||||
}
|
||||
|
||||
@ -373,6 +387,42 @@ namespace bsmd.AIS2Service
|
||||
|
||||
#endregion
|
||||
|
||||
#region ShipLocationReport
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to allow the web interface to load all ship alarms in a group
|
||||
/// (this makes sense since zones appear als columns in the overview)
|
||||
/// </summary>
|
||||
public List<ShipLocationReport> GetShipLocationReports(long groupId)
|
||||
{
|
||||
List<ShipLocationReport> slrs = new List<ShipLocationReport>();
|
||||
|
||||
string loadSLRString = "SELECT a.timestamp_first, a.timestamp_last, za.mmsi, mz.id FROM alarm a " +
|
||||
"INNER JOIN zone_assignment za ON a.zone_assignment_id = za.id " +
|
||||
"INNER JOIN monitor_zone mz ON za.monitor_zone_id = mz.id " +
|
||||
$"WHERE mz.monitor_group_id = {groupId} ORDER BY mz.sequence";
|
||||
|
||||
SQLiteCommand laCmd = new SQLiteCommand(loadSLRString, _connection);
|
||||
SQLiteDataReader reader = laCmd.ExecuteReader();
|
||||
if (reader.HasRows)
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
ShipLocationReport slr = new ShipLocationReport();
|
||||
slr.Timestamp_First = reader.GetDateTime(0);
|
||||
if (reader.IsDBNull(1)) continue; // we dont want very new alarms
|
||||
slr.Timestamp_Last = reader.GetDateTime(1);
|
||||
slr.MMSI = reader.GetInt32(2);
|
||||
slr.MonitorZoneId = reader.GetInt64(3);
|
||||
slrs.Add(slr);
|
||||
}
|
||||
}
|
||||
reader.Close();
|
||||
return slrs;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region private methods
|
||||
@ -620,5 +670,32 @@ namespace bsmd.AIS2Service
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable implementation
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if(_connection.State == ConnectionState.Open)
|
||||
{
|
||||
_connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
<value>60</value>
|
||||
</setting>
|
||||
<setting name="SQLiteDBConnectionString" serializeAs="String">
|
||||
<value>Data Source=ais_initial.db;Version=3;</value>
|
||||
<value>Data Source=ais_initial.db;Version=3;Synchronous=OFF;Journal Mode=MEMORY;</value>
|
||||
</setting>
|
||||
<setting name="PosReportDBCleanupDays" serializeAs="String">
|
||||
<value>7</value>
|
||||
@ -67,6 +67,12 @@
|
||||
<setting name="MonitorTargetSaturationSecs" serializeAs="String">
|
||||
<value>120</value>
|
||||
</setting>
|
||||
<setting name="MinAlarmIntervalMins" serializeAs="String">
|
||||
<value>60</value>
|
||||
</setting>
|
||||
<setting name="AutoAlarmExpiryHours" serializeAs="String">
|
||||
<value>24</value>
|
||||
</setting>
|
||||
</bsmd.AIS2Service.Properties.Settings>
|
||||
</applicationSettings>
|
||||
<runtime>
|
||||
|
||||
@ -1,311 +0,0 @@
|
||||
// Copyright (c) 2022 - schick Informatik
|
||||
// bsmd.AIS2Service [MonitorZone.cs]: %UserDisplayName%
|
||||
// Description: Represents a geographical area that should be checked against
|
||||
// ship positions
|
||||
|
||||
using log4net;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
|
||||
#region DBEntity
|
||||
|
||||
public class DBEntity
|
||||
{
|
||||
protected long _id; // PK from database
|
||||
|
||||
public DBEntity(long id)
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
public long Id { get { return _id; } set { _id = value; } }
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region class MonitorGroup
|
||||
|
||||
public class MonitorGroup : DBEntity
|
||||
{
|
||||
|
||||
private static readonly ILog _log = LogManager.GetLogger(typeof(MonitorGroup));
|
||||
|
||||
#region fields
|
||||
|
||||
private string _name;
|
||||
private readonly List<MonitorZone> _zones = new List<MonitorZone>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public MonitorGroup(long id, string name) : base( id )
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public List<MonitorZone> Zones { get { return _zones; } }
|
||||
|
||||
public string Name { get { return _name; } set { _name = value; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region public static methods
|
||||
|
||||
/// <summary>
|
||||
/// Da Basti nun eine Datei mit allen Elementen exportiert gibt es eine einzelne Funktion, die alles
|
||||
/// importiert
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
/// <returns></returns>
|
||||
public static List<MonitorGroup> LoadGroups(string filename)
|
||||
{
|
||||
List<MonitorGroup> groups = new List<MonitorGroup>();
|
||||
try
|
||||
{
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
XDocument kml = XDocument.Load(filename);
|
||||
XNamespace ns = "http://www.opengis.net/kml/2.2";
|
||||
|
||||
foreach(XElement rootFolderNode in kml.Root.Element(ns + "Document").Element(ns + "Folder").Elements(ns + "Folder"))
|
||||
{
|
||||
MonitorGroup mg = new MonitorGroup(-1, rootFolderNode.Element(ns + "name").Value);
|
||||
int sequence = 1;
|
||||
foreach(XElement placemark in rootFolderNode.Elements(ns + "Placemark"))
|
||||
{
|
||||
MonitorZone mz = new MonitorZone(-1, placemark.Element(ns + "name").Value);
|
||||
mz.Active = true;
|
||||
mz.Sequence = sequence;
|
||||
// now add all vertices
|
||||
string[] vertices = placemark.Element(ns + "Polygon").Element(ns + "outerBoundaryIs").Element(ns + "LinearRing").Element(ns + "coordinates").Value.Split(' ');
|
||||
for (int i = 0; i < vertices.Length - 1; i++)
|
||||
{
|
||||
string[] pointElems = vertices[i].Trim().Split(',');
|
||||
if (pointElems.Length != 3) continue;
|
||||
GeoPoint gp = new GeoPoint(-1);
|
||||
gp.Lon = Double.Parse(pointElems[0], System.Globalization.NumberFormatInfo.InvariantInfo);
|
||||
gp.Lat = Double.Parse(pointElems[1], System.Globalization.NumberFormatInfo.InvariantInfo);
|
||||
mz.Vertices.Add(gp);
|
||||
}
|
||||
|
||||
mg.Zones.Add(mz);
|
||||
sequence++;
|
||||
}
|
||||
|
||||
groups.Add(mg);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_log.Error(ex.ToString());
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region public methods
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Name;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region class MonitorZone
|
||||
|
||||
public class MonitorZone : DBEntity, IComparable<MonitorZone>
|
||||
{
|
||||
|
||||
#region fields
|
||||
|
||||
private readonly List<GeoPoint> _vertices = new List<GeoPoint>();
|
||||
private readonly List<MonitorAssignment> _assignments = new List<MonitorAssignment>();
|
||||
private readonly string _name;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public MonitorZone(long id, string name) : base (id)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public string Name { get { return _name; } }
|
||||
|
||||
public List<GeoPoint> Vertices { get { return _vertices; } }
|
||||
|
||||
public List<MonitorAssignment> Assignments { get { return _assignments; } }
|
||||
|
||||
public bool Active { get; set; }
|
||||
|
||||
public int Sequence { get; set; }
|
||||
|
||||
public long MonitorGroupId { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region public static methods
|
||||
|
||||
public static MonitorZone ImportFromKML(string filename)
|
||||
{
|
||||
MonitorZone result = null;
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
XDocument kml = XDocument.Load(filename);
|
||||
XNamespace ns = "http://www.opengis.net/kml/2.2";
|
||||
|
||||
string name = kml.Root.Element(ns + "Document").Element(ns + "name").Value;
|
||||
if (name.EndsWith(".kml")) name = name.Substring(0, name.Length - 4);
|
||||
result = new MonitorZone(-1, name);
|
||||
|
||||
// now find all vertices
|
||||
string[] vertices = kml.Root.Element(ns + "Document").Element(ns + "Placemark").Element(ns + "Polygon").Element(ns + "outerBoundaryIs").Element(ns + "LinearRing").Element(ns + "coordinates").Value.Split(' ');
|
||||
for (int i = 0; i < vertices.Length - 1; i++)
|
||||
{
|
||||
string[] pointElems = vertices[i].Trim().Split(',');
|
||||
if (pointElems.Length != 3) continue;
|
||||
GeoPoint gp = new GeoPoint(-1);
|
||||
gp.Lon = Double.Parse(pointElems[0], System.Globalization.NumberFormatInfo.InvariantInfo);
|
||||
gp.Lat = Double.Parse(pointElems[1], System.Globalization.NumberFormatInfo.InvariantInfo);
|
||||
result.Vertices.Add(gp);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region public methods
|
||||
|
||||
public bool IsPointInPolygon4(GeoPoint testPoint)
|
||||
{
|
||||
bool result = false;
|
||||
int j = _vertices.Count() - 1;
|
||||
for (int i = 0; i < _vertices.Count(); i++)
|
||||
{
|
||||
if (_vertices[i].Lat < testPoint.Lat && _vertices[j].Lat >= testPoint.Lat || _vertices[j].Lat < testPoint.Lat && _vertices[i].Lat >= testPoint.Lat)
|
||||
{
|
||||
if (_vertices[i].Lon + (testPoint.Lat - _vertices[i].Lat) / (_vertices[j].Lat - _vertices[i].Lat) * (_vertices[j].Lon - _vertices[i].Lon) < testPoint.Lon)
|
||||
{
|
||||
result = !result;
|
||||
}
|
||||
}
|
||||
j = i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int CompareTo(MonitorZone other)
|
||||
{
|
||||
return this.Sequence.CompareTo(other.Sequence);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{0} (Seq.:{1} #Vert.:{2}", this.Name, this.Sequence, this.Vertices.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region class GeoPoint
|
||||
|
||||
public class GeoPoint : DBEntity
|
||||
{
|
||||
|
||||
public GeoPoint(long id) : base (id)
|
||||
{}
|
||||
|
||||
public double Lat { get; set; }
|
||||
public double Lon { get; set; }
|
||||
|
||||
public long MonitorZoneId { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region class MonitorAssignment
|
||||
|
||||
public class MonitorAssignment : DBEntity
|
||||
{
|
||||
|
||||
public MonitorAssignment(long id) : base(id)
|
||||
{}
|
||||
|
||||
[Flags]
|
||||
public enum ZoneMonitorType
|
||||
{
|
||||
INACTIVE = 0,
|
||||
ENTER = 1,
|
||||
EXIT = 2,
|
||||
PASSTHROUGH = 4, // outside - enter - inside - exit - outside
|
||||
LEAVE_AND_RETURN = 8 // inside - exit - outside - enter - inside
|
||||
}
|
||||
|
||||
public int MMSI { get; set; }
|
||||
|
||||
public ZoneMonitorType MonitorType { get; set; } = ZoneMonitorType.INACTIVE;
|
||||
|
||||
public long MonitorZoneId { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{0} {1}", MMSI, MonitorType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region class Alarm
|
||||
|
||||
public class Alarm : DBEntity
|
||||
{
|
||||
private readonly MonitorAssignment _assignment;
|
||||
|
||||
public Alarm(long id, MonitorAssignment assignment) : base(id) { _assignment = assignment; }
|
||||
|
||||
public MonitorAssignment Assignment { get { return _assignment; } }
|
||||
|
||||
public DateTime Timestamp_First { get; set; }
|
||||
|
||||
public DateTime Timestamp_Last { get; set; }
|
||||
|
||||
public DateTime? Acknowledged { get; set; }
|
||||
|
||||
public MonitorAssignment.ZoneMonitorType ZoneMonitorType { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
20
AIS/bsmd.AIS2Service/Properties/Settings.Designer.cs
generated
20
AIS/bsmd.AIS2Service/Properties/Settings.Designer.cs
generated
@ -70,7 +70,7 @@ namespace bsmd.AIS2Service.Properties {
|
||||
|
||||
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("Data Source=ais_initial.db;Version=3;")]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("Data Source=ais_initial.db;Version=3;Synchronous=OFF;Journal Mode=MEMORY;")]
|
||||
public string SQLiteDBConnectionString {
|
||||
get {
|
||||
return ((string)(this["SQLiteDBConnectionString"]));
|
||||
@ -121,5 +121,23 @@ namespace bsmd.AIS2Service.Properties {
|
||||
return ((int)(this["MonitorTargetSaturationSecs"]));
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("60")]
|
||||
public int MinAlarmIntervalMins {
|
||||
get {
|
||||
return ((int)(this["MinAlarmIntervalMins"]));
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("24")]
|
||||
public int AutoAlarmExpiryHours {
|
||||
get {
|
||||
return ((int)(this["AutoAlarmExpiryHours"]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<Value Profile="(Default)">60</Value>
|
||||
</Setting>
|
||||
<Setting Name="SQLiteDBConnectionString" Type="System.String" Scope="Application">
|
||||
<Value Profile="(Default)">Data Source=ais_initial.db;Version=3;</Value>
|
||||
<Value Profile="(Default)">Data Source=ais_initial.db;Version=3;Synchronous=OFF;Journal Mode=MEMORY;</Value>
|
||||
</Setting>
|
||||
<Setting Name="PosReportDBCleanupDays" Type="System.Int32" Scope="Application">
|
||||
<Value Profile="(Default)">7</Value>
|
||||
@ -35,5 +35,11 @@
|
||||
<Setting Name="MonitorTargetSaturationSecs" Type="System.Int32" Scope="Application">
|
||||
<Value Profile="(Default)">120</Value>
|
||||
</Setting>
|
||||
<Setting Name="MinAlarmIntervalMins" Type="System.Int32" Scope="Application">
|
||||
<Value Profile="(Default)">60</Value>
|
||||
</Setting>
|
||||
<Setting Name="AutoAlarmExpiryHours" Type="System.Int32" Scope="Application">
|
||||
<Value Profile="(Default)">24</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
@ -94,25 +94,30 @@
|
||||
<DependentUpon>AIS2_Service.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="AISClass.cs" />
|
||||
<Compile Include="AISController.cs" />
|
||||
<Compile Include="AISDecoder.cs" />
|
||||
<Compile Include="webservice\AISController.cs" />
|
||||
<Compile Include="decoding\AISDecoder.cs" />
|
||||
<Compile Include="AISManager.cs" />
|
||||
<Compile Include="AISZoneMonitor.cs" />
|
||||
<Compile Include="AIS_BaseStation.cs" />
|
||||
<Compile Include="AIS_BaseStationReport.cs" />
|
||||
<Compile Include="AIS_ClassB.cs" />
|
||||
<Compile Include="AIS_ClassBExt.cs" />
|
||||
<Compile Include="AIS_ClassBStatic.cs" />
|
||||
<Compile Include="AIS_PosReport.cs" />
|
||||
<Compile Include="decoding\AIS_BaseStation.cs" />
|
||||
<Compile Include="decoding\AIS_BaseStationReport.cs" />
|
||||
<Compile Include="decoding\AIS_ClassB.cs" />
|
||||
<Compile Include="decoding\AIS_ClassBExt.cs" />
|
||||
<Compile Include="decoding\AIS_ClassBStatic.cs" />
|
||||
<Compile Include="decoding\AIS_PosReport.cs" />
|
||||
<Compile Include="AIS_SQLiteStorage.cs" />
|
||||
<Compile Include="AIS_StaticData.cs" />
|
||||
<Compile Include="decoding\AIS_StaticData.cs" />
|
||||
<Compile Include="AIS_Target.cs" />
|
||||
<Compile Include="IAISThread.cs" />
|
||||
<Compile Include="Lookup.cs" />
|
||||
<Compile Include="MonitorZone.cs" />
|
||||
<Compile Include="NMEA.cs" />
|
||||
<Compile Include="NMEA_AIS_Sentence.cs" />
|
||||
<Compile Include="NMEA_PNMLS_Sentence.cs" />
|
||||
<Compile Include="zone_alarm\Alarm.cs" />
|
||||
<Compile Include="zone_alarm\DBEntity.cs" />
|
||||
<Compile Include="zone_alarm\GeoPoint.cs" />
|
||||
<Compile Include="zone_alarm\MonitorAssignment.cs" />
|
||||
<Compile Include="zone_alarm\MonitorGroup.cs" />
|
||||
<Compile Include="zone_alarm\MonitorZone.cs" />
|
||||
<Compile Include="decoding\NMEA.cs" />
|
||||
<Compile Include="decoding\NMEA_AIS_Sentence.cs" />
|
||||
<Compile Include="decoding\NMEA_PNMLS_Sentence.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="ProjectInstaller.cs">
|
||||
<SubType>Component</SubType>
|
||||
@ -127,9 +132,12 @@
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SerialTCPReader.cs" />
|
||||
<Compile Include="webservice\ShipLocationReport.cs" />
|
||||
<Compile Include="SitRep.cs" />
|
||||
<Compile Include="StartupWebAPI.cs" />
|
||||
<Compile Include="webservice\SLRController.cs" />
|
||||
<Compile Include="webservice\StartupWebAPI.cs" />
|
||||
<Compile Include="Util.cs" />
|
||||
<Compile Include="webservice\ZonesController.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\SQL\ais_initial.db">
|
||||
|
||||
BIN
AIS/bsmd.AIS2Service/img/arrow_down_red.png
Normal file
BIN
AIS/bsmd.AIS2Service/img/arrow_down_red.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 933 B |
BIN
AIS/bsmd.AIS2Service/img/arrow_up_green.png
Normal file
BIN
AIS/bsmd.AIS2Service/img/arrow_up_green.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 950 B |
BIN
AIS/bsmd.AIS2Service/img/bullet_square_yellow.png
Normal file
BIN
AIS/bsmd.AIS2Service/img/bullet_square_yellow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 446 B |
BIN
AIS/bsmd.AIS2Service/img/clock.png
Normal file
BIN
AIS/bsmd.AIS2Service/img/clock.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
17
AIS/bsmd.AIS2Service/lauf.html
Normal file
17
AIS/bsmd.AIS2Service/lauf.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Zonen Übersicht</title>
|
||||
<script type="text/javascript" src="zonen.js"></script>
|
||||
</head>
|
||||
<body onload="createAreas()">
|
||||
<div class="table_group" id="root-div" />
|
||||
<div id="bottomcenter">
|
||||
(c) <a href="http://www.textbausteine.net" target="#">Informatikbüro Daniel Schick</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -6,123 +6,50 @@
|
||||
body{
|
||||
font-family: Helvetica;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background: rgba( 71, 147, 227, 1);
|
||||
}
|
||||
h2{
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: white;
|
||||
padding: 30px 0;
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
|
||||
.table-wrapper{
|
||||
margin: 10px 70px 70px;
|
||||
box-shadow: 0px 35px 50px rgba( 0, 0, 0, 0.2 );
|
||||
}
|
||||
|
||||
.fl-table {
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
border: none;
|
||||
.styled-table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
background-color: white;
|
||||
margin: 25px 0;
|
||||
font-size: 0.9em;
|
||||
font-family: sans-serif;
|
||||
min-width: 400px;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.fl-table td, .fl-table th {
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.fl-table td {
|
||||
border-right: 1px solid #f8f8f8;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.fl-table thead th {
|
||||
.styled-table thead tr {
|
||||
background-color: #009879;
|
||||
color: #ffffff;
|
||||
background: #4FC3A1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.styled-table th,
|
||||
.styled-table td {
|
||||
padding: 12px 15px;
|
||||
}
|
||||
|
||||
.styled-table tbody tr {
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
.styled-table tbody tr:nth-of-type(even) {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.styled-table tbody tr:last-of-type {
|
||||
border-bottom: 2px solid #009879;
|
||||
}
|
||||
|
||||
.styled-table tbody tr.active-row {
|
||||
font-weight: bold;
|
||||
color: #009879;
|
||||
}
|
||||
|
||||
|
||||
.fl-table thead th:nth-child(odd) {
|
||||
color: #ffffff;
|
||||
background: #324960;
|
||||
}
|
||||
|
||||
.fl-table tr:nth-child(even) {
|
||||
background: #F8F8F8;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.fl-table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
.table-wrapper:before{
|
||||
content: "Scroll horizontally >";
|
||||
display: block;
|
||||
text-align: right;
|
||||
font-size: 11px;
|
||||
color: white;
|
||||
padding: 0 0 10px;
|
||||
}
|
||||
.fl-table thead, .fl-table tbody, .fl-table thead th {
|
||||
display: block;
|
||||
}
|
||||
.fl-table thead th:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
.fl-table thead {
|
||||
float: left;
|
||||
}
|
||||
.fl-table tbody {
|
||||
width: auto;
|
||||
position: relative;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.fl-table td, .fl-table th {
|
||||
padding: 20px .625em .625em .625em;
|
||||
height: 60px;
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 120px;
|
||||
font-size: 13px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.fl-table thead th {
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #f7f7f9;
|
||||
}
|
||||
.fl-table tbody tr {
|
||||
display: table-cell;
|
||||
}
|
||||
.fl-table tbody tr:nth-child(odd) {
|
||||
background: none;
|
||||
}
|
||||
.fl-table tr:nth-child(even) {
|
||||
background: transparent;
|
||||
}
|
||||
.fl-table tr td:nth-child(odd) {
|
||||
background: #F8F8F8;
|
||||
border-right: 1px solid #E6E4E4;
|
||||
}
|
||||
.fl-table tr td:nth-child(even) {
|
||||
border-right: 1px solid #E6E4E4;
|
||||
}
|
||||
.fl-table tbody td {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
#bottomcenter {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: 20px;
|
||||
transform: translate(-50%, -50%);
|
||||
margin: 0 auto;
|
||||
}
|
||||
95
AIS/bsmd.AIS2Service/webservice/SLRController.cs
Normal file
95
AIS/bsmd.AIS2Service/webservice/SLRController.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Http;
|
||||
using log4net;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
public class SLRController : ApiController
|
||||
{
|
||||
private static readonly ILog _log = LogManager.GetLogger(typeof(SLRController));
|
||||
|
||||
[HttpGet]
|
||||
public IEnumerable<ShipLocationReport> Get([FromUri] int? id)
|
||||
{
|
||||
if (!id.HasValue) return null;
|
||||
List<ShipLocationReport> result = AISManager.SQLiteStorage.GetShipLocationReports(id.Value);
|
||||
|
||||
// remove results not in SitRep
|
||||
int obscureTargetCnt = result.RemoveAll(x => !AISManager.SitRep.ContainsKey(x.MMSI));
|
||||
_log.InfoFormat("removed {0} obscure targets", obscureTargetCnt);
|
||||
|
||||
// remove targets w/o name (i.e. static data)
|
||||
int unnamedPlayerCnt = result.RemoveAll(x => string.IsNullOrEmpty(AISManager.SitRep[x.MMSI].Name));
|
||||
_log.InfoFormat("removed {0} unnamed targets", unnamedPlayerCnt);
|
||||
|
||||
// Class B targets entfernen
|
||||
int classBReportCnt = result.RemoveAll(x => AISManager.SitRep[x.MMSI].IsClassB ?? false);
|
||||
_log.InfoFormat("removed {0} class B alarms from list", classBReportCnt); // tut des?
|
||||
|
||||
// auch alles entfernen was "abgelaufen" ist und nicht im SitRep enthalten ist
|
||||
int expiredCnt = result.RemoveAll(x => (DateTime.Now - x.Timestamp_Last).TotalMinutes > 1440);
|
||||
_log.InfoFormat("removed {0} expired (> 1 day) alarms from list", expiredCnt);
|
||||
|
||||
Dictionary<int, List<ShipLocationReport>> mmsiDict = new Dictionary<int, List<ShipLocationReport>>();
|
||||
foreach(ShipLocationReport report in result) {
|
||||
if (!mmsiDict.ContainsKey(report.MMSI)) mmsiDict[report.MMSI] = new List<ShipLocationReport>();
|
||||
mmsiDict[report.MMSI].Add(report);
|
||||
if (AISManager.SitRep.ContainsKey(report.MMSI))
|
||||
{
|
||||
report.Destination = AISManager.SitRep[report.MMSI].Destination;
|
||||
report.Name = AISManager.SitRep[report.MMSI].Name;
|
||||
report.NavStatus = AIS_PosReport.GetNavStatus(AISManager.SitRep[report.MMSI].NavStatus);
|
||||
report.IMO = AISManager.SitRep[report.MMSI].IMO;
|
||||
}
|
||||
}
|
||||
|
||||
// determine "state" of vessel through alarm comparison. Possible values are:
|
||||
// 0 = stationary (= equals)
|
||||
// 1 = incoming
|
||||
// 2 = outgoing
|
||||
// 3 = expired
|
||||
|
||||
foreach(int key in mmsiDict.Keys)
|
||||
{
|
||||
bool expired = true;
|
||||
DateTime? lastDate= null;
|
||||
bool? incoming = null;
|
||||
|
||||
// first run through alarm list to determine state
|
||||
foreach(ShipLocationReport slr in mmsiDict[key])
|
||||
{
|
||||
if((DateTime.Now - slr.Timestamp_Last).TotalHours < Properties.Settings.Default.AutoAlarmExpiryHours) expired = false;
|
||||
if (lastDate == null)
|
||||
{
|
||||
lastDate = slr.Timestamp_Last;
|
||||
}
|
||||
else
|
||||
{
|
||||
incoming = slr.Timestamp_Last < lastDate;
|
||||
}
|
||||
}
|
||||
|
||||
// second run through alarm list to set state flag in all entries
|
||||
foreach (ShipLocationReport slr in mmsiDict[key])
|
||||
{
|
||||
if (expired)
|
||||
{
|
||||
slr.VoyageDirection = 3;
|
||||
}
|
||||
else if (incoming.HasValue)
|
||||
{
|
||||
if (incoming.Value) { slr.VoyageDirection = 1; }
|
||||
else { slr.VoyageDirection = 2; }
|
||||
}
|
||||
else
|
||||
{
|
||||
slr.VoyageDirection = 0; // stationary / no comparison value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
AIS/bsmd.AIS2Service/webservice/ShipLocationReport.cs
Normal file
28
AIS/bsmd.AIS2Service/webservice/ShipLocationReport.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class to report a ship position / entry into a zone (aka alarm connected to zone)
|
||||
/// </summary>
|
||||
public class ShipLocationReport
|
||||
{
|
||||
public DateTime Timestamp_First { get; set; }
|
||||
|
||||
public DateTime Timestamp_Last { get; set; }
|
||||
|
||||
public long MonitorZoneId { get; set; }
|
||||
|
||||
public int MMSI { get; set; }
|
||||
|
||||
public int? IMO { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Destination { get; set; }
|
||||
|
||||
public string NavStatus { get; set; }
|
||||
|
||||
public int VoyageDirection { get; set; }
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,19 @@ namespace bsmd.AIS2Service
|
||||
routeTemplate: "api/{Controller}",
|
||||
defaults: new { id = RouteParameter.Optional, Controller = "AIS"}
|
||||
);
|
||||
|
||||
config.Routes.MapHttpRoute(
|
||||
name: "ZonesList",
|
||||
routeTemplate: "api/{Controller}",
|
||||
defaults: new { id = RouteParameter.Optional, Controller = "Zones" }
|
||||
);
|
||||
|
||||
config.Routes.MapHttpRoute(
|
||||
name: "SLRList",
|
||||
routeTemplate: "api/{Controller}",
|
||||
defaults: new { id = RouteParameter.Optional, Controller = "SLR" }
|
||||
);
|
||||
|
||||
config.EnableCors(cors);
|
||||
appBuilder.UseWebApi(config);
|
||||
|
||||
41
AIS/bsmd.AIS2Service/webservice/ZonesController.cs
Normal file
41
AIS/bsmd.AIS2Service/webservice/ZonesController.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Cors;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
public class ZonesController : ApiController
|
||||
{
|
||||
[HttpGet]
|
||||
public IEnumerable<MonitorGroup> Get() // Get([FromUri] int? id)
|
||||
{
|
||||
List<MonitorZone> allZones = AISManager.SQLiteStorage.LoadMonitorZones(false);
|
||||
List<MonitorGroup> groups = AISManager.SQLiteStorage.LoadGroups();
|
||||
foreach(MonitorGroup group in groups)
|
||||
{
|
||||
foreach(MonitorZone zone in allZones)
|
||||
{
|
||||
if(group.Id == zone.MonitorGroupId)
|
||||
{
|
||||
group.Zones.Add(zone);
|
||||
}
|
||||
}
|
||||
group.Zones.Sort();
|
||||
}
|
||||
return groups;
|
||||
|
||||
/*
|
||||
if (!id.HasValue)
|
||||
{
|
||||
List<MonitorGroup> groups = AISManager.SQLiteStorage.LoadGroups();
|
||||
return groups;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
30
AIS/bsmd.AIS2Service/zone_alarm/Alarm.cs
Normal file
30
AIS/bsmd.AIS2Service/zone_alarm/Alarm.cs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2023 - schick Informatik
|
||||
// bsmd.AIS2Service [Alarm.cs]: Daniel Schick
|
||||
// Description: alarm created if vessel interacts with zone
|
||||
//
|
||||
using System;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
#region class Alarm
|
||||
|
||||
public class Alarm : DBEntity
|
||||
{
|
||||
private readonly MonitorAssignment _assignment;
|
||||
|
||||
public Alarm(long id, MonitorAssignment assignment) : base(id) { _assignment = assignment; }
|
||||
|
||||
public MonitorAssignment Assignment { get { return _assignment; } }
|
||||
|
||||
public DateTime Timestamp_First { get; set; }
|
||||
|
||||
public DateTime? Timestamp_Last { get; set; }
|
||||
|
||||
public DateTime? Acknowledged { get; set; }
|
||||
|
||||
public MonitorAssignment.ZoneMonitorType ZoneMonitorType { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
25
AIS/bsmd.AIS2Service/zone_alarm/DBEntity.cs
Normal file
25
AIS/bsmd.AIS2Service/zone_alarm/DBEntity.cs
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2023 - schick Informatik
|
||||
// bsmd.AIS2Service [DBEntity.cs]: Daniel Schick
|
||||
// Description: Root class for SQlite database entities
|
||||
//
|
||||
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
#region DBEntity
|
||||
|
||||
public class DBEntity
|
||||
{
|
||||
protected long _id; // PK from database
|
||||
|
||||
public DBEntity(long id)
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
public long Id { get { return _id; } set { _id = value; } }
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
23
AIS/bsmd.AIS2Service/zone_alarm/GeoPoint.cs
Normal file
23
AIS/bsmd.AIS2Service/zone_alarm/GeoPoint.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2023 - schick Informatik
|
||||
// bsmd.AIS2Service.zone_alarm [GeoPoint.cs]: Daniel Schick
|
||||
// Description: Single vertex of a zone
|
||||
//
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
#region class GeoPoint
|
||||
|
||||
public class GeoPoint : DBEntity
|
||||
{
|
||||
|
||||
public GeoPoint(long id) : base(id)
|
||||
{ }
|
||||
|
||||
public double Lat { get; set; }
|
||||
public double Lon { get; set; }
|
||||
|
||||
public long MonitorZoneId { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
44
AIS/bsmd.AIS2Service/zone_alarm/MonitorAssignment.cs
Normal file
44
AIS/bsmd.AIS2Service/zone_alarm/MonitorAssignment.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2023 - schick Informatik
|
||||
// bsmd.AIS2Service.zone_alarm [MonitorAssignment.cs]: Daniel Schick
|
||||
// Description: Mapping between targets (MMSI) and zones
|
||||
//
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
#region class MonitorAssignment
|
||||
|
||||
public class MonitorAssignment : DBEntity
|
||||
{
|
||||
|
||||
public MonitorAssignment(long id) : base(id)
|
||||
{ }
|
||||
|
||||
[Flags]
|
||||
public enum ZoneMonitorType
|
||||
{
|
||||
INACTIVE = 0,
|
||||
ENTER = 1,
|
||||
EXIT = 2,
|
||||
PASSTHROUGH = 4, // outside - enter - inside - exit - outside
|
||||
LEAVE_AND_RETURN = 8 // inside - exit - outside - enter - inside
|
||||
}
|
||||
|
||||
public int MMSI { get; set; }
|
||||
|
||||
public ZoneMonitorType MonitorType { get; set; } = ZoneMonitorType.INACTIVE;
|
||||
|
||||
public long MonitorZoneId { get; set; }
|
||||
|
||||
public List<Alarm> Alarms { get; } = new List<Alarm>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{0} {1}", MMSI, MonitorType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
112
AIS/bsmd.AIS2Service/zone_alarm/MonitorGroup.cs
Normal file
112
AIS/bsmd.AIS2Service/zone_alarm/MonitorGroup.cs
Normal file
@ -0,0 +1,112 @@
|
||||
// Copyright (c) 2023 - schick Informatik
|
||||
// bsmd.AIS2Service [MonitorGroup.cs]: Daniel Schick
|
||||
// Description: Group container for zones
|
||||
//
|
||||
using log4net;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
#region class MonitorGroup
|
||||
|
||||
public class MonitorGroup : DBEntity
|
||||
{
|
||||
|
||||
private static readonly ILog _log = LogManager.GetLogger(typeof(MonitorGroup));
|
||||
|
||||
#region fields
|
||||
|
||||
private string _name;
|
||||
private readonly List<MonitorZone> _zones = new List<MonitorZone>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public MonitorGroup(long id, string name) : base(id)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public List<MonitorZone> Zones { get { return _zones; } }
|
||||
|
||||
public string Name { get { return _name; } set { _name = value; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region public static methods
|
||||
|
||||
/// <summary>
|
||||
/// Da Basti nun eine Datei mit allen Elementen exportiert gibt es eine einzelne Funktion, die alles
|
||||
/// importiert
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
/// <returns></returns>
|
||||
public static List<MonitorGroup> LoadGroups(string filename)
|
||||
{
|
||||
List<MonitorGroup> groups = new List<MonitorGroup>();
|
||||
try
|
||||
{
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
XDocument kml = XDocument.Load(filename);
|
||||
XNamespace ns = "http://www.opengis.net/kml/2.2";
|
||||
|
||||
foreach (XElement rootFolderNode in kml.Root.Element(ns + "Document").Element(ns + "Folder").Elements(ns + "Folder"))
|
||||
{
|
||||
MonitorGroup mg = new MonitorGroup(-1, rootFolderNode.Element(ns + "name").Value);
|
||||
int sequence = 1;
|
||||
foreach (XElement placemark in rootFolderNode.Elements(ns + "Placemark"))
|
||||
{
|
||||
MonitorZone mz = new MonitorZone(-1, placemark.Element(ns + "name").Value);
|
||||
mz.Active = true;
|
||||
mz.Sequence = sequence;
|
||||
// now add all vertices
|
||||
string[] vertices = placemark.Element(ns + "Polygon").Element(ns + "outerBoundaryIs").Element(ns + "LinearRing").Element(ns + "coordinates").Value.Split(' ');
|
||||
for (int i = 0; i < vertices.Length - 1; i++)
|
||||
{
|
||||
string[] pointElems = vertices[i].Trim().Split(',');
|
||||
if (pointElems.Length != 3) continue;
|
||||
GeoPoint gp = new GeoPoint(-1);
|
||||
gp.Lon = Double.Parse(pointElems[0], System.Globalization.NumberFormatInfo.InvariantInfo);
|
||||
gp.Lat = Double.Parse(pointElems[1], System.Globalization.NumberFormatInfo.InvariantInfo);
|
||||
mz.Vertices.Add(gp);
|
||||
}
|
||||
|
||||
mg.Zones.Add(mz);
|
||||
sequence++;
|
||||
}
|
||||
|
||||
groups.Add(mg);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error(ex.ToString());
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region public methods
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Name;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
121
AIS/bsmd.AIS2Service/zone_alarm/MonitorZone.cs
Normal file
121
AIS/bsmd.AIS2Service/zone_alarm/MonitorZone.cs
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2022 - schick Informatik
|
||||
// bsmd.AIS2Service [MonitorZone.cs]: %UserDisplayName%
|
||||
// Description: Represents a geographical area that should be checked against
|
||||
// ship positions
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
|
||||
#region class MonitorZone
|
||||
|
||||
public class MonitorZone : DBEntity, IComparable<MonitorZone>
|
||||
{
|
||||
|
||||
#region fields
|
||||
|
||||
private readonly List<GeoPoint> _vertices = new List<GeoPoint>();
|
||||
private readonly List<MonitorAssignment> _assignments = new List<MonitorAssignment>();
|
||||
private readonly string _name;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public MonitorZone(long id, string name) : base (id)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public string Name { get { return _name; } }
|
||||
|
||||
public List<GeoPoint> Vertices { get { return _vertices; } }
|
||||
|
||||
public List<MonitorAssignment> Assignments { get { return _assignments; } }
|
||||
|
||||
public bool Active { get; set; }
|
||||
|
||||
public int Sequence { get; set; }
|
||||
|
||||
public long MonitorGroupId { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region public static methods
|
||||
|
||||
public static MonitorZone ImportFromKML(string filename)
|
||||
{
|
||||
MonitorZone result = null;
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
XDocument kml = XDocument.Load(filename);
|
||||
XNamespace ns = "http://www.opengis.net/kml/2.2";
|
||||
|
||||
string name = kml.Root.Element(ns + "Document").Element(ns + "name").Value;
|
||||
if (name.EndsWith(".kml")) name = name.Substring(0, name.Length - 4);
|
||||
result = new MonitorZone(-1, name);
|
||||
|
||||
// now find all vertices
|
||||
string[] vertices = kml.Root.Element(ns + "Document").Element(ns + "Placemark").Element(ns + "Polygon").Element(ns + "outerBoundaryIs").Element(ns + "LinearRing").Element(ns + "coordinates").Value.Split(' ');
|
||||
for (int i = 0; i < vertices.Length - 1; i++)
|
||||
{
|
||||
string[] pointElems = vertices[i].Trim().Split(',');
|
||||
if (pointElems.Length != 3) continue;
|
||||
GeoPoint gp = new GeoPoint(-1);
|
||||
gp.Lon = Double.Parse(pointElems[0], System.Globalization.NumberFormatInfo.InvariantInfo);
|
||||
gp.Lat = Double.Parse(pointElems[1], System.Globalization.NumberFormatInfo.InvariantInfo);
|
||||
result.Vertices.Add(gp);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region public methods
|
||||
|
||||
public bool IsPointInPolygon4(GeoPoint testPoint)
|
||||
{
|
||||
bool result = false;
|
||||
int j = _vertices.Count() - 1;
|
||||
for (int i = 0; i < _vertices.Count(); i++)
|
||||
{
|
||||
if (_vertices[i].Lat < testPoint.Lat && _vertices[j].Lat >= testPoint.Lat || _vertices[j].Lat < testPoint.Lat && _vertices[i].Lat >= testPoint.Lat)
|
||||
{
|
||||
if (_vertices[i].Lon + (testPoint.Lat - _vertices[i].Lat) / (_vertices[j].Lat - _vertices[i].Lat) * (_vertices[j].Lon - _vertices[i].Lon) < testPoint.Lon)
|
||||
{
|
||||
result = !result;
|
||||
}
|
||||
}
|
||||
j = i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int CompareTo(MonitorZone other)
|
||||
{
|
||||
return this.Sequence.CompareTo(other.Sequence);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{0} (Seq.:{1} #Vert.:{2}", this.Name, this.Sequence, this.Vertices.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
175
AIS/bsmd.AIS2Service/zonen.js
Normal file
175
AIS/bsmd.AIS2Service/zonen.js
Normal file
@ -0,0 +1,175 @@
|
||||
|
||||
|
||||
|
||||
/* startup, load groups from database */
|
||||
|
||||
var groupIds = [];
|
||||
var groupZones = [];
|
||||
|
||||
async function loadAlarms(groupId)
|
||||
{
|
||||
let data;
|
||||
try {
|
||||
const res = await fetch('http://localhost:9050/api/slr?id=' + groupId);
|
||||
data = await res.json();
|
||||
updateData(data, groupId);
|
||||
}
|
||||
catch(error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateData(data, groupId) {
|
||||
|
||||
var table = document.getElementById('table-' + groupId);
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
|
||||
let row_id = "row_" + groupId + "_" + data[i].MMSI;
|
||||
row = document.getElementById(row_id);
|
||||
if(row == null) { // not found, create new row
|
||||
row = document.createElement('tr');
|
||||
row.setAttribute("id", row_id);
|
||||
// add leading cells
|
||||
var td1 = document.createElement('td');
|
||||
//td1.innerHTML = data[i].Name;
|
||||
row.appendChild(td1);
|
||||
var td2 = document.createElement('td');
|
||||
|
||||
row.appendChild(td2);
|
||||
var td3 = document.createElement('td');
|
||||
// td3.innerHTML = data[i].IMO;
|
||||
row.appendChild(td3);
|
||||
// create dummy cells for each zone
|
||||
for(var j = 0; j < groupZones[groupId].length; j++) {
|
||||
var td = document.createElement('td');
|
||||
td.id = "cell_" + groupId + "_" + groupZones[groupId][j] + "_" + data[i].MMSI;
|
||||
row.appendChild(td);
|
||||
}
|
||||
// add trailing cells
|
||||
var td4 = document.createElement('td');
|
||||
//td4.innerHTML = data[i].NavStatus;
|
||||
row.appendChild(td4);
|
||||
var td5 = document.createElement('td');
|
||||
//td5.innerHTML = data[i].MMSI;
|
||||
row.appendChild(td5);
|
||||
var td6 = document.createElement('td');
|
||||
//td6.innerHTML = data[i].Destination;
|
||||
row.appendChild(td6);
|
||||
|
||||
table.childNodes[1].appendChild(row); // append row to tbody subelement
|
||||
}
|
||||
row.setAttribute("isActive", "true");
|
||||
const colCount = row.childNodes.length;
|
||||
row.childNodes[0].innerHTML = data[i].Name;
|
||||
if(data[i].VoyageDirection == 0) row.childNodes[1].innerHTML = '<img src="img/bullet_square_yellow.png" />';
|
||||
if(data[i].VoyageDirection == 1) row.childNodes[1].innerHTML = '<img src="img/arrow_down_red.png" />';
|
||||
if(data[i].VoyageDirection == 2) row.childNodes[1].innerHTML = '<img src="img/arrow_up_green.png" />';
|
||||
if(data[i].VoyageDirection == 3) row.childNodes[1].innerHTML = '<img src="img/clock.png" />';
|
||||
row.childNodes[2].innerHTML = data[i].IMO;
|
||||
|
||||
// find alarm cell and set value
|
||||
let cellId = "cell_" + groupId + "_" + data[i].MonitorZoneId + "_" + data[i].MMSI;
|
||||
var cell = document.getElementById(cellId);
|
||||
if(cell != null) {
|
||||
const timestamp = Date.parse(data[i].Timestamp_First);
|
||||
const d = new Date(timestamp);
|
||||
cell.innerHTML = ("0" + d.getHours()).slice(-2) + ":" + ("0" + d.getMinutes()).slice(-2) + " " + d.getDate() + "." + (d.getMonth() + 1) + "." + ("" + d.getFullYear()).slice(-2);
|
||||
}
|
||||
|
||||
row.childNodes[colCount - 3].innerHTML = data[i].NavStatus;
|
||||
row.childNodes[colCount - 2].innerHTML = data[i].MMSI;
|
||||
row.childNodes[colCount - 1].innerHTML = data[i].Destination;
|
||||
}
|
||||
|
||||
// array in-place removal taking place
|
||||
var i = table.childNodes[1].rows.length;
|
||||
while (i--) {
|
||||
row = table.childNodes[1].rows[i];
|
||||
if(row["isActive"] !== "true") {
|
||||
table.childNodes[1].rows.remove(row);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function update() {
|
||||
|
||||
for(var i = 0; i < groupIds.length; i++)
|
||||
{
|
||||
loadAlarms(groupIds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function createAreas()
|
||||
{
|
||||
fetch('http://localhost:9050/api/zones')
|
||||
.then(function (response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
createAreasFromData(data);
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.log('error: ' + err);
|
||||
});
|
||||
|
||||
function createAreasFromData(data)
|
||||
{
|
||||
var root_div = document.getElementById('root-div');
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
groupIds.push(data[i].Id);
|
||||
groupZones[data[i].Id] = [];
|
||||
var aDiv = document.createElement('div');
|
||||
aDiv.className = '';
|
||||
aDiv.id = 'group-' + data[i].Id;
|
||||
aDiv.innerHTML = data[i].Name;
|
||||
root_div.appendChild(aDiv);
|
||||
|
||||
// create table header with zones
|
||||
var aTable = document.createElement('table');
|
||||
aTable.id = 'table-' + data[i].Id;
|
||||
aTable.className = "styled-table";
|
||||
var thead = document.createElement('thead');
|
||||
aTable.appendChild(thead);
|
||||
var tr = document.createElement('tr');
|
||||
thead.appendChild(tr);
|
||||
var th1 = document.createElement('th');
|
||||
th1.innerHTML='Name';
|
||||
tr.appendChild(th1);
|
||||
var th2 = document.createElement('th');
|
||||
th2.innerHTML='E/A';
|
||||
tr.appendChild(th2);
|
||||
var th3 = document.createElement('th');
|
||||
th3.innerHTML='IMO';
|
||||
tr.appendChild(th3);
|
||||
|
||||
for(var j = 0; j < data[i].Zones.length; j++) {
|
||||
groupZones[data[i].Id].push(data[i].Zones[j].Id);
|
||||
var aTH = document.createElement('th');
|
||||
aTH.id = 'zone-' + data[i].Zones[j].Id;
|
||||
aTH.innerHTML = data[i].Zones[j].Name;
|
||||
tr.appendChild(aTH);
|
||||
}
|
||||
|
||||
var th4 = document.createElement('th');
|
||||
th4.innerHTML='Nav. status';
|
||||
tr.appendChild(th4);
|
||||
var th5 = document.createElement('th');
|
||||
th5.innerHTML='MMSI';
|
||||
tr.appendChild(th5);
|
||||
var th6 = document.createElement('th');
|
||||
th6.innerHTML='Destination';
|
||||
tr.appendChild(th6);
|
||||
|
||||
tr.setAttribute("isActive", "true"); // set marker so it won't get deleted later
|
||||
|
||||
aTable.appendChild(document.createElement('tbody'));
|
||||
aDiv.appendChild(aTable);
|
||||
aDiv.appendChild(document.createElement('hr'));
|
||||
}
|
||||
|
||||
setInterval(function () { update(); }, 15000);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user