Skip to content
Snippets Groups Projects
Tracker.cs 8.3 KiB
Newer Older
philip.schell's avatar
philip.schell committed
using System;
using System.Text.RegularExpressions;
using Fraunhofer.Fit.Iot.Lora.Events;

philip.schell's avatar
philip.schell committed
namespace Fraunhofer.Fit.Iot.Lora.Trackers {
  public class Tracker {
    private enum ParseType {
      Update,
      Panic
    }

philip.schell's avatar
philip.schell committed
    public delegate void UpdateDataEvent(Object sender, DataUpdateEvent e);
    public delegate void UpdatePanicEvent(Object sender, PanicUpdateEvent e);
philip.schell's avatar
philip.schell committed
    public delegate void UpdateStatusEvent(Object sender, StatusUpdateEvent e);
    public event UpdateDataEvent DataUpdate;
    public event UpdatePanicEvent PanicUpdate;
philip.schell's avatar
philip.schell committed
    public event UpdateStatusEvent StatusUpdate;
philip.schell's avatar
philip.schell committed

    public Int32 Bandwidth { get; private set; }
philip.schell's avatar
philip.schell committed
    public Double BatteryLevel { get; private set; }
    public UInt16 CalculatedCRC { get; private set; }
    public Byte CodingRate { get; private set; }
    public String CRCStatus { get; private set; }
    public Status DeviceStatus { get; private set; }
    public UInt32 Frequency { get; private set; }
philip.schell's avatar
philip.schell committed
    public Int32 FrequencyOffset { get; private set; }
    public GpsInfo Gps { get; private set; }
    public String IpAddress { get; private set; }
    public String Modulation { get; private set; }
philip.schell's avatar
philip.schell committed
    public String Name { get; private set; }
    public Double PacketRssi { get; private set; }
    public Byte RecieverInterface { get; private set; }
    public Byte RecieverRadio { get; private set; }
    public DateTime ReceivedTime { get; private set; }
    public Double Rssi { get; private set; }
philip.schell's avatar
philip.schell committed
    public Double Snr { get; private set; }
    public Double SnrMax { get; private set; }
    public Double SnrMin { get; private set; }
    public Byte SpreadingFactor { get; private set; }
    public UInt32 Time { get; private set; }
philip.schell's avatar
philip.schell committed
    public Int32 Version { get; private set; }
    public Boolean WifiActive { get; private set; }
    public String WifiSsid { get; private set; }

