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)
{
_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
{

View File

@ -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()

View File

@ -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);

View File

@ -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; }

View File

@ -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);
}

View File

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

View File

@ -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

View File

@ -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>

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"]));
}
}
[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">
<Value Profile="(Default)">32100</Value>
</Setting>
<Setting Name="StaleTargetTimeoutMins" Type="System.UInt32" Scope="Application">
<Value Profile="(Default)">60</Value>
</Setting>
</Settings>
</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_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" />