SitRep Liste wird mit pos. und stat. Daten (A-Targets) aktualisiert
This commit is contained in:
parent
e8a3e6de16
commit
c1337e3134
@ -119,7 +119,7 @@ namespace bsmd.AIS2Service
|
||||
if(aisStatus == AISClass.Status.OK)
|
||||
{
|
||||
_outputAISClasses.Enqueue(decodedClass);
|
||||
_log.DebugFormat("Enqueuing {0} message for MMSI {1}", decodedClass.MessageType, decodedClass.MMSI);
|
||||
// _log.DebugFormat("Enqueuing {0} message for MMSI {1}", decodedClass.MessageType, decodedClass.MMSI);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -4,6 +4,7 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
@ -14,18 +15,41 @@ namespace bsmd.AIS2Service
|
||||
private static readonly ConcurrentQueue<string> _inputLines = new ConcurrentQueue<string>();
|
||||
private static readonly ConcurrentQueue<AISClass> _decodedClasses = new ConcurrentQueue<AISClass>();
|
||||
private static readonly ILog _log = LogManager.GetLogger(typeof(AISManager));
|
||||
private static readonly List<AIS_Target> _sitRepList = new List<AIS_Target>();
|
||||
private static readonly ConcurrentDictionary<int, AIS_Target> _sitRepList = new ConcurrentDictionary<int, AIS_Target>();
|
||||
private static Timer _staleTargetTimer;
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
_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));
|
||||
|
||||
foreach (var task in _tasks)
|
||||
{
|
||||
task.Start();
|
||||
_log.InfoFormat("{0} started", task.Name);
|
||||
}
|
||||
|
||||
_staleTargetTimer = new Timer(staleTimerCheck, null, 0, 60000); // check every minute
|
||||
|
||||
}
|
||||
|
||||
private static void staleTimerCheck(object state)
|
||||
{
|
||||
List<int> removeKeyList = new List<int>();
|
||||
foreach (int key in _sitRepList.Keys)
|
||||
{
|
||||
if (!_sitRepList[key].LastUpdate.HasValue) { removeKeyList.Add(key); }
|
||||
else
|
||||
{
|
||||
if ((DateTime.Now - _sitRepList[key].LastUpdate.Value).TotalMinutes > Properties.Settings.Default.StaleTargetTimeoutMins)
|
||||
removeKeyList.Add(key);
|
||||
}
|
||||
}
|
||||
foreach(int key in removeKeyList)
|
||||
{
|
||||
_sitRepList.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
|
||||
@ -70,17 +70,23 @@ namespace bsmd.AIS2Service
|
||||
{
|
||||
Status result = Status.OK;
|
||||
BitArray bits = DecodeBinary(_data);
|
||||
uint year = 0;
|
||||
uint month = 0;
|
||||
uint day = 0;
|
||||
uint hour = 0;
|
||||
uint minute = 0;
|
||||
uint second = 0;
|
||||
|
||||
try
|
||||
{
|
||||
_repeatIndicator = GetUInt(bits, 6, 7);
|
||||
_mmsi = GetInt(bits, 8, 37);
|
||||
uint year = GetUInt(bits, 38, 51);
|
||||
uint month = GetUInt(bits, 52, 55);
|
||||
uint day = GetUInt(bits, 56, 60);
|
||||
uint hour = GetUInt(bits, 61, 65);
|
||||
uint minute = GetUInt(bits, 66, 71);
|
||||
uint second = GetUInt(bits, 72, 77);
|
||||
year = GetUInt(bits, 38, 51);
|
||||
month = GetUInt(bits, 52, 55);
|
||||
day = GetUInt(bits, 56, 60);
|
||||
hour = GetUInt(bits, 61, 65);
|
||||
minute = GetUInt(bits, 66, 71);
|
||||
second = GetUInt(bits, 72, 77);
|
||||
_utcTimestamp = new DateTime((int) year, (int) month, (int) day, (int) hour, (int) minute, (int) second, DateTimeKind.Utc);
|
||||
_accuracy = GetInt(bits, 78, 78) == 1;
|
||||
_longitude = GetInt(bits, 79, 106);
|
||||
@ -90,6 +96,11 @@ namespace bsmd.AIS2Service
|
||||
_raim = GetInt(bits, 148, 148) == 1;
|
||||
_radio = GetUInt(bits, 149, 167);
|
||||
}
|
||||
catch(ArgumentOutOfRangeException)
|
||||
{
|
||||
_log.WarnFormat("Year: {0} Month: {1} Day: {2} Hour: {3} Minute: {4} Second: {5}", year, month, day, hour, minute, second);
|
||||
result = Status.PARSE_ERROR;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.WarnFormat("Error decoding AIS base station report: {0}", e.Message);
|
||||
|
||||
@ -38,11 +38,6 @@ namespace bsmd.AIS2Service
|
||||
get { return this.name; }
|
||||
}
|
||||
|
||||
public string ShipType
|
||||
{
|
||||
get { return AIS_StaticData.GetShipType(this.shipType); }
|
||||
}
|
||||
|
||||
public string VendorId
|
||||
{
|
||||
get { return this.vendorId; }
|
||||
|
||||
@ -18,7 +18,7 @@ namespace bsmd.AIS2Service
|
||||
private int trueheading;
|
||||
private DateTime timestamp;
|
||||
private int utcTimeSecond;
|
||||
private int reserved;
|
||||
private int maneuver_indicator;
|
||||
private int spare;
|
||||
private int raim;
|
||||
private int commstate;
|
||||
@ -104,7 +104,7 @@ namespace bsmd.AIS2Service
|
||||
}
|
||||
}
|
||||
|
||||
public int Reserved { get { return this.reserved; } }
|
||||
public int ManeuverIndicator { get { return this.maneuver_indicator; } }
|
||||
|
||||
public int Spare { get { return this.spare; } }
|
||||
|
||||
@ -173,8 +173,8 @@ namespace bsmd.AIS2Service
|
||||
this.cog = GetInt(bits, 116, 127);
|
||||
this.trueheading = GetInt(bits, 128, 136);
|
||||
this.utcTimeSecond = GetInt(bits, 137, 142);
|
||||
this.reserved = GetInt(bits, 143, 146);
|
||||
this.spare = GetInt(bits, 147, 147);
|
||||
this.maneuver_indicator = GetInt(bits, 143, 144);
|
||||
this.spare = GetInt(bits, 145, 147);
|
||||
this.raim = GetInt(bits, 148, 148);
|
||||
this.commstate = GetInt(bits, 149, 167);
|
||||
}
|
||||
|
||||
@ -40,8 +40,6 @@ namespace bsmd.AIS2Service
|
||||
|
||||
#region Properties
|
||||
|
||||
public int ShipTypeVal { get { return this.shiptype; } }
|
||||
|
||||
public string Callsign
|
||||
{
|
||||
get { return this.callsign; }
|
||||
@ -95,9 +93,9 @@ namespace bsmd.AIS2Service
|
||||
}
|
||||
}
|
||||
|
||||
public int Draught
|
||||
public double Draught
|
||||
{
|
||||
get { return this.maxpresetstaticdraught; }
|
||||
get { return this.maxpresetstaticdraught / 10.0; }
|
||||
}
|
||||
|
||||
public int Breadth
|
||||
@ -116,19 +114,11 @@ namespace bsmd.AIS2Service
|
||||
}
|
||||
}
|
||||
|
||||
public string ShipType
|
||||
{
|
||||
get
|
||||
{
|
||||
return AIS_StaticData.GetShipType(this.shiptype);
|
||||
}
|
||||
}
|
||||
public int ShipType { get { return this.shiptype; } }
|
||||
|
||||
public int DBShipType { get { return this.shiptype; } }
|
||||
public int Dimension { get { return this.dimension; } }
|
||||
|
||||
public int DBDimension { get { return this.dimension; } }
|
||||
|
||||
public int DBTypeOfDevice { get { return this.typeofdevice; } }
|
||||
public int TypeOfDevice { get { return this.typeofdevice; } }
|
||||
|
||||
public int DTE { get { return this.dte; } }
|
||||
|
||||
@ -149,6 +139,8 @@ namespace bsmd.AIS2Service
|
||||
}
|
||||
}
|
||||
|
||||
public int Version { get { return ais_version; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region abstract method implementation
|
||||
@ -253,116 +245,5 @@ namespace bsmd.AIS2Service
|
||||
|
||||
#endregion
|
||||
|
||||
#region public static methods
|
||||
|
||||
public static string GetShipType(int shiptype)
|
||||
{
|
||||
if (shiptype > 199) return "preserved for future use";
|
||||
if (shiptype > 99) return "preserved for regional use";
|
||||
int dig1, dig2;
|
||||
switch (shiptype)
|
||||
{
|
||||
case 50:
|
||||
return "Pilot vessel";
|
||||
case 51:
|
||||
return "SAR vessel";
|
||||
case 52:
|
||||
return "Tug";
|
||||
case 53:
|
||||
return "Port tender";
|
||||
case 54:
|
||||
return "Vessel with anti-pollution facility or equipment";
|
||||
case 55:
|
||||
return "Law enforcment vessel";
|
||||
case 56:
|
||||
return "Spare [local vessel]";
|
||||
case 57:
|
||||
return "Spare [local vessel]";
|
||||
case 58:
|
||||
return "Medical transport";
|
||||
case 59:
|
||||
return "Ship according to Resolution No. 18 (Mob-83)";
|
||||
default:
|
||||
{
|
||||
string comb = "";
|
||||
dig1 = shiptype / 10;
|
||||
dig2 = shiptype % 10;
|
||||
switch (dig1)
|
||||
{
|
||||
case 1:
|
||||
comb += "reserved for future use";
|
||||
break;
|
||||
case 2:
|
||||
comb += "WIG";
|
||||
break;
|
||||
case 3:
|
||||
comb += "Vessel";
|
||||
switch (dig2)
|
||||
{
|
||||
case 0:
|
||||
comb += " Fishing"; break;
|
||||
case 1:
|
||||
comb += " Towing"; break;
|
||||
case 2:
|
||||
comb += " Towing and length of tow exceeds 200m or breadth exceeds 25m"; break;
|
||||
case 3:
|
||||
comb += " Engaged in dredging or underwater operations"; break;
|
||||
case 4:
|
||||
comb += " Engaged in diving operations"; break;
|
||||
case 5:
|
||||
comb += " Engaged in military operations"; break;
|
||||
case 6:
|
||||
comb += " Sailing"; break;
|
||||
case 7:
|
||||
comb += " Pleasure craft"; break;
|
||||
default:
|
||||
comb += " reserved for future use";
|
||||
break;
|
||||
}
|
||||
return comb;
|
||||
case 4:
|
||||
comb += "HSC";
|
||||
break;
|
||||
case 6:
|
||||
comb += "Passenger ship";
|
||||
break;
|
||||
case 7:
|
||||
comb += "Cargo ship";
|
||||
break;
|
||||
case 8:
|
||||
comb += "Tanker";
|
||||
break;
|
||||
default:
|
||||
case 9:
|
||||
comb += "other";
|
||||
break;
|
||||
}
|
||||
switch (dig2)
|
||||
{
|
||||
case 0: break;
|
||||
case 1:
|
||||
comb += " carrying DG, HS or MP IMO hazard or pollutant category A"; break;
|
||||
case 2:
|
||||
comb += " carrying DG, HS or MP IMO hazard or pollutant category B"; break;
|
||||
case 3:
|
||||
comb += " carrying DG, HS or MP IMO hazard or pollutant category C"; break;
|
||||
case 4:
|
||||
comb += " carrying DG, HS or MP IMO hazard or pollutant category D"; break;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
comb += " reserved for future use"; break;
|
||||
case 9:
|
||||
comb += " no additional information"; break;
|
||||
}
|
||||
return comb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
@ -9,24 +7,34 @@ namespace bsmd.AIS2Service
|
||||
|
||||
#region private members
|
||||
|
||||
public static TimeSpan dbUpdateInterval = new TimeSpan(0, 2, 0); // neue Position in DB schreiben (min Interval)
|
||||
private int mmsi;
|
||||
private bool isClassB = false;
|
||||
private bool? isWatchkeeper = null;
|
||||
private DateTime? lastUpdate;
|
||||
private bool updateDB = false;
|
||||
private string name;
|
||||
private string station;
|
||||
private string lastDBName;
|
||||
private string callSign;
|
||||
private bool selected = false;
|
||||
private int _mmsi;
|
||||
private bool? _isClassB = false;
|
||||
private DateTime? _lastUpdate;
|
||||
private string _name;
|
||||
private string _callSign;
|
||||
private double? _latitude;
|
||||
private double? _longitude;
|
||||
private int? _heading;
|
||||
private double? _cog;
|
||||
private int? _rot;
|
||||
private double? _sog;
|
||||
private bool? _accuracy;
|
||||
private int? _maneuverIndicator;
|
||||
private bool? _raim;
|
||||
private int? _radio;
|
||||
private int? _imo;
|
||||
private int? _shipType;
|
||||
private int? _aisVersion;
|
||||
private int? _breadth;
|
||||
private int? _length;
|
||||
private int? _typeOfDevice;
|
||||
private DateTime? _eta;
|
||||
private double? _draught;
|
||||
private string _destination;
|
||||
private bool? _dte;
|
||||
|
||||
private AISClass staticInfo;
|
||||
private AISClass posReport;
|
||||
private AISClass lastAdditionalData;
|
||||
|
||||
private AIS_Target.Type type = Type.OTHER;
|
||||
private AIS_Target.NavStatus navStatus = AIS_Target.NavStatus.UNKNOWN;
|
||||
private AIS_Target.Type _type = Type.OTHER;
|
||||
private int _navStatus = 15; // not defined
|
||||
|
||||
#endregion
|
||||
|
||||
@ -44,23 +52,13 @@ namespace bsmd.AIS2Service
|
||||
OTHER
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// vereinfacht
|
||||
/// </summary>
|
||||
public enum NavStatus
|
||||
{
|
||||
UNKNOWN,
|
||||
UNDERWAY,
|
||||
MOORED
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public AIS_Target(int mmsi)
|
||||
{
|
||||
this.mmsi = mmsi;
|
||||
this._mmsi = mmsi;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -69,238 +67,180 @@ namespace bsmd.AIS2Service
|
||||
|
||||
public int MMSI
|
||||
{
|
||||
get { return this.mmsi; }
|
||||
get { return this._mmsi; }
|
||||
}
|
||||
|
||||
public DateTime? LastUpdate
|
||||
{
|
||||
get { return this.lastUpdate; }
|
||||
get { return this._lastUpdate; } private set { this._lastUpdate = value; }
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get {
|
||||
if ((this.name == null) || (this.name.Length == 0))
|
||||
return this.LastDBName;
|
||||
return this.name;
|
||||
}
|
||||
set { this.name = value; }
|
||||
get { return this._name; }
|
||||
private set { this._name = value; }
|
||||
}
|
||||
|
||||
public string Callsign
|
||||
{
|
||||
get { return this.callSign; }
|
||||
set { this.callSign = value; }
|
||||
}
|
||||
|
||||
public string LastDBName
|
||||
{
|
||||
get { return this.lastDBName; }
|
||||
set { this.lastDBName = value; }
|
||||
}
|
||||
|
||||
public string ReceivedFrom
|
||||
{
|
||||
get { return this.station; }
|
||||
}
|
||||
|
||||
internal AISClass LastPosReport
|
||||
{
|
||||
get { return this.posReport; }
|
||||
}
|
||||
|
||||
internal AISClass LastStaticData
|
||||
{
|
||||
get { return this.staticInfo; }
|
||||
get { return this._callSign; }
|
||||
private set { this._callSign = value; }
|
||||
}
|
||||
|
||||
public double? Latitude
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.LastPosReport == null) return null;
|
||||
if (this.LastPosReport is AIS_PosReport)
|
||||
return ((AIS_PosReport)this.LastPosReport).Latitude;
|
||||
if (this.LastPosReport is AIS_ClassB)
|
||||
return ((AIS_ClassB)this.LastPosReport).Latitude;
|
||||
if (this.LastPosReport is AIS_ClassBExt)
|
||||
return ((AIS_ClassBExt)this.LastPosReport).Latitude;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Type TargetType
|
||||
{
|
||||
get { return this.type; }
|
||||
set { this.type = value; }
|
||||
}
|
||||
|
||||
public NavStatus TargetNavStatus
|
||||
{
|
||||
get { return this.navStatus; }
|
||||
get { return this._latitude; } private set { this._latitude = value; }
|
||||
}
|
||||
|
||||
public double? Longitude
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.LastPosReport == null) return null;
|
||||
if (this.LastPosReport is AIS_PosReport)
|
||||
return ((AIS_PosReport)this.LastPosReport).Longitude;
|
||||
if (this.LastPosReport is AIS_ClassB)
|
||||
return ((AIS_ClassB)this.LastPosReport).Longitude;
|
||||
if (this.LastPosReport is AIS_ClassBExt)
|
||||
return ((AIS_ClassBExt)this.LastPosReport).Longitude;
|
||||
return null;
|
||||
get { return this._longitude; } private set { this._longitude = value; }
|
||||
}
|
||||
|
||||
public Type TargetType
|
||||
{
|
||||
get { return this._type; }
|
||||
set { this._type = value; }
|
||||
}
|
||||
|
||||
public int NavStatus
|
||||
{
|
||||
get { return this._navStatus; } private set { this._navStatus = value; }
|
||||
}
|
||||
|
||||
public bool? IsClassB
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.isClassB;
|
||||
}
|
||||
get { return this._isClassB; }
|
||||
private set { this._isClassB = value; }
|
||||
}
|
||||
|
||||
public int? Heading
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.LastPosReport == null) return null;
|
||||
if (this.LastPosReport is AIS_PosReport)
|
||||
return ((AIS_PosReport)this.LastPosReport).TrueHeading;
|
||||
if (this.LastPosReport is AIS_ClassB)
|
||||
return ((AIS_ClassB)this.LastPosReport).TrueHeading;
|
||||
if (this.LastPosReport is AIS_ClassBExt)
|
||||
return ((AIS_ClassBExt)this.LastPosReport).TrueHeading;
|
||||
return null;
|
||||
}
|
||||
get { return _heading; } private set { _heading = value; }
|
||||
}
|
||||
|
||||
public int? COG
|
||||
public double? COG
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.LastPosReport == null) return null;
|
||||
if (this.LastPosReport is AIS_PosReport)
|
||||
return (int)((AIS_PosReport)this.LastPosReport).COG;
|
||||
if (this.LastPosReport is AIS_ClassB)
|
||||
return (int)((AIS_ClassB)this.LastPosReport).Cog;
|
||||
if (this.LastPosReport is AIS_ClassBExt)
|
||||
return (int) ((AIS_ClassBExt)this.LastPosReport).Cog;
|
||||
return null;
|
||||
}
|
||||
get { return _cog; } private set { _cog = value; }
|
||||
}
|
||||
|
||||
public bool? IsWatchkeeperShip
|
||||
public int? ROT
|
||||
{
|
||||
get { return this.isWatchkeeper; }
|
||||
set { this.isWatchkeeper = value; }
|
||||
get { return _rot; } private set { _rot = value; }
|
||||
}
|
||||
|
||||
public bool Selected
|
||||
public double? SOG
|
||||
{
|
||||
get { return this.selected; }
|
||||
set { this.selected = value; }
|
||||
get { return _sog; } private set { _sog = value; }
|
||||
}
|
||||
|
||||
public string Station
|
||||
public bool? Accuracy
|
||||
{
|
||||
get { return this.station; }
|
||||
set { this.station = value; }
|
||||
get { return _accuracy; } private set { _accuracy = value; }
|
||||
}
|
||||
|
||||
public int? ManeuverIndicator
|
||||
{
|
||||
get { return _maneuverIndicator; } private set { _maneuverIndicator = value; }
|
||||
}
|
||||
|
||||
public bool? RAIM
|
||||
{
|
||||
get { return _raim; } private set { _raim = value; }
|
||||
}
|
||||
|
||||
public int? Radio
|
||||
{
|
||||
get { return _radio; } private set { _radio = value; }
|
||||
}
|
||||
|
||||
public int? AISVersion
|
||||
{
|
||||
get { return _aisVersion; } private set { _aisVersion = value; }
|
||||
}
|
||||
|
||||
public int? IMO
|
||||
{
|
||||
get { return _imo; } private set { _imo = value; }
|
||||
}
|
||||
|
||||
public int? ShipType
|
||||
{
|
||||
get { return _shipType; } private set { _shipType = value; }
|
||||
}
|
||||
|
||||
public int? Breadth
|
||||
{
|
||||
get { return _breadth; } private set { _breadth = value; }
|
||||
}
|
||||
|
||||
public int? Length
|
||||
{
|
||||
get { return _length; } private set { _length = value; }
|
||||
}
|
||||
|
||||
public int? TypeOfDevice
|
||||
{
|
||||
get { return _typeOfDevice; } private set { _typeOfDevice = value; }
|
||||
}
|
||||
|
||||
public DateTime? ETA
|
||||
{
|
||||
get { return _eta; } private set { _eta = value; }
|
||||
}
|
||||
|
||||
public double? Draught
|
||||
{
|
||||
get { return _draught; } private set { _draught = value; }
|
||||
}
|
||||
|
||||
public string Destination
|
||||
{
|
||||
get { return _destination; } private set { _destination = value; }
|
||||
}
|
||||
|
||||
public bool? DTE
|
||||
{
|
||||
get { return _dte; } private set { _dte = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region public methods
|
||||
|
||||
public static AIS_Target.NavStatus GetCurrentNavstatus(int status)
|
||||
internal void Update(AIS_PosReport posReport)
|
||||
{
|
||||
AIS_Target.NavStatus result = NavStatus.UNKNOWN;
|
||||
switch (status)
|
||||
{
|
||||
case 0:
|
||||
case 8:
|
||||
result = NavStatus.UNDERWAY;
|
||||
break;
|
||||
default:
|
||||
result = NavStatus.MOORED;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
this.NavStatus = posReport.NavStatusVal;
|
||||
this.ROT = posReport.ROT;
|
||||
this.SOG = posReport.SOG;
|
||||
this.Accuracy = (posReport.Accuracy == 1);
|
||||
this.Latitude = posReport.Latitude;
|
||||
this.Longitude = posReport.Longitude;
|
||||
this.COG = posReport.COG;
|
||||
this.Heading = posReport.TrueHeading;
|
||||
this.ManeuverIndicator = posReport.ManeuverIndicator;
|
||||
this.RAIM = posReport.Raim == 1;
|
||||
this.Radio = posReport.CommState;
|
||||
|
||||
this.LastUpdate = DateTime.Now;
|
||||
}
|
||||
|
||||
internal void Update(AISClass message)
|
||||
internal void Update(AIS_StaticData staticData)
|
||||
{
|
||||
this.AISVersion = staticData.Version;
|
||||
this.IMO = staticData.IMONumber;
|
||||
this.Callsign = staticData.Callsign;
|
||||
this.Name = staticData.Name;
|
||||
this.ShipType = staticData.ShipType;
|
||||
this.Breadth = staticData.Breadth;
|
||||
this.Length = staticData.Length;
|
||||
this.TypeOfDevice = staticData.TypeOfDevice;
|
||||
this.ETA = staticData.ETA;
|
||||
this.Draught = staticData.Draught;
|
||||
this.Destination = staticData.Destination;
|
||||
this.DTE = staticData.DTE == 0;
|
||||
|
||||
switch (message.MessageType)
|
||||
{
|
||||
case AISClass.AISType.POSITION_REPORT:
|
||||
case AISClass.AISType.POSITION_REPORT_ASSIGNED:
|
||||
case AISClass.AISType.POSITION_REPORT_SPECIAL:
|
||||
if ((this.lastUpdate.HasValue &&
|
||||
(((AIS_PosReport)message).Timestamp - this.lastUpdate.Value) > AIS_Target.dbUpdateInterval)
|
||||
|| (!this.lastUpdate.HasValue))
|
||||
{
|
||||
this.updateDB = true;
|
||||
this.lastUpdate = ((AIS_PosReport)message).Timestamp;
|
||||
}
|
||||
this.posReport = message;
|
||||
this.navStatus = AIS_Target.GetCurrentNavstatus(((AIS_PosReport)message).NavStatusVal);
|
||||
// System.Diagnostics.Trace.WriteLine(string.Format("pos report at {0}", this.lastUpdate));
|
||||
break;
|
||||
case AISClass.AISType.POSITION_REPORT_B_EQUIP:
|
||||
if ((this.lastUpdate.HasValue &&
|
||||
(((AIS_ClassB)message).Timestamp - this.lastUpdate.Value) > AIS_Target.dbUpdateInterval)
|
||||
|| (!this.lastUpdate.HasValue))
|
||||
{
|
||||
this.updateDB = true;
|
||||
this.lastUpdate = ((AIS_ClassB)message).Timestamp;
|
||||
this.isClassB = true;
|
||||
this.type = Type.YACHT;
|
||||
this.navStatus = NavStatus.UNDERWAY;
|
||||
}
|
||||
this.posReport = message;
|
||||
break;
|
||||
case AISClass.AISType.POSITION_REPORT_B_EQUIP_EXT:
|
||||
if ((this.lastUpdate.HasValue &&
|
||||
(((AIS_ClassBExt)message).Timestamp - this.lastUpdate.Value) > AIS_Target.dbUpdateInterval)
|
||||
|| (!this.lastUpdate.HasValue))
|
||||
{
|
||||
this.updateDB = true;
|
||||
this.lastUpdate = ((AIS_ClassBExt)message).Timestamp;
|
||||
this.isClassB = true;
|
||||
this.type = Type.YACHT;
|
||||
this.navStatus = NavStatus.UNDERWAY;
|
||||
}
|
||||
this.posReport = message;
|
||||
break;
|
||||
case AISClass.AISType.STATIC_VOYAGE_DATA:
|
||||
this.staticInfo = message;
|
||||
this.name = ((AIS_StaticData)message).Name;
|
||||
this.callSign = ((AIS_StaticData)message).Callsign;
|
||||
//this.type = AIS_StaticData.GetShipTypeSimple(((AIS_StaticData)message).ShipTypeVal);
|
||||
|
||||
break;
|
||||
case AISClass.AISType.CLASS_B_STATIC_DATA:
|
||||
if (((AIS_ClassBStatic)message).IsPartA)
|
||||
{
|
||||
this.name = ((AIS_ClassBStatic)message).Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.callSign = ((AIS_ClassBStatic)message).Callsign;
|
||||
}
|
||||
this.staticInfo = message;
|
||||
this.type = Type.YACHT;
|
||||
this.isClassB = true;
|
||||
break;
|
||||
default:
|
||||
this.lastAdditionalData = message;
|
||||
break;
|
||||
}
|
||||
|
||||
this.LastUpdate = DateTime.Now;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -40,6 +40,9 @@
|
||||
<setting name="DataSourcePort" serializeAs="String">
|
||||
<value>32100</value>
|
||||
</setting>
|
||||
<setting name="StaleTargetTimeoutMins" serializeAs="String">
|
||||
<value>60</value>
|
||||
</setting>
|
||||
</bsmd.AIS2Service.Properties.Settings>
|
||||
</applicationSettings>
|
||||
</configuration>
|
||||
164
AIS/bsmd.AIS2Service/Lookup.cs
Normal file
164
AIS/bsmd.AIS2Service/Lookup.cs
Normal file
@ -0,0 +1,164 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Class contains static AIS lookup tables (not likely to change..)
|
||||
/// </summary>
|
||||
internal static class Lookup
|
||||
{
|
||||
|
||||
#region Ship types
|
||||
|
||||
public static readonly Dictionary<int, string> ShipTypes = new Dictionary<int, string>
|
||||
{
|
||||
{0, "not available" },
|
||||
{1, "reserved" },
|
||||
{2, "reserved" },
|
||||
{3, "reserved" },
|
||||
{4, "reserved" },
|
||||
{5, "reserved" },
|
||||
{6, "reserved" },
|
||||
{7, "reserved" },
|
||||
{8, "reserved" },
|
||||
{9, "reserved" },
|
||||
{10, "reserved" },
|
||||
{11, "reserved" },
|
||||
{12, "reserved" },
|
||||
{13, "reserved" },
|
||||
{14, "reserved" },
|
||||
{15, "reserved" },
|
||||
{16, "reserved" },
|
||||
{17, "reserved" },
|
||||
{18, "reserved" },
|
||||
{19, "reserved" },
|
||||
{20, "WIG all types" },
|
||||
{21, "WIG Hazardous category A" },
|
||||
{22, "WIG Hazardous category B" },
|
||||
{23, "WIG Hazardous category C" },
|
||||
{24, "WIG Hazardous category D" },
|
||||
{25, "WIG reserved" },
|
||||
{26, "WIG reserved" },
|
||||
{27, "WIG reserved" },
|
||||
{28, "WIG reserved" },
|
||||
{29, "WIG reserved" },
|
||||
{30, "Fishing" },
|
||||
{31, "Towing" },
|
||||
{32, "Towing: Length>200m or Breadth>25m" },
|
||||
{33, "Dredging or underwater ops" },
|
||||
{34, "Diving ops" },
|
||||
{35, "Military ops" },
|
||||
{36, "Sailing" },
|
||||
{37, "Pleasure craft" },
|
||||
{38, "reserved" },
|
||||
{39, "reserved" },
|
||||
{40, "HSC, all types" },
|
||||
{41, "HSC Hazardous category A" },
|
||||
{42, "HSC Hazardous category B" },
|
||||
{43, "HSC Hazardous category C" },
|
||||
{44, "HSC Hazardous category D" },
|
||||
{45, "HSC reserved" },
|
||||
{46, "HSC reserved" },
|
||||
{47, "HSC reserved" },
|
||||
{48, "HSC reserved" },
|
||||
{49, "HSC no additional info" },
|
||||
{50, "Pilot" },
|
||||
{51, "SAR" },
|
||||
{52, "Tug" },
|
||||
{53, "Port tender" },
|
||||
{54, "Anti-pollution equipment" },
|
||||
{55, "Law enforcement" },
|
||||
{56, "Spare - local vessel" },
|
||||
{57, "Spare - local vessel" },
|
||||
{58, "Medical transport" },
|
||||
{59, "Noncombatant ship acc. Res. No. 18" },
|
||||
{60, "Passenger, all types" },
|
||||
{61, "Passenger Hazardous category A" },
|
||||
{62, "Passenger Hazardous category B" },
|
||||
{63, "Passenger Hazardous category C" },
|
||||
{64, "Passenger Hazardous category D" },
|
||||
{65, "Passenger reserved" },
|
||||
{66, "Passenger reserved" },
|
||||
{67, "Passenger reserved" },
|
||||
{68, "Passenger reserved" },
|
||||
{69, "Passenger no additional info" },
|
||||
{70, "Cargo, all types" },
|
||||
{71, "Cargo Hazardous category A" },
|
||||
{72, "Cargo Hazardous category B" },
|
||||
{73, "Cargo Hazardous category C" },
|
||||
{74, "Cargo Hazardous category D" },
|
||||
{75, "Cargo reserved" },
|
||||
{76, "Cargo reserved" },
|
||||
{77, "Cargo reserved" },
|
||||
{78, "Cargo reserved" },
|
||||
{79, "Cargo no additional info" },
|
||||
{80, "Tanker, all types" },
|
||||
{81, "Tanker Hazardous category A" },
|
||||
{82, "Tanker Hazardous category B" },
|
||||
{83, "Tanker Hazardous category C" },
|
||||
{84, "Tanker Hazardous category D" },
|
||||
{85, "Tanker reserved" },
|
||||
{86, "Tanker reserved" },
|
||||
{87, "Tanker reserved" },
|
||||
{88, "Tanker reserved" },
|
||||
{89, "Tanker no additional info" },
|
||||
{90, "Other type, all types" },
|
||||
{91, "Other type Hazardous category A" },
|
||||
{92, "Other type Hazardous category B" },
|
||||
{93, "Other type Hazardous category C" },
|
||||
{94, "Other type Hazardous category D" },
|
||||
{95, "Other type reserved" },
|
||||
{96, "Other type reserved" },
|
||||
{97, "Other type reserved" },
|
||||
{98, "Other type reserved" },
|
||||
{99, "Other type no additional info" },
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Navigation status
|
||||
|
||||
public static readonly Dictionary<int, string> NavStatus = new Dictionary<int, string>
|
||||
{
|
||||
{0, "Under way using engine" },
|
||||
{1, "At anchor" },
|
||||
{2, "Not under command" },
|
||||
{3, "Restricted manoeuverability" },
|
||||
{4, "Constrained by her draught" },
|
||||
{5, "Moored" },
|
||||
{6, "Aground" },
|
||||
{7, "Engaged in fishing" },
|
||||
{8, "Under way sailing" },
|
||||
{9, "Reserved for future amendment of Navigational Status for HSC" },
|
||||
{10, "Reserved for future amendment of Navigational Status for WIG" },
|
||||
{11, "Reserved for future use" },
|
||||
{12, "Reserved for future use" },
|
||||
{13, "Reserved for future use" },
|
||||
{14, "AIS-SART is active" },
|
||||
{15, "Not defined" }
|
||||
};
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Type of device
|
||||
|
||||
public static readonly Dictionary<int, string> DeviceTypes = new Dictionary<int, string>
|
||||
{
|
||||
{0, "Undefined" },
|
||||
{1, "GPS" },
|
||||
{2, "GLONASS" },
|
||||
{3, "Combined GPS/GLONASS" },
|
||||
{4, "Loran-C" },
|
||||
{5, "Chayka" },
|
||||
{6, "Integrated navigation system" },
|
||||
{7, "Surveyed" },
|
||||
{8, "Galileo" }
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@ -40,5 +40,14 @@ namespace bsmd.AIS2Service.Properties {
|
||||
return ((uint)(this["DataSourcePort"]));
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("60")]
|
||||
public uint StaleTargetTimeoutMins {
|
||||
get {
|
||||
return ((uint)(this["StaleTargetTimeoutMins"]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,5 +8,8 @@
|
||||
<Setting Name="DataSourcePort" Type="System.UInt32" Scope="Application">
|
||||
<Value Profile="(Default)">32100</Value>
|
||||
</Setting>
|
||||
<Setting Name="StaleTargetTimeoutMins" Type="System.UInt32" Scope="Application">
|
||||
<Value Profile="(Default)">60</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
111
AIS/bsmd.AIS2Service/SitRep.cs
Normal file
111
AIS/bsmd.AIS2Service/SitRep.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using log4net;
|
||||
|
||||
namespace bsmd.AIS2Service
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Manager / Thread Klasse um die "aktuelle" Situation zu aktualisieren und die Daten
|
||||
/// in die (Sqlite-Zwischen-) Datenbank zu schieben
|
||||
/// </summary>
|
||||
internal class SitRep : IAISThread
|
||||
{
|
||||
private Thread _thread;
|
||||
private bool _stopFlag = false;
|
||||
private static readonly ILog _log = LogManager.GetLogger(typeof(SitRep));
|
||||
private readonly ConcurrentQueue<AISClass> _inputQueue;
|
||||
private readonly ConcurrentDictionary<int, AIS_Target> _sitRep;
|
||||
private const int sleepMS = 250;
|
||||
|
||||
public SitRep(ConcurrentQueue<AISClass> inputQueue, ConcurrentDictionary<int, AIS_Target> sitRep)
|
||||
{
|
||||
_inputQueue = inputQueue; _sitRep = sitRep;
|
||||
}
|
||||
|
||||
private void ReadMessages()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!_stopFlag)
|
||||
{
|
||||
if(_inputQueue.TryDequeue(out AISClass aisMessage))
|
||||
{
|
||||
switch(aisMessage.MessageType)
|
||||
{
|
||||
case AISClass.AISType.POSITION_REPORT:
|
||||
case AISClass.AISType.POSITION_REPORT_ASSIGNED:
|
||||
case AISClass.AISType.POSITION_REPORT_SPECIAL:
|
||||
{
|
||||
AIS_PosReport posReport = aisMessage as AIS_PosReport;
|
||||
if(!_sitRep.ContainsKey(posReport.MMSI))
|
||||
{
|
||||
AIS_Target target = new AIS_Target(posReport.MMSI);
|
||||
_sitRep[posReport.MMSI] = target;
|
||||
}
|
||||
_sitRep[posReport.MMSI].Update(posReport);
|
||||
}
|
||||
break;
|
||||
case AISClass.AISType.STATIC_VOYAGE_DATA:
|
||||
{
|
||||
AIS_StaticData staticData = aisMessage as AIS_StaticData;
|
||||
if(!_sitRep.ContainsKey(staticData.MMSI))
|
||||
{
|
||||
AIS_Target target = new AIS_Target(staticData.MMSI);
|
||||
_sitRep[staticData.MMSI] = target;
|
||||
}
|
||||
_sitRep[staticData.MMSI].Update(staticData);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_log.InfoFormat("currently discarding AIS message {0}", aisMessage.MessageType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(sleepMS);
|
||||
_log.DebugFormat("Targets: {0}", _sitRep.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.ErrorFormat("Something bad has happened: {0}", ex.Message);
|
||||
this.FatalErrorOccurred?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
#region IAISThread implementation
|
||||
|
||||
public string Name { get { return "Sit rep"; } }
|
||||
|
||||
public event EventHandler FatalErrorOccurred;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_thread != null) return; // may not run twice
|
||||
ThreadStart runReader = new ThreadStart(this.ReadMessages);
|
||||
_thread = new Thread(runReader);
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (_thread == null) return;
|
||||
_stopFlag = true;
|
||||
_thread.Join();
|
||||
_thread = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@ -71,6 +71,7 @@
|
||||
<Compile Include="AIS_StaticData.cs" />
|
||||
<Compile Include="AIS_Target.cs" />
|
||||
<Compile Include="IAISThread.cs" />
|
||||
<Compile Include="Lookup.cs" />
|
||||
<Compile Include="NMEA.cs" />
|
||||
<Compile Include="NMEA_AIS_Sentence.cs" />
|
||||
<Compile Include="NMEA_PNMLS_Sentence.cs" />
|
||||
@ -82,6 +83,7 @@
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SerialTCPReader.cs" />
|
||||
<Compile Include="SitRep.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user