Initialer Stand von AIS nach BSMD portiert, bisher ohne Testing

This commit is contained in:
Daniel Schick 2018-02-04 10:09:48 +00:00
parent 8296296463
commit 061f3e1213
40 changed files with 4669 additions and 7 deletions

View File

@ -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;
}
/// <summary>
/// Factory method to create messages based on the message type
/// </summary>
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
/// <summary>
/// mehr dazu hier:
/// http://www.codeguru.com/cpp/cpp/algorithms/general/article.php/c5115/
/// </summary>
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
}
}

View File

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

View File

@ -0,0 +1,130 @@
using System;
using System.Collections;
using System.Text;
using System.Diagnostics;
namespace bsmd.AISService.AIS
{
/// <summary>
/// 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 ;)
/// </summary>
// 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;
}
}
}

View File

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

View File

@ -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<SerialPort> SerialPorts = new List<SerialPort>();
public List<TelnetConnection> TelnetConnections = new List<TelnetConnection>();
#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; }
}
/// <summary>
/// timer interval for database updates
/// </summary>
public int DBUpdateInterval
{
get { return this.dbUpdateInterval; }
set { this.dbUpdateInterval = value; }
}
/// <summary>
/// minimum amount of minutes between two position reports to be
/// written to database
/// </summary>
public int DBMinPosReportTimeDifference
{
get { return this.dbMinPosReportTimeDifference; }
set { this.dbMinPosReportTimeDifference = value; }
}
/// <summary>
/// number of seconds after which a station is marked offline since
/// sending the last pos report
/// </summary>
public int StationIsOfflineTimeDifferenceSecs
{
get { return this.stationIsOfflineTimeDifferenceSecs; }
set { this.stationIsOfflineTimeDifferenceSecs = value; }
}
/// <summary>
/// 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)
/// </summary>
public int TargetStaleMins
{
get { return this.targetStaleMins; }
set { this.targetStaleMins = value; }
}
/// <summary>
/// Root path to where Viewer stores OSM tiles
/// </summary>
public string TilePath { get; set; }
/// <summary>
/// full path to logfile
/// </summary>
public string LogfilePath { get; set; }
/// <summary>
/// outputs assembly version
/// </summary>
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
}
}

View File

@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;
namespace bsmd.AISService.AIS
{
/// <summary>
/// Diese Klasse setzt fragmentierte AIS Telegramme wieder zusammen und decodiert sie
/// </summary>
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<AISQueueElement> inputDataQueue = new Queue<AISQueueElement>();
private Thread decodingThread;
private bool runDecoder = true;
private int sleepMS = 250;
private Dictionary<int, List<AISQueueElement>> fragmentDict = new Dictionary<int, List<AISQueueElement>>();
#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
/// <summary>
/// Thread worker method
/// </summary>
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<AISQueueElement>());
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
/// <summary>
/// check to see if all fragments are available
/// </summary>
private static bool FragmentsComplete(List<AISQueueElement> 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;
}
/// <summary>
/// assembles message fragments. Care must be taken since fragments can appear
/// out of order
/// </summary>
private static string ConcatenateFragments(List<AISQueueElement> 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);
}
}
}

View File

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

View File

@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Timers;
using System.Diagnostics;
namespace bsmd.AISService.AIS
{
/// <summary>
/// Hier laufen die Fäden zusammen. Diese Klasse enthält alle Objekte und kann direkt von
/// Konsolen / services und Windowsprogrammen verwendet werden
/// </summary>
public class AIS_QueueManager
{
public delegate void AISQueueChangedHandler(AIS_Target target);
public event AISQueueChangedHandler AISQueueChanged;
public event AISQueueChangedHandler DBUpdateRequired;
private Dictionary<int, AIS_Target> activeTargets = new Dictionary<int, AIS_Target>();
private List<AIS_Target> activeTargetList = new List<AIS_Target>();
private List<AIS_Target> databaseTargets = new List<AIS_Target>();
private List<AIS_Target> watchkeeperTargets = new List<AIS_Target>();
private AIS_Configuration configuration;
private List<SerialDataHandler> serialHandlerList = new List<SerialDataHandler>();
private List<TelnetDataHandler> telnetHandlerList = new List<TelnetDataHandler>();
private List<AIS_Target> dbUpdateQueue = new List<AIS_Target>();
private Timer dbUpdateTimer = new Timer();
private bool isStarted = false;
#region Construction
public AIS_QueueManager(AIS_Configuration configuration, List<Serial_IO> serialIOs, List<AIS_Telnet> 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<AIS_Target> 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.activeTargetList.Count; i++)
{
if (!this.activeTargetList[i].LastUpdate.HasValue)
continue;
int diffmin = (int) (DateTime.Now - this.activeTargetList[i].LastUpdate.Value).TotalMinutes;
if (diffmin > 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
}
}

View File

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

View File

@ -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
}
/// <summary>
/// vereinfacht
/// </summary>
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
}
}

