dronecot 2.3.3__tar.gz → 2.3.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. {dronecot-2.3.3 → dronecot-2.3.5}/CHANGELOG.md +7 -0
  2. {dronecot-2.3.3/dronecot.egg-info → dronecot-2.3.5}/PKG-INFO +1 -1
  3. {dronecot-2.3.3 → dronecot-2.3.5/dronecot.egg-info}/PKG-INFO +1 -1
  4. {dronecot-2.3.3 → dronecot-2.3.5}/setup.cfg +3 -0
  5. dronecot-2.3.5/src/dronecot/VERSION +1 -0
  6. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/classes.py +41 -5
  7. {dronecot-2.3.3 → dronecot-2.3.5}/tests/test_status.py +64 -0
  8. dronecot-2.3.3/src/dronecot/VERSION +0 -1
  9. {dronecot-2.3.3 → dronecot-2.3.5}/LICENSE +0 -0
  10. {dronecot-2.3.3 → dronecot-2.3.5}/MANIFEST.in +0 -0
  11. {dronecot-2.3.3 → dronecot-2.3.5}/README.md +0 -0
  12. {dronecot-2.3.3 → dronecot-2.3.5}/dronecot.egg-info/SOURCES.txt +0 -0
  13. {dronecot-2.3.3 → dronecot-2.3.5}/dronecot.egg-info/dependency_links.txt +0 -0
  14. {dronecot-2.3.3 → dronecot-2.3.5}/dronecot.egg-info/entry_points.txt +0 -0
  15. {dronecot-2.3.3 → dronecot-2.3.5}/dronecot.egg-info/requires.txt +0 -0
  16. {dronecot-2.3.3 → dronecot-2.3.5}/dronecot.egg-info/top_level.txt +0 -0
  17. {dronecot-2.3.3 → dronecot-2.3.5}/setup.py +0 -0
  18. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/__init__.py +0 -0
  19. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/ble_capture.py +0 -0
  20. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/ble_parse.py +0 -0
  21. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/bluez_capture.py +0 -0
  22. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/commands.py +0 -0
  23. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/constants.py +0 -0
  24. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/dji_exceptions.py +0 -0
  25. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/dji_functions.py +0 -0
  26. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/dji_text_parser.py +0 -0
  27. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/functions.py +0 -0
  28. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/odid.py +0 -0
  29. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/open_drone_id.py +0 -0
  30. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/rid_normalize.py +0 -0
  31. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/rid_track.py +0 -0
  32. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/udp_rid.py +0 -0
  33. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/wifi_capture.py +0 -0
  34. {dronecot-2.3.3 → dronecot-2.3.5}/src/dronecot/wifi_parse.py +0 -0
  35. {dronecot-2.3.3 → dronecot-2.3.5}/tests/test_adsb_vehicle.py +0 -0
  36. {dronecot-2.3.3 → dronecot-2.3.5}/tests/test_bluez_capture.py +0 -0
  37. {dronecot-2.3.3 → dronecot-2.3.5}/tests/test_dji_listener.py +0 -0
  38. {dronecot-2.3.3 → dronecot-2.3.5}/tests/test_functions.py +0 -0
  39. {dronecot-2.3.3 → dronecot-2.3.5}/tests/test_mavlink_identity.py +0 -0
  40. {dronecot-2.3.3 → dronecot-2.3.5}/tests/test_rid_normalize.py +0 -0
  41. {dronecot-2.3.3 → dronecot-2.3.5}/tests/test_rid_track.py +0 -0
  42. {dronecot-2.3.3 → dronecot-2.3.5}/tests/test_sensor_detail.py +0 -0
  43. {dronecot-2.3.3 → dronecot-2.3.5}/tests/test_wifi_parse.py +0 -0
@@ -1,3 +1,10 @@
1
+ ## DroneCOT 2.3.4
2
+
3
+ - Include `dronecot/VERSION` in wheel and Debian installations. DroneCOT 2.3.3
4
+ omitted this runtime file and failed at import time after installation.
5
+ - Verify wheel and Debian archives contain the runtime version file before
6
+ publishing a release.
7
+
1
8
  ## DroneCOT 2.3.3
2
9
 
3
10
  - Fixed the AntSDR scanner-push listener retaining active client tasks while
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dronecot
3
- Version: 2.3.3
3
+ Version: 2.3.5
4
4
  Summary: Display Drones in Team Awareness Kit (TAK).
5
5
  Home-page: https://github.com/snstac/dronecot
6
6
  Maintainer: Greg Albrecht <oss@undef.net>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dronecot
3
- Version: 2.3.3
3
+ Version: 2.3.5
4
4
  Summary: Display Drones in Team Awareness Kit (TAK).
5
5
  Home-page: https://github.com/snstac/dronecot
6
6
  Maintainer: Greg Albrecht <oss@undef.net>
@@ -60,6 +60,9 @@ install_requires =
60
60
  [options.packages.find]
61
61
  where = src
62
62
 
63
+ [options.package_data]
64
+ dronecot = VERSION
65
+
63
66
  [options.extras_require]
64
67
  with_takproto = takproto >= 2.0.0
65
68
  wifi = scapy >= 2.5.0
@@ -0,0 +1 @@
1
+ 2.3.5
@@ -937,24 +937,60 @@ class DJIWorker(pytak.QueueWorker):
937
937
  def __init__(self, tx_queue: asyncio.Queue, config, net_queue: asyncio.Queue) -> None:
938
938
  super().__init__(tx_queue, config)
939
939
  self.net_queue = net_queue
