|
@@ -1,479 +0,0 @@
|
|
|
-using System;
|
|
|
-using System.Collections.Concurrent;
|
|
|
-using System.Collections.Generic;
|
|
|
-using System.Linq;
|
|
|
-using System.Net.Sockets;
|
|
|
-using System.Threading;
|
|
|
-using System.Windows.Media.Media3D;
|
|
|
-using Comal.Classes;
|
|
|
-using InABox.Core;
|
|
|
-using InABox.DigitalMatter;
|
|
|
-
|
|
|
-namespace PRSServer
|
|
|
-{
|
|
|
- /// <summary>
|
|
|
- /// Summary description for TCPSocketListener.
|
|
|
- /// </summary>
|
|
|
- internal class GPSListener
|
|
|
- {
|
|
|
- private Thread m_clientListenerThread;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Variables that are accessed by other classes indirectly.
|
|
|
- /// </summary>
|
|
|
- private Socket m_clientSocket;
|
|
|
-
|
|
|
- private DateTime m_currentReceiveDateTime;
|
|
|
- private readonly ConcurrentDictionary<string, Device> m_devices = new();
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Working Variables.
|
|
|
- /// </summary>
|
|
|
- private DateTime m_lastReceiveDateTime;
|
|
|
-
|
|
|
- private bool m_markedForDeletion;
|
|
|
- private bool m_stopClient;
|
|
|
- private readonly ConcurrentQueue<Tuple<GPSTrackerLocation, string>> m_updates;
|
|
|
-
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Client Socket Listener Constructor.
|
|
|
- /// </summary>
|
|
|
- /// <param name="clientSocket"></param>
|
|
|
- public GPSListener(Socket clientSocket,
|
|
|
- ConcurrentDictionary<string, Device> devices,
|
|
|
- ConcurrentQueue<Tuple<GPSTrackerLocation, string>> updates)
|
|
|
- {
|
|
|
- m_clientSocket = clientSocket;
|
|
|
- if (devices != null)
|
|
|
- m_devices = devices;
|
|
|
- m_updates = updates;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Client SocketListener Destructor.
|
|
|
- /// </summary>
|
|
|
- ~GPSListener()
|
|
|
- {
|
|
|
- StopSocketListener();
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Method that starts SocketListener Thread.
|
|
|
- /// </summary>
|
|
|
- public void StartSocketListener()
|
|
|
- {
|
|
|
- if (m_clientSocket != null)
|
|
|
- {
|
|
|
- m_clientListenerThread =
|
|
|
- new Thread(SocketListenerThreadStart);
|
|
|
-
|
|
|
- m_clientListenerThread.Start();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Thread method that does the communication to the client. This
|
|
|
- /// thread tries to receive from client and if client sends any data
|
|
|
- /// then parses it and again wait for the client data to come in a
|
|
|
- /// loop. The recieve is an indefinite time receive.
|
|
|
- /// </summary>
|
|
|
- private void SocketListenerThreadStart()
|
|
|
- {
|
|
|
- var _threshold = TimeSpan.FromMinutes(2);
|
|
|
-
|
|
|
- var size = 0;
|
|
|
- var buf = new byte[2048];
|
|
|
- var consolidated = new List<byte>();
|
|
|
-
|
|
|
- var m_product = "";
|
|
|
- var m_serial = "";
|
|
|
- var datarequests = new List<DMDataRequest>();
|
|
|
-
|
|
|
- //var filename = Path.Combine(Logger.LogFolder,DateTime.Now.Ticks.ToString()+".txt");
|
|
|
- //var writer = File.AppendText(filename);
|
|
|
-
|
|
|
- m_lastReceiveDateTime = DateTime.Now;
|
|
|
- m_currentReceiveDateTime = DateTime.Now;
|
|
|
-
|
|
|
- var t = new Timer(CheckClientCommInterval,
|
|
|
- null, 15000, 15000);
|
|
|
-
|
|
|
- while (!m_stopClient)
|
|
|
- {
|
|
|
- var bClose = false;
|
|
|
- try
|
|
|
- {
|
|
|
- size = m_clientSocket.Receive(buf);
|
|
|
- m_currentReceiveDateTime = DateTime.Now;
|
|
|
-
|
|
|
- if (size > 0)
|
|
|
- {
|
|
|
- var buf2 = buf.Take(size).ToArray();
|
|
|
- //writer.WriteLine(BitConverter.ToString(buf2).Replace("-", string.Empty));
|
|
|
- consolidated.AddRange(buf2);
|
|
|
- try
|
|
|
- {
|
|
|
- while (consolidated.Count > 0)
|
|
|
- {
|
|
|
- DMMessage message = null;
|
|
|
- try
|
|
|
- {
|
|
|
- message = DMFactory.ParseMessage(consolidated.ToArray());
|
|
|
- }
|
|
|
- catch (Exception e)
|
|
|
- {
|
|
|
- Logger.Send(
|
|
|
- LogType.Error,
|
|
|
- Thread.CurrentThread.ManagedThreadId.ToString(),
|
|
|
- string.Format("Unable to Parse Record: {0} ({1})", e.Message, BitConverter.ToString(consolidated.ToArray()))
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- if (message is DMHelloRequest hello)
|
|
|
- {
|
|
|
- m_product = DMFactory.GetDeviceName(hello.ProductID);
|
|
|
- m_serial = hello.SerialNumber.ToString();
|
|
|
- Logger.Send(LogType.Information, m_serial, string.Format("Hello {0} ({1})", m_product, m_serial));
|
|
|
- var ack = new DMHelloResponse();
|
|
|
- m_clientSocket.Send(ack.Encode());
|
|
|
- }
|
|
|
- else if (message is DMDataRequest)
|
|
|
- {
|
|
|
- //Logger.Send(LogType.Information, m_serial, String.Format("Data: {0}", String.Join(":",consolidated.Select(x => x.ToString("X2")))));
|
|
|
- var data = message as DMDataRequest;
|
|
|
- Logger.Send(LogType.Information, m_serial, string.Format("{0} DataRecords Received", data.Records.Length));
|
|
|
- var iRecord = 1;
|
|
|
- foreach (var record in data.Records)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Data Record #{0}: {1:dd MMM yy hh-mm-ss} ({2} Fields)", iRecord,
|
|
|
- record.TimeStampToDateTime(record.TimeStamp), record.Fields.Length));
|
|
|
- iRecord++;
|
|
|
- foreach (var field in record.Fields)
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format(" [{0}] {1}: {2}", field.IsValid() ? "X" : " ", DMFactory.GetFieldName(field.Type),
|
|
|
- field));
|
|
|
- }
|
|
|
-
|
|
|
- datarequests.Add(data);
|
|
|
- // Update the Server Here
|
|
|
- }
|
|
|
- else if (message is DMConfirmRequest)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial, string.Format("Goodbye {0} ({1})", m_product, m_serial));
|
|
|
-
|
|
|
- var updates = new List<GPSTrackerLocation>();
|
|
|
- foreach (var data in datarequests)
|
|
|
- foreach (var record in data.Records)
|
|
|
- {
|
|
|
- var gps = record.Fields.FirstOrDefault(x => x is DMGPSField && x.IsValid()) as DMGPSField;
|
|
|
- if (m_devices.ContainsKey(m_serial))
|
|
|
- {
|
|
|
- if (gps != null)
|
|
|
- {
|
|
|
- if (record.TimeStamp != 0 && gps.Latitude != 0 && gps.Longitude != 0)
|
|
|
- {
|
|
|
- if (!gps.StatusFlags().Any(x => x == GPSStatus.NoSignal))
|
|
|
- {
|
|
|
- var timestamp = record.TimeStampToDateTime(record.TimeStamp);
|
|
|
- var age = timestamp - m_devices[m_serial].TimeStamp;
|
|
|
- if (age > _threshold)
|
|
|
- {
|
|
|
- var device = m_devices[m_serial];
|
|
|
-
|
|
|
- var location = new GPSTrackerLocation();
|
|
|
- location.DeviceID = m_serial;
|
|
|
- location.Tracker.ID = device.ID;
|
|
|
- location.Location.Timestamp = timestamp;
|
|
|
- location.Location.Latitude = (double)gps.Latitude / 10000000.0F;
|
|
|
- location.Location.Longitude = (double)gps.Longitude / 10000000.0F;
|
|
|
- updates.Add(location);
|
|
|
-
|
|
|
- var analoguedata =
|
|
|
- record.Fields.FirstOrDefault(x =>
|
|
|
- x is DMAnalogueDataField16) as DMAnalogueDataField16;
|
|
|
- if (analoguedata != null)
|
|
|
- location.BatteryLevel = analoguedata.BatteryStrength
|
|
|
- ?? device.CalculateBatteryLevel(analoguedata.InternalVoltage);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Skipping: Recent Update ({0}) {1:mm\\:ss}", m_serial, age));
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Skipping: Invalid Signal ({0})", m_serial));
|
|
|
- }
|
|
|
-
|
|
|
- var taglists = record.Fields.Where(x => x is DMBluetoothTagList);
|
|
|
- foreach (DMBluetoothTagList taglist in taglists)
|
|
|
- foreach (var item in taglist.Items.Where(x => x.LogReason != 2))
|
|
|
- {
|
|
|
- var tagid = item.Tag.ID();
|
|
|
-
|
|
|
-
|
|
|
- if (!m_devices.ContainsKey(tagid) && tagid.Length == 17 && tagid.Split(':').Length == 6)
|
|
|
- {
|
|
|
- var truncated = tagid.Substring(0, 15);
|
|
|
- var newtag = m_devices.Keys.FirstOrDefault(x => x.StartsWith(truncated));
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Truncating BT Tag: {0} -> {1} -> {2}", tagid, truncated, newtag));
|
|
|
- if (!string.IsNullOrWhiteSpace(newtag))
|
|
|
- tagid = newtag;
|
|
|
- }
|
|
|
-
|
|
|
- if (m_devices.ContainsKey(tagid))
|
|
|
- {
|
|
|
- var timestamp = record.TimeStampToDateTime(record.TimeStamp);
|
|
|
- var age = timestamp - m_devices[tagid].TimeStamp;
|
|
|
- if (age > _threshold)
|
|
|
- {
|
|
|
- var device = m_devices[tagid];
|
|
|
-
|
|
|
- var btloc = new GPSTrackerLocation();
|
|
|
- btloc.DeviceID = tagid;
|
|
|
- btloc.Tracker.ID = device.ID;
|
|
|
- btloc.Location.Timestamp = timestamp;
|
|
|
- btloc.Location.Latitude = (double)gps.Latitude / 10000000.0F;
|
|
|
- btloc.Location.Longitude = (double)gps.Longitude / 10000000.0F;
|
|
|
-
|
|
|
- if (item.Tag is DMGuppyBluetoothTag guppy)
|
|
|
- {
|
|
|
- btloc.BatteryLevel = device.CalculateBatteryLevel(guppy.BatteryVoltage);
|
|
|
- //guppy.BatteryVoltage * 5F / 3F;
|
|
|
- }
|
|
|
- else if (item.Tag is DMSensorNodeBluetoothTag sensornode)
|
|
|
- {
|
|
|
- // Need to check with Kenrick about the calcs here..
|
|
|
- // Guppies have 1 battery (ie 1.5V) while Sensornodes have 3 (4.5V)
|
|
|
- btloc.BatteryLevel = device.CalculateBatteryLevel(sensornode.BatteryVoltage);
|
|
|
- //btloc.BatteryLevel = sensornode.BatteryVoltage * 5F / 3F;
|
|
|
- }
|
|
|
-
|
|
|
- updates.Add(btloc);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Skipping: Recent Update ({0}) {1:mm\\:ss}", tagid, age));
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Skipping: Unknown Tag ({0})", tagid));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var tags = record.Fields.Where(x => x is DMBluetoothTagData);
|
|
|
- foreach (DMBluetoothTagData tag in tags)
|
|
|
- if (tag.LogReason != 2 && tag.TimeStamp != 0 && tag.Latitude != 0 && tag.Longitude != 0)
|
|
|
- {
|
|
|
- var tagid = tag.Tag.ID();
|
|
|
-
|
|
|
- if (!m_devices.ContainsKey(tagid) && tagid.Length == 17 && tagid.Split(':').Length == 6)
|
|
|
- {
|
|
|
- var truncated = tagid.Substring(0, 15);
|
|
|
- var newtag = m_devices.Keys.FirstOrDefault(x => x.StartsWith(truncated));
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Truncating BT Tag: {0} -> {1} -> {2}", tagid, truncated,
|
|
|
- newtag));
|
|
|
- if (!string.IsNullOrWhiteSpace(newtag))
|
|
|
- tagid = newtag;
|
|
|
- }
|
|
|
-
|
|
|
- if (m_devices.ContainsKey(tagid))
|
|
|
- {
|
|
|
- var timestamp = record.TimeStampToDateTime(record.TimeStamp);
|
|
|
- var age = timestamp - m_devices[tagid].TimeStamp;
|
|
|
- if (age > _threshold)
|
|
|
- {
|
|
|
- var device = m_devices[tagid];
|
|
|
- var btloc = new GPSTrackerLocation();
|
|
|
- btloc.DeviceID = tagid;
|
|
|
- btloc.Tracker.ID = device.ID;
|
|
|
- btloc.Location.Timestamp = timestamp;
|
|
|
- btloc.Location.Latitude = (double)gps.Latitude / 10000000.0F;
|
|
|
- btloc.Location.Longitude = (double)gps.Longitude / 10000000.0F;
|
|
|
- updates.Add(btloc);
|
|
|
-
|
|
|
- if (tag.Tag is DMGuppyBluetoothTag guppy)
|
|
|
- {
|
|
|
- btloc.BatteryLevel = device.CalculateBatteryLevel(guppy.BatteryVoltage);
|
|
|
- //guppy.BatteryVoltage * 5F / 3F;
|
|
|
- }
|
|
|
- else if (tag.Tag is DMSensorNodeBluetoothTag sensornode)
|
|
|
- {
|
|
|
- // Need to check with Kenrick about the calcs here..
|
|
|
- // Guppies have 1 battery (ie 1.5V) while Sensornodes have 3 (4.5V)
|
|
|
- btloc.BatteryLevel = device.CalculateBatteryLevel(sensornode.BatteryVoltage);
|
|
|
- //btloc.BatteryLevel = sensornode.BatteryVoltage * 5F / 3F;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Skipping: Recent Update ({0}) {1:mm\\:ss}", tagid, age));
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Skipping: Unknown Tag ({0})", tagid));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Skipping: Invalid GPS Data ({0}) {1}{2}{3}", m_serial,
|
|
|
- gps.TimeStamp == 0 ? "Bad TimeStamp " : "", gps.Latitude == 0 ? "Bad Latitude " : "",
|
|
|
- gps.Longitude == 0 ? "Bad Longitude " : "").Trim());
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Skipping: Missing GPS Data ({0})", m_serial));
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial, string.Format("- Skipping: Unknown Device ({0})", m_serial));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (updates.Any())
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("Sending updates ({0}): {1}", updates.Count,
|
|
|
- string.Join(", ", updates.Select(x => x.DeviceID).Distinct())));
|
|
|
- foreach (var update in updates)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, m_serial,
|
|
|
- string.Format("- Updating Device Cache: ({0}): {1:yyyy-MM-dd hh:mm:ss}", update.DeviceID,
|
|
|
- update.Location.Timestamp));
|
|
|
- //if (m_devices.ContainsKey(update.DeviceID))
|
|
|
- var oldDevice = m_devices[update.DeviceID];
|
|
|
- m_devices[update.DeviceID] =
|
|
|
- new Device(oldDevice.ID, update.Location.Timestamp, oldDevice.BatteryFormula);
|
|
|
- }
|
|
|
-
|
|
|
- foreach (var update in updates)
|
|
|
- m_updates.Enqueue(new Tuple<GPSTrackerLocation, string>(update,
|
|
|
- string.Format("Updated by {0} ({1})", m_product, m_serial)));
|
|
|
-
|
|
|
- //new Client<GPSTrackerLocation>().Save(
|
|
|
- // updates,
|
|
|
- // String.Format("Updated by {0} ({1})", m_product, m_serial),
|
|
|
- // (locations, errors) =>
|
|
|
- // {
|
|
|
- // //if (locations != null)
|
|
|
- // //{
|
|
|
- // // foreach (var location in locations)
|
|
|
- // // {
|
|
|
- // // if (m_devices.ContainsKey(location.DeviceID))
|
|
|
- // // m_devices[location.DeviceID] = location.Tracker.ID;
|
|
|
- // // }
|
|
|
- // //}
|
|
|
- // }
|
|
|
- //);
|
|
|
- }
|
|
|
-
|
|
|
- var ack = new DMConfirmResponse();
|
|
|
- ack.Status = 1;
|
|
|
- m_clientSocket.Send(ack.Encode());
|
|
|
-
|
|
|
- //bClose = true;
|
|
|
- }
|
|
|
-
|
|
|
- consolidated.RemoveRange(0, message.CheckSum + 5);
|
|
|
- }
|
|
|
- }
|
|
|
- catch (Exception e)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Error, Thread.CurrentThread.ManagedThreadId.ToString(), e.Message + "\n" + e.StackTrace);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- bClose = true;
|
|
|
- }
|
|
|
- }
|
|
|
- catch (SocketException se)
|
|
|
- {
|
|
|
- bClose = true;
|
|
|
- }
|
|
|
-
|
|
|
- if (bClose)
|
|
|
- {
|
|
|
- //writer.Flush();
|
|
|
- //writer.Close();
|
|
|
- //writer.Dispose();
|
|
|
- //writer = null;
|
|
|
-
|
|
|
- m_stopClient = true;
|
|
|
- m_markedForDeletion = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- t.Change(Timeout.Infinite, Timeout.Infinite);
|
|
|
- t = null;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Method that stops Client SocketListening Thread.
|
|
|
- /// </summary>
|
|
|
- public void StopSocketListener()
|
|
|
- {
|
|
|
- if (m_clientSocket != null)
|
|
|
- {
|
|
|
- m_stopClient = true;
|
|
|
- m_clientSocket.Close();
|
|
|
-
|
|
|
- // Wait for one second for the the thread to stop.
|
|
|
- m_clientListenerThread.Join(1000);
|
|
|
-
|
|
|
- // If still alive; Get rid of the thread.
|
|
|
- if (m_clientListenerThread.IsAlive)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Error, "", "Thread didn't die in time.");
|
|
|
- }
|
|
|
- //m_clientListenerThread.Abort();
|
|
|
- m_clientListenerThread = null;
|
|
|
- m_clientSocket = null;
|
|
|
- m_markedForDeletion = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Method that returns the state of this object i.e. whether this
|
|
|
- /// object is marked for deletion or not.
|
|
|
- /// </summary>
|
|
|
- /// <returns></returns>
|
|
|
- public bool IsMarkedForDeletion()
|
|
|
- {
|
|
|
- return m_markedForDeletion;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Method that checks whether there are any client calls for the
|
|
|
- /// last 15 seconds or not. If not this client SocketListener will
|
|
|
- /// be closed.
|
|
|
- /// </summary>
|
|
|
- /// <param name="o"></param>
|
|
|
- private void CheckClientCommInterval(object o)
|
|
|
- {
|
|
|
- if (m_lastReceiveDateTime.Equals(m_currentReceiveDateTime))
|
|
|
- StopSocketListener();
|
|
|
- else
|
|
|
- m_lastReceiveDateTime = m_currentReceiveDateTime;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|