View File

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

View File

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

View File

@ -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;
}
}
/// <summary>
/// Factory method for nmea types
/// </summary>
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");
}
}
}

View File

@ -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
/// <summary>
/// 1-based total number of sentences for this ais message
/// </summary>
public int Total_Sentence_Nr
{
get { return this.total_sentence_nr; }
}
/// <summary>
/// 1-based fragment number of sentences
/// </summary>
public int Msg_Sentence_Nr
{
get { return this.msg_sentence_nr; }
}
/// <summary>
/// sequential message id for multi-sentence messages (can be empty)
/// </summary>
public int? Seq_Message_Ident
{
get { return this.seq_message_ident; }
}
/// <summary>
/// 'A' = 161.975Mhz (87B),
/// 'B' = 162.025Mhz (88B)
/// </summary>
public string AIS_Channel_nr
{
get { return this.ais_channel_nr; }
}
/// <summary>
/// AIS message data
/// </summary>
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");
}
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace bsmd.AISService.AIS
{
/// <summary>
/// NMEA PNMLS sentence
/// sentence shows signal level for preceding message
/// </summary>
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
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,37 @@
namespace bsmd.AISService
{
partial class AISService
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
this.ServiceName = "Service1";
}
#endregion
}
}

View File

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

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="bsmd.AISService.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<applicationSettings>
<bsmd.AISService.Properties.Settings>
<setting name="ConnectionString" serializeAs="String">
<value />
</setting>
</bsmd.AISService.Properties.Settings>
</applicationSettings>
</configuration>

View File

@ -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
{
/// <summary>
/// Saves a (class A or B) position report
/// </summary>
/// <param name="target">target to save</param>
/// <returns>id of insert operation (to update hotposition table)</returns>
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;
}
}
}

View File

@ -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("<em>MMSI</em>: {0} <br />", this.MMSI));
sb.Append(string.Format("<em>Name</em>: <span style=\"font-size: bigger\">{0}</span><br />", this.Name));
sb.Append(string.Format("Latitude: {0}° <br />", this.LastLatitude.HasValue ? this.LastLatitude.Value.ToString("N3") : "?"));
sb.Append(string.Format("Longitude: {0}° <br />", this.LastLongitude.HasValue ? this.LastLongitude.Value.ToString("N3") : "?"));
sb.Append(string.Format("Last report: {0} <br />", this.LastPositionReport.HasValue ? this.LastPositionReport.Value.ToString() : "?"));
sb.Append(string.Format("Type: {0} [{1}]<br />", this.Description, this.ShipType));
sb.Append(string.Format("Navstatus: {0}<br />", 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
/// <summary>
/// Saves a (class A or B) position report
/// </summary>
/// <param name="target">target to save</param>
/// <returns>id of insert operation (to update hotposition table)</returns>
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
/// <summary>
/// Loads shipname for display (until static data has been received)
/// </summary>
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;
}
/// <summary>
/// Loads callsign for display (until static data has been received)
/// </summary>
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;
}
/// <summary>
/// preload target with data from database until static data has been received
/// </summary>
/// <param name="target">target to load</param>
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();
}
/// <summary>
/// Load all ships that have a position and static data from database
/// </summary>
public static List<AISStaticData> LoadDBShips(DBConnector con)
{
List<AISStaticData> result = new List<AISStaticData>();
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<AISStaticData>
{
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
}
}

View File

@ -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<int, double> targets = new Dictionary<int, double>();
#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<int, double> 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;
}
}
/// <summary>
/// clear targets and reset coverage
/// </summary>
public void ResetStation()
{
this.targets.Clear();
this.coverage = 0;
this.rangeAverage = 0;
this.rangeMax = 0;
}
/// <summary>
/// deletes this station
/// </summary>
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<AISStation> LoadStations(DBConnector con)
{
List<AISStation> result = new List<AISStation>();
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<Serial_IO> CreateSerial_IOs(List<AISStation> stationList)
{
List<Serial_IO> result = new List<Serial_IO>();
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<AIS_Telnet> CreateAIS_Telnets(List<AISStation> stationList)
{
List<AIS_Telnet> result = new List<AIS_Telnet>();
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
}
}

View File

@ -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<AISWatchkeeper> GetWatchkeeperShips(DBConnector con)
{
List<AISWatchkeeper> result = new List<AISWatchkeeper>();
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;
}
}
}

