diff --git a/AIS/bsmd.AISService/AIS/AIS.cs b/AIS/bsmd.AISService/AIS/AIS.cs
new file mode 100644
index 00000000..47d85a2c
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS.cs
@@ -0,0 +1,266 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+// ship details suchbar hier: http://www.itu.int/cgi-bin/htsh/mars/ship_search.sh
+
+namespace bsmd.AISService.AIS
+{
+ public abstract class AIS
+ {
+ protected const double ERAD = 6378.135;
+ protected const double DE2RA = 0.01745329252;
+ protected const double AVG_ERAD = 6371.0;
+
+
+ #region enumerations
+
+ public enum Status
+ {
+ OK,
+ UNKNOWN_TYPE,
+ UNSUPPORTED,
+ ILLEGAL_ARGUMENT,
+ PARSE_ERROR
+ }
+
+ public enum AISType
+ {
+ AIS_NONE = 0,
+ POSITION_REPORT,
+ POSITION_REPORT_ASSIGNED,
+ POSITION_REPORT_SPECIAL,
+ BASE_STATION_REPORT,
+ STATIC_VOYAGE_DATA,
+ BINARY_ADDR_MSG,
+ BINARY_ACK,
+ BINARY_BCAST,
+ SAR_AIRCRAFT,
+ UTC_INQUIRY,
+ UTC_RESPONSE,
+ SAFETY_RELATED_MSG,
+ SAFETY_RELATED_ACK,
+ SAFETY_RELATED_BCAST,
+ INTERROGATION,
+ ASSIGN_MODE_CMD,
+ DGNSS_BCAST,
+ POSITION_REPORT_B_EQUIP,
+ POSITION_REPORT_B_EQUIP_EXT,
+ DATA_LINK_MAN,
+ AIDS_TO_NAV_REPORT,
+ CHANNEL_MNGNT,
+ GROUP_ASSIGNMENT,
+ CLASS_B_STATIC_DATA
+ };
+
+ #endregion
+
+ #region private members
+
+ private AISType type = AISType.AIS_NONE;
+ protected int userId;
+ protected string data;
+ private string station;
+
+ #endregion
+
+ #region Properties
+
+ public AISType MessageType
+ {
+ get { return this.type; }
+ }
+
+ public int MMSI
+ {
+ get { return this.userId; }
+ }
+
+ public string Station
+ {
+ get { return this.station; }
+ set { this.station = value; }
+ }
+
+ #endregion
+
+ #region abstract method signatures
+
+ protected abstract Status Decode();
+
+ #endregion
+
+ #region static methods
+
+ internal static AIS Decode(string data, ref Status status)
+ {
+ AIS result = null;
+
+ if (data == null || data.Length == 0)
+ {
+ status = Status.ILLEGAL_ARGUMENT;
+ return null;
+ }
+
+ BitArray bits = AIS.DecodeChar(data[0]);
+ int type = AIS.GetInt(bits, 0, 5);
+
+ result = AIS.CreateMessage(type);
+ if (result != null)
+ {
+ result.data = data;
+ status = result.Decode();
+ }
+ else
+ {
+ status = Status.UNSUPPORTED;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Factory method to create messages based on the message type
+ ///
+ protected static AIS CreateMessage(int type)
+ {
+ AIS result = null;
+
+ switch (type)
+ {
+ case 1:
+ result = new AIS_PosReport();
+ result.type = AISType.POSITION_REPORT;
+ break;
+ case 2:
+ result = new AIS_PosReport();
+ result.type = AISType.POSITION_REPORT_ASSIGNED;
+ break;
+ case 3:
+ result = new AIS_PosReport();
+ result.type = AISType.POSITION_REPORT_SPECIAL;
+ break;
+
+ case 5:
+ result = new AIS_StaticData();
+ result.type = AISType.STATIC_VOYAGE_DATA;
+ break;
+
+ case 18:
+ result = new AIS_ClassB();
+ result.type = AISType.POSITION_REPORT_B_EQUIP;
+ break;
+
+ case 19:
+ result = new AIS_ClassBExt();
+ result.type = AISType.POSITION_REPORT_B_EQUIP_EXT;
+ break;
+
+ case 24:
+ result = new AIS_ClassBStatic();
+ result.type = AISType.CLASS_B_STATIC_DATA;
+ break;
+
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ #region static helpers
+
+ protected static BitArray DecodeChar(char c)
+ {
+ Byte b = Convert.ToByte(c);
+ return DecodeByte(b);
+ }
+
+ protected static BitArray DecodeByte(byte c)
+ {
+ c += 40;
+ if (c > 128) c += 32;
+ else c += 40;
+
+ c &= 63;
+
+ BitArray b = new BitArray(6);
+
+ for (int i = 0; i < 6; i++)
+ {
+ b[i] = ((c >> (5-i)) & 1) == 1;
+ }
+
+ return b;
+ }
+
+ protected static BitArray DecodeBinary(string data)
+ {
+ BitArray result = new BitArray(data.Length * 6, false);
+
+ for (int i = 0; i < data.Length; i++)
+ {
+ BitArray charBits = DecodeChar(data[i]);
+
+ for (int j = 0; j < charBits.Count; j++)
+ result[i * 6 + j] = charBits[j];
+
+ }
+
+ return result;
+ }
+
+ protected static int GetInt(BitArray bits, int lo, int hi)
+ {
+ int result = 0;
+ int test = 1;
+
+ for (int i = hi; i >= lo; i--, test <<= 1)
+ {
+ if (bits[i]) result |= test;
+ }
+ return result;
+ }
+
+ protected static char GetAISChar(int val)
+ {
+ if(val < 32) return Convert.ToChar(val + 64);
+ if(val < 64) return Convert.ToChar(val);
+ return ' ';
+ }
+
+ #endregion
+
+ #region public static helpers
+
+ ///
+ /// mehr dazu hier:
+ /// http://www.codeguru.com/cpp/cpp/algorithms/general/article.php/c5115/
+ ///
+ public static double GetDistance(double lat1, double lon1, double lat2, double lon2)
+ {
+ lat1 *= AIS.DE2RA;
+ lon1 *= AIS.DE2RA;
+ lat2 *= AIS.DE2RA;
+ lon2 *= AIS.DE2RA;
+ double d = Math.Sin(lat1) * Math.Sin(lat2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(lon1 -lon2);
+ return (AIS.AVG_ERAD * Math.Acos(d));
+ }
+
+
+ #endregion
+
+ #endregion
+
+ #region overrides
+
+ public override string ToString()
+ {
+ return Enum.GetName(typeof(AIS.AISType), this.MessageType);
+ }
+
+ #endregion
+
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_ClassB.cs b/AIS/bsmd.AISService/AIS/AIS_ClassB.cs
new file mode 100644
index 00000000..afe47a95
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_ClassB.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections;
+using System.Text;
+using System.Diagnostics;
+
+namespace bsmd.AISService.AIS
+{
+ public class AIS_ClassB : AIS
+ {
+ #region private members
+
+ private int repeatIndicator;
+ private int reserved;
+ private int sog;
+ private int accuracy;
+ private int longitude;
+ private int latitude;
+ private int cog;
+ private int trueHeading;
+ private int utcTimestampSecs;
+ private DateTime timestamp;
+ private int reservedRegional;
+ private int spare;
+ private int assignedModeFlag;
+ private int raimFlag;
+ private int commStateSelectedFlag;
+ private int commState;
+
+ #endregion
+
+ #region Properties
+
+ public int CogVal { get { return this.cog; } }
+ public int SogVal { get { return this.sog; } }
+ public int LatitudeVal { get { return this.latitude; } }
+ public int LongitudeVal { get { return this.longitude; } }
+
+ public string DBTimestamp
+ {
+ get
+ {
+ return string.Format("{0}-{1}-{2} {3}:{4}:{5}",
+ this.timestamp.Year, this.timestamp.Month, this.timestamp.Day,
+ this.timestamp.Hour, this.timestamp.Minute, this.timestamp.Second);
+ }
+ }
+
+
+
+ public double Cog
+ {
+ get { return this.cog / 10.0f; }
+ }
+
+ public double Sog
+ {
+ get { return this.sog / 10.0f; }
+ }
+
+ public double Latitude
+ {
+ get { return this.latitude / 600000.0f; }
+ }
+
+ public double Longitude
+ {
+ get { return this.longitude / 600000.0f; }
+ }
+
+ public DateTime Timestamp
+ {
+ get { return this.timestamp; }
+ }
+
+ public int? TrueHeading
+ {
+ get
+ {
+ if (this.trueHeading == 511) return null;
+ return this.trueHeading;
+ }
+ }
+
+ #endregion
+
+ protected override AIS.Status Decode()
+ {
+ BitArray bits = AIS.DecodeBinary(this.data);
+ Status result = Status.OK;
+
+ try
+ {
+ int type = AIS.GetInt(bits, 0, 5);
+ if (type != 18)
+ {
+ result = Status.ILLEGAL_ARGUMENT;
+ }
+ else
+ {
+ this.repeatIndicator = AIS.GetInt(bits, 6, 7);
+ this.userId = AIS.GetInt(bits, 8, 37);
+ this.reserved = AIS.GetInt(bits, 38, 45);
+ this.sog = AIS.GetInt(bits, 46, 55);
+ this.accuracy = AIS.GetInt(bits, 56, 56);
+ this.longitude = AIS.GetInt(bits, 57, 84);
+ this.latitude = AIS.GetInt(bits, 85, 111);
+ this.cog = AIS.GetInt(bits, 112, 123);
+ this.trueHeading = AIS.GetInt(bits, 124, 132);
+ this.utcTimestampSecs = AIS.GetInt(bits, 133,138);
+ this.timestamp = DateTime.Now;
+ this.reservedRegional = AIS.GetInt(bits, 139, 140);
+ this.spare = AIS.GetInt(bits, 141, 145);
+ this.assignedModeFlag = AIS.GetInt(bits, 146, 146);
+ this.raimFlag = AIS.GetInt(bits, 147, 147);
+ this.commStateSelectedFlag = AIS.GetInt(bits, 148, 148);
+ this.commState = AIS.GetInt(bits, 149, 167);
+ }
+ }
+ catch (Exception e)
+ {
+ Trace.WriteLine(string.Format("Error decoding AIS class B posreport: {0}", e.Message));
+ result = Status.PARSE_ERROR;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_ClassBExt.cs b/AIS/bsmd.AISService/AIS/AIS_ClassBExt.cs
new file mode 100644
index 00000000..a3956611
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_ClassBExt.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections;
+using System.Text;
+using System.Diagnostics;
+
+namespace bsmd.AISService.AIS
+{
+ ///
+ /// Diese Nachricht wird normalerweise von Class B Geräten nicht verschickt.
+ /// Sie wird nur als Antwort auf einen sog. "Base station poll" gesendet.
+ /// Wir lesen sie trotzdem ;)
+ ///
+ // Todo
+ public class AIS_ClassBExt : AIS
+ {
+ #region private members
+
+ private int repeatIndicator;
+ private int spare1;
+ private int sog;
+ private int accuracy;
+ private int longitude;
+ private int latitude;
+ private int cog;
+ private int trueHeading;
+ private int utcTimestampSecond;
+ private DateTime timestamp;
+ private int spare2;
+ private string name;
+ private int shipType;
+ private int dimension;
+ private int typeofDevice;
+ private int raimFlag;
+ private int dte;
+ private int assignedMode;
+ private int spare3;
+
+ #endregion
+
+ #region Properties
+
+ public string Name
+ {
+ get { return this.name; }
+ }
+
+ public DateTime Timestamp
+ {
+ get { return this.timestamp; }
+ }
+
+ public double Latitude
+ {
+ get { return this.latitude / 600000.0f; }
+ }
+
+ public double Longitude
+ {
+ get { return this.longitude / 600000.0f; }
+ }
+
+ public int TrueHeading
+ {
+ get { return this.trueHeading; }
+ }
+
+ public double Cog
+ {
+ get { return (double)this.cog / 10.0f; }
+ }
+
+ #endregion
+
+ protected override AIS.Status Decode()
+ {
+ BitArray bits = AIS.DecodeBinary(this.data);
+ Status result = Status.OK;
+
+ try
+ {
+ int type = AIS.GetInt(bits, 0, 5);
+ if (type != 19)
+ {
+ result = Status.ILLEGAL_ARGUMENT;
+ }
+ else
+ {
+ this.repeatIndicator = AIS.GetInt(bits, 6, 7);
+ this.userId = AIS.GetInt(bits, 8, 37);
+ this.spare1 = AIS.GetInt(bits, 38, 45);
+ this.sog = AIS.GetInt(bits, 46, 55);
+ this.accuracy = AIS.GetInt(bits, 56, 56);
+ this.longitude = AIS.GetInt(bits, 57, 84);
+ this.latitude = AIS.GetInt(bits, 85, 111);
+ this.cog = AIS.GetInt(bits, 112, 123);
+ this.trueHeading = AIS.GetInt(bits, 124, 132);
+ this.utcTimestampSecond = AIS.GetInt(bits, 133, 138);
+ this.timestamp = DateTime.Now;
+ this.spare2 = AIS.GetInt(bits, 139, 142);
+
+ StringBuilder sb_name = new StringBuilder(20);
+ for (int i = 0; i < 20; i++)
+ {
+ int cval = AIS.GetInt(bits, 143 + (6 * i), 148 + (6 * i));
+ char ch = AIS.GetAISChar(cval);
+ if (ch == '@') ch = ' ';
+ sb_name.Append(ch);
+ }
+ this.name = sb_name.ToString().Trim();
+
+ this.shipType = AIS.GetInt(bits, 263, 270);
+ this.dimension = AIS.GetInt(bits, 271, 300);
+ this.typeofDevice = AIS.GetInt(bits, 301, 304);
+ this.raimFlag = AIS.GetInt(bits, 305, 305);
+ this.dte = AIS.GetInt(bits, 306, 306);
+ this.assignedMode = AIS.GetInt(bits, 307, 307);
+ this.spare3 = AIS.GetInt(bits, 308, 311);
+ }
+ }
+
+ catch (Exception e)
+ {
+ Trace.WriteLine(string.Format("Error decoding AIS class B Ext posreport: {0}", e.Message));
+ result = Status.PARSE_ERROR;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_ClassBStatic.cs b/AIS/bsmd.AISService/AIS/AIS_ClassBStatic.cs
new file mode 100644
index 00000000..89c51757
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_ClassBStatic.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections;
+using System.Text;
+using System.Diagnostics;
+
+namespace bsmd.AISService.AIS
+{
+ public class AIS_ClassBStatic : AIS
+ {
+ #region private members
+
+ private int repeatIndicator;
+ private int partNumber;
+ private string name;
+ private int shipType;
+ private string vendorId;
+ private string callsign;
+ private int dimension;
+ private int spare;
+
+ #endregion
+
+ #region Properties
+
+ public bool IsPartA
+ {
+ get { return this.partNumber == 0; }
+ }
+
+ public bool IsPartB
+ {
+ get { return this.partNumber == 1; }
+ }
+
+ public string Name
+ {
+ get { return this.name; }
+ }
+
+ public string ShipType
+ {
+ get { return AIS_StaticData.GetShipType(this.shipType); }
+ }
+
+ public string VendorId
+ {
+ get { return this.vendorId; }
+ }
+
+ public string Callsign
+ {
+ get { return this.callsign; }
+ }
+
+ public int ShipTypeVal
+ {
+ get { return this.shipType; }
+ }
+
+ // Todo: Dimensions..
+
+ #endregion
+
+ protected override AIS.Status Decode()
+ {
+ BitArray bits = AIS.DecodeBinary(this.data);
+ Status result = Status.OK;
+
+ try
+ {
+ int type = AIS.GetInt(bits, 0, 5);
+ if (type != 24)
+ {
+ result = Status.ILLEGAL_ARGUMENT;
+ }
+ else
+ {
+ this.repeatIndicator = AIS.GetInt(bits, 6, 7);
+ this.userId = AIS.GetInt(bits, 8, 37);
+ this.partNumber = AIS.GetInt(bits, 38, 39);
+ if (this.IsPartA)
+ {
+ StringBuilder sb_name = new StringBuilder(20);
+ for (int i = 0; i < 20; i++)
+ {
+ int cval = AIS.GetInt(bits, 40 + (6 * i), 45 + (6 * i));
+ char ch = AIS.GetAISChar(cval);
+ if (ch == '@') ch = ' ';
+ sb_name.Append(ch);
+ }
+ this.name = sb_name.ToString().Trim();
+ }
+ else
+ {
+ this.shipType = AIS.GetInt(bits, 40, 47);
+
+ StringBuilder sb_vendor = new StringBuilder(7);
+ for (int i = 0; i < 7; i++)
+ {
+ int cval = AIS.GetInt(bits, 48 + (6 * i), 53 + (6 * i));
+ char ch = AIS.GetAISChar(cval);
+ if (ch == '@') ch = ' ';
+ sb_vendor.Append(ch);
+ }
+ this.vendorId = sb_vendor.ToString().Trim();
+
+ StringBuilder sb_callsign = new StringBuilder(7);
+ for (int i = 0; i < 7; i++)
+ {
+ int cval = AIS.GetInt(bits, 90 + (6 * i), 95 + (6 * i));
+ char ch = AIS.GetAISChar(cval);
+ if (ch == '@') ch = ' ';
+ sb_callsign.Append(ch);
+ }
+ this.callsign = sb_callsign.ToString().Trim();
+ this.dimension = AIS.GetInt(bits, 141, 161);
+ this.spare = AIS.GetInt(bits, 162, 167);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Trace.WriteLine(string.Format("Error decoding AIS class B static data: {0}", e.Message));
+ result = Status.PARSE_ERROR;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_Configuration.cs b/AIS/bsmd.AISService/AIS/AIS_Configuration.cs
new file mode 100644
index 00000000..53fb5a4f
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_Configuration.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using System.Reflection;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace bsmd.AISService.AIS
+{
+ [Serializable]
+ public class AIS_Configuration
+ {
+ private string filename;
+ private string dbConnectionString;
+ private int dbUpdateInterval = 500; // milliseconds
+ private int dbMinPosReportTimeDifference = 120; // seconds
+ private int stationIsOfflineTimeDifferenceSecs = 180; // seconds
+ private int targetStaleMins = 31; // minutes
+
+ public List SerialPorts = new List();
+ public List TelnetConnections = new List();
+
+ #region Properties
+
+ public string Configuration_Path
+ {
+ get { return this.filename; }
+ set { this.filename = value; }
+ }
+
+ public string DBConnectionString
+ {
+ get { return this.dbConnectionString; }
+ set { this.dbConnectionString = value; }
+ }
+
+ ///
+ /// timer interval for database updates
+ ///
+ public int DBUpdateInterval
+ {
+ get { return this.dbUpdateInterval; }
+ set { this.dbUpdateInterval = value; }
+ }
+
+ ///
+ /// minimum amount of minutes between two position reports to be
+ /// written to database
+ ///
+ public int DBMinPosReportTimeDifference
+ {
+ get { return this.dbMinPosReportTimeDifference; }
+ set { this.dbMinPosReportTimeDifference = value; }
+ }
+
+ ///
+ /// number of seconds after which a station is marked offline since
+ /// sending the last pos report
+ ///
+ public int StationIsOfflineTimeDifferenceSecs
+ {
+ get { return this.stationIsOfflineTimeDifferenceSecs; }
+ set { this.stationIsOfflineTimeDifferenceSecs = value; }
+ }
+
+ ///
+ /// if last update is older than this value then the target ist removed from
+ /// the current target queue (target went offline or out of range)
+ ///
+ public int TargetStaleMins
+ {
+ get { return this.targetStaleMins; }
+ set { this.targetStaleMins = value; }
+ }
+
+ ///
+ /// Root path to where Viewer stores OSM tiles
+ ///
+ public string TilePath { get; set; }
+
+ ///
+ /// full path to logfile
+ ///
+ public string LogfilePath { get; set; }
+
+ ///
+ /// outputs assembly version
+ ///
+ public static string VersionInfo
+ {
+ get
+ {
+ Version version = Assembly.GetExecutingAssembly().GetName().Version;
+ return version.ToString();
+ }
+ }
+
+ #endregion
+
+ #region Load/Save
+
+ public static AIS_Configuration Load(string filename)
+ {
+ if (!File.Exists(filename)) return null;
+
+ // Create an instance of the XmlSerializer specifying type and namespace.
+ XmlSerializer serializer = new XmlSerializer(typeof(AIS_Configuration));
+
+ // A FileStream is needed to read the XML document.
+ FileStream fs = new FileStream(filename, FileMode.Open);
+ XmlReader reader = new XmlTextReader(fs);
+
+ AIS_Configuration configuration = serializer.Deserialize(reader) as AIS_Configuration;
+ reader.Close();
+ configuration.filename = filename;
+
+
+ return configuration;
+ }
+
+ public bool Save()
+ {
+ bool retval = true;
+ try
+ {
+ XmlSerializer serializer = new XmlSerializer(typeof(AIS_Configuration));
+ Stream fs = new FileStream(this.filename, FileMode.Create);
+ XmlWriter writer = new XmlTextWriter(fs, new UTF8Encoding());
+ serializer.Serialize(writer, this);
+ writer.Close();
+ }
+ catch (Exception e)
+ {
+ System.Diagnostics.Debug.Write("Error during Serialize: " + e.ToString());
+ retval = false;
+ }
+ return retval;
+ }
+
+ #endregion
+
+ #region internal classes
+
+ public class SerialPort
+ {
+ public string station;
+ public string ComPort;
+ public int BaudRate = 9600;
+ public bool enabled = false;
+ }
+
+ public class TelnetConnection
+ {
+ public string ipAddress;
+ public int port;
+ }
+
+ #endregion
+
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_Decoder.cs b/AIS/bsmd.AISService/AIS/AIS_Decoder.cs
new file mode 100644
index 00000000..46f8476e
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_Decoder.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Text;
+
+namespace bsmd.AISService.AIS
+{
+
+ ///
+ /// Diese Klasse setzt fragmentierte AIS Telegramme wieder zusammen und decodiert sie
+ ///
+ public class AIS_Decoder
+ {
+
+ public delegate void AISMessageHandler(AIS message);
+ public event AISMessageHandler AISMessageReceived;
+
+ #region class AISQueueElement
+
+ public class AISQueueElement
+ {
+ public int seq_nr;
+ public int total_nr;
+ public int? id;
+ public string data;
+ public string station;
+ }
+
+ #endregion
+
+ #region private members
+
+ private Queue inputDataQueue = new Queue();
+ private Thread decodingThread;
+ private bool runDecoder = true;
+ private int sleepMS = 250;
+ private Dictionary> fragmentDict = new Dictionary>();
+
+ #endregion
+
+ #region Properties
+
+ public int QueueSize
+ {
+ get { return this.inputDataQueue.Count; }
+ }
+
+ #endregion
+
+ #region public methods
+
+ public void Decode(string data, int seq_nr, int total_nr, int? id, string station)
+ {
+ lock (this.inputDataQueue)
+ {
+ AISQueueElement element = new AISQueueElement();
+ element.data = data;
+ element.seq_nr = seq_nr;
+ element.total_nr = total_nr;
+ element.id = id;
+ element.station = station;
+
+ this.inputDataQueue.Enqueue(element);
+ }
+ }
+
+ public void Start()
+ {
+ this.decodingThread = new Thread(new ThreadStart(this.Run));
+ this.decodingThread.Start();
+ }
+
+ public void Stop()
+ {
+ this.runDecoder = false;
+ if((this.decodingThread != null) &&
+ (this.decodingThread.ThreadState == ThreadState.Running))
+ this.decodingThread.Join();
+ this.inputDataQueue.Clear(); // discard unread elements
+ }
+
+ #endregion
+
+ ///
+ /// Thread worker method
+ ///
+ protected void Run()
+ {
+ while (this.runDecoder)
+ {
+ AISQueueElement inputData = null;
+
+ lock (this.inputDataQueue)
+ {
+ if (this.inputDataQueue.Count > 0)
+ {
+ inputData = this.inputDataQueue.Dequeue();
+ }
+ }
+
+ if (inputData == null)
+ Thread.Sleep(this.sleepMS);
+ else
+ {
+ string aisRawData = null;
+ if (inputData.total_nr == 1)
+ {
+ aisRawData = inputData.data;
+ }
+ else
+ {
+ int id = inputData.id ?? -1;
+
+ if (!this.fragmentDict.ContainsKey(id))
+ this.fragmentDict.Add(id, new List());
+ this.fragmentDict[id].Add(inputData);
+
+ // sind alle Fragmente vorhanden?
+ if (AIS_Decoder.FragmentsComplete(this.fragmentDict[id]))
+ {
+ // Fragmente zusammensetzen
+ aisRawData = AIS_Decoder.ConcatenateFragments(this.fragmentDict[id]);
+ this.fragmentDict.Remove(id);
+ }
+
+ }
+
+ if (aisRawData != null)
+ {
+ AIS.Status status = AIS.Status.OK;
+ AIS message = AIS.Decode(aisRawData, ref status);
+ if (status == AIS.Status.OK)
+ {
+ message.Station = inputData.station;
+ this.OnAISMessageReceived(message);
+ }
+ }
+
+ }
+ }
+ }
+
+ #region private helpers
+
+ ///
+ /// check to see if all fragments are available
+ ///
+ private static bool FragmentsComplete(List elements)
+ {
+ if (elements == null || elements.Count == 0) return false;
+ int num = elements[0].total_nr;
+
+ for (int i = 1; i <= num; i++)
+ {
+ bool foundElements = false;
+ for (int j = 0; j < elements.Count; j++)
+ {
+ if (elements[j].seq_nr == i)
+ foundElements = true;
+ }
+ if (!foundElements) return false; // etwas fehlt noch
+ }
+ return true;
+ }
+
+ ///
+ /// assembles message fragments. Care must be taken since fragments can appear
+ /// out of order
+ ///
+ private static string ConcatenateFragments(List elements)
+ {
+ if (elements == null || elements.Count == 0) return string.Empty;
+ int num = elements[0].total_nr;
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 1; i <= num; i++)
+ {
+ for (int j = 0; j < elements.Count; j++)
+ if (elements[j].seq_nr == i)
+ sb.Append(elements[j].data);
+ }
+ return sb.ToString();
+ }
+
+ #endregion
+
+ protected void OnAISMessageReceived(AIS message)
+ {
+ if (this.AISMessageReceived != null)
+ this.AISMessageReceived(message);
+ }
+
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_PosReport.cs b/AIS/bsmd.AISService/AIS/AIS_PosReport.cs
new file mode 100644
index 00000000..949e5203
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_PosReport.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Text;
+
+namespace bsmd.AISService.AIS
+{
+ public class AIS_PosReport : AIS
+ {
+ private int navstatus;
+ private int rot;
+ private int sog;
+ private int accur;
+ private int longitude;
+ private int latitude;
+ private int cog;
+ private int trueheading;
+ private DateTime timestamp;
+ private int utcTimeSecond;
+ private int reserved;
+ private int spare;
+ private int raim;
+ private int commstate;
+
+ #region Properties
+
+ public int NavStatusVal { get { return this.navstatus; } }
+ public int ROTVal { get { return this.rot; } }
+ public int SOGVal { get { return this.sog; } }
+ public int COGVal { get { return this.cog; } }
+ public int Accuracy { get { return this.accur; } }
+ public int LatitudeVal { get { return this.latitude; } }
+ public int LongitudeVal { get { return this.longitude; } }
+ public string DBTimestamp
+ {
+ get
+ {
+ return string.Format("{0}-{1}-{2} {3}:{4}:{5}",
+ this.timestamp.Year, this.timestamp.Month, this.timestamp.Day,
+ this.timestamp.Hour, this.timestamp.Minute, this.timestamp.Second);
+ }
+ }
+
+ public double SOG
+ {
+ get
+ {
+ return ((double)this.sog) / 10.0f;
+ }
+ }
+
+ public double COG
+ {
+ get
+ {
+ return ((double)this.cog) / 10.0f;
+ }
+ }
+
+ public int ROT
+ {
+ get
+ {
+ return (int)((double)(this.rot * this.rot) / 22.401289);
+ }
+ }
+
+ public double Latitude
+ {
+ get
+ {
+ return ((double)this.latitude) / 600000.0f;
+ }
+ }
+
+ public double Longitude
+ {
+ get
+ {
+ return ((double)this.longitude) / 600000.0f;
+ }
+ }
+
+
+ public string NavStatus
+ {
+ get { return GetNavStatus(this.navstatus); }
+ }
+
+ public DateTime Timestamp
+ {
+ get { return this.timestamp; }
+ }
+
+ public int? TrueHeading
+ {
+ get
+ {
+ if (this.trueheading == 511) return null;
+ return this.trueheading;
+ }
+ }
+
+ #endregion
+
+ #region static methods
+
+ public static string GetNavStatus(int navstatus)
+ {
+ switch (navstatus)
+ {
+ case 0:
+ return "under way using engine";
+ case 1:
+ return "at anchor";
+ case 2:
+ return "not under command";
+ case 3:
+ return "restricted manoeuvrability";
+ case 4:
+ return "contrained by her draught";
+ case 5:
+ return "moored";
+ case 6:
+ return "aground";
+ case 7:
+ return "engaged in fishing";
+ case 8:
+ return "under way sailing";
+ case 9:
+ return "reserved for future amendment of Navigational Status for HSC";
+ case 10:
+ return "reserved for future amendment of Navigational Status for WIG";
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ return "reserved for future use";
+ default:
+ return "not defined";
+ }
+ }
+
+ #endregion
+
+ #region overrides
+
+ protected override Status Decode()
+ {
+ Status result = Status.OK;
+ BitArray bits = AIS.DecodeBinary(this.data);
+
+ try
+ {
+ this.userId = AIS.GetInt(bits, 8, 37);
+ this.navstatus = AIS.GetInt(bits, 38, 41);
+ this.rot = AIS.GetInt(bits, 42, 49);
+ this.sog = AIS.GetInt(bits, 50, 59);
+ this.accur = AIS.GetInt(bits, 60, 60);
+ this.longitude = AIS.GetInt(bits, 61, 88);
+ this.latitude = AIS.GetInt(bits, 89, 115);
+ this.cog = AIS.GetInt(bits, 116, 127);
+ this.trueheading = AIS.GetInt(bits, 128, 136);
+ this.utcTimeSecond = AIS.GetInt(bits, 137, 142);
+ this.reserved = AIS.GetInt(bits, 143, 146);
+ this.spare = AIS.GetInt(bits, 147, 147);
+ this.raim = AIS.GetInt(bits, 148, 148);
+ this.commstate = AIS.GetInt(bits, 149, 167);
+ }
+ catch (Exception e)
+ {
+ Trace.WriteLine(string.Format("Error decoding AIS pos report: {0}", e.Message));
+ result = Status.PARSE_ERROR;
+ }
+
+ this.timestamp = DateTime.Now;
+ return result;
+ }
+
+ public override string ToString()
+ {
+ return string.Format("{0} - MMSI {1}", base.ToString(), this.MMSI);
+ }
+
+ #endregion
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_QueueManager.cs b/AIS/bsmd.AISService/AIS/AIS_QueueManager.cs
new file mode 100644
index 00000000..faecdace
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_QueueManager.cs
@@ -0,0 +1,218 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Timers;
+using System.Diagnostics;
+
+namespace bsmd.AISService.AIS
+{
+ ///
+ /// Hier laufen die Fäden zusammen. Diese Klasse enthält alle Objekte und kann direkt von
+ /// Konsolen / services und Windowsprogrammen verwendet werden
+ ///
+public class AIS_QueueManager
+ {
+
+ public delegate void AISQueueChangedHandler(AIS_Target target);
+ public event AISQueueChangedHandler AISQueueChanged;
+ public event AISQueueChangedHandler DBUpdateRequired;
+
+ private Dictionary activeTargets = new Dictionary();
+ private List activeTargetList = new List();
+ private List databaseTargets = new List();
+ private List watchkeeperTargets = new List();
+ private AIS_Configuration configuration;
+ private List serialHandlerList = new List();
+ private List telnetHandlerList = new List();
+ private List dbUpdateQueue = new List();
+ private Timer dbUpdateTimer = new Timer();
+ private bool isStarted = false;
+
+ #region Construction
+
+ public AIS_QueueManager(AIS_Configuration configuration, List serialIOs, List ais_Telnets)
+ {
+ this.configuration = configuration;
+
+ foreach (Serial_IO serialIO in serialIOs)
+ {
+ AIS_Decoder decoder = new AIS_Decoder();
+ decoder.AISMessageReceived += new AIS_Decoder.AISMessageHandler(this.decoder_AISMessageReceived);
+ SerialDataHandler handler = new SerialDataHandler(serialIO, decoder);
+ this.serialHandlerList.Add(handler);
+ }
+
+ foreach (AIS_Telnet aisTelnet in ais_Telnets)
+ {
+ AIS_Decoder decoder = new AIS_Decoder();
+ decoder.AISMessageReceived += new AIS_Decoder.AISMessageHandler(this.decoder_AISMessageReceived);
+ TelnetDataHandler tdn = new TelnetDataHandler(aisTelnet, decoder);
+ this.telnetHandlerList.Add(tdn);
+ }
+
+ AIS_Target.dbUpdateInterval = new TimeSpan(0, 0, configuration.DBMinPosReportTimeDifference);
+ this.dbUpdateTimer.Interval = configuration.DBUpdateInterval;
+ this.dbUpdateTimer.Elapsed += new ElapsedEventHandler(dbUpdateTimer_Elapsed);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public List ActiveTargets
+ {
+ get
+ {
+ return this.activeTargetList;
+ }
+ }
+
+ public bool IsStarted
+ {
+ get { return this.isStarted; }
+ }
+
+ #endregion
+
+ #region event handler
+
+ void dbUpdateTimer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ while (this.dbUpdateQueue.Count > 0)
+ {
+ AIS_Target currentTarget = null;
+ lock (this.dbUpdateQueue)
+ {
+ // Trace.WriteLine(string.Format("Update queue size: {0}", this.dbUpdateQueue.Count));
+ currentTarget = this.dbUpdateQueue[0];
+ this.dbUpdateQueue.RemoveAt(0);
+ }
+ this.OnDBUpdateRequired(currentTarget);
+ }
+
+ // remove stale targets
+ lock (this.activeTargetList)
+ {
+
+ for(int i=0;i this.configuration.TargetStaleMins)
+ {
+ this.activeTargetList.RemoveAt(i);
+ i--;
+ }
+ }
+ }
+
+ }
+
+ void decoder_AISMessageReceived(AIS message)
+ {
+ lock (this.activeTargets)
+ {
+ // Trace.WriteLine(string.Format("Queue manager: AIS message received, queue size: {0}", activeTargets.Count));
+ if (!this.activeTargets.ContainsKey(message.MMSI))
+ {
+ AIS_Target target = new AIS_Target(message.MMSI);
+ this.activeTargets.Add(message.MMSI, target);
+ lock (this.activeTargetList)
+ {
+ this.activeTargetList.Add(target);
+ }
+ }
+
+ this.activeTargets[message.MMSI].Update(message);
+ this.OnAISQueueChanged(this.activeTargets[message.MMSI]);
+
+ if (this.activeTargets[message.MMSI].UpdateDB)
+ {
+ lock (this.dbUpdateQueue)
+ {
+ if (!this.dbUpdateQueue.Contains(this.activeTargets[message.MMSI]))
+ this.dbUpdateQueue.Add(this.activeTargets[message.MMSI]);
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region public methods
+
+ public bool Start(ref string message)
+ {
+ bool retval = true;
+ if (this.isStarted)
+ {
+ message = "Queue manager already started";
+ return true;
+ }
+
+ foreach (SerialDataHandler sdh in this.serialHandlerList)
+ {
+ string messagePart = "";
+ retval &= sdh.Start(ref messagePart);
+ if (!retval)
+ message += messagePart + Environment.NewLine;
+ if(retval) sdh.AIS_Decoder.Start();
+ }
+
+ foreach (TelnetDataHandler tdh in this.telnetHandlerList)
+ {
+ string messagePart = "";
+ retval &= tdh.Start(ref messagePart);
+ if (!retval)
+ message += messagePart + Environment.NewLine;
+ if (retval) tdh.AIS_Decoder.Start();
+ }
+
+ if (retval)
+ this.dbUpdateTimer.Start();
+
+ if (retval) this.isStarted = true;
+
+ return retval;
+ }
+
+ public void Stop()
+ {
+ if (this.isStarted)
+ {
+ foreach (SerialDataHandler sdh in this.serialHandlerList)
+ {
+ sdh.Stop();
+ sdh.AIS_Decoder.Stop();
+ }
+ foreach (TelnetDataHandler tdh in this.telnetHandlerList)
+ {
+ tdh.Stop();
+ tdh.AIS_Decoder.Stop();
+ }
+ this.dbUpdateTimer.Stop();
+ this.isStarted = false;
+ }
+ }
+
+ #endregion
+
+ #region OnEvent methods
+
+ protected void OnAISQueueChanged(AIS_Target target)
+ {
+ if (this.AISQueueChanged != null) this.AISQueueChanged(target);
+ }
+
+ protected void OnDBUpdateRequired(AIS_Target target)
+ {
+ if (this.DBUpdateRequired != null) this.DBUpdateRequired(target);
+ }
+
+ #endregion
+
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_StaticData.cs b/AIS/bsmd.AISService/AIS/AIS_StaticData.cs
new file mode 100644
index 00000000..6bff5030
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_StaticData.cs
@@ -0,0 +1,381 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Text;
+
+namespace bsmd.AISService.AIS
+{
+ public class AIS_StaticData : AIS
+ {
+ #region private members
+
+ private int ais_version;
+ private int imoNumber;
+ private string callsign;
+ private string name;
+ private int shiptype;
+ private int dimension;
+ private int a;
+ private int b;
+ private int c;
+ private int d;
+
+ private int typeofdevice;
+ private int etamonth;
+ private int etaday;
+ private int etahour;
+ private int etaminute;
+ private DateTime? eta;
+
+ private int maxpresetstaticdraught;
+ private string destination;
+ private int dte;
+ private int spare;
+
+ #endregion
+
+ #region Properties
+
+ public int ShipTypeVal { get { return this.shiptype; } }
+
+ public string Callsign
+ {
+ get { return this.callsign; }
+ }
+
+ public string Name
+ {
+ get { return this.name; }
+ }
+
+ public DateTime? ETA
+ {
+ get { return this.eta; }
+ }
+
+ public string Destination
+ {
+ get { return this.destination; }
+ }
+
+ public int IMONumber
+ {
+ get { return this.imoNumber; }
+ }
+
+ public string DeviceName
+ {
+ get
+ {
+ switch (typeofdevice)
+ {
+ case 1:
+ return "GPS";
+ case 2:
+ return "GLONASS";
+ case 3:
+ return "Combined GPS/GLONASS";
+ case 4:
+ return "Loran-C";
+ case 5:
+ return "Chayka";
+ case 6:
+ return "Integrated Navigation System";
+ case 7:
+ return "surveyed";
+ case 8:
+ return "Galileo";
+ default:
+ return "undefined";
+ }
+ }
+ }
+
+ public int Draught
+ {
+ get { return this.maxpresetstaticdraught; }
+ }
+
+ public int Breadth
+ {
+ get
+ {
+ return this.c + this.d;
+ }
+ }
+
+ public int Length
+ {
+ get
+ {
+ return this.a + this.b;
+ }
+ }
+
+ public string ShipType
+ {
+ get
+ {
+ return AIS_StaticData.GetShipType(this.shiptype);
+ }
+ }
+
+ public string DBETA
+ {
+ get
+ {
+ if (this.eta.HasValue)
+ {
+ return string.Format("{0}-{1}-{2} {3}:{4}:{5}",
+ this.eta.Value.Year, this.eta.Value.Month, this.eta.Value.Day,
+ this.eta.Value.Hour, this.eta.Value.Minute, this.eta.Value.Second);
+ }
+ else
+ return "";
+ }
+ }
+
+
+ #endregion
+
+ #region abstract method implementation
+
+ protected override Status Decode()
+ {
+ BitArray bits = AIS.DecodeBinary(this.data);
+ Status result = Status.OK;
+
+
+ try
+ {
+ int type = AIS.GetInt(bits, 0, 5);
+ if (type != 5)
+ {
+ result = Status.ILLEGAL_ARGUMENT;
+ }
+ else
+ {
+ this.userId = AIS.GetInt(bits, 6, 37);
+ this.ais_version = AIS.GetInt(bits, 38, 39);
+ this.imoNumber = AIS.GetInt(bits, 40, 69);
+
+ StringBuilder sb_callsign = new StringBuilder(7);
+ for (int i = 0; i < 7; i++)
+ {
+ int cval = AIS.GetInt(bits, 70 + (6 * i), 75 + (6 * i));
+ char ch = AIS.GetAISChar(cval);
+ if (ch == '@') ch = ' ';
+ sb_callsign.Append(ch);
+ }
+ this.callsign = sb_callsign.ToString().Trim();
+
+ StringBuilder sb_name = new StringBuilder(20);
+ for (int i = 0; i < 20; i++)
+ {
+ int cval = AIS.GetInt(bits, 112 + (6 * i), 117 + (6 * i));
+ char ch = AIS.GetAISChar(cval);
+ if (ch == '@') ch = ' ';
+ sb_name.Append(ch);
+ }
+ this.name = sb_name.ToString().Trim();
+
+ this.shiptype = AIS.GetInt(bits, 232, 239);
+ this.dimension = AIS.GetInt(bits, 240, 269);
+ this.a = AIS.GetInt(bits, 240, 248);
+ this.b = AIS.GetInt(bits, 249, 257);
+ this.c = AIS.GetInt(bits, 258, 263);
+ this.d = AIS.GetInt(bits, 264, 269);
+ this.typeofdevice = AIS.GetInt(bits, 270, 273);
+ this.etamonth = AIS.GetInt(bits, 274, 277);
+ this.etaday = AIS.GetInt(bits, 278, 282);
+ this.etahour = AIS.GetInt(bits, 283, 287);
+ this.etaminute = AIS.GetInt(bits, 288, 293);
+ try
+ {
+ if ((this.etahour < 24) && (this.etaday > 0) && (this.etaminute < 60) && (this.etamonth > 0))
+ {
+ this.eta = new DateTime(DateTime.Now.Year, this.etamonth, this.etaday, this.etahour, this.etaminute, 0);
+ }
+ }
+ catch(Exception) {
+ Trace.WriteLine("ERROR creating ETA timestamp");
+ }
+ this.maxpresetstaticdraught = AIS.GetInt(bits, 294, 301);
+
+ StringBuilder sb_destination = new StringBuilder(20);
+ for (int i = 0; i < 20; i++)
+ {
+ int cval = AIS.GetInt(bits, 302 + (6 * i), 307 + (6 * i));
+ char ch = AIS.GetAISChar(cval);
+ if (ch == '@') ch = ' ';
+ sb_destination.Append(ch);
+ }
+ this.destination = sb_destination.ToString().Trim();
+
+ this.dte = AIS.GetInt(bits, 422, 422);
+ this.spare = AIS.GetInt(bits, 423, 423);
+
+ }
+ }
+ catch (Exception e)
+ {
+ Trace.WriteLine(string.Format("Error decoding AIS static data: {0}", e.Message));
+ result = Status.PARSE_ERROR;
+ }
+ return result;
+ }
+
+ public override string ToString()
+ {
+ return string.Format("{0} - {1} [{2}]", base.ToString(), this.MMSI, this.Name);
+ }
+
+ #endregion
+
+ #region public static methods
+
+ public static AIS_Target.Type GetShipTypeSimple(int shiptype)
+ {
+ switch (shiptype)
+ {
+ case 50:
+ case 51:
+ case 52:
+ case 53:
+ case 54:
+ case 55:
+ case 56:
+ case 57:
+ return AIS_Target.Type.TUG;
+ default:
+ int d1 = shiptype / 10;
+ switch (d1)
+ {
+ case 2:
+ return AIS_Target.Type.WIG;
+ case 3:
+ return AIS_Target.Type.OTHER;
+ case 4:
+ return AIS_Target.Type.HSC;
+ case 6:
+ return AIS_Target.Type.PASSENGER;
+ case 7:
+ return AIS_Target.Type.CARGO;
+ case 8:
+ return AIS_Target.Type.TANKER;
+ }
+ return AIS_Target.Type.OTHER;
+ }
+ }
+
+ 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
+
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_Target.cs b/AIS/bsmd.AISService/AIS/AIS_Target.cs
new file mode 100644
index 00000000..f3002406
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_Target.cs
@@ -0,0 +1,327 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace bsmd.AISService.AIS
+{
+ public class AIS_Target
+ {
+
+ #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 AIS staticInfo;
+ private AIS posReport;
+ private AIS lastAdditionalData;
+
+ private AIS_Target.Type type = Type.OTHER;
+ private AIS_Target.NavStatus navStatus = AIS_Target.NavStatus.UNKNOWN;
+
+ #endregion
+
+ #region public defs
+
+
+
+ public enum Type
+ {
+ PASSENGER,
+ CARGO,
+ TANKER,
+ HSC,
+ WIG,
+ TUG,
+ YACHT,
+ OTHER
+ }
+
+ ///
+ /// vereinfacht
+ ///
+ public enum NavStatus
+ {
+ UNKNOWN,
+ UNDERWAY,
+ MOORED
+ }
+
+ #endregion
+
+ #region Construction
+
+ public AIS_Target(int mmsi)
+ {
+ this.mmsi = mmsi;
+ }
+
+ #endregion
+
+ #region Properties
+
+ public bool UpdateDB
+ {
+ get { return this.updateDB; }
+ set { this.updateDB = value; }
+ }
+
+ public int MMSI
+ {
+ get { return this.mmsi; }
+ }
+
+ public DateTime? LastUpdate
+ {
+ get { return this.lastUpdate; }
+ }
+
+ public string Name
+ {
+ get {
+ if ((this.name == null) || (this.name.Length == 0))
+ return this.LastDBName;
+ return this.name;
+ }
+ 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; }
+ }
+
+ public AIS LastPosReport
+ {
+ get { return this.posReport; }
+ }
+
+ public AIS LastStaticData
+ {
+ get { return this.staticInfo; }
+ }
+
+ 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; }
+ }
+
+ 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;
+ }
+ }
+
+ public bool? IsClassB
+ {
+ get
+ {
+ return this.isClassB;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ public int? 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;
+ }
+ }
+
+ public bool? IsWatchkeeperShip
+ {
+ get { return this.isWatchkeeper; }
+ set { this.isWatchkeeper = value; }
+ }
+
+ public bool Selected
+ {
+ get { return this.selected; }
+ set { this.selected = value; }
+ }
+
+ public string Station
+ {
+ get { return this.station; }
+ set { this.station = value; }
+ }
+
+ #endregion
+
+ #region public methods
+
+ public static AIS_Target.NavStatus GetCurrentNavstatus(int status)
+ {
+ AIS_Target.NavStatus result = NavStatus.UNKNOWN;
+ switch (status)
+ {
+ case 0:
+ case 8:
+ result = NavStatus.UNDERWAY;
+ break;
+ default:
+ result = NavStatus.MOORED;
+ break;
+ }
+ return result;
+ }
+
+ public void Update(AIS message)
+ {
+ this.station = message.Station;
+
+ switch (message.MessageType)
+ {
+ case AIS.AISType.POSITION_REPORT:
+ case AIS.AISType.POSITION_REPORT_ASSIGNED:
+ case AIS.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 AIS.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 AIS.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 AIS.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 AIS.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
+
+ #region overrides
+
+ public override string ToString()
+ {
+ return string.Format("{0} [{1}]", this.Name, this.MMSI);
+ }
+
+ #endregion
+
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_Target_Comparer.cs b/AIS/bsmd.AISService/AIS/AIS_Target_Comparer.cs
new file mode 100644
index 00000000..300b38da
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_Target_Comparer.cs
@@ -0,0 +1,104 @@
+using System;
+using System.ComponentModel;
+using System.Collections.Generic;
+using System.Text;
+
+namespace bsmd.AISService.AIS
+{
+ public class AIS_Target_Comparer : IComparer
+ {
+ private SortPropertyEnum sortProperty = SortPropertyEnum.NAME;
+ private ListSortDirection sortDirection = ListSortDirection.Ascending;
+
+ public enum SortPropertyEnum
+ {
+ NONE,
+ MMSI,
+ NAME,
+ CALLSIGN,
+ LASTUPDATE,
+ STATION
+ }
+
+ #region Properties
+
+ public ListSortDirection SortDirection
+ {
+ get { return this.sortDirection; }
+ set { this.sortDirection = value; }
+ }
+
+ public SortPropertyEnum SortProperty
+ {
+ get { return this.sortProperty; }
+ set { this.sortProperty = value; }
+ }
+
+ #endregion
+
+
+ #region IComparer Members
+
+ public int Compare(AIS_Target x, AIS_Target y)
+ {
+ switch (this.sortProperty)
+ {
+ case SortPropertyEnum.NONE:
+ return 0;
+ case SortPropertyEnum.NAME:
+ {
+ string xName = x.LastDBName;
+ if (xName == null) xName = "";
+ string yName = y.LastDBName;
+ if (yName == null) yName = "";
+ if (this.sortDirection == ListSortDirection.Ascending)
+ return xName.CompareTo(yName);
+ else
+ return yName.CompareTo(xName);
+ }
+ case SortPropertyEnum.CALLSIGN:
+ {
+ string xCallsign = x.Callsign;
+ if (xCallsign == null) xCallsign = "";
+ string yCallsign = y.Callsign;
+ if (yCallsign == null) yCallsign = "";
+ if (this.sortDirection == ListSortDirection.Ascending)
+ return xCallsign.CompareTo(yCallsign);
+ else
+ return yCallsign.CompareTo(xCallsign);
+ }
+ case SortPropertyEnum.LASTUPDATE:
+ {
+ DateTime xTime = x.LastUpdate ?? DateTime.MinValue;
+ DateTime yTime = y.LastUpdate ?? DateTime.MinValue;
+ if (this.sortDirection == ListSortDirection.Ascending)
+ return xTime.CompareTo(yTime);
+ else
+ return yTime.CompareTo(xTime);
+ }
+ case SortPropertyEnum.MMSI:
+ {
+ if (this.sortDirection == ListSortDirection.Ascending)
+ return x.MMSI.CompareTo(y.MMSI);
+ else
+ return y.MMSI.CompareTo(x.MMSI);
+ }
+ case SortPropertyEnum.STATION:
+ {
+ if (this.sortDirection == ListSortDirection.Ascending)
+ return x.ReceivedFrom.CompareTo(y.ReceivedFrom);
+ else
+ return y.ReceivedFrom.CompareTo(x.ReceivedFrom);
+ }
+
+ default:
+ return 0;
+ }
+
+
+
+ }
+
+ #endregion
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/AIS_Telnet.cs b/AIS/bsmd.AISService/AIS/AIS_Telnet.cs
new file mode 100644
index 00000000..5a8ae60b
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/AIS_Telnet.cs
@@ -0,0 +1,150 @@
+//
+// Class: AIS_Telnet
+// Current CLR: 4.0.30319.296
+// System: Microsoft Visual Studio 10.0
+// Author: dani
+// Created: 3/16/2013 12:58:03 PM
+//
+// Copyright (c) 2013 Informatikbüro Daniel Schick. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Net.Sockets;
+
+namespace bsmd.AISService.AIS
+{
+ public class AIS_Telnet
+ {
+
+ #region private fields
+
+ private const int BFSIZE = 1024;
+
+ private NetworkStream tcpStream;
+ private TcpClient tcpSocket;
+ private string currentString = "";
+ private string hostname;
+ private int port;
+ private DateTime? lastRead;
+
+ #endregion
+
+ public AIS_Telnet(string theHostname, int thePort)
+ {
+ this.port = thePort;
+ this.hostname = theHostname;
+ this.Connect();
+ }
+
+ #region Properties
+
+ public bool IsConnected
+ {
+ get { return tcpSocket.Connected; }
+ }
+
+ public string Hostname { get { return this.hostname; } }
+
+ public int Port { get { return this.port; } }
+
+ public string StationName { get; set; }
+
+ #endregion
+
+ #region public methods
+
+ private string ReadCurrentUptoNewline()
+ {
+ int newlineIndex = currentString.IndexOf('\n');
+ string result = this.currentString.Substring(0, newlineIndex);
+ if (currentString.Length > (newlineIndex + 1))
+ currentString = currentString.Substring(newlineIndex + 1);
+ else
+ currentString = "";
+ return result;
+ }
+
+ public string ReadLine()
+ {
+
+ string result = "";
+ if (currentString.IndexOf('\n') >= 0)
+ return ReadCurrentUptoNewline();
+
+
+ byte[] inputBuffer = new byte[1024];
+
+ if ((tcpSocket == null) || (!tcpSocket.Connected) || !this.tcpStream.CanRead)
+ this.Connect();
+ if ((tcpSocket == null) || !tcpSocket.Connected)
+ {
+ System.Threading.Thread.Sleep(30000); // wait 5 mins if connect is unsuccessful
+ return result;
+ }
+ if (this.tcpStream.DataAvailable)
+ {
+ try
+ {
+ int bytesRead = this.tcpStream.Read(inputBuffer, 0, 1024);
+ if (bytesRead > 0)
+ {
+ this.lastRead = DateTime.Now;
+ this.currentString += Encoding.ASCII.GetString(inputBuffer, 0, bytesRead);
+ if (currentString.IndexOf('\n') >= 0)
+ return ReadCurrentUptoNewline();
+ if (this.currentString.Length > 1024) this.currentString = ""; // truncate to avoid overflow for wrong client data flow
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Trace.WriteLine(string.Format("exception reading from tcp stream: {0}", ex.Message));
+ result = "";
+ }
+ }
+ else
+ {
+ // wenn die Verbindung wegkracht ist immer noch connected true, aber DataAvailable false
+ // es gibt anscheinend keinen richtig guten Workaround. Hard case: Nach einer Stunde Inaktivität schließt der Client hier die
+ // Verbindung und versucht reconnects. Das bekommt der LS100PortProxy aber nicht immer mit.. Folge sind dann die "stehengebliebenen"
+ // Verbindungen
+ if (lastRead == null) lastRead = DateTime.Now;
+ if ((DateTime.Now - lastRead.Value).TotalSeconds > 600)
+ {
+ this.tcpSocket.Close();
+ this.tcpSocket = null;
+ System.Diagnostics.Trace.WriteLine("closing inactive TcpClient");
+ this.lastRead = DateTime.Now; // reset timer
+ }
+ }
+
+ return result;
+ }
+
+ public void Close()
+ {
+ if (this.tcpStream != null) this.tcpStream.Close();
+ this.tcpSocket.Close();
+ this.tcpStream.Dispose();
+ }
+
+ #endregion
+
+ public void Connect()
+ {
+ try
+ {
+ if ((this.tcpSocket != null) && (this.tcpSocket.Connected)) return;
+ this.tcpSocket = new TcpClient(this.hostname, this.port);
+ this.tcpStream = tcpSocket.GetStream();
+ System.Diagnostics.Trace.WriteLine(string.Format("TCP stream connected ({0}:{1})", this.hostname, this.port));
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Trace.WriteLine(
+ string.Format("AIS_Telnet: cannot connect to ({0}:{1}) : {2}", this.hostname, this.port, ex.Message));
+ }
+ }
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/NMEA.cs b/AIS/bsmd.AISService/AIS/NMEA.cs
new file mode 100644
index 00000000..71b74f2d
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/NMEA.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace bsmd.AISService.AIS
+{
+ internal abstract class NMEA
+ {
+ protected string type = "";
+ protected string data;
+ protected string[] elements = null;
+
+ public enum Status
+ {
+ OK,
+ UNKNOWN_TYPE,
+ CHECKSUM,
+ ILLEGAL_ARGUMENT
+ }
+
+ protected abstract void Decode();
+
+ public static NMEA Decode(string data, ref Status status)
+ {
+ try
+ {
+ if (data == null)
+ {
+ status = Status.ILLEGAL_ARGUMENT;
+ return null;
+ }
+
+ if (data[0] != '$' && data[0] != '!')
+ {
+ status = Status.ILLEGAL_ARGUMENT;
+ return null; // no NMEA sentence
+ }
+
+ string[] elements = data.Trim().Substring(1).Split(',');
+
+ NMEA sentence = NMEA.CreateNMEAElement(elements[0]);
+ if (sentence == null)
+ {
+ status = Status.UNKNOWN_TYPE;
+ return null;
+ }
+
+ sentence.elements = elements;
+ sentence.data = data.Trim(); ;
+
+ if (!sentence.IsChecksumOK)
+ {
+ status = Status.CHECKSUM;
+ return null;
+ }
+
+ sentence.Decode();
+
+ return sentence;
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Trace.WriteLine(string.Format("Error decoding sentence: {0}, {1}", ex.Message, ex.StackTrace));
+ return null;
+ }
+ }
+
+ ///
+ /// Factory method for nmea types
+ ///
+ protected static NMEA CreateNMEAElement(string type)
+ {
+ NMEA result = null;
+
+ switch (type.ToUpper())
+ {
+ case "AIVDM":
+ result = new NMEA_AIS_Sentence();
+ break;
+
+ case "PNMLS":
+ result = new NMEA_PNMLS_Sentence();
+ break;
+
+ default:
+ break;
+ }
+
+ if (result != null)
+ result.type = type.ToUpper();
+
+ return result;
+ }
+
+ protected bool IsChecksumOK
+ {
+ get
+ {
+ return this.data.Substring(this.data.IndexOf('*') + 1) == this.CalculateChecksum();
+
+ }
+ }
+
+ private string CalculateChecksum()
+ {
+ int checksum = Convert.ToByte(this.data[1]);
+ for (int i = 2; i < this.data.IndexOf('*'); i++)
+ {
+ checksum ^= Convert.ToByte(this.data[i]);
+ }
+ return checksum.ToString("X2");
+ }
+
+
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/NMEA_AIS_Sentence.cs b/AIS/bsmd.AISService/AIS/NMEA_AIS_Sentence.cs
new file mode 100644
index 00000000..9b916451
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/NMEA_AIS_Sentence.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace bsmd.AISService.AIS
+{
+ internal class NMEA_AIS_Sentence : NMEA
+ {
+ private int total_sentence_nr;
+ private int msg_sentence_nr;
+ private int? seq_message_ident;
+ private string ais_channel_nr;
+ private string ais_message;
+ private int fillbits;
+
+ #region Properties
+
+ ///
+ /// 1-based total number of sentences for this ais message
+ ///
+ public int Total_Sentence_Nr
+ {
+ get { return this.total_sentence_nr; }
+ }
+
+ ///
+ /// 1-based fragment number of sentences
+ ///
+ public int Msg_Sentence_Nr
+ {
+ get { return this.msg_sentence_nr; }
+ }
+
+ ///
+ /// sequential message id for multi-sentence messages (can be empty)
+ ///
+ public int? Seq_Message_Ident
+ {
+ get { return this.seq_message_ident; }
+ }
+
+ ///
+ /// 'A' = 161.975Mhz (87B),
+ /// 'B' = 162.025Mhz (88B)
+ ///
+ public string AIS_Channel_nr
+ {
+ get { return this.ais_channel_nr; }
+ }
+
+ ///
+ /// AIS message data
+ ///
+ public string AIS_Message
+ {
+ get { return this.ais_message; }
+ }
+
+ public int FillBits
+ {
+ get { return this.fillbits; }
+ }
+
+ #endregion
+
+ protected override void Decode()
+ {
+ this.total_sentence_nr = Convert.ToInt32(this.elements[1]);
+ this.msg_sentence_nr = Convert.ToInt32(this.elements[2]);
+ if (this.elements[3].Length > 0)
+ this.seq_message_ident = Convert.ToInt32(this.elements[3]);
+ this.ais_channel_nr = this.elements[4];
+ this.ais_message = this.elements[5];
+ try
+ {
+ string fillbits_string = this.elements[6].Substring(0, this.elements[6].IndexOf('*'));
+ if(!Int32.TryParse(fillbits_string, out this.fillbits))
+ System.Diagnostics.Trace.WriteLine("AIS_Sentence.Decode(): fillbits are no integer");
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ System.Diagnostics.Trace.WriteLine("AIS_Sentence.Decode(): split() problem, trouble decoding fillbits");
+ }
+ }
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/NMEA_PNMLS_Sentence.cs b/AIS/bsmd.AISService/AIS/NMEA_PNMLS_Sentence.cs
new file mode 100644
index 00000000..65a61dea
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/NMEA_PNMLS_Sentence.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics;
+
+namespace bsmd.AISService.AIS
+{
+ ///
+ /// NMEA PNMLS sentence
+ /// sentence shows signal level for preceding message
+ ///
+ class NMEA_PNMLS_Sentence : NMEA
+ {
+ private int signal_level;
+ private int detection_threshold;
+ private int interval;
+
+ #region Properties
+
+ public int Signal_Level
+ {
+ get { return this.signal_level; }
+ }
+
+ public int Detection_Threshold
+ {
+ get { return this.detection_threshold; }
+ }
+
+ public int Interval
+ {
+ get { return this.interval; }
+ }
+
+ #endregion
+
+ #region decode func
+
+ protected override void Decode()
+ {
+ try
+ {
+ this.signal_level = Convert.ToInt32(this.elements[1]);
+ this.detection_threshold = Convert.ToInt32(this.elements[2]);
+
+ string interval_string = this.elements[3].Substring(0, this.elements[3].IndexOf('*'));
+ this.interval = Convert.ToInt32(interval_string);
+ }
+ catch (FormatException)
+ {
+ Trace.WriteLine("NMEA [PNMLS] input format error");
+ }
+ }
+
+ #endregion
+
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/SerialDataHandler.cs b/AIS/bsmd.AISService/AIS/SerialDataHandler.cs
new file mode 100644
index 00000000..9e0395d8
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/SerialDataHandler.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics;
+
+namespace bsmd.AISService.AIS
+{
+ public class SerialDataHandler
+ {
+ private Serial_IO serial_IO;
+ private AIS_Decoder decoder;
+
+ public SerialDataHandler(Serial_IO io, AIS_Decoder decoder)
+ {
+ this.serial_IO = io;
+ this.decoder = decoder;
+ this.serial_IO.LineRead += new Serial_IO.LineReadHandler(serial_IO_LineRead);
+ }
+
+ public Serial_IO Serial_IO
+ {
+ get { return this.serial_IO; }
+ }
+
+ public AIS_Decoder AIS_Decoder
+ {
+ get { return this.decoder; }
+ }
+
+ public bool Start(ref string message)
+ {
+ return this.serial_IO.Open(ref message);
+ }
+
+ public void Stop()
+ {
+ this.serial_IO.Close();
+ }
+
+ protected void serial_IO_LineRead(string data)
+ {
+ NMEA.Status nmea_Status = NMEA.Status.OK;
+ if (data == null || data.Length == 0) return;
+
+ NMEA decodedSentence = NMEA.Decode(data, ref nmea_Status);
+ if (decodedSentence != null)
+ {
+ if (decodedSentence is NMEA_AIS_Sentence)
+ {
+ NMEA_AIS_Sentence aisSentence = decodedSentence as NMEA_AIS_Sentence;
+ this.decoder.Decode(aisSentence.AIS_Message, aisSentence.Msg_Sentence_Nr,
+ aisSentence.Total_Sentence_Nr, aisSentence.Seq_Message_Ident, this.Serial_IO.StationName);
+ }
+ }
+ else
+ {
+ Trace.WriteLine("Serial data handler: NMEA decoder returned null sentence");
+ }
+ }
+
+ public override string ToString()
+ {
+ return string.Format("Serial AIS Receiver {0} on {1}",
+ this.serial_IO.StationName,
+ this.serial_IO.ComPort);
+ }
+
+ }
+}
diff --git a/AIS/bsmd.AISService/AIS/Serial_IO.cs b/AIS/bsmd.AISService/AIS/Serial_IO.cs
new file mode 100644
index 00000000..05817b3c
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/Serial_IO.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO.Ports;
+using System.Threading;
+
+namespace bsmd.AISService.AIS
+{
+ public class Serial_IO
+ {
+ #region private fields
+ private string stationName;
+ private SerialPort port;
+ private bool runReader = true;
+ private Thread readerThread = null;
+ #endregion
+
+ // event fired if input line is available
+ public delegate void LineReadHandler(string data);
+ public event LineReadHandler LineRead;
+
+
+ public Serial_IO()
+ {
+ this.port = new SerialPort();
+ }
+
+ public bool Open(ref string message)
+ {
+ bool retval = true;
+ try
+ {
+ this.port.Open();
+ }
+ catch (Exception ex)
+ {
+ message = ex.Message;
+ retval = false;
+ }
+ if (retval)
+ {
+ this.readerThread = new Thread(new ThreadStart(this.Read));
+ this.runReader = true;
+ this.readerThread.Start();
+ }
+ return retval;
+ }
+
+ public void Close()
+ {
+ this.runReader = false;
+ if(readerThread != null)
+ if(readerThread.ThreadState == ThreadState.Running)
+ this.readerThread.Join();
+ if (this.port.IsOpen)
+ {
+ this.port.BaseStream.Flush();
+ this.port.Close();
+ }
+ }
+
+ public string[] GetComPorts()
+ {
+ return SerialPort.GetPortNames();
+ }
+
+ #region Properties
+
+ public int BaudRate
+ {
+ get { return this.port.BaudRate; }
+ set { this.port.BaudRate = value; }
+ }
+
+ public string ComPort
+ {
+ get { return this.port.PortName; }
+ set { this.port.PortName = value; }
+ }
+
+ public string StationName
+ {
+ get { return this.stationName; }
+ set { this.stationName = value; }
+ }
+
+ #endregion
+
+ #region protected methods
+
+ protected void Read()
+ {
+ while (runReader)
+ {
+ try
+ {
+ string line = this.port.ReadLine();
+ this.OnInputLineRead(line);
+ //System.Diagnostics.Trace.WriteLine(line);
+ }
+ catch (Exception) { }
+ }
+ }
+
+ protected void OnInputLineRead(string line)
+ {
+ if (this.LineRead != null)
+ this.LineRead(line);
+ }
+
+ #endregion
+
+ }
+
+
+
+}
diff --git a/AIS/bsmd.AISService/AIS/TelnetDataHandler.cs b/AIS/bsmd.AISService/AIS/TelnetDataHandler.cs
new file mode 100644
index 00000000..686c737d
--- /dev/null
+++ b/AIS/bsmd.AISService/AIS/TelnetDataHandler.cs
@@ -0,0 +1,112 @@
+//
+// Class: TelnetDataHandler
+// Current CLR: 4.0.30319.296
+// System: Microsoft Visual Studio 10.0
+// Author: dani
+// Created: 3/16/2013 2:12:35 PM
+//
+// Copyright (c) 2013 Informatikbüro Daniel Schick. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Diagnostics;
+
+namespace bsmd.AISService.AIS
+{
+ public class TelnetDataHandler
+ {
+ AIS_Telnet aisTelnet;
+ AIS_Decoder decoder;
+ Thread readerThread;
+ bool requestStop;
+
+ public TelnetDataHandler(AIS_Telnet telnetConnection, AIS_Decoder aisDecoder)
+ {
+ this.aisTelnet = telnetConnection;
+ this.decoder = aisDecoder;
+ }
+
+ public AIS_Decoder AIS_Decoder
+ {
+ get { return this.decoder; }
+ }
+
+ public override string ToString()
+ {
+ return string.Format("Telnet AIS Receiver {0}:{1}",
+ this.aisTelnet.Hostname,
+ this.aisTelnet.Port);
+ }
+
+ public bool Start(ref string message)
+ {
+ if (readerThread != null) return true; // already running
+ try
+ {
+ this.readerThread = new Thread(new ThreadStart(this.ReaderThread));
+ readerThread.Start();
+ this.requestStop = false;
+ message = "reader thread started";
+ return true;
+ }
+ catch (Exception ex)
+ {
+ message = ex.Message;
+ return false;
+ }
+ }
+
+ public void Stop()
+ {
+ if (readerThread.IsAlive)
+ {
+ this.requestStop = true;
+ readerThread.Join();
+ }
+ this.readerThread = null;
+ }
+
+ private void ReaderThread()
+ {
+ NMEA.Status nmea_Status = NMEA.Status.OK;
+ System.Diagnostics.Trace.WriteLine("starting telnet reader thread");
+ while (!requestStop)
+ {
+ try
+ {
+
+ string data = this.aisTelnet.ReadLine();
+ // Trace.WriteLine(data);
+ if (data != null && data.Length > 0)
+ {
+ NMEA decodedSentence = NMEA.Decode(data, ref nmea_Status);
+ if (decodedSentence != null)
+ {
+ if (decodedSentence is NMEA_AIS_Sentence)
+ {
+ NMEA_AIS_Sentence aisSentence = decodedSentence as NMEA_AIS_Sentence;
+ this.decoder.Decode(aisSentence.AIS_Message, aisSentence.Msg_Sentence_Nr,
+ aisSentence.Total_Sentence_Nr, aisSentence.Seq_Message_Ident, this.aisTelnet.StationName);
+ }
+ }
+ else
+ {
+ Trace.WriteLine("Serial data handler: NMEA decoder returned null/empty sentence");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ var st = new StackTrace(ex, true);
+ var frame = st.GetFrame(0);
+ var line = frame.GetFileLineNumber();
+ Trace.WriteLine(string.Format("Exception in telnet reader thread: {0}, top frame ln {1}", ex.Message, line));
+ Trace.WriteLine(ex.StackTrace);
+ }
+ Thread.Sleep(100);
+ }
+ aisTelnet.Close();
+ }
+ }
+}
diff --git a/AIS/bsmd.AISService/AISService.Designer.cs b/AIS/bsmd.AISService/AISService.Designer.cs
new file mode 100644
index 00000000..6285cc2e
--- /dev/null
+++ b/AIS/bsmd.AISService/AISService.Designer.cs
@@ -0,0 +1,37 @@
+namespace bsmd.AISService
+{
+ partial class AISService
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ this.ServiceName = "Service1";
+ }
+
+ #endregion
+ }
+}
diff --git a/AIS/bsmd.AISService/AISService.cs b/AIS/bsmd.AISService/AISService.cs
new file mode 100644
index 00000000..8f8444b9
--- /dev/null
+++ b/AIS/bsmd.AISService/AISService.cs
@@ -0,0 +1,93 @@
+// Copyright (c) 2008-2018 schick Informatik
+// Description:
+//
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Diagnostics;
+using System.Linq;
+using System.ServiceProcess;
+using System.Text;
+using System.Threading.Tasks;
+
+using bsmd.AISService.AIS;
+using bsmd.AISService.DB;
+
+using log4net;
+using System.IO;
+
+namespace bsmd.AISService
+{
+ public partial class AISService : ServiceBase
+ {
+ private const string config_filename = "ais_config.xml";
+ private ILog _log = LogManager.GetLogger(typeof(AISService));
+ private AIS_QueueManager qManager;
+
+ public AISService()
+ {
+ Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
+ InitializeComponent();
+ }
+
+ protected override void OnStart(string[] args)
+ {
+ string errorMessage = "";
+
+ this.EventLog.Source = this.ServiceName;
+ this.EventLog.Log = "Application";
+ this.Init(args);
+ if (qManager.Start(ref errorMessage))
+ {
+ this.EventLog.WriteEntry("BSMD AIS Service started.", EventLogEntryType.Information);
+ System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
+ FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
+ string version = fvi.FileVersion;
+ _log.InfoFormat("Starting AIS Service. v.{0} -------------- ", version);
+ } else
+ {
+ _log.ErrorFormat("AIS Service start failed: {0}", errorMessage);
+ }
+
+ }
+
+ protected override void OnStop()
+ {
+ this.qManager.Stop();
+ }
+
+ protected void Init(string[] args)
+ {
+ AIS_Configuration configuration = AIS_Configuration.Load(config_filename);
+
+ if (configuration == null)
+ {
+ Console.WriteLine(string.Format("cannot read configuration {0}", config_filename));
+ return;
+ }
+
+ DBConnector dbConnector = new DBConnector();
+ dbConnector.ConnectionString = configuration.DBConnectionString;
+ if (!dbConnector.Open())
+ {
+ Console.WriteLine("Error connecting to database");
+ return;
+ }
+
+ List stationList = AISStation.LoadStations(dbConnector);
+
+ this.qManager = new AIS_QueueManager(configuration, AISStation.CreateSerial_IOs(stationList), AISStation.CreateAIS_Telnets(stationList));
+ qManager.DBUpdateRequired += new AIS_QueueManager.AISQueueChangedHandler(dbConnector.Update);
+ qManager.AISQueueChanged += new AIS_QueueManager.AISQueueChangedHandler(aisDecoder_AISMessageReceived);
+ }
+
+ protected void aisDecoder_AISMessageReceived(AIS_Target target)
+ {
+ Console.WriteLine(string.Format("{0}: {1} Pos:{2} {3} at {4}", target.Station, target.Name, target.Latitude, target.Longitude, target.LastUpdate));
+ }
+
+
+ }
+}
diff --git a/AIS/bsmd.AISService/App.config b/AIS/bsmd.AISService/App.config
new file mode 100644
index 00000000..0ab7508f
--- /dev/null
+++ b/AIS/bsmd.AISService/App.config
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AIS/bsmd.AISService/DB/AISPosReport.cs b/AIS/bsmd.AISService/DB/AISPosReport.cs
new file mode 100644
index 00000000..ab6e6bc7
--- /dev/null
+++ b/AIS/bsmd.AISService/DB/AISPosReport.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Text;
+
+using bsmd.AISService.AIS;
+
+namespace bsmd.AISService.DB
+{
+
+ internal class AISPosReport
+ {
+ ///
+ /// Saves a (class A or B) position report
+ ///
+ /// target to save
+ /// id of insert operation (to update hotposition table)
+ public static int? Save(AIS_Target target, DBConnector con, AISStation aisStation)
+ {
+ if (target.LastPosReport == null) return null;
+
+ if (target.LastPosReport is AIS_PosReport)
+ {
+ // Trace.WriteLine("saving class A pos report");
+ AIS_PosReport pr = target.LastPosReport as AIS_PosReport;
+
+ if (aisStation != null)
+ {
+ aisStation.UpdateWithPositionReport(pr.MMSI, pr.Latitude, pr.Longitude, pr.Timestamp);
+ aisStation.LastPosTimestamp = pr.Timestamp;
+ aisStation.OnAir = true;
+ }
+
+ string query = string.Format("INSERT INTO aisposreport (mmsi, navstatus, rot, cog, sog, accur, longitude, latitude, heading, timestamp, stationid) VALUES ({0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, '{9}', {10})",
+ pr.MMSI, pr.NavStatusVal, pr.ROTVal, pr.COGVal, pr.SOGVal, pr.Accuracy,
+ pr.LongitudeVal, pr.LatitudeVal, pr.TrueHeading ?? 511, pr.DBTimestamp,
+ (aisStation != null) ? aisStation.Id : 0);
+
+ con.ExecuteNonQuery(query);
+
+ object result = con.ExecuteScalar("SELECT LAST_INSERT_ID()");
+ if (result == null) return null;
+ int pid = Convert.ToInt32(result);
+ return pid;
+ }
+
+ if (target.LastPosReport is AIS_ClassB)
+ {
+ // Trace.WriteLine("saving class B pos report");
+ AIS_ClassB pr = target.LastPosReport as AIS_ClassB;
+ aisStation.UpdateWithPositionReport(pr.MMSI, pr.Latitude, pr.Longitude, pr.Timestamp);
+
+ string query = string.Format("INSERT INTO aisposreport (mmsi, navstatus, rot, cog, sog, accur, longitude, latitude, heading, timestamp, stationid) VALUES ({0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, '{9}', {10})",
+ pr.MMSI, 0, 0, pr.CogVal, pr.SogVal, 0, pr.LongitudeVal, pr.LatitudeVal,
+ pr.TrueHeading ?? 511, pr.DBTimestamp, (aisStation != null) ? aisStation.Id : 0);
+
+ con.ExecuteNonQuery(query);
+
+ object result = con.ExecuteScalar("SELECT LAST_INSERT_ID()");
+ if (result == null) return null;
+ int pid = Convert.ToInt32(result);
+ return pid;
+ }
+
+ if (target.LastPosReport is AIS_ClassBExt)
+ {
+ Trace.WriteLine("AIS class B ext not supported (yet)");
+ // TODO: Import ClassB Extended report!
+
+ }
+
+ Trace.WriteLine(string.Format("save pos report: we should not be here.. class type: {0}", target));
+
+ return null;
+ }
+
+
+ }
+}
diff --git a/AIS/bsmd.AISService/DB/AISStaticData.cs b/AIS/bsmd.AISService/DB/AISStaticData.cs
new file mode 100644
index 00000000..7bcc3cce
--- /dev/null
+++ b/AIS/bsmd.AISService/DB/AISStaticData.cs
@@ -0,0 +1,521 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Data;
+using System.ComponentModel;
+using System.Diagnostics;
+
+using bsmd.AISService.AIS;
+
+namespace bsmd.AISService.DB
+{
+
+ public class AISStaticData
+ {
+
+ public enum DisplayStringType
+ {
+ NAME,
+ MMSI,
+ CALLSIGN
+ }
+
+ private const string LoadDBShipsQuery = "SELECT aisposreport.MMSI, aisposreport.timestamp, aisposreport.latitude, aisposreport.longitude, aisposreport.stationid, aisposreport.cog, aisposreport.heading, aisposreport.navstatus, aisstaticdata.callsign, aisstaticdata.name, aisstaticdata.shiptype, aisstaticdata.classb, aisstaticdata.shipdescription FROM aisstaticdata JOIN hotposition ON aisstaticdata.mmsi = hotposition.mmsi JOIN aisposreport ON aisposreport.id = hotposition.pid ORDER BY aisstaticdata.name";
+
+ #region Fields
+
+ private int mmsi;
+ private string name;
+ private string callsign;
+ private DateTime? lastposition;
+ private double? lastLatitude;
+ private double? lastLongitude;
+ private int stationid;
+ private int navstatus;
+ private int shiptype;
+ private string shipdescription;
+ private int cog;
+ private int heading;
+ private bool isClassB;
+ private bool isWatchkeeper;
+ private bool isTimedOut = false;
+ private bool isSelected = false;
+ private static DisplayStringType displayStringType = DisplayStringType.NAME;
+
+ #endregion
+
+ #region Properties
+
+ public int MMSI
+ {
+ get { return this.mmsi; }
+ }
+
+ public string Name
+ {
+ get { return this.name; }
+ }
+
+ public string Callsign
+ {
+ get { return this.callsign; }
+ }
+
+ public DateTime? LastPositionReport
+ {
+ get { return this.lastposition; }
+ }
+
+ public double? LastLatitude
+ {
+ get { return this.lastLatitude; }
+ }
+
+ public double? LastLongitude
+ {
+ get { return this.lastLongitude; }
+ }
+
+ public bool IsClassB
+ {
+ get { return this.isClassB; }
+ }
+
+ public int ShipType
+ {
+ get { return this.shiptype; }
+ }
+
+ public string Description
+ {
+ get { return this.shipdescription; }
+ }
+
+ public bool IsWatchkeeperShip
+ {
+ get { return this.isWatchkeeper; }
+ set { this.isWatchkeeper = value; }
+ }
+
+ public int StationId
+ {
+ get { return this.stationid; }
+ }
+
+ public int Heading
+ {
+ get { return this.heading; }
+ }
+
+ public int COG
+ {
+ get { return this.cog; }
+ }
+
+ public int NavStatus
+ {
+ get { return this.navstatus; }
+ }
+
+ public bool Selected
+ {
+ get { return this.isSelected; }
+ set { this.isSelected = value; }
+ }
+
+ public static DisplayStringType DisplayStringTyp
+ {
+ get { return displayStringType; }
+ set { displayStringType = value; }
+ }
+
+ public bool IsTimedOut
+ {
+ get { return this.isTimedOut; }
+ set { this.isTimedOut = value; }
+ }
+ #endregion
+
+ #region public methods
+
+ public string ToHtmlString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.Append(string.Format("MMSI: {0}
", this.MMSI));
+ sb.Append(string.Format("Name: {0}
", this.Name));
+ sb.Append(string.Format("Latitude: {0}°
", this.LastLatitude.HasValue ? this.LastLatitude.Value.ToString("N3") : "?"));
+ sb.Append(string.Format("Longitude: {0}°
", this.LastLongitude.HasValue ? this.LastLongitude.Value.ToString("N3") : "?"));
+ sb.Append(string.Format("Last report: {0}
", this.LastPositionReport.HasValue ? this.LastPositionReport.Value.ToString() : "?"));
+ sb.Append(string.Format("Type: {0} [{1}]
", this.Description, this.ShipType));
+ sb.Append(string.Format("Navstatus: {0}
", this.NavStatus));
+
+ return sb.ToString();
+ }
+
+ #endregion
+
+ #region overrides
+
+ public override string ToString()
+ {
+ switch (displayStringType)
+ {
+ case DisplayStringType.NAME:
+ string result = "?";
+ if (this.name != null && this.name.Length > 0)
+ result = this.name;
+ return result;
+ case DisplayStringType.MMSI:
+ return this.mmsi.ToString();
+ case DisplayStringType.CALLSIGN:
+ if (this.Callsign == null || this.Callsign.Length == 0)
+ return "?";
+ return this.Callsign;
+ default:
+ return string.Format("{0} - {1}", this.name, this.mmsi);
+ }
+ }
+
+ #endregion
+
+ #region static methods
+
+ #region save a position report
+
+ ///
+ /// Saves a (class A or B) position report
+ ///
+ /// target to save
+ /// id of insert operation (to update hotposition table)
+ public static int? Save(AIS_Target target, DBConnector con, AISStation aisStation)
+ {
+ if(target.LastStaticData == null) return null;
+
+ int mmsi = -1;
+ int id = -1;
+
+ if(target.LastStaticData is AIS_StaticData) {
+ mmsi = ((AIS_StaticData)target.LastStaticData).MMSI;
+ }
+ if(target.LastStaticData is AIS_ClassBStatic) {
+ mmsi = ((AIS_ClassBStatic)target.LastStaticData).MMSI;
+ }
+
+ string query = string.Format("SELECT id FROM aisstaticdata WHERE mmsi={0}", mmsi);
+ object result = con.ExecuteScalar(query);
+
+ if (result != null) // update
+ {
+ id = Convert.ToInt32(result);
+ }
+
+ #region Class A
+
+ if (target.LastStaticData is AIS_StaticData)
+ {
+ AIS_StaticData staticData = target.LastStaticData as AIS_StaticData;
+
+ if (id >= 0)
+ {
+ if (staticData.ETA.HasValue)
+ {
+ query = string.Format("UPDATE aisstaticdata SET imonumber={0}, callsign='{1}', name='{2}', shiptype={3}, typeofdevice='{4}', shipdescription='{5}', eta='{6}', destination='{7}', breadth={8}, length={9}, draught='{10}', stationid={11}, classb=0 WHERE id={12}",
+ staticData.IMONumber,
+ staticData.Callsign.Replace("'","''"),
+ staticData.Name.Replace("'", "''"),
+ staticData.ShipTypeVal,
+ staticData.DeviceName,
+ staticData.ShipType,
+ staticData.DBETA,
+ staticData.Destination.Replace("'", "''"),
+ staticData.Breadth,
+ staticData.Length,
+ staticData.Draught,
+ aisStation.Id,
+ id);
+ }
+ else
+ {
+ query = string.Format("UPDATE aisstaticdata SET imonumber={0}, callsign='{1}', name='{2}', shiptype={3}, typeofdevice='{4}', shipdescription='{5}', destination='{6}', breadth={7}, length={8}, draught='{9}', stationid={10}, classb=0 WHERE id={11}",
+ staticData.IMONumber,
+ staticData.Callsign.Replace("'", "''"),
+ staticData.Name.Replace("'", "''"),
+ staticData.ShipTypeVal,
+ staticData.DeviceName,
+ staticData.ShipType,
+ staticData.Destination.Replace("'", "''"),
+ staticData.Breadth,
+ staticData.Length,
+ staticData.Draught,
+ aisStation.Id,
+ id);
+
+ }
+ con.ExecuteNonQuery(query);
+ }
+ else
+ {
+ if (staticData.ETA.HasValue)
+ {
+
+ query = string.Format("INSERT INTO aisstaticdata SET imonumber={0}, callsign='{1}', name='{2}', shiptype={3}, typeofdevice='{4}', shipdescription='{5}', eta='{6}', destination='{7}', breadth={8}, length={9}, draught='{10}', stationid={11}, mmsi={12}, classb=0",
+ staticData.IMONumber,
+ staticData.Callsign.Replace("'", "''"),
+ staticData.Name.Replace("'", "''"),
+ staticData.ShipTypeVal,
+ staticData.DeviceName,
+ staticData.ShipType,
+ staticData.DBETA,
+ staticData.Destination.Replace("'", "''"),
+ staticData.Breadth,
+ staticData.Length,
+ staticData.Draught,
+ aisStation.Id,
+ staticData.MMSI);
+ }
+ else
+ {
+ query = string.Format("INSERT INTO aisstaticdata SET imonumber={0}, callsign='{1}', name='{2}', shiptype={3}, typeofdevice='{4}', shipdescription='{5}', destination='{6}', breadth={7}, length={8}, draught='{9}', stationid={10}, mmsi={11}, classb=0",
+ staticData.IMONumber,
+ staticData.Callsign.Replace("'", "''"),
+ staticData.Name.Replace("'", "''"),
+ staticData.ShipTypeVal,
+ staticData.DeviceName,
+ staticData.ShipType,
+ staticData.Destination.Replace("'", "''"),
+ staticData.Breadth,
+ staticData.Length,
+ staticData.Draught,
+ aisStation.Id,
+ staticData.MMSI);
+ }
+
+ con.ExecuteNonQuery(query);
+
+ id = Convert.ToInt32(con.ExecuteScalar("SELECT LAST_INSERT_ID()"));
+
+ }
+
+ }
+
+ #endregion
+
+ #region Class B
+
+ if (target.LastStaticData is AIS_ClassBStatic)
+ {
+ AIS_ClassBStatic staticData = target.LastStaticData as AIS_ClassBStatic;
+
+ if (id >= 0) // Update
+ {
+ query = string.Format("UPDATE aisstaticdata SET stationid={0}, shiptype={1}, classb=1", aisStation.Id, staticData.ShipTypeVal);
+ if(staticData.Callsign != null) query += string.Format(", callsign='{0}'", staticData.Callsign);
+ if(staticData.Name != null) query += string.Format(", name='{0}'", staticData.Name);
+ if(staticData.VendorId != null) query += string.Format(", typeofdevice='{0}'", staticData.VendorId);
+ if(staticData.ShipType != null) query += string.Format(", shipdescription='{0}'", staticData.ShipType);
+ query += string.Format(" WHERE id={0}", id);
+
+ con.ExecuteNonQuery(query);
+ }
+ else // Insert
+ {
+ query = string.Format("INSERT INTO aisstaticdata SET callsign='{0}', name='{1}', shiptype={2}, typeofdevice='{3}', shipdescription='{4}', stationid={5}, mmsi={6}, classb=1",
+ staticData.Callsign,
+ staticData.Name,
+ staticData.ShipTypeVal,
+ staticData.VendorId,
+ staticData.ShipType,
+ aisStation.Id,
+ staticData.MMSI
+ );
+
+ con.ExecuteNonQuery(query);
+ id = Convert.ToInt32(con.ExecuteScalar("SELECT LAST_INSERT_ID()"));
+ }
+ }
+
+ #endregion
+
+ return id;
+ }
+
+ #endregion
+
+ ///
+ /// Loads shipname for display (until static data has been received)
+ ///
+ public static string LoadName(int mmsi, DBConnector con)
+ {
+ string query = string.Format("SELECT name FROM aisstaticdata where mmsi={0}", mmsi);
+ string result = con.ExecuteScalar(query) as string;
+ if (result == null) result = "";
+ return result;
+ }
+
+ ///
+ /// Loads callsign for display (until static data has been received)
+ ///
+ public static string LoadCallsign(int mmsi, DBConnector con)
+ {
+ string query = string.Format("SELECT callsign FROM aisstaticdata where mmsi={0}", mmsi);
+ string result = con.ExecuteScalar(query) as string;
+ if (result == null) result = "";
+ return result;
+ }
+
+ ///
+ /// preload target with data from database until static data has been received
+ ///
+ /// target to load
+ public static void PreloadTarget(AIS_Target target, DBConnector con)
+ {
+ if (target.MMSI == 0) return;
+ string query = string.Format("SELECT name, callsign, shiptype FROM aisstaticdata where mmsi={0}", target.MMSI);
+ IDataReader reader = con.ExecuteQuery(query);
+ if (reader.Read())
+ {
+ if (!reader.IsDBNull(0)) target.LastDBName = reader.GetString(0);
+ if (!reader.IsDBNull(1)) target.Callsign = reader.GetString(1);
+ if (!reader.IsDBNull(2))
+ target.TargetType = AIS_StaticData.GetShipTypeSimple(reader.GetInt32(2));
+ }
+ reader.Close();
+ }
+
+ ///
+ /// Load all ships that have a position and static data from database
+ ///
+ public static List LoadDBShips(DBConnector con)
+ {
+ List result = new List();
+ IDataReader reader = con.ExecuteQuery(AISStaticData.LoadDBShipsQuery);
+ while (reader.Read())
+ {
+ AISStaticData ship = new AISStaticData();
+ ship.mmsi = reader.GetInt32(0);
+ ship.lastposition = reader.GetDateTime(1);
+ ship.lastLatitude = (double) Math.Round(reader.GetInt32(2) / 600000.0, 4);
+ ship.lastLongitude = (double) Math.Round(reader.GetInt32(3) / 600000.0, 4);
+ ship.stationid = reader.GetInt32(4);
+ ship.cog = reader.GetInt32(5);
+ ship.heading = reader.GetInt32(6);
+ ship.navstatus = reader.GetInt32(7);
+ if(!reader.IsDBNull(8))
+ ship.callsign = reader.GetString(8);
+ if(!reader.IsDBNull(9))
+ ship.name = reader.GetString(9);
+ ship.shiptype = reader.GetInt32(10);
+ if (reader.IsDBNull(11)) ship.isClassB = false;
+ else ship.isClassB = reader.GetBoolean(11);
+ ship.shipdescription = reader.GetString(12);
+ result.Add(ship);
+ }
+ reader.Close();
+ Trace.WriteLine(string.Format("AISStaticData: {0} ships loaded from DB", result.Count));
+ return result;
+ }
+
+ #endregion
+
+ }
+
+ #region Comparer Class for grid
+
+ public class AISStaticData_Comparer : IComparer
+ {
+ private SortPropertyEnum sortProperty = SortPropertyEnum.NAME;
+ private ListSortDirection sortDirection = ListSortDirection.Ascending;
+
+ public enum SortPropertyEnum
+ {
+ NONE,
+ MMSI,
+ NAME,
+ CALLSIGN,
+ LASTUPDATE,
+ DESCRIPTION
+ }
+
+ #region Properties
+
+ public ListSortDirection SortDirection
+ {
+ get { return this.sortDirection; }
+ set { this.sortDirection = value; }
+ }
+
+ public SortPropertyEnum SortProperty
+ {
+ get { return this.sortProperty; }
+ set { this.sortProperty = value; }
+ }
+
+ #endregion
+
+ public int Compare(AISStaticData x, AISStaticData y)
+ {
+ switch (this.sortProperty)
+ {
+ case SortPropertyEnum.NONE:
+ return 0;
+ case SortPropertyEnum.NAME:
+ {
+ string xName = x.Name;
+ if (xName == null) xName = "";
+ string yName = y.Name;
+ if (yName == null) yName = "";
+ if (this.sortDirection == ListSortDirection.Ascending)
+ return xName.CompareTo(yName);
+ else
+ return yName.CompareTo(xName);
+ }
+ case SortPropertyEnum.LASTUPDATE:
+ {
+ DateTime xTime = x.LastPositionReport ?? DateTime.MinValue;
+ DateTime yTime = y.LastPositionReport ?? DateTime.MinValue;
+ if (this.sortDirection == ListSortDirection.Ascending)
+ return xTime.CompareTo(yTime);
+ else
+ return yTime.CompareTo(xTime);
+ }
+ case SortPropertyEnum.MMSI:
+ {
+ if (this.sortDirection == ListSortDirection.Ascending)
+ return x.MMSI.CompareTo(y.MMSI);
+ else
+ return y.MMSI.CompareTo(x.MMSI);
+ }
+ case SortPropertyEnum.CALLSIGN:
+ {
+ string xCallsign = x.Callsign;
+ if (xCallsign == null) xCallsign = "";
+ string yCallsign = y.Callsign;
+ if (yCallsign == null) yCallsign = "";
+ if (this.sortDirection == ListSortDirection.Ascending)
+ return xCallsign.CompareTo(yCallsign);
+ else
+ return yCallsign.CompareTo(xCallsign);
+ }
+ case SortPropertyEnum.DESCRIPTION:
+ {
+ string xDescription = x.Description;
+ if (xDescription == null) xDescription = "";
+ string yDescription = y.Description;
+ if (yDescription == null) yDescription = "";
+ if (this.sortDirection == ListSortDirection.Ascending)
+ return xDescription.CompareTo(yDescription);
+ else
+ return yDescription.CompareTo(xDescription);
+ }
+ default:
+ return 0;
+ }
+
+
+
+ }
+
+ #endregion
+ }
+}
diff --git a/AIS/bsmd.AISService/DB/AISStation.cs b/AIS/bsmd.AISService/DB/AISStation.cs
new file mode 100644
index 00000000..b3d159ea
--- /dev/null
+++ b/AIS/bsmd.AISService/DB/AISStation.cs
@@ -0,0 +1,258 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Data;
+using System.Globalization;
+
+using bsmd.AISService.AIS;
+
+namespace bsmd.AISService.DB
+{
+
+ public class AISStation
+ {
+ #region private members
+
+ private int station_Id;
+ private string name;
+ private bool active;
+ private string comport;
+ private int baudrate;
+ private int telnetPort;
+ private string telnetHost;
+ private bool onAir = false;
+ private double rangeMax = 0;
+ private double rangeAverage;
+ private double coverage;
+ private double latitude;
+ private double longitude;
+ private string address;
+ private DateTime? lastPosTimestamp;
+ private bool isDirty = false;
+ private Dictionary targets = new Dictionary();
+
+ #endregion
+
+ #region Properties
+
+ public string Name { get { return this.name; } set { this.name = value; } }
+ public int Id { get { return this.station_Id; } }
+ public bool Active { get { return this.active; } set { this.active = value; } }
+ public string COMPort { get { return this.comport; } set { this.comport = value; } }
+ public int Baudrate { get { return this.baudrate; } set { this.baudrate = value; } }
+ public string TelnetHost { get { return this.telnetHost; } set { this.telnetHost = value; } }
+ public int TelnetPort { get { return this.telnetPort; } set { this.telnetPort = value; } }
+ public bool OnAir { get { return this.onAir; } set { this.onAir = value; } }
+ public double RangeMax { get { return this.rangeMax; } }
+ public double RangeAverage { get { return this.rangeAverage; } }
+ public double Coverage { get { return this.coverage; } }
+ public string CoverageText { get { return string.Format("{0} qkm", this.coverage.ToString("N2")); } }
+ public double Latitude
+ {
+ get { return this.latitude; }
+ set
+ {
+ this.latitude = value;
+ this.isDirty = true;
+ this.rangeMax = 0;
+ }
+ }
+ public double Longitude
+ {
+ get { return this.longitude; }
+ set
+ {
+ this.longitude = value;
+ this.isDirty = true;
+ this.rangeMax = 0;
+ }
+ }
+ public string Address { get { return this.address; } }
+ public bool IsDirty { get { return this.isDirty; } }
+
+ public DateTime? LastPosTimestamp
+ {
+ get { return this.lastPosTimestamp; }
+ set
+ {
+ this.lastPosTimestamp = value;
+ }
+ }
+
+ public int NumTargets
+ {
+ get { return this.targets.Count; }
+ }
+
+ public Dictionary Targets { get { return this.targets; } }
+
+ public bool MustDelete { get; set; }
+
+ #endregion
+
+ #region public methods
+
+ public bool Save(DBConnector con)
+ {
+ string query = string.Format("UPDATE aisstation SET lat={0}, lon={1}, telnetHost='{2}', telnetPort={3}, comPort='{4}', name='{5}', baudrate={6} WHERE id={7}",
+ (int) (this.latitude * 600000),
+ (int) (this.longitude * 600000),
+ this.telnetHost,
+ this.telnetPort,
+ this.comport,
+ this.name,
+ this.baudrate,
+ this.station_Id);
+
+ if (con.ExecuteNonQuery(query) == 1)
+ {
+ this.isDirty = false;
+ return true;
+ }
+ return false;
+ }
+
+ public void UpdateWithPositionReport(int mmsi, double lat, double lon, DateTime timestamp)
+ {
+
+ double distance = bsmd.AISService.AIS.AIS.GetDistance(this.Latitude, this.Longitude, lat, lon);
+ if (distance > this.rangeMax)
+ {
+ this.rangeMax = distance;
+ this.coverage = Math.PI * this.rangeMax * this.rangeMax;
+ }
+
+ lock (this.Targets)
+ {
+ if (!this.targets.ContainsKey(mmsi))
+ this.targets.Add(mmsi, distance);
+ else this.targets[mmsi] = distance;
+ }
+
+ // durchschnittl. Reichweite
+ double sumRange = 0;
+ foreach (int key in this.targets.Keys)
+ sumRange += this.targets[mmsi];
+ this.rangeAverage = sumRange / (double)this.targets.Count;
+
+ if (!this.lastPosTimestamp.HasValue) this.lastPosTimestamp = timestamp;
+ else
+ {
+ if (this.lastPosTimestamp.Value < timestamp)
+ this.lastPosTimestamp = timestamp;
+ }
+
+ }
+
+ ///
+ /// clear targets and reset coverage
+ ///
+ public void ResetStation()
+ {
+ this.targets.Clear();
+ this.coverage = 0;
+ this.rangeAverage = 0;
+ this.rangeMax = 0;
+ }
+
+ ///
+ /// deletes this station
+ ///
+ public void Delete(DBConnector con)
+ {
+ string query = string.Format("DELETE FROM aisstation WHERE id={0}", this.Id);
+ con.ExecuteNonQuery(query);
+ }
+
+ #endregion
+
+ #region static methods
+
+ public static List LoadStations(DBConnector con)
+ {
+ List result = new List();
+ string query = "SELECT id, name, active, lat, lon, address, telnetHost, telnetPort, comPort, baudrate FROM aisstation";
+ IDataReader reader = con.ExecuteQuery(query);
+ if (reader == null) return result;
+
+ while (reader.Read())
+ {
+ AISStation station = new AISStation();
+ station.station_Id = reader.GetInt32(0);
+ station.name = reader.GetString(1);
+ station.active = reader.GetBoolean(2);
+ station.latitude = (double) reader.GetInt32(3) / 600000;
+ station.longitude = (double) reader.GetInt32(4) / 600000;
+ if(!reader.IsDBNull(5))
+ station.address = reader.GetString(5);
+ if (!reader.IsDBNull(6))
+ station.telnetHost = reader.GetString(6);
+ if (!reader.IsDBNull(7))
+ station.telnetPort = reader.GetInt32(7);
+ if (!reader.IsDBNull(8))
+ station.comport = reader.GetString(8);
+ if (!reader.IsDBNull(9))
+ station.baudrate = reader.GetInt32(9);
+ result.Add(station);
+ }
+ reader.Close();
+ return result;
+ }
+
+ public static AISStation CreateStation(string name, DBConnector con)
+ {
+ AISStation newStation = new AISStation();
+ newStation.name = name;
+ newStation.active = true;
+ string query = string.Format("INSERT INTO aisstation SET name='{0}',active=1",
+ name);
+ con.ExecuteNonQuery(query);
+ newStation.station_Id = Convert.ToInt32(con.ExecuteScalar("SELECT LAST_INSERT_ID()"));
+
+ return newStation;
+ }
+
+ public static List CreateSerial_IOs(List stationList)
+ {
+ List result = new List();
+ foreach (AISStation station in stationList)
+ {
+ if ((station.COMPort != null) && (station.COMPort.Length > 0))
+ {
+ Serial_IO serialIO = new Serial_IO();
+ serialIO.BaudRate = (station.Baudrate == 0) ? 9600 : station.Baudrate;
+ serialIO.ComPort = station.COMPort;
+ serialIO.StationName = station.Name;
+ result.Add(serialIO);
+ }
+ }
+ return result;
+ }
+
+ public static List CreateAIS_Telnets(List stationList)
+ {
+ List result = new List();
+ foreach (AISStation station in stationList)
+ {
+ if ((station.TelnetHost != null) && (station.TelnetHost.Length > 0))
+ {
+ try
+ {
+ AIS_Telnet telnet = new AIS_Telnet(station.TelnetHost, station.TelnetPort);
+ telnet.StationName = station.Name;
+ result.Add(telnet);
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Trace.WriteLine(string.Format("AIS_Telnet: cannot connect to host {0} port {1}: {2}",
+ station.TelnetHost ?? "", station.TelnetPort, ex.Message));
+ }
+ }
+ }
+ return result;
+ }
+
+ #endregion
+
+ }
+}
diff --git a/AIS/bsmd.AISService/DB/AISWatchkeeper.cs b/AIS/bsmd.AISService/DB/AISWatchkeeper.cs
new file mode 100644
index 00000000..0732ce9d
--- /dev/null
+++ b/AIS/bsmd.AISService/DB/AISWatchkeeper.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Data;
+
+using bsmd.AISService.AIS;
+
+namespace bsmd.AISService.DB
+{
+ public class AISWatchkeeper
+ {
+ private static string GetAllWatchkeeperShipsQuery = "SELECT mmsi, name, aktiv, watching, tracking FROM wk_ship";
+ private int mmsi;
+ private string name;
+ private bool aktiv;
+ private bool watching;
+ private bool tracking;
+
+ public int MMSI
+ {
+ get { return this.mmsi; }
+ }
+
+ public bool Aktiv
+ {
+ get { return this.aktiv; }
+ }
+
+ public static List GetWatchkeeperShips(DBConnector con)
+ {
+ List result = new List();
+ IDataReader reader = con.ExecuteQuery(AISWatchkeeper.GetAllWatchkeeperShipsQuery);
+ if (reader == null) return result;
+ while (reader.Read())
+ {
+ AISWatchkeeper wkShip = new AISWatchkeeper();
+ wkShip.mmsi = reader.GetInt32(0);
+ wkShip.name = reader.GetString(1);
+ wkShip.aktiv = reader.GetBoolean(2);
+ wkShip.watching = reader.GetBoolean(3);
+ wkShip.tracking = reader.GetBoolean(4);
+ result.Add(wkShip);
+ }
+ reader.Close();
+ return result;
+ }
+
+
+ }
+}
diff --git a/AIS/bsmd.AISService/DB/DBConnector.cs b/AIS/bsmd.AISService/DB/DBConnector.cs
new file mode 100644
index 00000000..d0faf89e
--- /dev/null
+++ b/AIS/bsmd.AISService/DB/DBConnector.cs
@@ -0,0 +1,212 @@
+// Copyright (c) 2008-2018 schick Informatik
+// Description: Database connector
+//
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Data;
+using System.Data.SqlClient;
+
+using bsmd.AISService.AIS;
+
+namespace bsmd.AISService.DB
+{
+
+ public class DBConnector
+ {
+ private string connectionString;
+ private SqlConnection dbCon = null;
+ private List watchkeeperShips = null;
+ private List dbShips = null;
+ private Dictionary updateStations = null;
+
+ public DBConnector() { }
+
+ #region Properties
+
+ public string ConnectionString
+ {
+ get { return this.connectionString; }
+ set { this.connectionString = value; }
+ }
+
+ public List DBShips
+ {
+ get
+ {
+ if (this.dbShips == null)
+ {
+ lock (this.dbCon)
+ {
+ this.dbShips = AISStaticData.LoadDBShips(this);
+ }
+ }
+ return this.dbShips;
+ }
+ set { this.dbShips = value; }
+ }
+
+ public List WatchkeeperShips
+ {
+ get
+ {
+ if (this.watchkeeperShips == null) this.watchkeeperShips = AISWatchkeeper.GetWatchkeeperShips(this);
+ return this.watchkeeperShips;
+ }
+ }
+
+ #endregion
+
+ #region public methods
+
+ public SqlDataReader ExecuteQuery(string query)
+ {
+ if (!this.CheckConnection()) return null;
+
+ SqlCommand cmd = new SqlCommand(query, this.dbCon);
+ return cmd.ExecuteReader();
+ }
+
+ public int ExecuteNonQuery(string query)
+ {
+ if (!this.CheckConnection()) return 0;
+ SqlCommand cmd = new SqlCommand(query, this.dbCon);
+ return cmd.ExecuteNonQuery();
+ }
+
+ public object ExecuteScalar(string query)
+ {
+ if (!this.CheckConnection()) return 0;
+ SqlCommand cmd = new SqlCommand(query, this.dbCon);
+ return cmd.ExecuteScalar();
+ }
+
+ public bool Open()
+ {
+ if (this.dbCon != null && this.dbCon.State == System.Data.ConnectionState.Open) return true;
+ try
+ {
+ this.dbCon = new SqlConnection(this.connectionString);
+ this.dbCon.Open();
+ if (this.dbCon.State == System.Data.ConnectionState.Open)
+ return true;
+ }
+ catch (SqlException anException)
+ {
+ Trace.WriteLine(string.Format("cannot open SQL DB connection: {0}", anException.Message));
+ }
+ return false;
+ }
+
+ public void Close()
+ {
+ try
+ {
+ if (this.dbCon != null && this.dbCon.State == System.Data.ConnectionState.Open)
+ this.dbCon.Close();
+ }
+ catch (Exception) { } // egal
+ }
+
+ public void Update(AIS_Target target)
+ {
+ if (this.dbCon.State != System.Data.ConnectionState.Open) // reopen
+ {
+ this.dbCon.Close();
+ this.dbCon.Open();
+ }
+
+ if (this.updateStations == null)
+ {
+ this.updateStations = new Dictionary();
+ Trace.WriteLine("loading stations..");
+ List stations = AISStation.LoadStations(this);
+ Trace.WriteLine(string.Format("{0} stations loaded", stations.Count));
+ foreach (AISStation station in stations)
+ if (!updateStations.ContainsKey(station.Name))
+ updateStations.Add(station.Name, station);
+ }
+
+ if (target.LastPosReport != null)
+ {
+ Hotposition hotposition = Hotposition.LoadForMMSI(target.MMSI, this);
+ int? pid = AISPosReport.Save(target, this, updateStations.ContainsKey(target.Station) ? updateStations[target.Station] : null);
+ if (pid.HasValue)
+ {
+ hotposition.PosReportId = pid.Value;
+ hotposition.Save(this);
+ }
+ }
+ else
+ {
+ Trace.WriteLine(string.Format("last pos report is null for target {0}", target.MMSI));
+ }
+ if (target.LastStaticData != null)
+ {
+ AISStaticData.Save(target, this, updateStations.ContainsKey(target.Station) ? updateStations[target.Station] : null);
+ }
+ if ((target.Name == null || target.LastDBName == null) && (target.MMSI > 0))
+ {
+ // preload values from DB
+ AISStaticData.PreloadTarget(target, this);
+ }
+
+ target.UpdateDB = false; // reset update flag
+ // Watchkeeper check
+
+ if (this.watchkeeperShips == null)
+ this.watchkeeperShips = AISWatchkeeper.GetWatchkeeperShips(this);
+
+
+ if (!target.IsWatchkeeperShip.HasValue && this.watchkeeperShips != null)
+ {
+ for (int i = 0; i < this.watchkeeperShips.Count; i++)
+ {
+ if (this.watchkeeperShips[i].MMSI == target.MMSI) // found it
+ target.IsWatchkeeperShip = true;
+ }
+
+ if (!target.IsWatchkeeperShip.HasValue) // didn't find it
+ target.IsWatchkeeperShip = false;
+ }
+
+
+ }
+
+ public void ResetWatchkeeperList()
+ {
+ this.watchkeeperShips = null;
+ }
+
+ public void SaveStation(AISStation station)
+ {
+ station.Save(this);
+ }
+
+ #endregion
+
+ #region private methods
+
+ private bool CheckConnection()
+ {
+ // if connection has been closed, re-open the connection
+ if (this.dbCon.State != System.Data.ConnectionState.Open)
+ {
+ try
+ {
+ this.dbCon.Close();
+ this.Open();
+ }
+ catch (SqlException ex)
+ {
+ System.Diagnostics.Trace.WriteLine(ex.ToString());
+ }
+ }
+ return this.dbCon.State == System.Data.ConnectionState.Open;
+ }
+
+ #endregion
+
+ }
+}
diff --git a/AIS/bsmd.AISService/DB/Hotposition.cs b/AIS/bsmd.AISService/DB/Hotposition.cs
new file mode 100644
index 00000000..25d85d7c
--- /dev/null
+++ b/AIS/bsmd.AISService/DB/Hotposition.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Data;
+using System.Data.SqlClient;
+using bsmd.AISService.AIS;
+
+namespace bsmd.AISService.DB
+{
+ internal class Hotposition
+ {
+ private int id;
+ private int mmsi;
+ private int pid;
+
+ public int MMSI
+ {
+ get { return this.mmsi; }
+ }
+
+ public int PosReportId
+ {
+ get { return this.pid; }
+ set { this.pid = value; }
+ }
+
+ public void Save(DBConnector con)
+ {
+ string query = string.Format("UPDATE hotposition SET mmsi={0}, pid={1} WHERE id={2}",
+ this.mmsi, this.pid, this.id);
+ con.ExecuteNonQuery(query);
+ }
+
+ public static Hotposition LoadForMMSI(int mmsi, DBConnector con)
+ {
+ List results = new List();
+ string query = string.Format("SELECT id, pid FROM hotposition WHERE mmsi={0}", mmsi);
+ SqlDataReader reader = con.ExecuteQuery(query);
+ if (reader != null)
+ {
+ while (reader.Read())
+ {
+ Hotposition hp = new Hotposition();
+ hp.id = reader.GetInt32(0);
+ hp.mmsi = mmsi;
+ if (!reader.IsDBNull(1))
+ hp.pid = reader.GetInt32(1);
+ results.Add(hp);
+ }
+ reader.Close();
+ }
+
+ if (results.Count == 0)
+ {
+ // neuen Eintrag erzeugen
+ Hotposition hp = new Hotposition();
+ string insertQuery = string.Format("INSERT INTO hotposition SET mmsi={0}", mmsi);
+ con.ExecuteNonQuery(insertQuery);
+
+ object ob = con.ExecuteScalar("SELECT LAST_INSERT_ID()");
+ hp.id = Convert.ToInt32(ob);
+ hp.mmsi = mmsi;
+ return hp;
+ }
+ else if (results.Count == 1)
+ {
+ return results[0];
+ }
+ else
+ {
+ // überschüssige HP's löschen (jeweils nur eins pro MMSI)
+ for (int i = 1; i < results.Count; i++)
+ {
+ string delQuery = string.Format("DELETE FROM hotposition WHERE id={0}", results[i].id);
+ con.ExecuteNonQuery(delQuery);
+ }
+
+ return results[0];
+ }
+ }
+
+ }
+}
diff --git a/AIS/bsmd.AISService/Program.cs b/AIS/bsmd.AISService/Program.cs
new file mode 100644
index 00000000..28f96537
--- /dev/null
+++ b/AIS/bsmd.AISService/Program.cs
@@ -0,0 +1,127 @@
+// Copyright (c) 2008-2018 schick Informatik
+// Description:
+//
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Configuration.Install;
+using System.Linq;
+using System.Reflection;
+using System.ServiceProcess;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace bsmd.AISService
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ public static int Main(string[] args)
+ {
+ if (Environment.UserInteractive)
+ {
+ if (args.Length > 0)
+ {
+ string arg = args[0].ToLowerInvariant().Substring(0, 2);
+ switch (arg)
+ {
+ case "/i": // install
+ return InstallService();
+
+ case "/u": // uninstall
+ return UninstallService();
+
+ default: // unknown option
+ Console.WriteLine("Argument not recognized: {0}", args[0]);
+ Console.WriteLine(string.Empty);
+ DisplayUsage();
+ return 1;
+ }
+ }
+ else
+ {
+ DisplayUsage();
+ }
+ }
+ else
+ {
+ ServiceBase[] ServicesToRun;
+ ServicesToRun = new ServiceBase[]
+ {
+ new AISService()
+ };
+ ServiceBase.Run(ServicesToRun);
+ }
+ return 0;
+ }
+
+ private static void DisplayUsage()
+ {
+ //..
+ }
+
+ private static int InstallService()
+ {
+ var service = new AISService();
+
+ try
+ {
+ // perform specific install steps for our queue service.
+ //service.InstallService();
+
+ // install the service with the Windows Service Control Manager (SCM)
+ ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
+ }
+ catch (Exception ex)
+ {
+ if (ex.InnerException != null && ex.InnerException.GetType() == typeof(Win32Exception))
+ {
+ Win32Exception wex = (Win32Exception)ex.InnerException;
+ Console.WriteLine("Error(0x{0:X}): Service already installed!", wex.ErrorCode);
+ return wex.ErrorCode;
+ }
+ else
+ {
+ Console.WriteLine(ex.ToString());
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+
+ private static int UninstallService()
+ {
+ var service = new AISService();
+
+ try
+ {
+ // perform specific uninstall steps for our queue service
+ //service.UninstallService();
+
+ // uninstall the service from the Windows Service Control Manager (SCM)
+ ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
+ }
+ catch (Exception ex)
+ {
+ if (ex.InnerException.GetType() == typeof(Win32Exception))
+ {
+ Win32Exception wex = (Win32Exception)ex.InnerException;
+ Console.WriteLine("Error(0x{0:X}): Service not installed!", wex.ErrorCode);
+ return wex.ErrorCode;
+ }
+ else
+ {
+ Console.WriteLine(ex.ToString());
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+
+ }
+}
diff --git a/AIS/bsmd.AISService/Properties/AssemblyInfo.cs b/AIS/bsmd.AISService/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..27dbd0ff
--- /dev/null
+++ b/AIS/bsmd.AISService/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("bsmd.AISService")]
+[assembly: AssemblyDescription("Windows Service zum Einlesen von AIS Daten aus einem TCP/IP Datenstrom")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("schick Informatik")]
+[assembly: AssemblyProduct("bsmd.AISService")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("a26a6de3-8505-4ec2-9eb5-12e5ebe83b11")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AIS/bsmd.AISService/Properties/Settings.Designer.cs b/AIS/bsmd.AISService/Properties/Settings.Designer.cs
new file mode 100644
index 00000000..d3648523
--- /dev/null
+++ b/AIS/bsmd.AISService/Properties/Settings.Designer.cs
@@ -0,0 +1,35 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace bsmd.AISService.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string ConnectionString {
+ get {
+ return ((string)(this["ConnectionString"]));
+ }
+ }
+ }
+}
diff --git a/AIS/bsmd.AISService/Properties/Settings.settings b/AIS/bsmd.AISService/Properties/Settings.settings
new file mode 100644
index 00000000..ef60b87c
--- /dev/null
+++ b/AIS/bsmd.AISService/Properties/Settings.settings
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AIS/bsmd.AISService/bsmd.AISService.csproj b/AIS/bsmd.AISService/bsmd.AISService.csproj
new file mode 100644
index 00000000..158fffcc
--- /dev/null
+++ b/AIS/bsmd.AISService/bsmd.AISService.csproj
@@ -0,0 +1,126 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {A26A6DE3-8505-4EC2-9EB5-12E5EBE83B11}
+ WinExe
+ Properties
+ bsmd.AISService
+ bsmd.AISService
+ v4.5.2
+ 512
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ true
+
+
+ bsmdKey.snk
+
+
+
+ packages\log4net.2.0.8\lib\net45-full\log4net.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Component
+
+
+ AISService.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Component
+
+
+ ProjectInstaller.cs
+
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+ ProjectInstaller.cs
+
+
+
+
+
\ No newline at end of file
diff --git a/AIS/bsmd.AISService/bsmd.AISService.licenseheader b/AIS/bsmd.AISService/bsmd.AISService.licenseheader
new file mode 100644
index 00000000..fa18f4f0
--- /dev/null
+++ b/AIS/bsmd.AISService/bsmd.AISService.licenseheader
@@ -0,0 +1,16 @@
+extensions: designer.cs generated.cs
+extensions: .cs .cpp .h
+// Copyright (c) 2008-2018 schick Informatik
+// Description:
+//
+
+extensions: .aspx .ascx
+<%--
+Copyright (c) 2008-2018 schick Informatik
+--%>
+extensions: .vb
+'Sample license text.
+extensions: .xml .config .xsd
+
\ No newline at end of file
diff --git a/AIS/bsmd.AISService/bsmd.AISService.sln b/AIS/bsmd.AISService/bsmd.AISService.sln
new file mode 100644
index 00000000..4b965867
--- /dev/null
+++ b/AIS/bsmd.AISService/bsmd.AISService.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25123.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bsmd.AISService", "bsmd.AISService.csproj", "{A26A6DE3-8505-4EC2-9EB5-12E5EBE83B11}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A26A6DE3-8505-4EC2-9EB5-12E5EBE83B11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A26A6DE3-8505-4EC2-9EB5-12E5EBE83B11}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A26A6DE3-8505-4EC2-9EB5-12E5EBE83B11}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A26A6DE3-8505-4EC2-9EB5-12E5EBE83B11}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AIS/bsmd.AISService/bsmdKey.snk b/AIS/bsmd.AISService/bsmdKey.snk
new file mode 100644
index 00000000..fd20ba24
Binary files /dev/null and b/AIS/bsmd.AISService/bsmdKey.snk differ
diff --git a/AIS/bsmd.AISService/packages.config b/AIS/bsmd.AISService/packages.config
new file mode 100644
index 00000000..2b3696ab
--- /dev/null
+++ b/AIS/bsmd.AISService/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/ENI-2/ENI2/ENI2/App.config b/ENI-2/ENI2/ENI2/App.config
index 097725ac..2233c98e 100644
--- a/ENI-2/ENI2/ENI2/App.config
+++ b/ENI-2/ENI2/ENI2/App.config
@@ -26,12 +26,12 @@
1000
- http://192.168.2.4/LockingService/LockingService.svc
-
+
+ http://heupferd/bsmd.LockingService/LockingService.svc
- Data Source=192.168.2.12;Initial Catalog=nsw;Uid=dfuser;Pwd=dfpasswd;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False
-
+
+ Data Source=(localdb)\Projects;Initial Catalog=nsw;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False
diff --git a/Stundensheet.xlsx b/Stundensheet.xlsx
index ae7317bb..fc3530a8 100644
Binary files a/Stundensheet.xlsx and b/Stundensheet.xlsx differ
diff --git a/nsw/Source/bsmd.ExcelReadService/ExcelReader.cs b/nsw/Source/bsmd.ExcelReadService/ExcelReader.cs
index 959f44e1..b33d3df0 100644
--- a/nsw/Source/bsmd.ExcelReadService/ExcelReader.cs
+++ b/nsw/Source/bsmd.ExcelReadService/ExcelReader.cs
@@ -310,8 +310,8 @@ namespace bsmd.ExcelReadService
if (val.Length == 2)
{
- this.Conf.ConfirmText(lookup, val, ReadState.OK);
val = val.ToUpper();
+ this.Conf.ConfirmText(lookup, val, ReadState.OK);
}
}
return val;
diff --git a/nsw/Source/bsmd.database/INFO.cs b/nsw/Source/bsmd.database/INFO.cs
index 694e2de7..57140b88 100644
--- a/nsw/Source/bsmd.database/INFO.cs
+++ b/nsw/Source/bsmd.database/INFO.cs
@@ -196,7 +196,7 @@ namespace bsmd.database
public override void Validate(List errors, List violations)
{
- if ((PortArea.Length >= 2) && (PortArea.Length <= 4))
+ if ((PortArea != null) && (PortArea.Length >= 2) && (PortArea.Length <= 4))
{
if ((RuleEngine.PortAreaChecker != null) && (this.MessageCore != null))
if (!RuleEngine.PortAreaChecker(this.MessageCore.PoC, this.PortArea))
@@ -204,7 +204,8 @@ namespace bsmd.database
}
else
{
- errors.Add(RuleEngine.CreateError(ValidationCode.PORTAREA, "PortArea", this.PortArea, "INFO", "", this.Tablename));
+ if(this.MessageCore.PoC != "DEHAM")
+ errors.Add(RuleEngine.CreateError(ValidationCode.PORTAREA, "PortArea", this.PortArea ?? "", "INFO", "", this.Tablename));
}
}