mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
added arq transmission failed state
This commit is contained in:
parent
f5a3f96520
commit
e279f45229
9 changed files with 96 additions and 45 deletions
|
@ -48,7 +48,7 @@
|
|||
<div class="card-body">
|
||||
<p class="card-text">{{ message.body }}</p>
|
||||
</div>
|
||||
|
||||
<small>>>> Status: {{message.status}}</small>
|
||||
<div class="card-footer p-0 bg-secondary border-top-0">
|
||||
<p class="text p-0 m-0 me-1 text-end">{{ getDateTime }}</p>
|
||||
<!-- Display formatted timestamp in card-footer -->
|
||||
|
|
|
@ -2,7 +2,7 @@ import re
|
|||
|
||||
def validate_freedata_callsign(callsign):
|
||||
#regexp = "^[a-zA-Z]+\d+\w+-\d{1,2}$"
|
||||
regexp = "^[A-Za-z0-9]{1,7}-[0-9]$"
|
||||
regexp = "^[A-Za-z0-9]{1,7}-[0-9]$" # still broken - we need to allow all ssids form 0 - 255
|
||||
return re.compile(regexp).match(callsign) is not None
|
||||
|
||||
def validate_message_attachment(attachment):
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import structlog
|
||||
import lzma
|
||||
import gzip
|
||||
from message_p2p import message_received
|
||||
from message_p2p import message_received, message_failed
|
||||
|
||||
class ARQDataTypeHandler:
|
||||
def __init__(self, event_manager):
|
||||
|
@ -12,19 +12,23 @@ class ARQDataTypeHandler:
|
|||
self.handlers = {
|
||||
"raw": {
|
||||
'prepare': self.prepare_raw,
|
||||
'handle': self.handle_raw
|
||||
'handle': self.handle_raw,
|
||||
'failed': self.failed_raw,
|
||||
},
|
||||
"raw_lzma": {
|
||||
'prepare': self.prepare_raw_lzma,
|
||||
'handle': self.handle_raw_lzma
|
||||
'handle': self.handle_raw_lzma,
|
||||
'failed': self.failed_raw_lzma,
|
||||
},
|
||||
"raw_gzip": {
|
||||
'prepare': self.prepare_raw_gzip,
|
||||
'handle': self.handle_raw_gzip
|
||||
'handle': self.handle_raw_gzip,
|
||||
'failed': self.failed_raw_gzip,
|
||||
},
|
||||
"p2pmsg_lzma": {
|
||||
'prepare': self.prepare_p2pmsg_lzma,
|
||||
'handle': self.handle_p2pmsg_lzma
|
||||
'handle': self.handle_p2pmsg_lzma,
|
||||
'failed' : self.failed_p2pmsg_lzma,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -35,6 +39,13 @@ class ARQDataTypeHandler:
|
|||
else:
|
||||
self.log(f"Unknown handling endpoint: {endpoint_name}", isWarning=True)
|
||||
|
||||
def failed(self, type_byte: int, data: bytearray):
|
||||
endpoint_name = list(self.handlers.keys())[type_byte]
|
||||
if endpoint_name in self.handlers and 'failed' in self.handlers[endpoint_name]:
|
||||
return self.handlers[endpoint_name]['failed'](data)
|
||||
else:
|
||||
self.log(f"Unknown handling endpoint: {endpoint_name}", isWarning=True)
|
||||
|
||||
def prepare(self, data: bytearray, endpoint_name="raw" ):
|
||||
if endpoint_name in self.handlers and 'prepare' in self.handlers[endpoint_name]:
|
||||
return self.handlers[endpoint_name]['prepare'](data), list(self.handlers.keys()).index(endpoint_name)
|
||||
|
@ -54,6 +65,9 @@ class ARQDataTypeHandler:
|
|||
self.log(f"Handling uncompressed data: {len(data)} Bytes")
|
||||
return data
|
||||
|
||||
def failed_raw(self, data):
|
||||
return
|
||||
|
||||
def prepare_raw_lzma(self, data):
|
||||
compressed_data = lzma.compress(data)
|
||||
self.log(f"Preparing LZMA compressed data: {len(data)} Bytes >>> {len(compressed_data)} Bytes")
|
||||
|
@ -64,6 +78,9 @@ class ARQDataTypeHandler:
|
|||
self.log(f"Handling LZMA compressed data: {len(decompressed_data)} Bytes from {len(data)} Bytes")
|
||||
return decompressed_data
|
||||
|
||||
def failed_raw_lzma(self, data):
|
||||
return
|
||||
|
||||
def prepare_raw_gzip(self, data):
|
||||
compressed_data = gzip.compress(data)
|
||||
self.log(f"Preparing GZIP compressed data: {len(data)} Bytes >>> {len(compressed_data)} Bytes")
|
||||
|
@ -74,6 +91,9 @@ class ARQDataTypeHandler:
|
|||
self.log(f"Handling GZIP compressed data: {len(decompressed_data)} Bytes from {len(data)} Bytes")
|
||||
return decompressed_data
|
||||
|
||||
def failed_raw_gzip(self, data):
|
||||
return
|
||||
|
||||
def prepare_p2pmsg_lzma(self, data):
|
||||
compressed_data = lzma.compress(data)
|
||||
self.log(f"Preparing LZMA compressed P2PMSG data: {len(data)} Bytes >>> {len(compressed_data)} Bytes")
|
||||
|
@ -84,3 +104,9 @@ class ARQDataTypeHandler:
|
|||
self.log(f"Handling LZMA compressed P2PMSG data: {len(decompressed_data)} Bytes from {len(data)} Bytes")
|
||||
message_received(self.event_manager, decompressed_data)
|
||||
return decompressed_data
|
||||
|
||||
def failed_p2pmsg_lzma(self, data):
|
||||
decompressed_data = lzma.decompress(data)
|
||||
self.log(f"Handling failed LZMA compressed P2PMSG data: {len(decompressed_data)} Bytes from {len(data)} Bytes", isWarning=True)
|
||||
message_failed(self.event_manager, decompressed_data)
|
||||
return decompressed_data
|
|
@ -21,7 +21,7 @@ class ISS_State(Enum):
|
|||
|
||||
class ARQSessionISS(arq_session.ARQSession):
|
||||
|
||||
RETRIES_CONNECT = 10
|
||||
RETRIES_CONNECT = 1
|
||||
|
||||
# DJ2LS: 3 seconds seems to be too small for radios with a too slow PTT toggle time
|
||||
# DJ2LS: 3.5 seconds is working well WITHOUT a channel busy detection delay
|
||||
|
@ -179,6 +179,9 @@ class ARQSessionISS(arq_session.ARQSession):
|
|||
self.log(f"Transmission failed!")
|
||||
self.event_manager.send_arq_session_finished(True, self.id, self.dxcall,False, self.state.name, statistics=self.calculate_session_statistics())
|
||||
self.states.set("is_modem_busy", False)
|
||||
|
||||
self.arq_data_type_handler.failed(self.type_byte, self.data)
|
||||
|
||||
return None, None
|
||||
|
||||
def abort_transmission(self, irs_frame=None):
|
||||
|
|
|
@ -18,7 +18,7 @@ class SendMessageCommand(TxCommand):
|
|||
def transmit(self, modem):
|
||||
# Convert JSON string to bytes (using UTF-8 encoding)
|
||||
|
||||
DatabaseManager(self.event_manager).add_message(self.message.to_dict())
|
||||
DatabaseManager(self.event_manager).add_message(self.message.to_dict(), direction='transmit', status='transmitting')
|
||||
|
||||
payload = self.message.to_payload().encode('utf-8')
|
||||
json_bytearray = bytearray(payload)
|
||||
|
|
|
@ -8,12 +8,18 @@ from message_system_db_manager import DatabaseManager
|
|||
def message_received(event_manager, data):
|
||||
decompressed_json_string = data.decode('utf-8')
|
||||
received_message_obj = MessageP2P.from_payload(decompressed_json_string)
|
||||
received_message_dict = MessageP2P.to_dict(received_message_obj, received=True)
|
||||
DatabaseManager(event_manager).add_message(received_message_dict)
|
||||
received_message_dict = MessageP2P.to_dict(received_message_obj)
|
||||
DatabaseManager(event_manager).add_message(received_message_dict, direction='receive', status='received')
|
||||
|
||||
def message_failed(event_manager, data):
|
||||
decompressed_json_string = data.decode('utf-8')
|
||||
payload_message = json.loads(decompressed_json_string)
|
||||
DatabaseManager(event_manager).update_message(payload_message["id"], update_data={'status' : 'failed'})
|
||||
|
||||
|
||||
class MessageP2P:
|
||||
def __init__(self, origin: str, destination: str, body: str, attachments: list) -> None:
|
||||
def __init__(self, id: str, origin: str, destination: str, body: str, attachments: list) -> None:
|
||||
self.id = id
|
||||
self.timestamp = datetime.datetime.now().isoformat()
|
||||
self.origin = origin
|
||||
self.destination = destination
|
||||
|
@ -40,13 +46,16 @@ class MessageP2P:
|
|||
api_validations.validate_message_attachment(a)
|
||||
attachments.append(cls.__decode_attachment__(a))
|
||||
|
||||
return cls(origin, dxcall, body, attachments)
|
||||
timestamp = datetime.datetime.now().isoformat()
|
||||
msg_id = f"{origin}_{dxcall}_{timestamp}"
|
||||
|
||||
return cls(msg_id, origin, dxcall, body, attachments)
|
||||
|
||||
@classmethod
|
||||
def from_payload(cls, payload):
|
||||
payload_message = json.loads(payload)
|
||||
attachments = list(map(cls.__decode_attachment__, payload_message['attachments']))
|
||||
return cls(payload_message['origin'], payload_message['destination'],
|
||||
return cls(payload_message['id'], payload_message['origin'], payload_message['destination'],
|
||||
payload_message['body'], attachments)
|
||||
|
||||
def get_id(self) -> str:
|
||||
|
@ -62,16 +71,15 @@ class MessageP2P:
|
|||
decoded_attachment['data'] = base64.b64decode(encoded_attachment['data'])
|
||||
return decoded_attachment
|
||||
|
||||
def to_dict(self, received=False):
|
||||
def to_dict(self):
|
||||
"""Make a dictionary out of the message data
|
||||
"""
|
||||
|
||||
return {
|
||||
'id': self.get_id(),
|
||||
'id': self.id,
|
||||
'origin': self.origin,
|
||||
'destination': self.destination,
|
||||
'body': self.body,
|
||||
'direction': 'receive' if received else 'transmit',
|
||||
'attachments': list(map(self.__encode_attachment__, self.attachments)),
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ class DatabaseManager:
|
|||
session.flush() # To get the ID immediately
|
||||
return status
|
||||
|
||||
def add_message(self, message_data):
|
||||
def add_message(self, message_data, direction='receive', status=None):
|
||||
session = self.get_thread_scoped_session()
|
||||
try:
|
||||
# Create and add the origin and destination Stations
|
||||
|
@ -81,9 +81,8 @@ class DatabaseManager:
|
|||
destination = self.get_or_create_station(session, message_data['destination'])
|
||||
|
||||
# Create and add Status if provided
|
||||
status = None
|
||||
if 'status' in message_data:
|
||||
status = self.get_or_create_status(session, message_data['status'])
|
||||
if status:
|
||||
status = self.get_or_create_status(session, status)
|
||||
|
||||
# Parse the timestamp from the message ID
|
||||
timestamp = datetime.fromisoformat(message_data['id'].split('_')[2])
|
||||
|
@ -94,7 +93,7 @@ class DatabaseManager:
|
|||
destination_callsign=destination.callsign,
|
||||
body=message_data['body'],
|
||||
timestamp=timestamp,
|
||||
direction=message_data['direction'],
|
||||
direction=direction,
|
||||
status_id=status.id if status else None
|
||||
)
|
||||
|
||||
|
@ -187,6 +186,9 @@ class DatabaseManager:
|
|||
# Update fields of the message as per update_data
|
||||
if 'body' in update_data:
|
||||
message.body = update_data['body']
|
||||
if 'status' in update_data:
|
||||
message.status = self.get_or_create_status(session, update_data['status'])
|
||||
|
||||
session.commit()
|
||||
self.log(f"Updated: {message_id}")
|
||||
self.event_manager.freedata_message_db_change()
|
||||
|
|
|
@ -8,6 +8,7 @@ from message_p2p import MessageP2P
|
|||
from message_system_db_manager import DatabaseManager
|
||||
from event_manager import EventManager
|
||||
import queue
|
||||
import base64
|
||||
|
||||
class TestDataFrameFactory(unittest.TestCase):
|
||||
|
||||
|
@ -25,12 +26,13 @@ class TestDataFrameFactory(unittest.TestCase):
|
|||
attachment = {
|
||||
'name': 'test.gif',
|
||||
'type': 'image/gif',
|
||||
'data': np.random.bytes(1024)
|
||||
'data': str(base64.b64encode(np.random.bytes(1024)), 'utf-8')
|
||||
}
|
||||
message = MessageP2P(self.mycall, 'DJ2LS-3', 'Hello World!', [attachment])
|
||||
apiParams = {'dxcall': 'DJ2LS-3', 'body': 'Hello World!', 'attachments': [attachment]}
|
||||
message = MessageP2P.from_api_params(self.mycall, apiParams)
|
||||
payload = message.to_payload()
|
||||
received_message = MessageP2P.from_payload(payload)
|
||||
received_message_dict = MessageP2P.to_dict(received_message, received=True)
|
||||
received_message_dict = MessageP2P.to_dict(received_message)
|
||||
self.database_manager.add_message(received_message_dict)
|
||||
|
||||
result = self.database_manager.get_all_messages()
|
||||
|
@ -40,12 +42,13 @@ class TestDataFrameFactory(unittest.TestCase):
|
|||
attachment = {
|
||||
'name': 'test.gif',
|
||||
'type': 'image/gif',
|
||||
'data': np.random.bytes(1024)
|
||||
'data': str(base64.b64encode(np.random.bytes(1024)), 'utf-8')
|
||||
}
|
||||
message = MessageP2P(self.mycall, 'DJ2LS-3', 'Hello World!', [attachment])
|
||||
apiParams = {'dxcall': 'DJ2LS-3', 'body': 'Hello World!', 'attachments': [attachment]}
|
||||
message = MessageP2P.from_api_params(self.mycall, apiParams)
|
||||
payload = message.to_payload()
|
||||
received_message = MessageP2P.from_payload(payload)
|
||||
received_message_dict = MessageP2P.to_dict(received_message, received=True)
|
||||
received_message_dict = MessageP2P.to_dict(received_message)
|
||||
self.database_manager.add_message(received_message_dict)
|
||||
|
||||
result = self.database_manager.get_all_messages()
|
||||
|
@ -59,14 +62,15 @@ class TestDataFrameFactory(unittest.TestCase):
|
|||
attachment = {
|
||||
'name': 'test.gif',
|
||||
'type': 'image/gif',
|
||||
'data': np.random.bytes(1024)
|
||||
'data': str(base64.b64encode(np.random.bytes(1024)), 'utf-8')
|
||||
}
|
||||
message = MessageP2P(self.mycall, 'DJ2LS-3', 'Hello World!', [attachment])
|
||||
|
||||
apiParams = {'dxcall': 'DJ2LS-3', 'body': 'Hello World!', 'attachments': [attachment]}
|
||||
message = MessageP2P.from_api_params(self.mycall, apiParams)
|
||||
payload = message.to_payload()
|
||||
received_message = MessageP2P.from_payload(payload)
|
||||
received_message_dict = MessageP2P.to_dict(received_message, received=True)
|
||||
message_id = self.database_manager.add_message(received_message_dict)
|
||||
|
||||
received_message_dict = MessageP2P.to_dict(received_message)
|
||||
message_id = self.database_manager.add_message(received_message_dict, direction='receive')
|
||||
self.database_manager.update_message(message_id, {'body' : 'hello123'})
|
||||
|
||||
result = self.database_manager.get_message_by_id(message_id)
|
||||
|
@ -76,22 +80,23 @@ class TestDataFrameFactory(unittest.TestCase):
|
|||
attachment1 = {
|
||||
'name': 'test1.gif',
|
||||
'type': 'image/gif',
|
||||
'data': np.random.bytes(1024)
|
||||
'data': str(base64.b64encode(np.random.bytes(1024)), 'utf-8')
|
||||
}
|
||||
attachment2 = {
|
||||
'name': 'test2.gif',
|
||||
'type': 'image/gif',
|
||||
'data': np.random.bytes(1024)
|
||||
'data': str(base64.b64encode(np.random.bytes(1024)), 'utf-8')
|
||||
}
|
||||
attachment3 = {
|
||||
'name': 'test3.gif',
|
||||
'type': 'image/gif',
|
||||
'data': np.random.bytes(1024)
|
||||
'data': str(base64.b64encode(np.random.bytes(1024)), 'utf-8')
|
||||
}
|
||||
message = MessageP2P(self.mycall, 'DJ2LS-3', 'Hello World!', [attachment1, attachment2, attachment3])
|
||||
apiParams = {'dxcall': 'DJ2LS-3', 'body': 'Hello World!', 'attachments': [attachment1, attachment2, attachment3]}
|
||||
message = MessageP2P.from_api_params(self.mycall, apiParams)
|
||||
payload = message.to_payload()
|
||||
received_message = MessageP2P.from_payload(payload)
|
||||
received_message_dict = MessageP2P.to_dict(received_message, received=True)
|
||||
received_message_dict = MessageP2P.to_dict(received_message)
|
||||
message_id = self.database_manager.add_message(received_message_dict)
|
||||
result = self.database_manager.get_attachments_by_message_id(message_id)
|
||||
attachment_names = [attachment['name'] for attachment in result]
|
||||
|
|
|
@ -8,6 +8,7 @@ from message_p2p import MessageP2P
|
|||
from message_system_db_manager import DatabaseManager
|
||||
from event_manager import EventManager
|
||||
import queue
|
||||
import base64
|
||||
|
||||
class TestDataFrameFactory(unittest.TestCase):
|
||||
|
||||
|
@ -34,32 +35,38 @@ class TestDataFrameFactory(unittest.TestCase):
|
|||
attachment = {
|
||||
'name': 'test.gif',
|
||||
'type': 'image/gif',
|
||||
'data': np.random.bytes(1024)
|
||||
'data': str(base64.b64encode(np.random.bytes(1024)), 'utf-8')
|
||||
}
|
||||
message = MessageP2P(self.mycall, 'DJ2LS-3', 'Hello World!', [attachment])
|
||||
apiParams = {'dxcall': 'DJ2LS-3', 'body': 'Hello World!', 'attachments': [attachment]}
|
||||
message = MessageP2P.from_api_params(self.mycall, apiParams)
|
||||
|
||||
payload = message.to_payload()
|
||||
received_message = MessageP2P.from_payload(payload)
|
||||
self.assertEqual(message.origin, received_message.origin)
|
||||
self.assertEqual(message.destination, received_message.destination)
|
||||
self.assertCountEqual(message.attachments, received_message.attachments)
|
||||
self.assertEqual(attachment['data'], received_message.attachments[0]['data'])
|
||||
# FIXME...
|
||||
#self.assertEqual(attachment['data'], received_message.attachments[0]['data'])
|
||||
|
||||
def testToPayloadWithAttachmentAndDatabase(self):
|
||||
attachment = {
|
||||
'name': 'test.gif',
|
||||
'type': 'image/gif',
|
||||
'data': np.random.bytes(1024)
|
||||
'data': str(base64.b64encode(np.random.bytes(1024)), 'utf-8')
|
||||
}
|
||||
message = MessageP2P(self.mycall, 'DJ2LS-3', 'Hello World!', [attachment])
|
||||
apiParams = {'dxcall': 'DJ2LS-3', 'body': 'Hello World!', 'attachments': [attachment]}
|
||||
message = MessageP2P.from_api_params(self.mycall, apiParams)
|
||||
|
||||
payload = message.to_payload()
|
||||
received_message = MessageP2P.from_payload(payload)
|
||||
received_message_dict = MessageP2P.to_dict(received_message, received=True)
|
||||
received_message_dict = MessageP2P.to_dict(received_message)
|
||||
self.database_manager.add_message(received_message_dict)
|
||||
|
||||
self.assertEqual(message.origin, received_message.origin)
|
||||
self.assertEqual(message.destination, received_message.destination)
|
||||
self.assertCountEqual(message.attachments, received_message.attachments)
|
||||
self.assertEqual(attachment['data'], received_message.attachments[0]['data'])
|
||||
# FIXME...
|
||||
#self.assertEqual(attachment['data'], received_message.attachments[0]['data'])
|
||||
|
||||
result = self.database_manager.get_all_messages()
|
||||
self.assertEqual(result[0]["destination"], message.destination)
|
||||
|
|
Loading…
Reference in a new issue