SafeDispatch/CPlus_GW/TallysmanReceiveThread.cs
2024-02-22 18:43:59 +02:00

721 lines
35 KiB
C#

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace CPlus_GW
{
public class TallysmanReceiveThread
{
private static UdpClient udpClient;
private ushort Port = 0;
//private GeneralLocationManager locManager;
public TallysmanReceiveThread(ushort port)
{
Port = port;
Port = 4010;
//locManager = new GeneralLocationManager(mIP, mPort);
}
public void handleConnection()
{
SafeMobileLib.Utils.WriteLine("♥♥♥ Tallysman Receive Thread Started on port " + Port);
udpClient = null;
try
{
udpClient = new UdpClient();
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, Port));
}
catch (Exception ex)
{
SafeMobileLib.Utils.WriteLine("TallysmanReceiveThread handleConnection exception: " + ex.ToString(), ConsoleColor.Red);
}
IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
Byte[] receivedBytes = new Byte[]{};
try
{
// Blocks until a message returns on this socket from a remote host.
receivedBytes = udpClient.Receive(ref remoteIpEndPoint);
char[] separator = { '.' };
string[] su = remoteIpEndPoint.Address.ToString().Split(separator);
uint radioID = (Convert.ToUInt32(su[1])) * 256 * 256 + (Convert.ToUInt32(su[2])) * 256 + Convert.ToUInt32(su[3]);
string suid = radioID.ToString();
int seqNo = receivedBytes[0] * 256 + receivedBytes[1];
String seqID = TallysmanConfirm.getFromConfirmationQueue(seqNo);
SafeMobileLib.Utils.WriteLine("TallysmanReceiveThread received data from radio " + radioID + " [" + seqID + "]", ConsoleColor.Gray);
// process the message
Byte toreturn = ProcessPacket2(receivedBytes, receivedBytes.Length, radioID.ToString().Trim());
/*
//put information on message bus
Byte[] toSendMulticast = Utils.createMulticastMessage(233, suid, receivedBytes);
udpMulticast.Send(toSendMulticast, toSendMulticast.Length);
Utils.WriteLine("TallysmanReceiveThread successfully sent data to message bus");
*/
Thread.Sleep(100);
}
catch (Exception e)
{
SafeMobileLib.Utils.WriteLine("##### TallysmanReceiveThread Exception #########\n" + e.ToString(), ConsoleColor.Red);
//if(receivedBytes.Length > 0)
//Utils.WriteLine(Utils.printBytesArray(receivedBytes), ConsoleColor.Yellow);
}
}
}
public Byte ProcessPacket2(byte[] data, int len, String imei)
{
#region variable declaration
Byte numberofMessByte = 0;
String pLat = "0.0", pLong = "0.0";
Int64 itime70 = 0;
double iSpeed = 0;
int altitude = 0;
#region fields for safenet db
int bearing = -1;
int levelOfConfidence = -1;
int accuracy_horizontal = -1;
int accuracy_vertical = -1;
int odometer = -1;
int vio_status = -1;
int vio_changed = -1;
float rssi = -1;
int vital_id = -1;
int runtime, idletime = -1;
int eventTimeSec = 0;
long eventField = -1;
string firmware_version = string.Empty;
double average_speed = 0;
DateTime eventTime = DateTime.Now;
DateTime positionTime = DateTime.Now;
#endregion
#region correction length variables
int start_parse = 0;
int delta = 0;
int sum = 0;
#endregion
int[] v = new int[19];
int protocol, version, messageLength, messageType,
TransactionId, UnackedCount, LocationReportLength,
LogId, EventId, EventIndex;
Int32 step = 0;
#endregion
//first 2 bytes are Protocol and Version 0xFA 0x08
protocol = data[step++];
version = data[step++];
#region Message Length
string sByteMessLength = string.Empty;
do
{
for (int j = 6; j >= 0; j--)
{
sByteMessLength += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
messageLength = (int)(Convert.ToInt32(sByteMessLength, 2));
#endregion
#region Message Type
messageType = data[step++];
#endregion
#region Transaction ID
string sBytetrid = string.Empty;
do
{
for (int j = 6; j >= 0; j--)
{
sBytetrid += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
TransactionId = (int)(Convert.ToInt32(sBytetrid, 2));
#endregion
#region NOTE: Tallysman Reporting Types
// There are 3 types of reports: Autonomus report 0x03, Immediate response 0x02, Log retrieval report 0x05
//Between Transaction ID and Location report length foreach report type there are the following fields:
//A. Autonomus report 0x03 - Unacked cound
//B. Immediate response 0x02 - none
//C. Log retrieval report 0x05 - Retrieval Flags (1 byte), Returned count, Unreturned count, NextID all uintvar
#region Autonomus report 0x03 - Unacked cound
if (messageType == 3)
{
string sByteUnkCount = string.Empty;
do
{
for (int j = 6; j >= 0; j--)
{
sByteUnkCount += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
UnackedCount = (int)(Convert.ToInt32(sByteUnkCount, 2));
}
#endregion
#region Immediate response 0x02 - none
if (messageType == 2)
{
// do nothing
}
#endregion
#region Log retrieval report 0x05 - Retrieval Flags (1 byte), Returned count, Unreturned count, NextID all uintvar
if (messageType == 5)
{
//Retrieval Flags
step++;
//Returned count
do {/*nothing*/} while (GetBitsAsByte(data[step++], 7, 1) == 1);
//Unreturned count
do {/*nothing*/} while (GetBitsAsByte(data[step++], 7, 1) == 1);
//NextID
do {/*nothing*/} while (GetBitsAsByte(data[step++], 7, 1) == 1);
}
#endregion
#endregion
#region BUNDLE/NORMAL PARSER
do
{
#region Reset Variables
pLat = pLong = "0.0";
itime70 = 0;
iSpeed = 0;
altitude = 0;
//fields for safenet db
bearing = -1;
levelOfConfidence = -1;
accuracy_horizontal = -1;
accuracy_vertical = -1;
odometer = -1;
vio_status = -1;
vio_changed = -1;
rssi = -1;
vital_id = -1;
runtime = idletime = -1;
eventTimeSec = 0;
eventField = -1;
firmware_version = string.Empty;
average_speed = 0;
eventTime = DateTime.Now;
positionTime = DateTime.Now;
#endregion
#region Location Report Length
string sByteLocRepLength = string.Empty;
do
{
for (int j = 6; j >= 0; j--)
{
sByteLocRepLength += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
LocationReportLength = (int)(Convert.ToInt32(sByteLocRepLength, 2));
start_parse = step;
#endregion
#region Log ID
string sByteLogId = string.Empty;
do
{
for (int j = 6; j >= 0; j--)
{
sByteLogId += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
LogId = (int)(Convert.ToInt32(sByteLogId, 2));
#endregion
#region Event ID
string sByteEventID = string.Empty;
do
{
for (int j = 6; j >= 0; j--)
{
sByteEventID += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
EventId = (int)(Convert.ToInt32(sByteEventID, 2)) % 128;
EventIndex = (int)(Convert.ToInt32(sByteEventID, 2)) / 128;
#endregion
#region Report Form parser
int count = 0;
v = new int[19];
do
{
for (int i = 0; i < 7; i++)
{
v[count++] = (int)GetBitsAsByte(data[step], i, 1);
if (count == 19) break;
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
#endregion
#region Parse data based on report form's bits
for (int i = 0; i <= 18; i++)
{
if (v[i] == 1)
{
switch (i)
{
//Property: Type, Form Flag + Name, Fixed/Variable Length, Length(bytes)
#region Type: Latitude and Longitude, F0 - latitude and longitude, Fixed Length 8 bytes total
case (int)FS.F0:
{
//process LAT
bool sign = false;
string hexLatitude = string.Empty;
if ((data[step] & 0x80) != 0)
{
hexLatitude = (data[step++] - 128).ToString("X2");
sign = true;
}
else hexLatitude += data[step++].ToString("X2");
for (int j = 0; j < 3; j++)
{
hexLatitude += data[step++].ToString("X2");
}
long decAgainLatiude = int.Parse(hexLatitude, System.Globalization.NumberStyles.HexNumber);
double dlat = decAgainLatiude * 90 / (double)Math.Pow(2, 31);
if (sign) dlat *= -1;
pLat = Convert.ToString(dlat);
//process LONG
string hexLongitude = string.Empty;
if ((data[step] & 0x80) != 0)
{
hexLongitude = "FFFFFFFF" + (data[step++]).ToString("X2");
}
else hexLongitude += data[step++].ToString("X2");
for (int j = 0; j < 3; j++)
{
hexLongitude += data[step++].ToString("X2");
}
long decAgainLongitude = Int64.Parse(hexLongitude, System.Globalization.NumberStyles.HexNumber);
double dlong = decAgainLongitude * 360 / (double)Math.Pow(2, 32);
pLong = Convert.ToString(dlong);
}
break;
#endregion
#region Type: Timestamp, F1 - gps fix time, Fixed Length 5 bytes
case (int)FS.F1:
{
string hexGPS = string.Empty;
for (int j = 0; j < 5; j++)
{
hexGPS += data[step++].ToString("X2");
}
decimal decAgainGPS = Int64.Parse(hexGPS, System.Globalization.NumberStyles.HexNumber);
int year = (int)Decimal.Truncate(decAgainGPS / (decimal)Math.Pow(2, 26));
decimal modYear = decAgainGPS % (decimal)Math.Pow(2, 26);
int month = (int)Decimal.Truncate(modYear / (decimal)Math.Pow(2, 22));
decimal modMonth = modYear % (decimal)Math.Pow(2, 22);
int day = (int)Decimal.Truncate(modMonth / (decimal)Math.Pow(2, 17));
decimal modDay = modMonth % (decimal)Math.Pow(2, 17);
int hour = (int)Decimal.Truncate(modDay / (decimal)Math.Pow(2, 12));
decimal modHour = modDay % (decimal)Math.Pow(2, 12);
int minute = (int)Decimal.Truncate(modHour / (decimal)Math.Pow(2, 6));
int second = (int)(modHour % (decimal)Math.Pow(2, 6));
SafeMobileLib.Utils.WriteLine("Tallysman GPS Time : " + hour + ":" + minute + ":" + second, ConsoleColor.Red);
positionTime = new DateTime(year, month, day, hour, minute, second);
//positionTime = positionTime.ToLocalTime();
itime70 = (long)positionTime.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
}
break;
#endregion
#region Type: Speed, F2 - horizontal speed, F8 - vertical speed, F14 - average speed, Variable Length 1->3 bytes
case (int)FS.F2:
case (int)FS.F8:
case (int)FS.F14:
{
double speed = 0;
string sByte = string.Empty;
do
{
for (int j = 6; j >= 0; j--)
{
sByte += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
if (i == (int)FS.F2)
{
//meteres per second
speed = (double)(Convert.ToInt32(sByte, 2)) / 10;
//transform to km per hour
speed *= (double)3.6;
speed = (int)Math.Round(speed);
}
switch (i)
{
case (int)FS.F2:
iSpeed = speed;
break;
case (int)FS.F8:
break;
case (int)FS.F14:
average_speed = speed;
break;
}
}
break;
#endregion
#region Type: Sintvar, F4 - altitude, Variable Length 1->3 bytes
case (int)FS.F4:
{
string sByte = string.Empty;
bool sign = false;
do
{
for (int j = 6; j >= 0; j--)
{
if (GetBitsAsByte(data[step], 7, 1) == 1 && GetBitsAsByte(data[step], j, 1) == 1 && j == 6)
sign = true;
else
sByte += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
altitude = (int)(Convert.ToInt32(sByte, 2));
if (sign) altitude *= -1;
}
break;
#endregion
#region Type: Uint8, F5 - confidence level, Fixed Length 1 byte
case (int)FS.F5:
levelOfConfidence = data[step++];
break;
#endregion
#region Type: Uint16, F10 - odometer, F17 - RSSI radio signal strength in dBm
case (int)FS.F10:
// Odometer on 2 bytes - ignoring
step += 2;
break;
case (int)FS.F17:
// msb - most significant bit, lsb - least significant bit
int msb = data[step++];
int lsb = data[step++];
rssi = (msb + (lsb * 1000 + 128) / 256000);
break;
#endregion
#region Type: Uint16 F12 - VIO current / VIO changed 4 bytes 2 for current, 2 for changed
case (int)FS.F12:
//step += 4;
{
//2 bytes for current VIO
//Hardware VIO 1 Sprite Protocol page 67/68
string hexVio = "";
for (int j = 0; j < 2; j++)
{
hexVio += data[step++].ToString("X2");
}
vio_status = int.Parse(hexVio, System.Globalization.NumberStyles.HexNumber);
SafeMobileLib.Utils.WriteLine("Vio Status: " + vio_status.ToString(), ConsoleColor.Red);
//2 bytes for changed VIO
string hexVioChanged = "";
for (int j = 0; j < 2; j++)
{
hexVioChanged += data[step++].ToString("X2");
}
vio_changed = int.Parse(hexVioChanged, System.Globalization.NumberStyles.HexNumber);
SafeMobileLib.Utils.WriteLine("Vio Changed: " + vio_changed.ToString(), ConsoleColor.Red);
// raise telemetry event
if (OnTelemetryReceived != null && vio_changed != 0)
{
OnTelemetryReceived(new TelemetryReceivedEventArgs()
{
RadioID = imei,
GPIO = vio_status + ""
});
}
}
break;
#endregion
#region Type: RTM + ITM
// F11 - Run time/idle time
case (int)FS.F11:
{
string sByte = string.Empty;
#region runtime
//Indicates the run time in minutes. The run time is the current value of a timer that
//continuously increments whenever a specified input is detected (e.g. ignition-on). The
//ignition line from the vehicle must be wired to the Sprite.
do
{
for (int j = 6; j >= 0; j--)
{
sByte += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
runtime = (int)(Convert.ToInt32(sByte, 2));
#endregion
#region idletime
//Indicates the idle time in minutes. The idle time is the current value of a timer that
//continuously increments whenever a specified input is detected (e.g. ignition-on) and
//the unit is not moving (i.e. stopped). The ignition line from the vehicle must be wired to
//the Sprite.
sByte = string.Empty;
do
{
for (int j = 6; j >= 0; j--)
{
sByte += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
idletime = (int)(Convert.ToInt32(sByte, 2));
#endregion
}
break;
#endregion
#region Type: Uintvar, F3, F6, F7, F9, F13, F18
// F3 - bearing, F6 - horizontal accuracy, F7 - vertical accuracy, F9 - odometer,
// F13 - event time, F18 - vital ID
case (int)FS.F3:
case (int)FS.F6:
case (int)FS.F7:
case (int)FS.F9:
case (int)FS.F13:
case (int)FS.F18:
{
string sByte = string.Empty;
do
{
for (int j = 6; j >= 0; j--)
{
sByte += ((int)GetBitsAsByte(data[step], j, 1)).ToString();
}
}
while (GetBitsAsByte(data[step++], 7, 1) == 1);
switch (i)
{
case (int)FS.F3:
bearing = (int)(Convert.ToInt32(sByte, 2));
break;
case (int)FS.F6:
accuracy_horizontal = (int)(Convert.ToInt32(sByte, 2));
break;
case (int)FS.F7:
accuracy_vertical = (int)(Convert.ToInt32(sByte, 2));
break;
case (int)FS.F9:
odometer = (int)(Convert.ToInt32(sByte, 2));
break;
case (int)FS.F13:
// The number of seconds that have passed since the GPS fix time for the report.
// If the GPS fix is old, then the GPS fix time will not be the time that the report was generated.
// This field, in combination with the GPS fix time field, allows the time of the event to be calculated.
// The GPS fix time should always be requested if the Event time is requested.
eventTimeSec = (int)(Convert.ToInt32(sByte, 2));
// add seconds to the position time
eventTime = positionTime.AddSeconds(eventTimeSec);
break;
case (int)FS.F18:
vital_id = (int)(Convert.ToInt32(sByte, 2));
break;
}
}
break;
#endregion
#region 4 bytes FS: F15 - event field
//for (int j = 0; j < 4; j++)
//{
// eventField <<= 8;
// eventField |= (Int64)data[step];
// //Console.Write(" 0x" + data[step].ToString("X2"));
// step++;
//}
#endregion
#region 6 bytes FS: F16-firmware version, configuration version, configuration update count
case (int)FS.F16:
int major_version = data[step++];
int minor_version = data[step++];
int micro_version = data[step++];
firmware_version = string.Format("{0}.{1}.{2}", major_version, minor_version, micro_version);
step += 3;
break;
#endregion
}
}
}
#endregion
// check if position is from the inside
if (levelOfConfidence == 0)
{
pLat = "0";
pLong = "0";
positionTime = DateTime.Now.ToUniversalTime();
itime70 = (long)((positionTime.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds));
}
// get the time difference between the GPS Position and the Global Time (which can be an internet time)
TimeSpan locationDifference = (positionTime).Subtract(DateTime.Now.ToUniversalTime());
// if position in the future with more than 2 days
if (locationDifference.TotalSeconds > 172800)
{
SafeMobileLib.Utils.WriteLine("Tallysman POSITION DIFFERENCE IS " + locationDifference.Seconds, ConsoleColor.Red);
positionTime = DateTime.Now.ToUniversalTime();
itime70 = (long)((positionTime.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds));
}
double lat = 0;
double lng = 0;
Double.TryParse(pLat, out lat);
Double.TryParse(pLong, out lng);
Int64 radioID = 0;
Int64.TryParse(imei, out radioID);
if (OnLocationReceived != null)
{
OnLocationReceived(new LocationEventArgs()
{
RadioID = radioID,
GPSTime = itime70,
Speed = iSpeed,
Latitude = lat,
Longitude = lng,
Altitude = altitude,
LevelOfConfidence = levelOfConfidence
});
}
TallysmanEventArgs.TallysmanEventType EventType = TallysmanEventArgs.TallysmanEventType.GetEventType(EventId);
// fix period type issue -> the event type is periodic with the event index
// telling which type of it is... so...
if (TallysmanEventArgs.TallysmanEventType.GetEventType(EventId)
== TallysmanEventArgs.TallysmanEventType.PERIODIC)
{
switch(EventIndex)
{
case 0: EventType = TallysmanEventArgs.TallysmanEventType.PERIODIC1; break;
case 1: EventType = TallysmanEventArgs.TallysmanEventType.PERIODIC2; break;
case 2: EventType = TallysmanEventArgs.TallysmanEventType.PERIODIC3; break;
case 3: EventType = TallysmanEventArgs.TallysmanEventType.PERIODIC4; break;
default: EventType = TallysmanEventArgs.TallysmanEventType.GetEventType(EventId); break;
}
}
else if (TallysmanEventArgs.TallysmanEventType.GetEventType(EventId)
== TallysmanEventArgs.TallysmanEventType.VIO)
{
switch (EventIndex)
{
case 0: EventType = TallysmanEventArgs.TallysmanEventType.VIO1; break;
case 1: EventType = TallysmanEventArgs.TallysmanEventType.VIO2; break;
case 2: EventType = TallysmanEventArgs.TallysmanEventType.VIO3; break;
case 3: EventType = TallysmanEventArgs.TallysmanEventType.VIO4; break;
default: EventType = TallysmanEventArgs.TallysmanEventType.GetEventType(EventId); break;
}
}
else if (TallysmanEventArgs.TallysmanEventType.GetEventType(EventId)
== TallysmanEventArgs.TallysmanEventType.DISTANCE)
{
switch (EventIndex)
{
case 0: EventType = TallysmanEventArgs.TallysmanEventType.VIO1; break;
case 1: EventType = TallysmanEventArgs.TallysmanEventType.VIO2; break;
default: EventType = TallysmanEventArgs.TallysmanEventType.GetEventType(EventId); break;
}
}
// trigger the event for when any tallysman event is received
if (OnTallysmanEventReceived != null)
{
OnTallysmanEventReceived(new TallysmanEventArgs()
{
EventType = EventType,
LevelOfConfidence = levelOfConfidence,
Latitude = lat,
Longitude = lng,
Altitude = altitude,
Speed = iSpeed,
AverageSpeed = average_speed,
HorizontalAccuracy = accuracy_horizontal,
VerticalAccuracy = accuracy_vertical,
Bearing = bearing,
FirmwareVersion = firmware_version,
Odometer = odometer,
RadioID = radioID,
RSSI = rssi,
IdleTime = idletime,
RunTime = runtime,
GPSFixTime = itime70,
EventTime = eventTimeSec,
VioChanged = vio_changed,
VioStatus = vio_status,
VitalId = vital_id,
LogID = LogId
});
}
sum = LocationReportLength + start_parse;
delta = sum - step;
step += delta;
}
while (len > step + 1);
#endregion
return numberofMessByte;
}
/// <summary>
/// Gets a specific group of bits as a byte values. This process is created appling a
/// custom mask and then shifting the remaining bits to right
/// </summary>
/// <param name="b">The byte from which the bits will be extracted</param>
/// <param name="offset">The number of bits which will be skipped from right[LST]. O for no offset</param>
/// <param name="count">Number of bits which are required</param>
/// <returns>The byte value of the required bits. This values is generated shifting the bits towards LST</returns>
public static byte GetBitsAsByte(byte b, int offset, int count)
{
return (byte)((b >> offset) & ((1 << count) - 1));
}
public enum FS
{
F0 = 0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10,
F11, F12, F13, F14, F15, F16, F17, F18
}
public delegate void LocationReceived(LocationEventArgs e);
public event LocationReceived OnLocationReceived;
public delegate void TelemetryReceived(TelemetryReceivedEventArgs e);
public event TelemetryReceived OnTelemetryReceived;
public delegate void TallysmanEventReceived(TallysmanEventArgs e);
public event TallysmanEventReceived OnTallysmanEventReceived;
}
}