940
+ self.status = make_status("dronecot", dronecot.__version__)
940
941
 
941
942
  async def handle_data(self, data) -> None:
943
+ if not data:
944
+ return
945
+
946
+ self.status.count("rx")
942
947
  if isinstance(data, str):
943
948
  events = dronecot.dji_handle_text_line(data, self.config)
949
+ source = "dji-text"
944
950
  elif isinstance(data, bytes) and data.startswith(b"dji_O,"):
945
- events = dronecot.dji_handle_text_line(data.decode("utf-8", errors="replace"), self.config)
951
+ events = dronecot.dji_handle_text_line(
952
+ data.decode("utf-8", errors="replace"), self.config
953
+ )
954
+ source = "dji-text"
946
955
  else:
947
956
  events = dronecot.dji_handle_frame(data, self.config)
957
+ source = "dji-binary"
958
+
959
+ if events:
960
+ self.status.count("emitted", len(events))
961
+ else:
962
+ self.status.count("no_cot")
963
+ # Do not copy aircraft identifiers or the raw RF payload into the
964
+ # world-readable status file. Operators need to know whether the
965
+ # AntSDR is producing usable data, not which aircraft it heard.
966
+ self.status.record(source=source, placed=bool(events), events=len(events))
967
+ self.status.write()
968
+
948
969
  for event in events:
949
970
  await self.put_queue(event)
950
971
 
951
972
  async def run(self, _=-1) -> None:
952
973
  self._logger.info("Running %s", self.__class__)
974
+ self.status.set(
975
+ feed=urlparse(str(self.config.get("FEED_URL", ""))).scheme or None,
976
+ decoder="dji",
977
+ )
978
+ self.status.write(force=True)
979
+ heartbeat = asyncio.ensure_future(self._heartbeat())
980
+ try:
981
+ while True:
982
+ received = await self.net_queue.get()
983
+ if not received:
984
+ continue
985
+ await self.handle_data(received)
986
+ finally:
987
+ heartbeat.cancel()
988
+
989
+ async def _heartbeat(self, interval: float = 5.0) -> None:
990
+ """Keep DJI status fresh when the RF feed is connected but quiet."""
953
991
  while True:
954
- received = await self.net_queue.get()
955
- if not received:
956
- continue
957
- await self.handle_data(received)
992
+ await asyncio.sleep(interval)
993
+ self.status.write(force=True)
958
994
 
959
995
 
960
996
  class _DJIFeedWorker(pytak.QueueWorker):
@@ -209,6 +209,70 @@ class TestStatusHeartbeat:
209
209
  assert doc["tracked"] == 0
210
210
 
211
211
 
212
+ @needs_statuswriter
213
+ class TestDJIStatusSurface:
214
+ """AntSDR/DJI feeds use DJIWorker rather than RIDWorker."""
215
+
216
+ def _worker(self, tmp_path):
217
+ worker = classes.DJIWorker.__new__(classes.DJIWorker)
218
+ worker.config = {"FEED_URL": "tcp://172.31.100.2:52002"}
219
+ worker.net_queue = asyncio.Queue()
220
+ worker._logger = logging.getLogger("test")
221
+ worker.status = pytak.StatusWriter(
222
+ "dronecot-test", path=str(tmp_path / "status.json")
223
+ )
224
+ worker.put_queue = _noop_put
225
+ return worker
226
+
227
+ def _doc(self, worker):
228
+ with open(worker.status.path) as handle:
229
+ return json.load(handle)
230
+
231
+ def test_text_frame_counts_received_and_emitted(self, tmp_path, monkeypatch):
232
+ worker = self._worker(tmp_path)
233
+ monkeypatch.setattr(
234
+ classes.dronecot,
235
+ "dji_handle_text_line",
236
+ lambda data, config: [b"<event/>", b"<event/>"],
237
+ )
238
+
239
+ asyncio.run(worker.handle_data("dji_O,test"))
240
+
241
+ doc = self._doc(worker)
242
+ assert doc["counters"] == {"rx": 1, "emitted": 2}
243
+ assert doc["recent"][-1] == {
244
+ "t": doc["recent"][-1]["t"],
245
+ "source": "dji-text",
246
+ "placed": True,
247
+ "events": 2,
248
+ }
249
+
250
+ def test_unrenderable_binary_frame_is_visible(self, tmp_path, monkeypatch):
251
+ worker = self._worker(tmp_path)
252
+ monkeypatch.setattr(
253
+ classes.dronecot, "dji_handle_frame", lambda data, config: []
254
+ )
255
+
256
+ asyncio.run(worker.handle_data(b"binary-frame"))
257
+
258
+ doc = self._doc(worker)
259
+ assert doc["counters"] == {"rx": 1, "no_cot": 1}
260
+ assert doc["recent"][-1]["source"] == "dji-binary"
261
+ assert doc["recent"][-1]["placed"] is False
262
+
263
+ def test_heartbeat_refreshes_a_quiet_dji_feed(self, tmp_path):
264
+ worker = self._worker(tmp_path)
265
+
266
+ async def _drive():
267
+ task = asyncio.ensure_future(worker._heartbeat(interval=0.01))
268
+ await asyncio.sleep(0.05)
269
+ task.cancel()
270
+
271
+ asyncio.run(_drive())
272
+
273
+ assert self._doc(worker)["counters"] == {}
274
+
275
+
212
276
  class TestStatusDegradesVisibly:
213
277
  """A pytak without StatusWriter must not take the gateway down.
214
278
 
@@ -1 +0,0 @@
1
- 2.3.3
File without changes
File without changes
File without changes
File without changes
File without changes