17
17
18
18
"""PyTAK Class Definitions."""
19
19
20
+ import abc
20
21
import asyncio
21
- import importlib .util
22
22
import ipaddress
23
23
import logging
24
24
import multiprocessing as mp
27
27
28
28
import xml .etree .ElementTree as ET
29
29
30
- from typing import Optional , Set , Union
30
+ from dataclasses import dataclass
31
+ from typing import Set , Union
31
32
32
33
from configparser import ConfigParser , SectionProxy
33
34
34
35
import pytak
35
36
36
37
try :
37
- import takproto
38
+ import takproto # type: ignore
38
39
except ImportError :
39
- pass
40
+ takproto = None
40
41
41
42
42
- class Worker : # pylint: disable=too-few-public-methods
43
+ class Worker :
43
44
"""Meta class for all other Worker Classes."""
44
45
45
46
_logger = logging .getLogger (__name__ )
@@ -74,17 +75,15 @@ def __init__(
74
75
75
76
tak_proto_version = int (self .config .get ("TAK_PROTO" ) or pytak .DEFAULT_TAK_PROTO )
76
77
77
- if tak_proto_version > 0 and importlib . util . find_spec ( " takproto" ) is None :
78
+ if tak_proto_version > 0 and takproto is None :
78
79
self ._logger .warning (
79
80
"TAK_PROTO is set to '%s', but the 'takproto' Python module is not installed.\n "
80
81
"Try: python -m pip install pytak[with_takproto]\n "
81
82
"See Also: https://pytak.rtfd.io/en/latest/compatibility/#tak-protocol-payload-version-1-protobuf" ,
82
83
tak_proto_version ,
83
84
)
84
85
85
- self .use_protobuf = tak_proto_version > 0 and importlib .util .find_spec (
86
- "takproto"
87
- )
86
+ self .use_protobuf = tak_proto_version > 0 and takproto is not None
88
87
89
88
async def fts_compat (self ) -> None :
90
89
"""Apply FreeTAKServer (FTS) compatibility.
@@ -100,9 +99,10 @@ async def fts_compat(self) -> None:
100
99
self ._logger .debug ("COMPAT: Sleeping for %ss" , sleep_period )
101
100
await asyncio .sleep (sleep_period )
102
101
102
+ @abc .abstractmethod
103
103
async def handle_data (self , data : bytes ) -> None :
104
104
"""Handle data (placeholder method, please override)."""
105
- raise NotImplementedError ( "Subclasses need to override this method" )
105
+ pass
106
106
107
107
async def run (self , number_of_iterations = - 1 ):
108
108
"""Run this Thread, reads Data from Queue & passes data to next Handler."""
@@ -131,7 +131,7 @@ async def run(self, number_of_iterations=-1):
131
131
number_of_iterations -= 1
132
132
133
133
134
- class TXWorker (Worker ): # pylint: disable=too-few-public-methods
134
+ class TXWorker (Worker ):
135
135
"""Works data queue and hands off to Protocol Workers.
136
136
137
137
You should create an TXWorker Instance using the `pytak.txworker_factory()`
@@ -190,7 +190,7 @@ async def send_data(self, data: bytes) -> None:
190
190
self .writer .flush ()
191
191
192
192
193
- class RXWorker (Worker ): # pylint: disable=too-few-public-methods
193
+ class RXWorker (Worker ):
194
194
"""Async receive (input) queue worker.
195
195
196
196
Reads events from a `pytak.protocol_factory()` reader and adds them to
@@ -212,6 +212,11 @@ def __init__(
212
212
self .reader : asyncio .Protocol = reader
213
213
self .reader_queue = None
214
214
215
+ @abc .abstractmethod
216
+ async def handle_data (self , data : bytes ) -> None :
217
+ """Handle data (placeholder method, please override)."""
218
+ pass
219
+
215
220
async def readcot (self ):
216
221
"""Read CoT from the wire until we hit an event boundary."""
217
222
try :
@@ -241,7 +246,7 @@ async def run(self, number_of_iterations=-1) -> None:
241
246
self .queue .put_nowait (data )
242
247
243
248
244
- class QueueWorker (Worker ): # pylint: disable=too-few-public-methods
249
+ class QueueWorker (Worker ):
245
250
"""Read non-CoT Messages from an async network client.
246
251
247
252
(`asyncio.Protocol` or similar async network client)
@@ -263,6 +268,11 @@ def __init__(
263
268
super ().__init__ (queue , config )
264
269
self ._logger .info ("Using COT_URL='%s'" , self .config .get ("COT_URL" ))
265
270
271
+ @abc .abstractmethod
272
+ async def handle_data (self , data : bytes ) -> None :
273
+ """Handle data (placeholder method, please override)."""
274
+ pass
275
+
266
276
async def put_queue (
267
277
self , data : bytes , queue_arg : Union [asyncio .Queue , mp .Queue , None ] = None
268
278
) -> None :
@@ -408,3 +418,63 @@ async def run(self):
408
418
409
419
for task in done :
410
420
self ._logger .info ("Complete: %s" , task )
421
+
422
+
423
+ @dataclass
424
+ class SimpleCOTEvent :
425
+ """CoT Event Dataclass."""
426
+
427
+ lat : Union [bytes , str , float , None ] = None
428
+ lon : Union [bytes , str , float , None ] = None
429
+ uid : Union [str , None ] = None
430
+ stale : Union [float , int , None ] = None
431
+ cot_type : Union [str , None ] = None
432
+
433
+ def __str__ (self ) -> str :
434
+ """Return a formatted string representation of the dataclass."""
435
+ event = self .to_xml ()
436
+ return ET .tostring (event , encoding = "unicode" )
437
+
438
+ def to_bytes (self ) -> bytes :
439
+ """Return the class as bytes."""
440
+ event = self .to_xml ()
441
+ return ET .tostring (event , encoding = "utf-8" )
442
+
443
+ def to_xml (self ) -> ET .Element :
444
+ """Return a CoT Event as an XML string."""
445
+ cotevent = COTEvent (
446
+ lat = self .lat ,
447
+ lon = self .lon ,
448
+ uid = self .uid ,
449
+ stale = self .stale ,
450
+ cot_type = self .cot_type ,
451
+ le = pytak .DEFAULT_COT_VAL ,
452
+ ce = pytak .DEFAULT_COT_VAL ,
453
+ hae = pytak .DEFAULT_COT_VAL ,
454
+ )
455
+ event = pytak .cot2xml (cotevent )
456
+ return event
457
+
458
+
459
+ @dataclass
460
+ class COTEvent (SimpleCOTEvent ):
461
+ """COT Event Dataclass."""
462
+
463
+ ce : Union [bytes , str , float , int , None ] = None
464
+ hae : Union [bytes , str , float , int , None ] = None
465
+ le : Union [bytes , str , float , int , None ] = None
466
+
467
+ def to_xml (self ) -> ET .Element :
468
+ """Return a CoT Event as an XML string."""
469
+ cotevent = COTEvent (
470
+ lat = self .lat ,
471
+ lon = self .lon ,
472
+ uid = self .uid ,
473
+ stale = self .stale ,
474
+ cot_type = self .cot_type ,
475
+ le = self .le ,
476
+ ce = self .ce ,
477
+ hae = self .hae ,
478
+ )
479
+ event = pytak .cot2xml (cotevent )
480
+ return event
0 commit comments