Browse Source

Fixed up the GPS engine and made sure threads get killed.

Kenric Nugteren 2 years ago
parent
commit
974eb1b44d

+ 1 - 1
prs.desktop/Panels/Products/Job Requisitions/JobRequisitionsPanel.xaml.cs

@@ -72,7 +72,7 @@ namespace PRSDesktop
 
         private void RefreshJobRequiGrids()
         {
-            unProcessedJobRequiItems.bRefreshing = false;           
+            //unProcessedJobRequiItems.bRefreshing = false;           
             unProcessedJobRequiItems.Refresh(false, true);
         }
 

+ 0 - 399
prs.server/Engines/GPS OLD/GPSEngineOld.cs

@@ -1,399 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Net.Sockets;
-using System.Threading;
-using System.Threading.Tasks;
-using Comal.Classes;
-using InABox.Clients;
-using InABox.Core;
-using InABox.DigitalMatter;
-using InABox.IPC;
-using PRSServer.Engines;
-
-namespace PRSServer
-{
-    internal class Device
-    {
-        public Guid ID { get; set; }
-        public DateTime TimeStamp { get; set; }
-        public CoreExpression<GPSBatteryFormulaModel, double>? BatteryFormula { get; set; }
-
-        public Device(Guid id, DateTime timeStamp, CoreExpression<GPSBatteryFormulaModel, double>? batteryFormula)
-        {
-            ID = id;
-            TimeStamp = timeStamp;
-            BatteryFormula = batteryFormula;
-        }
-
-        /// <summary>
-        /// Should return a percentage
-        /// </summary>
-        /// <returns></returns>
-        public double CalculateBatteryLevel(double batteryValue)
-        {
-            if (BatteryFormula != null)
-                return BatteryFormula.Evaluate(new Dictionary<string, object?>
-                {
-                    { nameof(GPSBatteryFormulaModel.BatteryLevel), batteryValue }
-                });
-            return batteryValue;
-        }
-    }
-
-    /// <summary>
-    ///     TCPServer is the Server class. When "StartServer" method is called
-    ///     this Server object tries to connect to a IP Address specified on a port
-    ///     configured. Then the server start listening for client socket requests.
-    ///     As soon as a requestcomes in from any client then a Client Socket
-    ///     Listening thread will be started. That thread is responsible for client
-    ///     communication.
-    /// </summary>
-    internal class GPSEngineOld : Engine<GPSServerProperties>
-    {
-        /// <summary>
-        ///     Default Constants.
-        /// </summary>
-        public static IPAddress DEFAULT_SERVER = IPAddress.Any;
-
-        public static int DEFAULT_PORT = 7999;
-
-        public static IPEndPoint DEFAULT_IP_END_POINT = new(DEFAULT_SERVER, DEFAULT_PORT);
-
-        private readonly CancellationTokenSource cts = new();
-
-        private readonly ConcurrentDictionary<string, Device> m_devices = new();
-        private Task m_purgingThread;
-
-        private DateTime m_RefreshDate = DateTime.MinValue;
-
-        /// <summary>
-        ///     Local Variables Declaration.
-        /// </summary>
-        private TcpListener m_server;
-
-        private Task m_serverThread;
-        private ArrayList m_socketListenersList;
-        private bool m_stopPurging;
-        private bool m_stopServer;
-
-        private readonly ConcurrentQueue<Tuple<GPSTrackerLocation, string>> m_updates = new();
-        private Task m_updatetask;
-        
-        public GPSEngineOld()
-        {
-            Init(DEFAULT_IP_END_POINT);
-        }
-
-        public GPSEngineOld(IPAddress serverIP)
-        {
-            Init(new IPEndPoint(serverIP, DEFAULT_PORT));
-        }
-
-        public GPSEngineOld(int port)
-        {
-            Init(new IPEndPoint(DEFAULT_SERVER, port));
-        }
-
-        public GPSEngineOld(IPAddress serverIP, int port)
-        {
-            Init(new IPEndPoint(serverIP, port));
-        }
-
-        public GPSEngineOld(IPEndPoint ipNport)
-        {
-            Init(ipNport);
-        }
-
-        ~GPSEngineOld()
-        {
-            Stop();
-        }
-
-        public override void Configure(Server server)
-        {
-            base.Configure(server);
-        }
-
-        /// <summary>
-        ///     Init method that create a server (TCP Listener) Object based on the
-        ///     IP Address and Port information that is passed in.
-        /// </summary>
-        /// <param name="ipNport"></param>
-        private void Init(IPEndPoint ipNport)
-        {
-            try
-            {
-                m_server = new TcpListener(ipNport);
-            }
-            catch (Exception e)
-            {
-                m_server = null;
-            }
-
-            m_updatetask = Task.Run(() =>
-            {
-                while (m_server != null && !cts.IsCancellationRequested)
-                    if (m_updates.Any())
-                        if (m_updates.TryDequeue(out var tuple))
-                        {
-                            Logger.Send(LogType.Information, "",
-                                string.Format("Updating Server ({0}): {1} - {2}", m_updates.Count, tuple.Item1.DeviceID, tuple.Item2));
-                            new Client<GPSTrackerLocation>().Save(tuple.Item1, tuple.Item2, (_, __) => { });
-                            Thread.Sleep(Properties.UpdateTimer);
-                        }
-            }, cts.Token);
-        }
-
-        private static byte[] StringToByteArray(string hex)
-        {
-            var NumberChars = hex.Length;
-            var bytes = new byte[NumberChars / 2];
-            for (var i = 0; i < NumberChars; i += 2)
-                bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
-            return bytes;
-        }
-
-        private bool CheckConnection()
-        {
-            if (ClientFactory.UserGuid == Guid.Empty)
-            {
-                // Wait for server connection
-                while (!Client.Ping())
-                {
-                    Logger.Send(LogType.Error, "", "Database server unavailable. Trying again in 30 seconds...");
-                    Task.Delay(30_000).Wait();
-                    Logger.Send(LogType.Information, "", "Retrying connection...");
-                }
-                ClientFactory.SetBypass();
-            }
-
-            if (DateTime.Now - m_RefreshDate > new TimeSpan(0, 5, 0))
-            {
-                Logger.Send(LogType.Information, "", "Refreshing Tracker Cache");
-                var table = new Client<GPSTracker>().Query(
-                    null,
-                    new Columns<GPSTracker>(x => x.ID, x => x.DeviceID, x => x.Type.BatteryFormula)
-                );
-
-                m_RefreshDate = DateTime.Now;
-                Logger.Send(LogType.Information, "", string.Format("- Tracker Cache: {0} devices", table.Rows.Count));
-                m_devices.Clear();
-                foreach (var row in table.Rows)
-                {
-                    var formula = row.Get<GPSTracker, string?>(x => x.Type.BatteryFormula);
-                    var expression = string.IsNullOrWhiteSpace(formula) ? null : new CoreExpression<GPSBatteryFormulaModel, double>(formula);
-                    m_devices[row.Get<GPSTracker, string>(x => x.DeviceID)] =
-                        new Device(row.Get<GPSTracker, Guid>(x => x.ID), DateTime.MinValue, expression);
-                }
-            }
-
-            return true;
-        }
-
-        public void StartSigfoxListener()
-        {
-            if(Properties.SigfoxListenPort == 0)
-            {
-                Logger.Send(LogType.Information, "", "No Sigfox listen port specified\n");
-                return;
-            }
-
-            var listener = new Listener<SigfoxHandler, SigfoxHandlerProperties>(new SigfoxHandlerProperties(m_devices));
-            listener.InitPort((ushort)Properties.SigfoxListenPort);
-
-            Logger.Send(LogType.Information, "", "Starting Sigfox Listener on port " + Properties.SigfoxListenPort);
-
-            listener.Start();
-        }
-
-        /// <summary>
-        ///     Method that starts TCP/IP Server.
-        /// </summary>
-        public override void Run()
-        {
-            if (string.IsNullOrWhiteSpace(Properties.Server))
-            {
-                Logger.Send(LogType.Error, "", "Server is blank!");
-                return;
-            }
-
-            Logger.Send(LogType.Information, "", "Registering Classes");
-
-            CoreUtils.RegisterClasses();
-            ComalUtils.RegisterClasses();
-
-            ClientFactory.SetClientType(typeof(IPCClient<>), Platform.GPSEngine, Version, DatabaseServerProperties.GetPipeName(Properties.Server));
-            CheckConnection();
-
-            DMFactory.Initialise(Properties.DumpFormat, Properties.DumpFile);
-
-            if (m_server != null)
-            {
-                // Create a ArrayList for storing SocketListeners before
-                // starting the server.
-                m_socketListenersList = new ArrayList();
-
-                // Start the Server and start the thread to listen client 
-                // requests.
-                m_server.Start();
-                m_serverThread = Task.Run(ServerThreadStart, cts.Token);
-
-                // Create a low priority thread that checks and deletes client
-                // SocktConnection objcts that are marked for deletion.
-                m_purgingThread = Task.Run(PurgingThreadStart, cts.Token);
-            }
-
-            StartSigfoxListener();
-        }
-
-        /// <summary>
-        ///     Method that stops the TCP/IP Server.
-        /// </summary>
-        public override void Stop()
-        {
-            if (m_server != null)
-            {
-                // It is important to Stop the server first before doing
-                // any cleanup. If not so, clients might being added as
-                // server is running, but supporting data structures
-                // (such as m_socketListenersList) are cleared. This might
-                // cause exceptions.
-
-                cts.Cancel();
-
-                // Stop the TCP/IP Server.
-                m_stopServer = true;
-                m_server.Stop();
-
-                // Wait for one second for the the thread to stop.
-                //m_serverThread.Join(1000);
-
-                // If still alive; Get rid of the thread.
-                //if (m_serverThread.IsAlive)
-                //{
-                //   m_serverThread.Abort();
-                //}
-                m_serverThread = null;
-
-                m_stopPurging = true;
-                //m_purgingThread.Join(1000);
-                //if (m_purgingThread.IsAlive)
-                //{
-                //    m_purgingThread.Abort();
-                //}
-                //m_purgingThread = null;
-
-                // Free Server Object.
-                m_server = null;
-
-                while (m_updates.Any())
-                    if (m_updates.TryDequeue(out var tuple))
-                    {
-                        Logger.Send(LogType.Information, "",
-                            string.Format("Updating Server ({0}): {1} - {2}", m_updates.Count, tuple.Item1.DeviceID, tuple.Item2));
-                        new Client<GPSTrackerLocation>().Save(tuple.Item1, tuple.Item2, (_, __) => { });
-                        Thread.Sleep(2000);
-                    }
-
-                // Stop All clients.
-                StopAllSocketListers();
-            }
-        }
-
-
-        /// <summary>
-        ///     Method that stops all clients and clears the list.
-        /// </summary>
-        private void StopAllSocketListers()
-        {
-            foreach (GPSListener socketListener
-                     in m_socketListenersList)
-                socketListener.StopSocketListener();
-            // Remove all elements from the list.
-            m_socketListenersList.Clear();
-            m_socketListenersList = null;
-        }
-
-        /// <summary>
-        ///     TCP/IP Server Thread that is listening for clients.
-        /// </summary>
-        private void ServerThreadStart()
-        {
-            // Client Socket variable;
-            Socket clientSocket = null;
-            GPSListener socketListener = null;
-            while (!m_stopServer)
-                try
-                {
-                    if (!CheckConnection())
-                        return;
-
-                    // Wait for any client requests and if there is any 
-                    // request from any client accept it (Wait indefinitely).
-                    clientSocket = m_server.AcceptSocket();
-
-                    // Create a SocketListener object for the client.
-                    socketListener = new GPSListener(clientSocket, m_devices, m_updates);
-
-                    // Add the socket listener to an array list in a thread 
-                    // safe fashon.
-                    //Monitor.Enter(m_socketListenersList);
-                    lock (m_socketListenersList)
-                    {
-                        m_socketListenersList.Add(socketListener);
-                    }
-
-                    //Monitor.Exit(m_socketListenersList);
-                    // Start a communicating with the client in a different
-                    // thread.
-                    socketListener.StartSocketListener();
-                }
-                catch (SocketException se)
-                {
-                    m_stopServer = true;
-                }
-        }
-
-        /// <summary>
-        ///     Thread method for purging Client Listeneres that are marked for
-        ///     deletion (i.e. clients with socket connection closed). This thead
-        ///     is a low priority thread and sleeps for 10 seconds and then check
-        ///     for any client SocketConnection obects which are obselete and
-        ///     marked for deletion.
-        /// </summary>
-        private void PurgingThreadStart()
-        {
-            while (!m_stopPurging)
-            {
-                var deleteList = new ArrayList();
-
-                // Check for any clients SocketListeners that are to be
-                // deleted and put them in a separate list in a thread sage
-                // fashon.
-                //Monitor.Enter(m_socketListenersList);
-                lock (m_socketListenersList)
-                {
-                    foreach (GPSListener socketListener
-                             in m_socketListenersList)
-                        if (socketListener.IsMarkedForDeletion())
-                        {
-                            deleteList.Add(socketListener);
-                            socketListener.StopSocketListener();
-                        }
-
-                    // Delete all the client SocketConnection ojects which are
-                    // in marked for deletion and are in the delete list.
-                    for (var i = 0; i < deleteList.Count; ++i) m_socketListenersList.Remove(deleteList[i]);
-                }
-                //Monitor.Exit(m_socketListenersList);
-
-                deleteList = null;
-                Thread.Sleep(10000);
-            }
-        }
-    }
-}

