GPSTrackerLocation.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using InABox.Core;
  5. namespace Comal.Classes
  6. {
  7. public enum SigfoxType
  8. {
  9. Location = 0,
  10. DeviceStats = 1,
  11. DownloadAcknowledgement = 2
  12. }
  13. public abstract class SigfoxBase
  14. {
  15. public SigfoxBase(byte[] buffer)
  16. {
  17. if (buffer == null)
  18. throw new Exception("Invalid Payload");
  19. var recordType = buffer[0] & 0x0f;
  20. if (recordType != (int)ValidType())
  21. throw new Exception(string.Format("Invalid Record Type: {0}", recordType));
  22. Parse(buffer);
  23. }
  24. private SigfoxBase()
  25. {
  26. }
  27. public static byte[] hex2Bytes(string val)
  28. {
  29. if (string.IsNullOrWhiteSpace(val)) return null; //return new byte[] { };
  30. val = val.Trim();
  31. if (val.StartsWith("0x")) val = val.Substring(2); //get rid of starting '0x'
  32. var numBytes = val.Length / 2;
  33. //byte[] bytes = new byte[] { };
  34. var byteList = new List<byte>();
  35. for (var i = 0; i < numBytes; i++)
  36. {
  37. var sSub = val.Substring(i * 2, 2);
  38. var bSub = byte.Parse(sSub, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
  39. byteList.Add(bSub); //, 16));
  40. }
  41. return byteList.ToArray();
  42. }
  43. protected int ParseLittleEndianInt32(byte[] buffer, int offset)
  44. {
  45. return (buffer[offset + 3] << 24) +
  46. (buffer[offset + 2] << 16) +
  47. (buffer[offset + 1] << 8) +
  48. buffer[offset];
  49. }
  50. protected int ParseLittleEndianInt16(byte[] buffer, int offset)
  51. {
  52. var arg1 = buffer[offset + 1] << 8;
  53. var arg2 = buffer[offset];
  54. return arg1 + arg2;
  55. }
  56. protected int ParseLittleEndianInt16Bits(byte[] buffer, int offset, int bitOffset, int bitLength)
  57. {
  58. var temp = ParseLittleEndianInt16(buffer, offset);
  59. temp = temp >> bitOffset;
  60. var mask = 0xffff >> (16 - bitLength);
  61. return temp & mask;
  62. }
  63. protected abstract SigfoxType ValidType();
  64. protected abstract void Parse(byte[] buffer);
  65. }
  66. public class SigfoxLocation : SigfoxBase
  67. {
  68. public SigfoxLocation(byte[] buffer) : base(buffer)
  69. {
  70. }
  71. public double Latitude { get; private set; }
  72. public double Longitude { get; private set; }
  73. public bool InTrip { get; private set; }
  74. public bool LastFixFailed { get; private set; }
  75. public int Heading { get; private set; }
  76. public int Speed { get; private set; }
  77. public double BatteryLevel { get; private set; }
  78. protected override SigfoxType ValidType()
  79. {
  80. return SigfoxType.Location;
  81. }
  82. protected override void Parse(byte[] buffer)
  83. {
  84. var flags = buffer[0] & 0xF0;
  85. InTrip = (flags & 0x10) > 0;
  86. LastFixFailed = (flags & 0x20) > 0;
  87. Latitude = ParseLittleEndianInt32(buffer, 1) * 1e-7;
  88. Longitude = ParseLittleEndianInt32(buffer, 5) * 1e-7;
  89. Heading = buffer[9] * 2;
  90. Speed = buffer[10];
  91. BatteryLevel = buffer[11] * 25 / 1000.0f;
  92. }
  93. }
  94. public class SigfoxDeviceStatistics : SigfoxBase
  95. {
  96. public SigfoxDeviceStatistics(byte[] buffer) : base(buffer)
  97. {
  98. }
  99. public int UptimeWeeks { get; private set; }
  100. public int TxCountRaw { get; private set; }
  101. public byte RxCountRaw { get; private set; }
  102. public int TripCountRaw { get; private set; }
  103. public int GpsSuccessRaw { get; private set; }
  104. public int GpsFailuresRaw { get; private set; }
  105. public int AverageFixTime { get; private set; }
  106. public int AverageFailTime { get; private set; }
  107. public int AverageFreshenTime { get; private set; }
  108. public int WakeupsPerTrip { get; private set; }
  109. protected override void Parse(byte[] buffer)
  110. {
  111. if (buffer == null)
  112. return;
  113. var recordType = buffer[0] & 0x0f;
  114. if (recordType == 2)
  115. {
  116. UptimeWeeks = ParseLittleEndianInt16Bits(buffer, 0, 4, 9 /*bits*/);
  117. TxCountRaw = ParseLittleEndianInt16Bits(buffer, 1, 5, 11 /*bits*/);
  118. RxCountRaw = buffer[3];
  119. TripCountRaw = ParseLittleEndianInt16Bits(buffer, 4, 0, 13 /*bits*/);
  120. GpsSuccessRaw = ParseLittleEndianInt16Bits(buffer, 5, 5, 10 /*bits*/);
  121. GpsFailuresRaw = ParseLittleEndianInt16Bits(buffer, 6, 7, 8 /*bits*/);
  122. AverageFixTime = ParseLittleEndianInt16Bits(buffer, 7, 7, 9 /*bits*/);
  123. AverageFailTime = ParseLittleEndianInt16Bits(buffer, 9, 0, 9 /*bits*/);
  124. AverageFreshenTime = ParseLittleEndianInt16Bits(buffer, 10, 1, 8 /*bits*/);
  125. WakeupsPerTrip = buffer[11] >> 1;
  126. }
  127. }
  128. protected override SigfoxType ValidType()
  129. {
  130. return SigfoxType.DeviceStats;
  131. }
  132. }
  133. public class SigfoxDownlinkAcknowledgement : SigfoxBase
  134. {
  135. public SigfoxDownlinkAcknowledgement(byte[] buffer) : base(buffer)
  136. {
  137. }
  138. public bool DownlinkAccepted { get; private set; }
  139. public byte FirmwareMajor { get; private set; }
  140. public byte FirmwareMinor { get; private set; }
  141. public int[] DownlinkData { get; private set; }
  142. protected override SigfoxType ValidType()
  143. {
  144. return SigfoxType.DownloadAcknowledgement;
  145. }
  146. protected override void Parse(byte[] buffer)
  147. {
  148. var flags = buffer[0] & 0xF0;
  149. DownlinkAccepted = (flags & 0x10) > 0;
  150. FirmwareMajor = buffer[2];
  151. FirmwareMinor = buffer[3];
  152. var data = new List<int>();
  153. for (var i = 0; i < 8; i++) data.Add(i + 4);
  154. DownlinkData = data.ToArray();
  155. }
  156. }
  157. public static class SigfoxFactory
  158. {
  159. public static SigfoxBase Parse(string data)
  160. {
  161. var bytes = SigfoxBase.hex2Bytes(data);
  162. var type = bytes[0] & 0x0f;
  163. if (type == (int)SigfoxType.Location)
  164. return new SigfoxLocation(bytes);
  165. if (type == (int)SigfoxType.DeviceStats)
  166. return new SigfoxDeviceStatistics(bytes);
  167. if (type == (int)SigfoxType.DownloadAcknowledgement)
  168. return new SigfoxDownlinkAcknowledgement(bytes);
  169. throw new Exception(string.Format("Invalid Record Type: {0}", type));
  170. }
  171. }
  172. [Caption("Locations")]
  173. [UserTracking(typeof(GPSTracker))]
  174. public class GPSTrackerLocation : Entity, IPersistent, IRemotable, ILicense<GPSTrackerLicense>, IOneToMany<GPSTracker>
  175. {
  176. [EditorSequence(1)]
  177. [DateTimeEditor(Visible = Visible.Default, Editable = Editable.Disabled)]
  178. public override DateTime Created
  179. {
  180. get => base.Created;
  181. set => base.Created = value;
  182. }
  183. [EditorSequence(2)]
  184. public GPSTrackerLink Tracker { get; set; }
  185. [EditorSequence(3)]
  186. public Location Location { get; set; }
  187. [Obsolete("Replaced with Location.Address")]
  188. [TextBoxEditor]
  189. [EditorSequence(4)]
  190. public string Address { get; set; }
  191. [DoubleEditor]
  192. [EditorSequence(5)]
  193. public double BatteryLevel { get; set; }
  194. [DoubleEditor]
  195. [EditorSequence(6)]
  196. public double Hours { get; set; }
  197. [DoubleEditor]
  198. [EditorSequence(7)]
  199. public double Distance { get; set; }
  200. [DoubleEditor]
  201. [EditorSequence(8)]
  202. public double Counter1 { get; set; }
  203. [DoubleEditor]
  204. [EditorSequence(9)]
  205. public double Counter2 { get; set; }
  206. [DoubleEditor]
  207. [EditorSequence(10)]
  208. public double Counter3 { get; set; }
  209. [DoubleEditor]
  210. [EditorSequence(11)]
  211. public double Counter4 { get; set; }
  212. [DoubleEditor]
  213. [EditorSequence(12)]
  214. public double Counter5 { get; set; }
  215. [NullEditor]
  216. public string DeviceID { get; set; }
  217. [NullEditor]
  218. public string MessageID { get; set; }
  219. [NullEditor]
  220. public string Payload { get; set; }
  221. [NullEditor]
  222. public long TimeStamp { get; set; }
  223. [NullEditor]
  224. public bool InTrip { get; set; }
  225. [NullEditor]
  226. public bool LastFixFailed { get; set; }
  227. [NullEditor]
  228. public int Heading { get; set; }
  229. [NullEditor]
  230. public int Speed { get; set; }
  231. public override string ToString()
  232. {
  233. if (!string.IsNullOrWhiteSpace(Payload))
  234. return string.Format("ID {0} TimeStamp {1} Payload {2}", DeviceID, TimeStamp, Payload);
  235. return string.Format(
  236. "ID {0} Lat: {1:F4} Lng: {2:F4} Batt: {3:F2} Hrs: {4:F2} Dist: {5:F2} C1: {6:F2} C2: {7:F2} C3: {8:F2} C4: {9:F2} C5: {10:F2}",
  237. DeviceID, Location.Latitude, Location.Longitude, BatteryLevel, Hours, Distance, Counter1, Counter2, Counter3, Counter4, Counter5);
  238. }
  239. }
  240. }