SitRep Liste wird mit pos. und stat. Daten (A-Targets) aktualisiert

This commit is contained in:
Daniel Schick 2022-10-12 09:27:13 +02:00
parent e8a3e6de16
commit c1337e3134
13 changed files with 505 additions and 362 deletions

View File

@ -119,7 +119,7 @@ namespace bsmd.AIS2Service
if(aisStatus == AISClass.Status.OK) if(aisStatus == AISClass.Status.OK)
{ {
_outputAISClasses.Enqueue(decodedClass); _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 else
{ {

View File

@ -4,6 +4,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace bsmd.AIS2Service namespace bsmd.AIS2Service
@ -14,18 +15,41 @@ namespace bsmd.AIS2Service
private static readonly ConcurrentQueue<string> _inputLines = new ConcurrentQueue<string>(); private static readonly ConcurrentQueue<string> _inputLines = new ConcurrentQueue<string>();
private static readonly ConcurrentQueue<AISClass> _decodedClasses = new ConcurrentQueue<AISClass>(); private static readonly ConcurrentQueue<AISClass> _decodedClasses = new ConcurrentQueue<AISClass>();
private static readonly ILog _log = LogManager.GetLogger(typeof(AISManager)); 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() public static void Start()
{ {
_tasks.Add(new SerialTCPReader(Properties.Settings.Default.DataSourceHost, Properties.Settings.Default.DataSourcePort, _inputLines)); _tasks.Add(new SerialTCPReader(Properties.Settings.Default.DataSourceHost, Properties.Settings.Default.DataSourcePort, _inputLines));
_tasks.Add(new AISDecoder(_inputLines, _decodedClasses)); _tasks.Add(new AISDecoder(_inputLines, _decodedClasses));
_tasks.Add(new SitRep(_decodedClasses, _sitRepList));
foreach (var task in _tasks) foreach (var task in _tasks)
{ {
task.Start(); task.Start();
_log.InfoFormat("{0} started", task.Name); _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() public static void Stop()

View File

@ -70,17 +70,23 @@ namespace bsmd.AIS2Service
{ {
Status result = Status.OK; Status result = Status.OK;
BitArray bits = DecodeBinary(_data); BitArray bits = DecodeBinary(_data);
uint year = 0;
uint month = 0;
uint day = 0;
uint hour = 0;
uint minute = 0;
uint second = 0;
try try
{ {
_repeatIndicator = GetUInt(bits, 6, 7); _repeatIndicator = GetUInt(bits, 6, 7);
_mmsi = GetInt(bits, 8, 37); _mmsi = GetInt(bits, 8, 37);
uint year = GetUInt(bits, 38, 51); year = GetUInt(bits, 38, 51);
uint month = GetUInt(bits, 52, 55); month = GetUInt(bits, 52, 55);
uint day = GetUInt(bits, 56, 60); day = GetUInt(bits, 56, 60);
uint hour = GetUInt(bits, 61, 65); hour = GetUInt(bits, 61, 65);
uint minute = GetUInt(bits, 66, 71); minute = GetUInt(bits, 66, 71);
uint second = GetUInt(bits, 72, 77); second = GetUInt(bits, 72, 77);
_utcTimestamp = new DateTime((int) year, (int) month, (int) day, (int) hour, (int) minute, (int) second, DateTimeKind.Utc); _utcTimestamp = new DateTime((int) year, (int) month, (int) day, (int) hour, (int) minute, (int) second, DateTimeKind.Utc);
_accuracy = GetInt(bits, 78, 78) == 1; _accuracy = GetInt(bits, 78, 78) == 1;
_longitude = GetInt(bits, 79, 106); _longitude = GetInt(bits, 79, 106);
@ -90,6 +96,11 @@ namespace bsmd.AIS2Service
_raim = GetInt(bits, 148, 148) == 1; _raim = GetInt(bits, 148, 148) == 1;
_radio = GetUInt(bits, 149, 167); _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) catch (Exception e)
{ {
_log.WarnFormat("Error decoding AIS base station report: {0}", e.Message); _log.WarnFormat("Error decoding AIS base station report: {0}", e.Message);

View File

@ -38,11 +38,6 @@ namespace bsmd.AIS2Service
get { return this.name; } get { return this.name; }
} }
public string ShipType
{
get { return AIS_StaticData.GetShipType(this.shipType); }
}
public string VendorId public string VendorId
{ {
get { return this.vendorId; } get { return this.vendorId; }

View File

@ -18,7 +18,7 @@ namespace bsmd.AIS2Service
private int trueheading; private int trueheading;
private DateTime timestamp; private DateTime timestamp;
private int utcTimeSecond; private int utcTimeSecond;
private int reserved; private int maneuver_indicator;
private int spare; private int spare;
private int raim; private int raim;
private int commstate; 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; } } public int Spare { get { return this.spare; } }
@ -173,8 +173,8 @@ namespace bsmd.AIS2Service
this.cog = GetInt(bits, 116, 127); this.cog = GetInt(bits, 116, 127);
this.trueheading = GetInt(bits, 128, 136); this.trueheading = GetInt(bits, 128, 136);
this.utcTimeSecond = GetInt(bits, 137, 142); this.utcTimeSecond = GetInt(bits, 137, 142);
this.reserved = GetInt(bits, 143, 146); this.maneuver_indicator = GetInt(bits, 143, 144);
this.spare = GetInt(bits, 147, 147); this.spare = GetInt(bits, 145, 147);
this.raim = GetInt(bits, 148, 148); this.raim = GetInt(bits, 148, 148);
this.commstate = GetInt(bits, 149, 167); this.commstate = GetInt(bits, 149, 167);
} }

View File

@ -40,8 +40,6 @@ namespace bsmd.AIS2Service
#region Properties #region Properties
public int ShipTypeVal { get { return this.shiptype; } }
public string Callsign public string Callsign
{ {
get { return this.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 public int Breadth
@ -116,19 +114,11 @@ namespace bsmd.AIS2Service
} }
} }
public string ShipType public int ShipType { get { return this.shiptype; } }
{
get
{
return AIS_StaticData.GetShipType(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 TypeOfDevice { get { return this.typeofdevice; } }
public int DBTypeOfDevice { get { return this.typeofdevice; } }
public int DTE { get { return this.dte; } } public int DTE { get { return this.dte; } }
@ -149,6 +139,8 @@ namespace bsmd.AIS2Service
} }
} }
public int Version { get { return ais_version; } }
#endregion #endregion
#region abstract method implementation #region abstract method implementation
@ -253,116 +245,5 @@ namespace bsmd.AIS2Service
#endregion #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
} }
} }

View File

@ -1,6 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
namespace bsmd.AIS2Service namespace bsmd.AIS2Service
{ {
@ -9,24 +7,34 @@ namespace bsmd.AIS2Service
#region private members #region private members
public static TimeSpan dbUpdateInterval = new TimeSpan(0, 2, 0); // neue Position in DB schreiben (min Interval) private int _mmsi;
private int mmsi; private bool? _isClassB = false;
private bool isClassB = false; private DateTime? _lastUpdate;
private bool? isWatchkeeper = null; private string _name;
private DateTime? lastUpdate; private string _callSign;
private bool updateDB = false; private double? _latitude;
private string name; private double? _longitude;
private string station; private int? _heading;
private string lastDBName; private double? _cog;
private string callSign; private int? _rot;
private bool selected = false; 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 AIS_Target.Type _type = Type.OTHER;
private AISClass posReport; private int _navStatus = 15; // not defined
private AISClass lastAdditionalData;
private AIS_Target.Type type = Type.OTHER;
private AIS_Target.NavStatus navStatus = AIS_Target.NavStatus.UNKNOWN;
#endregion #endregion
@ -44,23 +52,13 @@ namespace bsmd.AIS2Service
OTHER OTHER
} }
/// <summary>
/// vereinfacht
/// </summary>
public enum NavStatus
{
UNKNOWN,
UNDERWAY,
MOORED
}
#endregion #endregion
#region Construction #region Construction
public AIS_Target(int mmsi) public AIS_Target(int mmsi)
{ {
this.mmsi = mmsi; this._mmsi = mmsi;
} }
#endregion #endregion
@ -69,238 +67,180 @@ namespace bsmd.AIS2Service
public int MMSI public int MMSI
{ {
get { return this.mmsi; } get { return this._mmsi; }
} }
public DateTime? LastUpdate public DateTime? LastUpdate
{ {
get { return this.lastUpdate; } get { return this._lastUpdate; } private set { this._lastUpdate = value; }
} }
public string Name public string Name
{ {
get { get { return this._name; }
if ((this.name == null) || (this.name.Length == 0)) private set { this._name = value; }
return this.LastDBName;
return this.name;
}
set { this.name = value; }
} }
public string Callsign public string Callsign
{ {
get { return this.callSign; } get { return this._callSign; }
set { this.callSign = value; } private 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; }
} }
public double? Latitude public double? Latitude
{ {
get get { return this._latitude; } private set { this._latitude = value; }
{
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; }
} }
public double? Longitude public double? Longitude
{ {
get get { return this._longitude; } private set { this._longitude = value; }
{
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;
} }
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 public bool? IsClassB
{ {
get get { return this._isClassB; }
{ private set { this._isClassB = value; }
return this.isClassB;
}
} }
public int? Heading public int? Heading
{ {
get get { return _heading; } private set { _heading = value; }
{
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;
}
} }
public int? COG public double? COG
{ {
get get { return _cog; } private set { _cog = value; }
{
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;
}
} }
public bool? IsWatchkeeperShip public int? ROT
{ {
get { return this.isWatchkeeper; } get { return _rot; } private set { _rot = value; }
set { this.isWatchkeeper = value; }
} }
public bool Selected public double? SOG
{ {
get { return this.selected; } get { return _sog; } private set { _sog = value; }
set { this.selected = value; }
} }
public string Station public bool? Accuracy
{ {
get { return this.station; } get { return _accuracy; } private set { _accuracy = value; }
set { this.station = 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 #endregion
#region public methods #region public methods
public static AIS_Target.NavStatus GetCurrentNavstatus(int status) internal void Update(AIS_PosReport posReport)
{ {
AIS_Target.NavStatus result = NavStatus.UNKNOWN; this.NavStatus = posReport.NavStatusVal;
switch (status) this.ROT = posReport.ROT;
{ this.SOG = posReport.SOG;
case 0: this.Accuracy = (posReport.Accuracy == 1);
case 8: this.Latitude = posReport.Latitude;
result = NavStatus.UNDERWAY; this.Longitude = posReport.Longitude;
break; this.COG = posReport.COG;
default: this.Heading = posReport.TrueHeading;
result = NavStatus.MOORED; this.ManeuverIndicator = posReport.ManeuverIndicator;
break; this.RAIM = posReport.Raim == 1;
} this.Radio = posReport.CommState;
return result;
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) this.LastUpdate = DateTime.Now;
{
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;
}
} }
#endregion #endregion

View File

@ -40,6 +40,9 @@
<setting name="DataSourcePort" serializeAs="String"> <setting name="DataSourcePort" serializeAs="String">
<value>32100</value> <value>32100</value>
</setting> </setting>
<setting name="StaleTargetTimeoutMins" serializeAs="String">
<value>60</value>
</setting>
</bsmd.AIS2Service.Properties.Settings> </bsmd.AIS2Service.Properties.Settings>
</applicationSettings> </applicationSettings>
</configuration> </configuration>

View 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
}
}

View File

@ -40,5 +40,14 @@ namespace bsmd.AIS2Service.Properties {
return ((uint)(this["DataSourcePort"])); 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"]));
}
}
} }
} }

