using log4net;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.Threading;
using System.Threading.Tasks;
namespace bsmd.AIS2Service
{
///
/// 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.
///
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 _inputQueue;
private readonly Dictionary _storageTargets = new Dictionary();
#endregion
#region Construction
public AIS_SQLiteStorage (ConcurrentQueue inputQueue)
{
_inputQueue = inputQueue;
_connection = new SQLiteConnection(Properties.Settings.Default.SQLiteDBConnectionString);
_connection.Open();
}
public AIS_SQLiteStorage(string connectionString)
{
_connection = new SQLiteConnection(connectionString);
_connection.Open();
}
#endregion
#region public methods
#region MonitorZone
///
/// monitor zone loader func for the zone alarm watchdog (doesn't need groups or such)
///
///
public List LoadMonitorZones(bool loadInnerCollections = true)
{
List monitorZones = new List();
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();
if (loadInnerCollections)
{
// 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 LoadAlarms(MonitorAssignment assignment)
{
List result = new List();
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);
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);
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) 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();
return (insertedRows == 1);
}
else
{
// update
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);
}
}
#endregion
#region MonitorGroup
public List LoadGroups()
{
List groups = new List();
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);
}
}
reader.Close();
laCmd.Dispose();
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 LoadVerticesForZone(long zoneId)
{
List vertices = new List();
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;
if (assignment.Id <= 0) // insert
{
string saveAssignmentString = $"INSERT INTO zone_assignment (mmsi, monitor_zone_id, type) VALUES ({assignment.MMSI}, {assignment.MonitorZoneId}, {(int)assignment.MonitorType})";
SQLiteCommand cmd = new SQLiteCommand(saveAssignmentString, _connection);
int insertedRows = cmd.ExecuteNonQuery();
cmd.Dispose();
return insertedRows == 1;
}
else // update
{
string saveAssignmentString = $"UPDATE zone_assignment SET type = {(int) assignment.MonitorType} WHERE id = {assignment.Id}";
SQLiteCommand cmd = new SQLiteCommand(saveAssignmentString, _connection);
int updatedRows = cmd.ExecuteNonQuery();
cmd.Dispose();
return updatedRows == 1;
}
}
public List LoadAssignmentsForZone(long zoneId)
{
List assignments = new List();
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
#region ShipLocationReport
///
/// 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)
///
public List GetShipLocationReports(long groupId)
{
List slrs = new List();
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
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());
}
}
///
/// pre-load all targets already stored in the sqlite database
///
public async Task> 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;
}
///
/// cleanup DB by removing past pos reports
///
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
#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
}
}