Add P2P send message command.

This commit is contained in:
Pedro 2024-01-18 11:35:44 +01:00 committed by DJ2LS
parent 83ecc2bca1
commit 67001ac842
5 changed files with 137 additions and 0 deletions

View file

@ -3,3 +3,10 @@ import re
def validate_freedata_callsign(callsign):
regexp = "^[a-zA-Z]+\d+\w+-\d{1,2}$"
return re.compile(regexp).match(callsign) is not None
def validate_message_attachment(attachment):
for field in ['name', 'type', 'data']:
if field not in attachment:
raise ValueError(f"Attachment missing '{field}'")
if len(attachment[field]) < 1:
raise ValueError(f"Attachment has empty '{field}'")

View file

@ -0,0 +1,23 @@
from command import TxCommand
import api_validations
import base64
from queue import Queue
from arq_session_iss import ARQSessionISS
from message_p2p import MessageP2P
class SendMessageCommand(TxCommand):
"""Command to send a P2P message using an ARQ transfer session
"""
def set_params_from_api(self, apiParams):
origin = f"{self.config['STATION']['mycall']}-{self.config['STATION']['myssid']}"
self.message = MessageP2P.from_api_params(origin, apiParams)
def transmit(self, modem):
iss = ARQSessionISS(self.config, modem,
self.message.destination,
self.message.to_payload(),
self.state_manager)
self.state_manager.register_arq_iss_session(iss)
iss.start()

62
modem/message_p2p.py Normal file
View file

@ -0,0 +1,62 @@
import datetime
import api_validations
import base64
import json
import lzma
class MessageP2P:
def __init__(self, origin: str, destination: str, body: str, attachments: list) -> None:
self.timestamp = datetime.datetime.now().isoformat()
self.origin = origin
self.destination = destination
self.body = body
self.attachments = attachments
@classmethod
def from_api_params(cls, origin: str, params: dict):
dxcall = params['dxcall']
if not api_validations.validate_freedata_callsign(dxcall):
dxcall = f"{dxcall}-0"
if not api_validations.validate_freedata_callsign(dxcall):
raise ValueError(f"Invalid dxcall given ({params['dxcall']})")
body = params['body']
if len(body) < 1:
raise ValueError(f"Body cannot be empty")
attachments = []
if 'attachments' in params:
for a in params['attachments']:
api_validations.validate_message_attachment(a)
attachments.append({
'name': a['name'],
'type': a['type'],
'data': base64.decode(a['data']),
})
return cls(origin, dxcall, body, attachments)
def get_id(self) -> str:
return f"{self.origin}.{self.destination}.{self.timestamp}"
def to_dict(self):
"""Make a dictionary out of the message data
"""
message = {
'id': self.get_id(),
'origin': self.origin,
'destination': self.destination,
'body': self.body,
'attachments': self.attachments,
}
return message
def to_payload(self):
"""Make a byte array ready to be sent out of the message data"""
json_string = json.dumps(self.to_dict())
json_bytes = bytes(json_string, 'utf-8')
final_payload = lzma.compress(json_bytes)
return final_payload

View file

@ -16,6 +16,7 @@ import command_ping
import command_feq
import command_test
import command_arq_raw
import command_message_send
import event_manager
app = Flask(__name__)
@ -234,6 +235,13 @@ def get_post_radio():
elif request.method == 'GET':
return api_response(app.state_manager.get_radio_status())
@app.route('/freedata/messages', methods=['POST'])
def post_freedata_message():
if enqueue_tx_command(command_message_send.SendMessageCommand, request.json):
return api_response(request.json)
else:
api_abort('Error executing command...', 500)
# @app.route('/modem/arq_connect', methods=['POST'])
# @app.route('/modem/arq_disconnect', methods=['POST'])
# @app.route('/modem/send_raw', methods=['POST'])

37
tests/test_message_p2p.py Executable file
View file

@ -0,0 +1,37 @@
import sys
sys.path.append('modem')
import unittest
from config import CONFIG
from message_p2p import MessageP2P
class TestDataFrameFactory(unittest.TestCase):
@classmethod
def setUpClass(cls):
config_manager = CONFIG('modem/config.ini.example')
cls.config = config_manager.read()
cls.mycall = f"{cls.config['STATION']['mycall']}-{cls.config['STATION']['myssid']}"
def testFromApiParams(self):
api_params = {
'dxcall': 'DJ2LS-3',
'body': 'Hello World!',
}
message = MessageP2P.from_api_params(self.mycall, api_params)
self.assertEqual(message.destination, api_params['dxcall'])
self.assertEqual(message.body, api_params['body'])
def testToPayload(self):
api_params = {
'dxcall': 'DJ2LS-3',
'body': 'Hello World!',
}
message = MessageP2P.from_api_params(self.mycall, api_params)
payload = message.to_payload()
self.assertGreater(len(payload), 0)
self.assertIsInstance(payload, bytes)
if __name__ == '__main__':
unittest.main()