diff --git a/.github/workflows/master-build.yml b/.github/workflows/master-build.yml index 868ca30..e7e8417 100644 --- a/.github/workflows/master-build.yml +++ b/.github/workflows/master-build.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index a788b80..29f9f9c 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/aprsd/packets/core.py b/aprsd/packets/core.py index 1b3dd8c..cfede1f 100644 --- a/aprsd/packets/core.py +++ b/aprsd/packets/core.py @@ -1,7 +1,6 @@ import abc from dataclasses import asdict, dataclass, field -import datetime -import json +from datetime import datetime import logging import re import time @@ -9,9 +8,9 @@ import time from typing import List import dacite +from dataclasses_json import dataclass_json from aprsd.utils import counter -from aprsd.utils import json as aprsd_json LOG = logging.getLogger("APRSD") @@ -28,12 +27,20 @@ PACKET_TYPE_BEACON = "beacon" PACKET_TYPE_THIRDPARTY = "thirdparty" PACKET_TYPE_UNCOMPRESSED = "uncompressed" +NO_DATE = datetime(1900, 10, 24) + def _init_timestamp(): """Build a unix style timestamp integer""" return int(round(time.time())) +def _init_send_time(): + # We have to use a datetime here, or the json encoder + # Fails on a NoneType. + return NO_DATE + + def _init_msgNo(): # noqa: N802 """For some reason __post__init doesn't get called. @@ -45,6 +52,32 @@ def _init_msgNo(): # noqa: N802 return c.value +def factory_from_dict(packet_dict): + pkt_type = get_packet_type(packet_dict) +# print(f"pkt_type {pkt_type}") + if pkt_type: +# if pkt_type == 'unknown': +# # try to determine it by the raw +# raw = packet_dict.get('raw') +# if raw: +# import aprslib +# type = get_packet_type(aprslib.parse(raw)) +# print(f"raw type {type}") + + cls = TYPE_LOOKUP[pkt_type] +# print(f"CLS {cls}") + + return cls.from_dict(packet_dict) + + + +def factory_from_json(packet_dict): + pkt_type = get_packet_type(packet_dict) + if pkt_type: + return TYPE_LOOKUP[pkt_type].from_json(packet_dict) + + +@dataclass_json @dataclass(unsafe_hash=True) class Packet(metaclass=abc.ABCMeta): from_call: str = field(default=None) @@ -64,7 +97,17 @@ class Packet(metaclass=abc.ABCMeta): # Fields related to sending packets out send_count: int = field(repr=False, default=0, compare=False, hash=False) retry_count: int = field(repr=False, default=3, compare=False, hash=False) - last_send_time: datetime.timedelta = field(repr=False, default=None, compare=False, hash=False) + #last_send_time: datetime = field( + # metadata=dc_json_config( + # encoder=datetime.isoformat, + # decoder=datetime.fromisoformat, + # ), + # repr=True, + # default_factory=_init_send_time, + # compare=False, + # hash=False + #) + last_send_time: float = field(repr=False, default=0, compare=False, hash=False) # Do we allow this packet to be saved to send later? allow_delay: bool = field(repr=False, default=True, compare=False, hash=False) path: List[str] = field(default_factory=list, compare=False, hash=False) @@ -73,16 +116,13 @@ class Packet(metaclass=abc.ABCMeta): def __post__init__(self): LOG.warning(f"POST INIT {self}") - @property - def __dict__(self): - return asdict(self) - @property def json(self): """ get the json formated string """ - return json.dumps(self.__dict__, cls=aprsd_json.EnhancedJSONEncoder) + #return json.dumps(self.__dict__, cls=aprsd_json.EnhancedJSONEncoder) + return self.to_json() def get(self, key, default=None): """Emulate a getter on a dict.""" @@ -288,7 +328,7 @@ class RejectPacket(Packet): def _build_payload(self): self.payload = f":{self.to_call.ljust(9)} :rej{self.msgNo}" - +@dataclass_json @dataclass(unsafe_hash=True) class MessagePacket(Packet): message_text: str = field(default=None) @@ -436,6 +476,13 @@ class GPSPacket(Packet): ) +@dataclass +class StatusPacket(Packet): + status: str = None + messagecapable: bool = False + comment: str = None + + @dataclass class MicEPacket(GPSPacket): messagecapable: bool = False @@ -567,7 +614,7 @@ class WeatherPacket(GPSPacket): class ThirdParty(Packet): # Holds the encapsulated packet - subpacket: Packet = None + subpacket: Packet = field(default=None, compare=True, hash=False) def __repr__(self): """Build the repr version of the packet.""" @@ -600,7 +647,7 @@ def get_packet_type(packet: dict): pkt_format = packet.get("format", None) msg_response = packet.get("response", None) - packet_type = "unknown" + packet_type = PACKET_TYPE_UNKNOWN if pkt_format == "message" and msg_response == "ack": packet_type = PACKET_TYPE_ACK elif pkt_format == "message" and msg_response == "rej": @@ -620,6 +667,10 @@ def get_packet_type(packet: dict): packet_type = PACKET_TYPE_WX elif pkt_format == PACKET_TYPE_THIRDPARTY: packet_type = PACKET_TYPE_THIRDPARTY + + if packet_type == PACKET_TYPE_UNKNOWN: + if "latitude" in packet: + packet_type = PACKET_TYPE_BEACON return packet_type diff --git a/aprsd/threads/tx.py b/aprsd/threads/tx.py index 0ceb355..708d263 100644 --- a/aprsd/threads/tx.py +++ b/aprsd/threads/tx.py @@ -1,4 +1,3 @@ -import datetime import logging import time @@ -128,10 +127,10 @@ class SendPacketThread(aprsd_threads.APRSDThread): # Message is still outstanding and needs to be acked. if packet.last_send_time: # Message has a last send time tracking - now = datetime.datetime.now() + now = int(round(time.time())) sleeptime = (packet.send_count + 1) * 31 delta = now - packet.last_send_time - if delta > datetime.timedelta(seconds=sleeptime): + if delta > sleeptime: # It's time to try to send it again send_now = True else: @@ -140,7 +139,7 @@ class SendPacketThread(aprsd_threads.APRSDThread): if send_now: # no attempt time, so lets send it, and start # tracking the time. - packet.last_send_time = datetime.datetime.now() + packet.last_send_time = int(round(time.time())) send(packet, direct=True) packet.send_count += 1 @@ -173,13 +172,13 @@ class SendAckThread(aprsd_threads.APRSDThread): if self.packet.last_send_time: # Message has a last send time tracking - now = datetime.datetime.now() + now = int(round(time.time())) # aprs duplicate detection is 30 secs? # (21 only sends first, 28 skips middle) sleep_time = 31 delta = now - self.packet.last_send_time - if delta > datetime.timedelta(seconds=sleep_time): + if delta > sleep_time: # It's time to try to send it again send_now = True elif self.loop_count % 10 == 0: @@ -190,7 +189,7 @@ class SendAckThread(aprsd_threads.APRSDThread): if send_now: send(self.packet, direct=True) self.packet.send_count += 1 - self.packet.last_send_time = datetime.datetime.now() + self.packet.last_send_time = int(round(time.time())) time.sleep(1) self.loop_count += 1 diff --git a/requirements.txt b/requirements.txt index a42b7a4..2b09381 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ # # pip-compile --annotation-style=line requirements.in # +dataclasses-json aprslib==0.7.2 # via -r requirements.in attrs==23.1.0 diff --git a/tox.ini b/tox.ini index c0fe0c1..c1d946d 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ minversion = 2.9.0 skipdist = True skip_missing_interpreters = true -envlist = pep8,py{39,310} +envlist = pep8,py{39,310,311,312} #requires = tox-pipenv # pip==22.0.4 # pip-tools==5.4.0