View File

@ -8,5 +8,8 @@
<Setting Name="DataSourcePort" Type="System.UInt32" Scope="Application"> <Setting Name="DataSourcePort" Type="System.UInt32" Scope="Application">
<Value Profile="(Default)">32100</Value> <Value Profile="(Default)">32100</Value>
</Setting> </Setting>
<Setting Name="StaleTargetTimeoutMins" Type="System.UInt32" Scope="Application">
<Value Profile="(Default)">60</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View 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
}
}

View File

@ -71,6 +71,7 @@
<Compile Include="AIS_StaticData.cs" /> <Compile Include="AIS_StaticData.cs" />
<Compile Include="AIS_Target.cs" /> <Compile Include="AIS_Target.cs" />
<Compile Include="IAISThread.cs" /> <Compile Include="IAISThread.cs" />
<Compile Include="Lookup.cs" />
<Compile Include="NMEA.cs" /> <Compile Include="NMEA.cs" />
<Compile Include="NMEA_AIS_Sentence.cs" /> <Compile Include="NMEA_AIS_Sentence.cs" />
<Compile Include="NMEA_PNMLS_Sentence.cs" /> <Compile Include="NMEA_PNMLS_Sentence.cs" />
@ -82,6 +83,7 @@
<DependentUpon>Settings.settings</DependentUpon> <DependentUpon>Settings.settings</DependentUpon>
</Compile> </Compile>
<Compile Include="SerialTCPReader.cs" /> <Compile Include="SerialTCPReader.cs" />
<Compile Include="SitRep.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />