608 lines
28 KiB
C#
608 lines
28 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Threading.Tasks;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Data.SQLite;
|
|
using log4net;
|
|
using System.Collections.Concurrent;
|
|
|
|
namespace bsmd.AIS2Service
|
|
{
|
|
/// <summary>
|
|
/// This class handles the (short-time) storage of AIS targets with a limited
|
|
/// 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
|
|
{
|
|
#region Fields
|
|
|
|
private readonly SQLiteConnection _connection;
|
|
private Thread _thread;
|
|
private bool _stopFlag = false;
|
|
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>();
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
public AIS_SQLiteStorage (ConcurrentQueue<AIS_Target> inputQueue)
|
|
{
|
|
_inputQueue = inputQueue;
|
|
_connection = new SQLiteConnection(Properties.Settings.Default.SQLiteDBConnectionString);
|
|
_connection.Open();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region public methods
|
|
|
|
#region MonitorZone
|
|
|
|
/// <summary>
|
|
/// monitor zone loader func for the zone alarm watchdog (doesn't need groups or such)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public List<MonitorZone> LoadMonitorZones()
|
|
{
|
|
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
|
|
|
|
// load zones
|
|
string loadZoneString = "SELECT id, name, active, monitor_group_id, sequence FROM monitor_zone";
|
|
SQLiteCommand lzCmd = new SQLiteCommand(loadZoneString, _connection);
|
|
SQLiteDataReader reader = lzCmd.ExecuteReader();
|
|
if(reader.HasRows)
|
|
{
|
|
while(reader.Read())
|
|
{
|
|
int id = reader.GetInt32(0);
|
|
string name = reader.GetString(1);
|
|
MonitorZone mz = new MonitorZone(id, name);
|
|
mz.Active = reader.GetInt32(2) == 1;
|
|
mz.MonitorGroupId = reader.GetInt32(3);
|
|
mz.Sequence = reader.GetInt32(4);
|
|
monitorZones.Add(mz);
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
lvCmd.Parameters.Clear();
|
|
lvCmd.Parameters.AddWithValue("@ID", mz.Id);
|
|
reader = lvCmd.ExecuteReader();
|
|
if(reader.HasRows)
|
|
{
|
|
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();
|
|
}
|
|
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)
|
|
{
|
|
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();
|
|
}
|
|
laCmd.Dispose();
|
|
|
|
return monitorZones;
|
|
}
|
|
|
|
public bool Save(MonitorZone mZone)
|
|
{
|
|
if (mZone == null) return false;
|
|
|
|
if(mZone.Id <= 0)
|
|
{
|
|
// insert
|
|
string insertZoneString = $"INSERT INTO monitor_zone (name, active, monitor_group_id, sequence) VALUES ('{mZone.Name}', {mZone.Active}, {mZone.MonitorGroupId}, {mZone.Sequence})";
|
|
SQLiteCommand cmd = new SQLiteCommand(insertZoneString, _connection);
|
|
int insertedRows = cmd.ExecuteNonQuery();
|
|
cmd.Dispose();
|
|
mZone.Id = GetLastInsertId();
|
|
return insertedRows == 1;
|
|
}
|
|
else
|
|
{
|
|
// update
|
|
string updateZoneString = $"UPDATE monitor_zone SET name = '{mZone.Name}', active = '{mZone.Active}', sequence = '{mZone.Sequence}' WHERE id = {mZone.Id}'";
|
|
SQLiteCommand cmd = new SQLiteCommand(updateZoneString, _connection);
|
|
int updatedRows = cmd.ExecuteNonQuery();
|
|
cmd.Dispose();
|
|
return updatedRows == 1;
|
|
}
|
|
}
|
|
|
|
bool Delete(MonitorZone mZone)
|
|
{
|
|
if (mZone == null) return false;
|
|
string deleteAlarmString = $"DELETE FROM monitor_zone WHERE id = {mZone.Id}";
|
|
SQLiteCommand cmd = new SQLiteCommand(deleteAlarmString, _connection);
|
|
int affectedRows = cmd.ExecuteNonQuery();
|
|
return (affectedRows == 1);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Alarm
|
|
|
|
public List<Alarm> LoadAlarms(MonitorAssignment assignment)
|
|
{
|
|
List<Alarm> result = new List<Alarm>();
|
|
string loadAlarmString = "SELECT id, timestamp_first, timestamp_last, type, acknowledged FROM alarm WHERE zone_assignment_id = @ID";
|
|
SQLiteCommand laCmd = new SQLiteCommand(loadAlarmString, _connection);
|
|
laCmd.Parameters.AddWithValue("@ID", assignment.Id);
|
|
SQLiteDataReader reader = laCmd.ExecuteReader();
|
|
if (reader.HasRows)
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
int id = reader.GetInt32(0);
|
|
Alarm alarm = new Alarm(id, assignment);
|
|
alarm.Timestamp_First = reader.GetDateTime(1);
|
|
alarm.Timestamp_Last = reader.GetDateTime(2);
|
|
alarm.ZoneMonitorType = (MonitorAssignment.ZoneMonitorType)reader.GetInt32(3);
|
|
if(!reader.IsDBNull(4))
|
|
alarm.Acknowledged = reader.GetDateTime(4);
|
|
result.Add(alarm);
|
|
}
|
|
reader.Close();
|
|
}
|
|
laCmd.Dispose();
|
|
return result;
|
|
}
|
|
|
|
public bool Delete(Alarm alarm)
|
|
{
|
|
if (alarm == null) return false;
|
|
string deleteAlarmString = $"DELETE FROM alarm WHERE id = {alarm.Id}";
|
|
SQLiteCommand cmd = new SQLiteCommand(deleteAlarmString, _connection);
|
|
int affectedRows = cmd.ExecuteNonQuery();
|
|
return (affectedRows == 1);
|
|
}
|
|
|
|
public bool Save(Alarm alarm)
|
|
{
|
|
if (alarm == null) return false;
|
|
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})";
|
|
SQLiteCommand cmd = new SQLiteCommand(saveAlarmString, _connection);
|
|
int insertedRows = cmd.ExecuteNonQuery();
|
|
cmd.Dispose();
|
|
alarm.Id = GetLastInsertId();
|
|
return (insertedRows == 1);
|
|
}
|
|
else
|
|
{
|
|
// update
|
|
string updateAlarmString = $"UPDATE alarm SET acknowledged = {alarm.Acknowledged}, timestamp_last = '{alarm.Timestamp_Last}' WHERE id = {alarm.Id}";
|
|
SQLiteCommand cmd = new SQLiteCommand(updateAlarmString, _connection);
|
|
int updatedRows = cmd.ExecuteNonQuery();
|
|
cmd.Dispose();
|
|
return (updatedRows == 1);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MonitorGroup
|
|
|
|
public List<MonitorGroup> LoadGroups()
|
|
{
|
|
List<MonitorGroup> groups = new List<MonitorGroup>();
|
|
string loadGroupsString = "SELECT id, name from monitor_group ORDER BY name";
|
|
SQLiteCommand laCmd = new SQLiteCommand(loadGroupsString, _connection);
|
|
SQLiteDataReader reader = laCmd.ExecuteReader();
|
|
if (reader.HasRows)
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
int id = reader.GetInt32(0);
|
|
string name = "";
|
|
if(!reader.IsDBNull(1))
|
|
name = reader.GetString(1);
|
|
MonitorGroup mGroup = new MonitorGroup(id, name);
|
|
groups.Add(mGroup);
|
|
}
|
|
}
|
|
return groups;
|
|
}
|
|
|
|
public bool Delete(MonitorGroup mGroup)
|
|
{
|
|
if (mGroup == null) return false;
|
|
string deleteGroupString = $"DELETE FROM monitor_group WHERE id = {mGroup.Id}";
|
|
SQLiteCommand cmd = new SQLiteCommand(deleteGroupString, _connection);
|
|
int deletedGroups = cmd.ExecuteNonQuery();
|
|
cmd.Dispose();
|
|
return deletedGroups == 1;
|
|
}
|
|
|
|
public bool Save(MonitorGroup mGroup)
|
|
{
|
|
if(mGroup == null) return false;
|
|
if(mGroup.Id <= 0)
|
|
{
|
|
// insert
|
|
string saveGroupString = $"INSERT INTO monitor_group (name) VALUES ('{mGroup.Name}')";
|
|
SQLiteCommand cmd = new SQLiteCommand(saveGroupString, _connection);
|
|
int insertedRows = cmd.ExecuteNonQuery();
|
|
cmd.Dispose();
|
|
mGroup.Id = GetLastInsertId();
|
|
return (insertedRows == 1);
|
|
}
|
|
else
|
|
{
|
|
// update
|
|
string saveGroupString = $"UPDATE monitor_group SET name = '{mGroup.Name}' WHERE id = {mGroup.Id}";
|
|
SQLiteCommand cmd = new SQLiteCommand(saveGroupString, _connection);
|
|
int updatedRows = cmd.ExecuteNonQuery();
|
|
cmd.Dispose();
|
|
return (updatedRows == 1);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ZoneVertex
|
|
|
|
public bool Save(GeoPoint geoPoint)
|
|
{
|
|
if (geoPoint == null) return false;
|
|
string saveGeoPointString = $"INSERT INTO zone_vertex (monitor_zone_id, latitude, longitude) VALUES ({geoPoint.MonitorZoneId}, {geoPoint.Lat.ToDotString()}, {geoPoint.Lon.ToDotString()})";
|
|
SQLiteCommand cmd = new SQLiteCommand(saveGeoPointString, _connection);
|
|
int insertedRows = cmd.ExecuteNonQuery();
|
|
cmd.Dispose();
|
|
return insertedRows == 1;
|
|
}
|
|
|
|
public List<GeoPoint> LoadVerticesForZone(long zoneId)
|
|
{
|
|
List<GeoPoint> vertices = new List<GeoPoint>();
|
|
string loadVerticesString = "SELECT id, monitor_zone_id, latitude, longitude FROM zone_vertex WHERE monitor_zone_id = @ID";
|
|
SQLiteCommand laCmd = new SQLiteCommand(loadVerticesString, _connection);
|
|
laCmd.Parameters.AddWithValue("@ID", zoneId);
|
|
SQLiteDataReader reader = laCmd.ExecuteReader();
|
|
if (reader.HasRows)
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
long id = reader.GetInt64(0);
|
|
GeoPoint gp = new GeoPoint(id);
|
|
gp.MonitorZoneId = reader.GetInt64(1);
|
|
gp.Lat = reader.GetDouble(2);
|
|
gp.Lon = reader.GetDouble(3);
|
|
vertices.Add(gp);
|
|
}
|
|
reader.Close();
|
|
}
|
|
laCmd.Dispose();
|
|
return vertices;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MonitorAssignment
|
|
|
|
public bool Save(MonitorAssignment assignment)
|
|
{
|
|
if (assignment == null) return false;
|
|
string saveGeoPointString = $"INSERT INTO zone_assignment (mmsi, monitor_zone_id, type) VALUES ({assignment.MMSI}, {assignment.MonitorZoneId}, {(int) assignment.MonitorType})";
|
|
SQLiteCommand cmd = new SQLiteCommand(saveGeoPointString, _connection);
|
|
int insertedRows = cmd.ExecuteNonQuery();
|
|
cmd.Dispose();
|
|
return insertedRows == 1;
|
|
}
|
|
|
|
public List<MonitorAssignment> LoadAssignmentsForZone(long zoneId)
|
|
{
|
|
List<MonitorAssignment> assignments = new List<MonitorAssignment>();
|
|
string loadAssignmentsString = "SELECT za.id, za.mmsi, za.monitor_zone_id, za.type FROM zone_assignment za WHERE za.monitor_zone_id = @ID";
|
|
SQLiteCommand laCmd = new SQLiteCommand(loadAssignmentsString, _connection);
|
|
laCmd.Parameters.AddWithValue("@ID", zoneId);
|
|
SQLiteDataReader reader = laCmd.ExecuteReader();
|
|
if (reader.HasRows)
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
long id = reader.GetInt64(0);
|
|
MonitorAssignment ma = new MonitorAssignment(id);
|
|
ma.MMSI = reader.GetInt32(1);
|
|
ma.MonitorZoneId = reader.GetInt64(2);
|
|
ma.MonitorType = (MonitorAssignment.ZoneMonitorType)reader.GetInt32(3);
|
|
assignments.Add(ma);
|
|
}
|
|
reader.Close();
|
|
}
|
|
laCmd.Dispose();
|
|
return assignments;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region private methods
|
|
|
|
private void ReadQueue()
|
|
{
|
|
try
|
|
{
|
|
|
|
// we create "common" commands and just reload the parameters after each shot :P
|
|
string updateStaticString = "UPDATE staticdata SET destination = ?, eta = ?, version = ?, imo = ?, callsign = ?, name = ?, shiptype = ?, typeofdevice = ?, maxpresetstaticdraught = ?, dte = ?, classb = ?, breadth = ?, length = ? WHERE mmsi = ?";
|
|
SQLiteCommand updateStaticCmd = new SQLiteCommand(updateStaticString, _connection);
|
|
updateStaticCmd.Parameters.Add("DESTINATION", DbType.String);
|
|
updateStaticCmd.Parameters.Add("ETA", DbType.DateTime);
|
|
updateStaticCmd.Parameters.Add("VERSION", DbType.Int32);
|
|
updateStaticCmd.Parameters.Add("IMO", DbType.Int32);
|
|
updateStaticCmd.Parameters.Add("CALLSIGN", DbType.String);
|
|
updateStaticCmd.Parameters.Add("NAME", DbType.String);
|
|
updateStaticCmd.Parameters.Add("SHIPTYPE", DbType.Int32);
|
|
updateStaticCmd.Parameters.Add("TYPEOFDEVICE", DbType.Int32);
|
|
updateStaticCmd.Parameters.Add("DRAUGHT", DbType.Int32);
|
|
updateStaticCmd.Parameters.Add("DTE", DbType.Boolean);
|
|
updateStaticCmd.Parameters.Add("CLASSB", DbType.Boolean);
|
|
updateStaticCmd.Parameters.Add("BREADTH", DbType.Int32);
|
|
updateStaticCmd.Parameters.Add("LENGTH", DbType.Int32);
|
|
updateStaticCmd.Parameters.Add("MMSI", DbType.Int32);
|
|
|
|
string insertStaticString = "INSERT INTO staticdata (mmsi, version, imo, callsign, name, shiptype, typeofdevice, maxpresetstaticdraught, destination, dte, classb, breadth, length, eta) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
SQLiteCommand insertStaticCmd = new SQLiteCommand(insertStaticString, _connection);
|
|
insertStaticCmd.Parameters.Add("MMSI", DbType.Int32);
|
|
insertStaticCmd.Parameters.Add("VERSION", DbType.Int32);
|
|
insertStaticCmd.Parameters.Add("IMO", DbType.Int32);
|
|
insertStaticCmd.Parameters.Add("CALLSIGN", DbType.String);
|
|
insertStaticCmd.Parameters.Add("NAME", DbType.String);
|
|
insertStaticCmd.Parameters.Add("SHIPTYPE", DbType.Int32);
|
|
insertStaticCmd.Parameters.Add("TYPEOFDEVICE", DbType.Int32);
|
|
insertStaticCmd.Parameters.Add("DRAUGHT", DbType.Int32);
|
|
insertStaticCmd.Parameters.Add("DESTINATION", DbType.String);
|
|
insertStaticCmd.Parameters.Add("DTE", DbType.Boolean);
|
|
insertStaticCmd.Parameters.Add("CLASSB", DbType.Boolean);
|
|
insertStaticCmd.Parameters.Add("BREADTH", DbType.Int32);
|
|
insertStaticCmd.Parameters.Add("LENGTH", DbType.Int32);
|
|
insertStaticCmd.Parameters.Add("ETA", DbType.DateTime);
|
|
|
|
string insertPosReportString = "INSERT INTO posreport (mmsi, navstatus, rot, cog, sog, accuracy, longitude, latitude, heading, timestamp, raim, commstate) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
SQLiteCommand insertPosReportCmd = new SQLiteCommand(insertPosReportString, _connection);
|
|
insertPosReportCmd.Parameters.Add("MMSI", DbType.Int32);
|
|
insertPosReportCmd.Parameters.Add("NAVSTATUS", DbType.Int32);
|
|
insertPosReportCmd.Parameters.Add("ROT", DbType.Int32);
|
|
insertPosReportCmd.Parameters.Add("COG", DbType.Int32);
|
|
insertPosReportCmd.Parameters.Add("SOG", DbType.Int32);
|
|
insertPosReportCmd.Parameters.Add("ACCURACY", DbType.Boolean);
|
|
insertPosReportCmd.Parameters.Add("LONGITUDE", DbType.Int32);
|
|
insertPosReportCmd.Parameters.Add("LATITUDE", DbType.Int32);
|
|
insertPosReportCmd.Parameters.Add("HEADING", DbType.Int32);
|
|
insertPosReportCmd.Parameters.Add("TIMESTAMP", DbType.DateTime);
|
|
insertPosReportCmd.Parameters.Add("RAIM", DbType.Boolean);
|
|
insertPosReportCmd.Parameters.Add("COMMSTATE", DbType.Int32);
|
|
|
|
while (!_stopFlag)
|
|
{
|
|
if(_inputQueue.TryDequeue(out AIS_Target target))
|
|
{
|
|
|
|
|
|
if(!_storageTargets.ContainsKey(target.MMSI))
|
|
{
|
|
// insert
|
|
insertStaticCmd.Parameters["MMSI"].Value = target.MMSI;
|
|
insertStaticCmd.Parameters["VERSION"].Value = target.AISVersion ?? (object)DBNull.Value;
|
|
insertStaticCmd.Parameters["IMO"].Value = target.IMO ?? (object) DBNull.Value;
|
|
insertStaticCmd.Parameters["CALLSIGN"].Value = target.Callsign ?? "";
|
|
insertStaticCmd.Parameters["NAME"].Value = target.Name ?? "";
|
|
insertStaticCmd.Parameters["SHIPTYPE"].Value = target.ShipType ?? (object) DBNull.Value;
|
|
insertStaticCmd.Parameters["TYPEOFDEVICE"].Value = target.TypeOfDevice;
|
|
if (target.Draught.HasValue)
|
|
insertStaticCmd.Parameters["DRAUGHT"].Value = (int) (target.Draught * 10);
|
|
else
|
|
insertStaticCmd.Parameters["DRAUGHT"].Value = DBNull.Value;
|
|
insertStaticCmd.Parameters["DESTINATION"].Value = target.Destination ?? "";
|
|
insertStaticCmd.Parameters["DTE"].Value = target.DTE ?? (object)DBNull.Value ;
|
|
insertStaticCmd.Parameters["CLASSB"].Value = target.IsClassB ?? (object)DBNull.Value;
|
|
insertStaticCmd.Parameters["BREADTH"].Value = target.Breadth ?? (object)DBNull.Value;
|
|
insertStaticCmd.Parameters["LENGTH"].Value = target.Length ?? (object)DBNull.Value;
|
|
insertStaticCmd.Parameters["ETA"].Value = target.ETA ?? (object)DBNull.Value;
|
|
|
|
if (_stopFlag) break;
|
|
if (insertStaticCmd.ExecuteNonQuery() != 1)
|
|
{
|
|
_log.WarnFormat("Query didn't affect any rows");
|
|
}
|
|
_storageTargets.Add(target.MMSI, target);
|
|
|
|
}
|
|
else
|
|
{
|
|
// update
|
|
updateStaticCmd.Parameters["DESTINATION"].Value = target.Destination ?? "";
|
|
updateStaticCmd.Parameters["ETA"].Value = target.ETA ?? (object)DBNull.Value;
|
|
updateStaticCmd.Parameters["MMSI"].Value = target.MMSI;
|
|
updateStaticCmd.Parameters["VERSION"].Value = target.AISVersion ?? (object)DBNull.Value;
|
|
updateStaticCmd.Parameters["IMO"].Value = target.IMO ?? (object)DBNull.Value;
|
|
updateStaticCmd.Parameters["CALLSIGN"].Value = target.Callsign ?? "";
|
|
updateStaticCmd.Parameters["NAME"].Value = target.Name ?? "";
|
|
updateStaticCmd.Parameters["SHIPTYPE"].Value = target.ShipType ?? (object)DBNull.Value;
|
|
updateStaticCmd.Parameters["TYPEOFDEVICE"].Value = target.TypeOfDevice ?? (object)DBNull.Value;
|
|
if (target.Draught.HasValue)
|
|
updateStaticCmd.Parameters["DRAUGHT"].Value = (int) (target.Draught * 10);
|
|
else
|
|
updateStaticCmd.Parameters["DRAUGHT"].Value = DBNull.Value;
|
|
updateStaticCmd.Parameters["DTE"].Value = target.DTE ?? (object)DBNull.Value;
|
|
updateStaticCmd.Parameters["CLASSB"].Value = target.IsClassB ?? (object)DBNull.Value;
|
|
updateStaticCmd.Parameters["BREADTH"].Value = target.Breadth ?? (object) DBNull.Value;
|
|
updateStaticCmd.Parameters["LENGTH"].Value = target.Length ?? (object)DBNull.Value;
|
|
if (_stopFlag) break;
|
|
if (updateStaticCmd.ExecuteNonQuery() != 1)
|
|
{
|
|
_log.WarnFormat("Query didn't affect any rows");
|
|
}
|
|
}
|
|
|
|
// now create a position report
|
|
insertPosReportCmd.Parameters["MMSI"].Value = target.MMSI;
|
|
insertPosReportCmd.Parameters["NAVSTATUS"].Value = target.NavStatus;
|
|
insertPosReportCmd.Parameters["ROT"].Value = target.ROT;
|
|
if(target.COG.HasValue)
|
|
insertPosReportCmd.Parameters["COG"].Value = (int) (target.COG * 10);
|
|
else
|
|
insertPosReportCmd.Parameters["COG"].Value = DBNull.Value;
|
|
if (target.SOG.HasValue)
|
|
insertPosReportCmd.Parameters["SOG"].Value = (int) (target.SOG * 10);
|
|
else
|
|
insertPosReportCmd.Parameters["SOG"].Value = DBNull.Value;
|
|
insertPosReportCmd.Parameters["ACCURACY"].Value = target.Accuracy;
|
|
if (target.Longitude.HasValue)
|
|
insertPosReportCmd.Parameters["LONGITUDE"].Value = (int)(target.Longitude.Value * 1000);
|
|
else
|
|
insertPosReportCmd.Parameters["LONGITUDE"].Value = DBNull.Value;
|
|
if (target.Latitude.HasValue)
|
|
insertPosReportCmd.Parameters["LATITUDE"].Value = (int)(target.Latitude.Value * 1000);
|
|
else
|
|
insertPosReportCmd.Parameters["LATITUDE"].Value = DBNull.Value;
|
|
insertPosReportCmd.Parameters["HEADING"].Value = target.Heading;
|
|
insertPosReportCmd.Parameters["TIMESTAMP"].Value = target.LastUpdate;
|
|
insertPosReportCmd.Parameters["RAIM"].Value = target.RAIM;
|
|
insertPosReportCmd.Parameters["COMMSTATE"].Value = target.Radio;
|
|
if (_stopFlag) break;
|
|
if (insertPosReportCmd.ExecuteNonQuery() != 1)
|
|
{
|
|
_log.WarnFormat("Query didn't affect any rows");
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Thread.Sleep(Properties.Settings.Default.ThreadSleepMS);
|
|
}
|
|
}
|
|
}
|
|
|
|
catch (Exception ex)
|
|
{
|
|
_log.ErrorFormat("Something bad has happened: {0}", ex.Message);
|
|
if(!_stopFlag)
|
|
this.FatalErrorOccurred?.Invoke(this, new EventArgs());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// pre-load all targets already stored in the sqlite database
|
|
/// </summary>
|
|
public async Task<Dictionary<int, AIS_Target>> LoadTargets()
|
|
{
|
|
await Task.Run(() =>
|
|
{
|
|
string staticQuery = "SELECT mmsi, version, imo, callsign, name, shiptype, typeofdevice, maxpresetstaticdraught, destination, dte, classb, breadth, length, eta, changed, updatecount FROM staticdata";
|
|
SQLiteCommand cmd = new SQLiteCommand(staticQuery, _connection);
|
|
SQLiteDataReader reader = cmd.ExecuteReader();
|
|
while(reader.Read())
|
|
{
|
|
AIS_Target target = AIS_Target.CreateFromReader(reader);
|
|
_storageTargets.Add(target.MMSI, target);
|
|
}
|
|
reader.Close();
|
|
cmd.Dispose();
|
|
});
|
|
|
|
return _storageTargets;
|
|
}
|
|
|
|
/// <summary>
|
|
/// cleanup DB by removing past pos reports
|
|
/// </summary>
|
|
public void RemoveOldPosReports()
|
|
{
|
|
try
|
|
{
|
|
string removeQuery = string.Format("DELETE FROM posreport where timestamp <= datetime('now', '-{0} day')",
|
|
Properties.Settings.Default.PosReportDBCleanupDays);
|
|
SQLiteCommand cmd = new SQLiteCommand(removeQuery, _connection);
|
|
int rows = cmd.ExecuteNonQuery();
|
|
if(rows > 0)
|
|
_log.InfoFormat("Removed {0} position reports older than {1} days", rows, Properties.Settings.Default.PosReportDBCleanupDays);
|
|
cmd.Dispose();
|
|
}
|
|
catch(SQLiteException ex)
|
|
{
|
|
_log.ErrorFormat("Failed to delete stale position reports: {0}", ex.Message);
|
|
}
|
|
}
|
|
|
|
public long GetLastInsertId()
|
|
{
|
|
string sql = "SELECT last_insert_rowid()";
|
|
SQLiteCommand cmd = new SQLiteCommand(sql, _connection);
|
|
long lastID = (long) cmd.ExecuteScalar();
|
|
return lastID;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IAISThread implementation
|
|
|
|
public string Name { get { return "SQLite storage"; } }
|
|
|
|
public event EventHandler FatalErrorOccurred;
|
|
|
|
public void Start()
|
|
{
|
|
if (_thread != null) return; // may not run twice
|
|
if (_inputQueue == null) return; // run thread only if we have a queue
|
|
ThreadStart runReader = new ThreadStart(this.ReadQueue);
|
|
_thread = new Thread(runReader);
|
|
_thread.Start();
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (_thread == null) return;
|
|
_stopFlag = true;
|
|
_thread.Join();
|
|
_thread = null;
|
|
_connection.Close();
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|