git_bsmd/AIS/bsmd.AISService/AIS/AIS_Decoder.cs

195 lines
6.0 KiB
C#

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