+ 0 - 479
prs.server/Engines/GPS OLD/GPSListener.cs

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

+ 26 - 2
prs.server/Engines/GPS/GPSEngine.cs

@@ -18,6 +18,32 @@ using System.Timers;
 
 namespace PRSServer
 {
+    internal class Device
+    {
+        public Guid ID { get; set; }
+        public DateTime TimeStamp { get; set; }
+        public CoreExpression<GPSBatteryFormulaModel, double>? BatteryFormula { get; set; }
+
+        public Device(Guid iD, DateTime timeStamp, CoreExpression<GPSBatteryFormulaModel, double>? batteryFormula)
+        {
+            ID = iD;
+            TimeStamp = timeStamp;
+            BatteryFormula = batteryFormula;
+        }
+
+        public double CalculateBatteryLevel(double batteryValue)
+        {
+            if(BatteryFormula != null)
+            {
+                return BatteryFormula.Evaluate(new Dictionary<string, object?>
+                {
+                    { nameof(GPSBatteryFormulaModel.BatteryLevel), batteryValue }
+                });
+            }
+            return batteryValue;
+        }
+    }
+
     public class GPSDeviceUpdate : ISerializeBinary
     {
         public string AuditTrail { get; set; }
@@ -60,7 +86,6 @@ namespace PRSServer
 
         public int GetNumberOfItems()
         {
-            using var profiler = new Profiler(true);
             return Directory.EnumerateFiles(QueuePath).Count();
         }
 
@@ -70,7 +95,6 @@ namespace PRSServer
         /// <returns>A list of (filename, update) tuples.</returns>
         public IEnumerable<Tuple<string, GPSDeviceUpdate>> GetFirstItems()
         {
-            using var profiler = new Profiler(true);
             var files = Directory.EnumerateFiles(QueuePath).OrderBy(x => x);
 
             foreach (var filename in files)

+ 70 - 40
prs.server/Engines/GPS/OEMConnection.cs

@@ -27,6 +27,7 @@ namespace PRSServer
         private List<byte> ConsolidatedBuffer;
 
         private bool Finished = false;
+        private bool Confirmed = false;
         private Task? ReadTask;
         private CancellationTokenSource TokenSource = new();
 
@@ -275,6 +276,7 @@ namespace PRSServer
             else if (message is DMConfirmRequest confirm)
             {
                 response = HandleConfirm(confirm);
+                Confirmed = true;
             }
             else
             {
@@ -284,7 +286,7 @@ namespace PRSServer
 
             if(response is not null)
             {
-                Stream.Write(response.Encode());
+                Stream.Write(response.EncodeArray());
             }
         }
 
@@ -302,55 +304,79 @@ namespace PRSServer
             // We pass in the cancellation token to prevent the task from continuing if we call stop.
             ReadTask = Stream.ReadAsync(Buffer, 0, Buffer.Length).ContinueWith(t =>
             {
-                readData = true;
-                var nBytes = t.Result;
-                if (nBytes > 0)
+                if(t.Exception is not null)
                 {
-                    ConsolidatedBuffer.AddRange(Buffer.Take(nBytes));
-                    try
+                    if (Finished)
                     {
-                        while (ConsolidatedBuffer.Count > 0)
+                        Finish();
+                    }
+                    else
+                    {
+                        Logger.Send(LogType.Error, Environment.CurrentManagedThreadId.ToString(), CoreUtils.FormatException(t.Exception));
+                    }
+                }
+                else
+                {
+                    readData = true;
+                    var nBytes = t.Result;
+                    if (nBytes > 0)
+                    {
+                        ConsolidatedBuffer.AddRange(Buffer.Take(nBytes));
+                        try
                         {
-                            DMMessage? message = null;
-                            try
-                            {
-                                message = DMFactory.ParseMessage(ConsolidatedBuffer.ToArray());
-                            }
-                            catch (Exception e)
+                            while (ConsolidatedBuffer.Count > 0)
                             {
-                                Logger.Send(
-                                    LogType.Error,
-                                    Environment.CurrentManagedThreadId.ToString(),
-                                    string.Format("Unable to Parse Record: {0} ({1})", e.Message, BitConverter.ToString(ConsolidatedBuffer.ToArray()))
-                                );
-                            }
+                                DMMessage? message = null;
+                                try
+                                {
+                                    var payloadLength = DMFactory.PeekPayloadLength(ConsolidatedBuffer);
+                                    if (nBytes == Buffer.Length && ConsolidatedBuffer.Count < payloadLength)
+                                    {
+                                        // Probably we need more data.
+                                        break;
+                                    }
+                                    else
+                                    {
+                                        message = DMFactory.ParseMessage(ConsolidatedBuffer);
+                                    }
+                                }
+                                catch (Exception e)
+                                {
+                                    Logger.Send(
+                                        LogType.Error,
+                                        Environment.CurrentManagedThreadId.ToString(),
+                                        string.Format("Unable to Parse Record: {0} ({1})", e.Message, BitConverter.ToString(ConsolidatedBuffer.ToArray()))
+                                    );
+                                    break;
+                                }
 
-                            if (message is not null)
-                            {
-                                HandleMessage(message);
-                                ConsolidatedBuffer.RemoveRange(0, message.CheckSum + 5);
+                                if (message is not null)
+                                {
+                                    HandleMessage(message);
+                                    ConsolidatedBuffer.RemoveRange(0, message.CheckSum + 5);
+                                }
                             }
                         }
+                        catch (Exception e)
+                        {
+                            Logger.Send(LogType.Error, Environment.CurrentManagedThreadId.ToString(), e.Message + "\n" + e.StackTrace);
+                        }
                     }
-                    catch (Exception e)
+                    else
                     {
-                        Logger.Send(LogType.Error, Environment.CurrentManagedThreadId.ToString(), e.Message + "\n" + e.StackTrace);
+                        Finished = true;
                     }
-                }
-                else
-                {
-                    Finished = true;
-                }
 
-                if (Finished)
-                {
-                    Finish();
-                }
-                else
-                {
-                    // If we still have stuff to do, try reading again. Note that this isn't a recursive problem or anything, because
-                    // this method returns very soon.
-                    DoRead();
+                    if (Finished)
+                    {
+                        Finish();
+                    }
+                    else
+                    {
+                        // If we still have stuff to do, try reading again. Note that this isn't a recursive problem or anything, because
+                        // this method returns very soon.
+                        DoRead();
+                    }
                 }
             }, TokenSource.Token);
 
@@ -362,7 +388,11 @@ namespace PRSServer
                 if (!readData)
                 {
                     Finished = true;
-                    Logger.Send(LogType.Error, "", "DEBUG: No data read for 15 seconds.");
+                    Logger.Send(LogType.Error, "", $"DEBUG: No data read for 15 seconds. Confirmed: {Confirmed}");
+                    if (Confirmed)
+                    {
+                        Stream.Close();
+                    }
                 }
             });
         }

+ 1 - 1
prs.server/Engines/GPS/OEMListener.cs

@@ -69,7 +69,7 @@ namespace PRSServer
                     connection.Run();
 
                     // Accept a new client.
-                    AcceptClient();
+                    //AcceptClient();
                 }
                 catch (Exception e)
                 {

+ 0 - 0
prs.server/Engines/GPS OLD/SigfoxListener.cs → prs.server/Engines/GPS/SigfoxListener.cs