    public enum Status {
      Unknown,
      Startup,
      Powersave,
      Shutdown
    }
philip.schell's avatar
philip.schell committed

philip.schell's avatar
philip.schell committed
    #region Private Parsers and Helpers
    private void Parse(Byte[] data, ParseType dataType) {
      if (data.Length == 21) {
philip.schell's avatar
philip.schell committed
        this.Name = GetName(data);
        Single lat = BitConverter.ToSingle(data, 3);
        Single lon = BitConverter.ToSingle(data, 7);
        Single hdop = ((Single)data[11]) / 10;
        Single height = ((Single)BitConverter.ToUInt16(data, 12)) / 10;
        Byte hour = data[14];
        Byte minute = data[15];
        Byte second = data[16];
        Byte day = data[17];
        Byte month = data[18];
        UInt16 year = (UInt16)(data[19] + 2000);
        this.BatteryLevel = (((Single)data[20]) + 230) / 100;
        this.Gps.SetUpdate(lat, lon, height, hdop, hour, minute, second, day, month, year);
        //Console.WriteLine("lat: " + lat + " lon: " + lon + " hdop: " + hdop + " heigt: " + height + " hh:mm:ss: " + hour + ":" + minute + ":" + second + " DD.MM.YY: " + day + "." + month + "." + year + " bat: " + this.BatteryLevel);
          this.PanicUpdate?.Invoke(this, new PanicUpdateEvent(this));
        }
        this.DataUpdate?.Invoke(this, new DataUpdateEvent(this));
philip.schell's avatar
philip.schell committed
      }
    }

philip.schell's avatar
philip.schell committed
    private void Parse(String text) {
      String[] texts = text.Split(new String[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
      this.Name = GetName(text, 0);
      String[] infos = texts[1].Split(',');
      if (infos.Length >= 6 && Double.TryParse(infos[5], out Double batteryLevel)) {
        this.BatteryLevel = batteryLevel;
      }
      this.Gps.SetUpdate(texts[1]);
philip.schell's avatar
philip.schell committed
      this.DataUpdate?.Invoke(this, new DataUpdateEvent(this));
    }

    private void ParseStatus(String text) {
      String[] texts = text.Split(new String[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
      if(texts.Length != 3) {
        return;
      }
      this.Name = GetName(text, 1);
      String[] infos = texts[2].Split(',');
philip.schell's avatar
philip.schell committed
        return;
      }
      if (Int32.TryParse(infos[0], out Int32 version)) {
        this.Version = version;
      }
      this.IpAddress = infos[1];
      this.WifiSsid = infos[2];
      this.WifiActive = infos[3] == "t";
      if (Double.TryParse(infos[4], out Double batteryLevel)) {
        this.BatteryLevel = batteryLevel;
      }
      if (Int32.TryParse(infos[5], out Int32 freqOffset)) {
        this.FrequencyOffset = freqOffset;
      }
      if(Int32.TryParse(infos[6], out Int32 deviceStatus)) {
        if(deviceStatus == 0) {
          this.DeviceStatus = Status.Shutdown;
        } else if(deviceStatus == 1) {
          this.DeviceStatus = Status.Startup;
        } else if(deviceStatus == 2) {
          this.DeviceStatus = Status.Powersave;
        }
philip.schell's avatar
philip.schell committed
      }
      this.StatusUpdate?.Invoke(this, new StatusUpdateEvent(this));
philip.schell's avatar
philip.schell committed
    }

    private void SetUpdate(LoraClientEvent e) {
      if(e is Ic800ALoraClientEvent) {
        Ic800ALoraClientEvent ic = e as Ic800ALoraClientEvent;
        this.Bandwidth = ic.Bandwidth;
        this.CalculatedCRC = ic.Calculatedcrc;
        this.CodingRate = ic.CodingRate;
        this.CRCStatus = ic.CrcStatus;
        this.Frequency = ic.Frequency;
        this.RecieverInterface = ic.Interface;
        this.Modulation = ic.Modulation;
        this.RecieverRadio = ic.Radio;
        this.SnrMax = ic.SnrMax;
        this.SnrMin = ic.SnrMin;
        this.SpreadingFactor = ic.Spreadingfactor;
        this.Time = ic.Time;
      } 
philip.schell's avatar
philip.schell committed
      this.PacketRssi = e.Packetrssi;
      this.Rssi = e.Rssi;
      this.Snr = e.Snr;
      this.ReceivedTime = e.UpdateTime;
philip.schell's avatar
philip.schell committed
    }
philip.schell's avatar
philip.schell committed
    #endregion
philip.schell's avatar
philip.schell committed

philip.schell's avatar
philip.schell committed
    #region External update Methods
philip.schell's avatar
philip.schell committed
    public void SetUpdate(LoraClientEvent e, String data) {
      this.SetUpdate(e);
      this.Parse(data);
    }

    public void SetUpdate(LoraClientEvent e, Byte[] data) {
      this.SetUpdate(e);
      this.Parse(data, ParseType.Update);
    }
    public void SetPanics(LoraClientEvent e, Byte[] data) {
      this.SetUpdate(e);
      this.Parse(data, ParseType.Panic);
philip.schell's avatar
philip.schell committed
    }

philip.schell's avatar
philip.schell committed
    public void SetStatus(LoraClientEvent e, String textStatus) {
      this.SetUpdate(e);
      this.ParseStatus(textStatus);
    }
    #endregion

    #region Static Functions
    public static Boolean CheckPacket(String message) {
philip.schell's avatar
philip.schell committed
      String[] m;
      if (message.Contains("\r\n")) {
        m = message.Split(new String[] { "\r\n" }, StringSplitOptions.None);
      } else if (message.Contains("\n")) {
        m = message.Split(new String[] { "\n" }, StringSplitOptions.None);
      } else {
        return false;
      }
philip.schell's avatar
philip.schell committed
      if (m.Length == 2) { //Normal Data Packet
        if (m[0] == "") { //Name should not be empty
philip.schell's avatar
philip.schell committed
          return false;
        }
philip.schell's avatar
philip.schell committed
        if (!Regex.Match(m[1], "[0-9]+.[0-9]{5,10},[0-9]+.[0-9]{5,10},[0-9]{6},[0-9]+.[0-9]{2},[0-9]+.[0-9],[0-9].[0-9]{2}").Success) { //lon,lat,hhmmss,hdop,height,bat not match
philip.schell's avatar
philip.schell committed
          return false;
        }
        return true;
      }
philip.schell's avatar
philip.schell committed
      if (m.Length == 3) { //Debug Packet
        if (m[0] != "deb") { //first must be "deb"
          return false;
        }
        if (m[1] == "") { //Name should not be empty
philip.schell's avatar
philip.schell committed
          return false;
        }
        //version,ip,ssid,wififlag,battery,offset,statusmode
        if (!Regex.Match(m[2], "^[0-9]+,[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3},[^,]+,[tf],[0-9].[0-9]{2},(-[0-9]+|[0-9]+),[0-9]").Success) {
philip.schell's avatar
philip.schell committed
          return false;
        }
        return true;
      }
      return false;
    }

philip.schell's avatar
philip.schell committed
    public static String GetName(String message, Int32 index) {
      if (message.Contains("\r\n")) {
        return message.Split(new String[] { "\r\n" }, StringSplitOptions.None)[index].Trim();
      } else if (message.Contains("\n")) {
        return message.Split(new String[] { "\n" }, StringSplitOptions.None)[index].Trim();
      } else {
        return "";
      }
philip.schell's avatar
philip.schell committed
    }

    public static String GetName(Byte[] data) {
      if(data.Length >= 3) {
        Byte[] ret = new Byte[2];
        for (Int32 i = 0; i < 2; i++) {
philip.schell's avatar
philip.schell committed
          ret[i] = data[i + 1];
        }
        if (ret[1] == 0) {
          return System.Text.Encoding.ASCII.GetString(new Byte[] { ret[0] }).Trim();
        } else {
          return System.Text.Encoding.ASCII.GetString(ret).Trim();
        }
philip.schell's avatar
philip.schell committed
      }
      return "";
    }
philip.schell's avatar
philip.schell committed
    #endregion
philip.schell's avatar
philip.schell committed
  }
}