257 lines
14 KiB
C#
257 lines
14 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.
|
|
/// </summary>
|
|
internal 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 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);
|
|
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();
|
|
});
|
|
|
|
return _storageTargets;
|
|
}
|
|
|
|
#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
|
|
ThreadStart runReader = new ThreadStart(this.ReadQueue);
|
|
_thread = new Thread(runReader);
|
|
_thread.Start();
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (_thread == null) return;
|
|
_connection.Close();
|
|
_stopFlag = true;
|
|
_thread.Join();
|
|
_thread = null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|