View File

@ -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<AISWatchkeeper> watchkeeperShips = null;
private List<AISStaticData> dbShips = null;
private Dictionary<string, AISStation> updateStations = null;
public DBConnector() { }
#region Properties
public string ConnectionString
{
get { return this.connectionString; }
set { this.connectionString = value; }
}
public List<AISStaticData> DBShips
{
get
{
if (this.dbShips == null)
{
lock (this.dbCon)
{
this.dbShips = AISStaticData.LoadDBShips(this);
}
}
return this.dbShips;
}
set { this.dbShips = value; }
}
public List<AISWatchkeeper> 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<string, AISStation>();
Trace.WriteLine("loading stations..");
List<AISStation> 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
}
}

View File

@ -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<Hotposition> results = new List<Hotposition>();
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];
}
}
}
}

View File

@ -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
{
/// <summary>
/// The main entry point for the application.
/// </summary>
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;
}
}
}

View File

@ -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")]

View File

@ -0,0 +1,35 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
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"]));
}
}
}
}

View File

@ -0,0 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="bsmd.AISService.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="ConnectionString" Type="System.String" Scope="Application">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A26A6DE3-8505-4EC2-9EB5-12E5EBE83B11}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>bsmd.AISService</RootNamespace>
<AssemblyName>bsmd.AISService</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>bsmdKey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AISService.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="AISService.Designer.cs">
<DependentUpon>AISService.cs</DependentUpon>
</Compile>
<Compile Include="AIS\AIS.cs" />
<Compile Include="AIS\AIS_ClassB.cs" />
<Compile Include="AIS\AIS_ClassBExt.cs" />
<Compile Include="AIS\AIS_ClassBStatic.cs" />
<Compile Include="AIS\AIS_Configuration.cs" />
<Compile Include="AIS\AIS_Decoder.cs" />
<Compile Include="AIS\AIS_PosReport.cs" />
<Compile Include="AIS\AIS_QueueManager.cs" />
<Compile Include="AIS\AIS_StaticData.cs" />
<Compile Include="AIS\AIS_Target.cs" />
<Compile Include="AIS\AIS_Target_Comparer.cs" />
<Compile Include="AIS\AIS_Telnet.cs" />
<Compile Include="AIS\NMEA.cs" />
<Compile Include="AIS\NMEA_AIS_Sentence.cs" />
<Compile Include="AIS\NMEA_PNMLS_Sentence.cs" />
<Compile Include="AIS\SerialDataHandler.cs" />
<Compile Include="AIS\Serial_IO.cs" />
<Compile Include="AIS\TelnetDataHandler.cs" />
<Compile Include="DB\AISPosReport.cs" />
<Compile Include="DB\AISStaticData.cs" />
<Compile Include="DB\AISStation.cs" />
<Compile Include="DB\AISWatchkeeper.cs" />
<Compile Include="DB\DBConnector.cs" />
<Compile Include="DB\Hotposition.cs" />
<Compile Include="Program.cs" />
<Compile Include="ProjectInstaller.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ProjectInstaller.Designer.cs">
<DependentUpon>ProjectInstaller.cs</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="bsmd.AISService.licenseheader" />
<None Include="bsmdKey.snk" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ProjectInstaller.resx">
<DependentUpon>ProjectInstaller.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -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
<!--
Sample license text.
-->

View File

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

Binary file not shown.

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.8" targetFramework="net452" />
</packages>

View File

@ -26,12 +26,12 @@
<value>1000</value>
</setting>
<setting name="LockingServerAddress" serializeAs="String">
<value>http://192.168.2.4/LockingService/LockingService.svc</value>
<!--value>http://heupferd/bsmd.LockingService/LockingService.svc</value-->
<!--value>http://192.168.2.4/LockingService/LockingService.svc</value-->
<value>http://heupferd/bsmd.LockingService/LockingService.svc</value>
</setting>
<setting name="ConnectionString" serializeAs="String">
<value>Data Source=192.168.2.12;Initial Catalog=nsw;Uid=dfuser;Pwd=dfpasswd;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False</value>
<!--value>Data Source=(localdb)\Projects;Initial Catalog=nsw;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False</value-->
<!--value>Data Source=192.168.2.12;Initial Catalog=nsw;Uid=dfuser;Pwd=dfpasswd;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False</value-->
<value>Data Source=(localdb)\Projects;Initial Catalog=nsw;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False</value>
</setting>
</ENI2.Properties.Settings>
</applicationSettings>

Binary file not shown.

View File

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

View File

@ -196,7 +196,7 @@ namespace bsmd.database
public override void Validate(List<MessageError> errors, List<MessageViolation> 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));
}
}