almost at the goal

This commit is contained in:
Robin Müller 2024-04-19 10:27:01 +02:00
parent 7d51c2813d
commit 452a2c4360
Signed by: muellerr
GPG Key ID: A649FB78196E3849
8 changed files with 252 additions and 150 deletions

120
pytmtc/pyserver.py Executable file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env python3
import socket
import abc
import select
import logging
from threading import Thread
from collections import deque
from multiprocessing import Queue
from spacepackets.ccsds.spacepacket import parse_space_packets, PacketId
from spacepackets.ecss.tc import PacketType
EXP_ID = 278
EXP_APID = 1024 + EXP_ID
EXP_PACKET_ID_TM = PacketId(PacketType.TM, True, EXP_APID)
EXP_PACKET_ID_TC = PacketId(PacketType.TC, True, EXP_APID)
OPSSAT_SERVER_PORT = 4096
TMTC_SERVER_PORT = 4097
TC_QUEUE = Queue()
TM_QUEUE = Queue()
_LOGGER = logging.getLogger(__name__)
def main():
logging.basicConfig(
format="[%(asctime)s] [%(levelname)-5s] %(message)s",
level=logging.INFO,
datefmt="%Y-%m-%d %H:%M:%S",
)
print("Starting OPS-SAT ground TMTC server")
ops_sat_thread = OpsSatServer()
ops_sat_thread.start()
tmtc_thread = TmtcServer()
tmtc_thread.start()
ops_sat_thread.join()
tmtc_thread.join()
class BaseServer(Thread):
def __init__(self, log_prefix: str, port: int):
self.log_prefix = log_prefix
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_addr = ("0.0.0.0", port)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind(server_addr)
super().__init__()
def run(self) -> None:
while True:
self.server_socket.listen()
(conn_socket, conn_addr) = self.server_socket.accept()
# conn_socket.setblocking(True)
# conn_socket.settimeout(0.2)
print(f"{self.log_prefix} TCP client {conn_addr} connected")
analysis_deque = deque()
while True:
(readable, writable, _) = select([conn_socket], [], [], 0.2)
try:
bytes_recvd = conn_socket.recv(4096)
if len(bytes_recvd) > 0:
_LOGGER.debug(f"{self.log_prefix} RX RAW: {bytes_recvd}")
analysis_deque.append(bytes_recvd)
elif len(bytes_recvd) == 0:
self.handle_read_bytestream(analysis_deque)
break
else:
print("error receiving data from TCP client")
except TimeoutError:
if len(analysis_deque) > 0:
self.handle_read_bytestream(analysis_deque)
self.send_data_to_client(conn_socket)
@abc.abstractmethod
def handle_read_bytestream(self, analysis_deque: deque):
pass
@abc.abstractmethod
def send_data_to_client(self, conn_socket: socket.socket):
pass
class OpsSatServer(BaseServer):
def __init__(self):
super().__init__("[OPS-SAT]", OPSSAT_SERVER_PORT)
def handle_read_bytestream(self, analysis_deque: deque):
parsed_packets = parse_space_packets(analysis_deque, [EXP_PACKET_ID_TM])
for packet in parsed_packets:
_LOGGER.info(f"{self.log_prefix} RX TM: [{packet.hex(sep=',')}]")
TM_QUEUE.put(packet)
def send_data_to_client(self, conn_socket: socket.socket):
while not TC_QUEUE.empty():
next_packet = TC_QUEUE.get()
_LOGGER.info(f"{self.log_prefix} TX TC [{next_packet.hex(sep=',')}]")
conn_socket.sendall(next_packet)
class TmtcServer(BaseServer):
def __init__(self):
super().__init__("[TMTC]", TMTC_SERVER_PORT)
def handle_read_bytestream(self, analysis_deque: deque):
parsed_packets = parse_space_packets(analysis_deque, [EXP_PACKET_ID_TC])
for packet in parsed_packets:
_LOGGER.info(f"{self.log_prefix} RX TC: [{packet.hex(sep=',')}]")
TC_QUEUE.put(packet)
def send_data_to_client(self, conn_socket: socket.socket):
while not TM_QUEUE.empty():
next_packet = TM_QUEUE.get()
_LOGGER.info(f"{self.log_prefix} TX TM [{next_packet.hex(sep=',')}]")
conn_socket.sendall(next_packet)
if __name__ == "__main__":
main()

View File

@ -28,9 +28,9 @@ class TcpServer(ComInterface):
self._server_socket: Optional[socket.socket] = None self._server_socket: Optional[socket.socket] = None
self._server_thread = Thread(target=self._server_task, daemon=True) self._server_thread = Thread(target=self._server_task, daemon=True)
self._connected = False self._connected = False
self._conn_start = None # self._conn_start = None
self._writing_done = False # self._writing_done = False
self._reading_done = False # self._reading_done = False
@property @property
def connected(self) -> bool: def connected(self) -> bool:
@ -66,55 +66,57 @@ class TcpServer(ComInterface):
while True and not self._kill_signal.is_set(): while True and not self._kill_signal.is_set():
try: try:
(conn_socket, conn_addr) = self._server_socket.accept() (conn_socket, conn_addr) = self._server_socket.accept()
self.conn_start = time.time() self._handle_connection(conn_socket, conn_addr)
while True: # conn_socket.close()
self._handle_connection(conn_socket, conn_addr) """
if ( if (
self._reading_done and self._writing_done self._reading_done and self._writing_done
) or time.time() - self.conn_start > 0.5: ) or time.time() - self.conn_start > 0.5:
print("reading and writing done") print("reading and writing done")
break break
"""
except TimeoutError: except TimeoutError:
print("timeout error")
continue continue
def _handle_connection(self, conn_socket: socket.socket, conn_addr: Any): def _handle_connection(self, conn_socket: socket.socket, conn_addr: Any):
_LOGGER.info(f"TCP client {conn_addr} connected") _LOGGER.info(f"TCP client {conn_addr} connected")
(readable, writable, _) = select.select( queue_len = 0
[conn_socket],
[conn_socket],
[],
0.1,
)
# TODO: Why is the stupid conn socket never readable? while True:
print(f"Writable: {writable}")
print(f"Readable: {readable}")
if writable and writable[0]:
queue_len = 0
with self._tc_lock: with self._tc_lock:
queue_len = len(self._tc_packet_queue) queue_len = len(self._tc_packet_queue)
while queue_len > 0: outputs = []
next_packet = bytes() if queue_len > 0:
with self._tc_lock: outputs.append(conn_socket)
next_packet = self._tc_packet_queue.popleft() (readable, writable, _) = select.select(
if len(next_packet) > 0: [conn_socket],
conn_socket.sendall(next_packet) outputs,
queue_len -= 1 [],
self._writing_done = True 0.2,
if readable and readable[0]: )
print("reading shit")
while True: if writable and writable[0]:
bytes_recvd = conn_socket.recv(4096) print("writeable")
if len(bytes_recvd) > 0: while queue_len > 0:
print(f"Received bytes from TCP client: {bytes_recvd.decode()}") next_packet = bytes()
with self._tm_lock: with self._tc_lock:
self._tm_packet_queue.append(bytes_recvd) next_packet = self._tc_packet_queue.popleft()
elif len(bytes_recvd) == 0: if len(next_packet) > 0:
self._reading_done = True conn_socket.sendall(next_packet)
break queue_len -= 1
else: if readable and readable[0]:
print("error receiving data from TCP client") print("readable")
while True:
bytes_recvd = conn_socket.recv(4096)
if len(bytes_recvd) > 0:
print(f"Received bytes from TCP client: {bytes_recvd.decode()}")
with self._tm_lock:
self._tm_packet_queue.append(bytes_recvd)
elif len(bytes_recvd) == 0:
break
else:
print("error receiving data from TCP client")
def is_open(self) -> bool: def is_open(self) -> bool:
"""Can be used to check whether the communication interface is open. This is useful if """Can be used to check whether the communication interface is open. This is useful if

View File

@ -1,39 +0,0 @@
#!/usr/bin/env python3
import socket
from spacepackets.ecss.tc import PusTelecommand
EXP_ID = 278
APID = 1024 + EXP_ID
SEND_PING_ONCE = True
def main():
global SEND_PING_ONCE
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_addr = ("localhost", 4096)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(server_addr)
while True:
server_socket.listen()
(conn_socket, conn_addr) = server_socket.accept()
print(f"TCP client {conn_addr} connected")
while True:
bytes_recvd = conn_socket.recv(4096)
if len(bytes_recvd) > 0:
print(f"Received bytes from TCP client: {bytes_recvd}")
elif len(bytes_recvd) == 0:
break
else:
print("error receiving data from TCP client")
if SEND_PING_ONCE:
ping_tc = PusTelecommand(service=17, subservice=1, seq_count=0, apid=APID)
conn_socket.sendall(ping_tc.pack())
SEND_PING_ONCE = False
conn_socket.close()
continue
# server_socket.close()
if __name__ == "__main__":
main()

View File

@ -22,7 +22,8 @@ pub const VALID_PACKET_ID_LIST: &[PacketId] = &[PacketId::new_for_tc(true, EXPER
// TODO: Would be nice if this can be commanded as well.. // TODO: Would be nice if this can be commanded as well..
/// Can be enabled to print all SPP packets received from the SPP server on port 4096. /// Can be enabled to print all SPP packets received from the SPP server on port 4096.
pub const SPP_CLIENT_WIRETAPPING_RX: bool = false; pub const SPP_CLIENT_WIRETAPPING_RX: bool = true;
pub const SPP_CLIENT_WIRETAPPING_TX: bool = true;
#[derive(Copy, Clone, PartialEq, Eq, Debug, TryFromPrimitive, IntoPrimitive)] #[derive(Copy, Clone, PartialEq, Eq, Debug, TryFromPrimitive, IntoPrimitive)]
#[repr(u8)] #[repr(u8)]

View File

@ -5,7 +5,7 @@ use std::sync::mpsc;
use mio::net::TcpStream; use mio::net::TcpStream;
use mio::{Events, Interest, Poll, Token}; use mio::{Events, Interest, Poll, Token};
use ops_sat_rs::config::tasks::STOP_CHECK_FREQUENCY; use ops_sat_rs::config::tasks::STOP_CHECK_FREQUENCY;
use ops_sat_rs::config::{SPP_CLIENT_WIRETAPPING_RX, TCP_SPP_SERVER_PORT}; use ops_sat_rs::config::{SPP_CLIENT_WIRETAPPING_RX, SPP_CLIENT_WIRETAPPING_TX};
use satrs::encoding::ccsds::parse_buffer_for_ccsds_space_packets; use satrs::encoding::ccsds::parse_buffer_for_ccsds_space_packets;
use satrs::queue::GenericSendError; use satrs::queue::GenericSendError;
use satrs::spacepackets::PacketId; use satrs::spacepackets::PacketId;
@ -23,15 +23,24 @@ pub enum ClientError {
Io(#[from] io::Error), Io(#[from] io::Error),
} }
#[derive(Debug, PartialEq, Eq)]
pub enum ConnectionStatus {
Unknown,
Connected,
LostConnection,
TryingReconnect,
}
pub struct TcpSppClient { pub struct TcpSppClient {
id: ComponentId, id: ComponentId,
poll: Poll, poll: Poll,
events: Events, events: Events,
// Optional to allow periodic reconnection attempts on the TCP server. // Optional to allow periodic reconnection attempts on the TCP server.
client: TcpStream, client: Option<TcpStream>,
read_buf: [u8; 4096], read_buf: [u8; 4096],
tm_tcp_client_rx: mpsc::Receiver<PacketAsVec>, tm_tcp_client_rx: mpsc::Receiver<PacketAsVec>,
server_addr: SocketAddr, server_addr: SocketAddr,
connection: ConnectionStatus,
tc_source_tx: mpsc::Sender<PacketAsVec>, tc_source_tx: mpsc::Sender<PacketAsVec>,
validator: SimpleSpValidator, validator: SimpleSpValidator,
} }
@ -46,74 +55,68 @@ impl TcpSppClient {
) -> io::Result<Self> { ) -> io::Result<Self> {
let poll = Poll::new()?; let poll = Poll::new()?;
let events = Events::with_capacity(128); let events = Events::with_capacity(128);
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port); let mut client = Self {
let mut client = TcpStream::connect(server_addr)?; id,
poll.registry().register( poll,
events,
client: None,
connection: ConnectionStatus::Unknown,
read_buf: [0; 4096],
server_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port),
tm_tcp_client_rx,
tc_source_tx,
validator: SimpleSpValidator::new(TcpComponent::Client, valid_ids.to_vec()),
};
client.connect()?;
Ok(client)
}
pub fn connect(&mut self) -> io::Result<()> {
let mut client = TcpStream::connect(self.server_addr)?;
self.poll.registry().register(
&mut client, &mut client,
Token(0), Token(0),
Interest::READABLE | Interest::WRITABLE, Interest::READABLE | Interest::WRITABLE,
)?; )?;
Ok(Self { self.client = Some(client);
id, self.connection = ConnectionStatus::TryingReconnect;
poll,
events,
client,
read_buf: [0; 4096],
server_addr,
tm_tcp_client_rx,
tc_source_tx,
validator: SimpleSpValidator::new(TcpComponent::Client, valid_ids.to_vec()),
})
}
pub fn periodic_operation(&mut self) -> Result<(), ClientError> {
// if self.client.is_some() {
return self.perform_regular_operation();
/*
} else {
log::info!("attempting reconnect");
let client_result = self.attempt_connection();
match client_result {
Ok(_) => {
self.perform_regular_operation()?;
}
Err(ref e) => {
log::warn!(
"connection to TCP server {} failed: {}",
self.server_addr,
e
);
}
}
}
*/
Ok(()) Ok(())
} }
pub fn perform_regular_operation(&mut self) -> Result<(), ClientError> { pub fn operation(&mut self) -> Result<(), ClientError> {
self.poll match self.connection {
.poll(&mut self.events, Some(STOP_CHECK_FREQUENCY))?; ConnectionStatus::TryingReconnect | ConnectionStatus::Unknown => {
let events: Vec<mio::event::Event> = self.events.iter().cloned().collect(); self.check_conn_status()?
for event in events {
if event.token() == Token(0) {
if event.is_readable() {
log::info!("readable event");
self.check_conn_status()?;
self.read_from_server()?;
}
if event.is_writable() {
log::info!("writable event");
self.check_conn_status()?;
self.write_to_server()?;
}
} }
} ConnectionStatus::Connected => {
self.check_conn_status()?;
self.poll
.poll(&mut self.events, Some(STOP_CHECK_FREQUENCY))?;
let events: Vec<mio::event::Event> = self.events.iter().cloned().collect();
for event in events {
if event.token() == Token(0) {
if event.is_readable() {
log::warn!("TCP client is readable");
self.read_from_server()?;
}
if event.is_writable() {
log::warn!("TCP client is writable");
self.write_to_server()?;
}
}
}
return Ok(());
}
ConnectionStatus::LostConnection => self.connect()?,
};
std::thread::sleep(STOP_CHECK_FREQUENCY);
Ok(()) Ok(())
} }
pub fn read_from_server(&mut self) -> Result<(), ClientError> { pub fn read_from_server(&mut self) -> Result<(), ClientError> {
match self.client.read(&mut self.read_buf) { match self.client.as_mut().unwrap().read(&mut self.read_buf) {
Ok(0) => (), // return Err(io::Error::from(io::ErrorKind::BrokenPipe).into()), Ok(0) => (),
Ok(read_bytes) => self.handle_read_bytstream(read_bytes)?, Ok(read_bytes) => self.handle_read_bytstream(read_bytes)?,
Err(e) => return Err(e.into()), Err(e) => return Err(e.into()),
} }
@ -124,7 +127,14 @@ impl TcpSppClient {
loop { loop {
match self.tm_tcp_client_rx.try_recv() { match self.tm_tcp_client_rx.try_recv() {
Ok(tm) => { Ok(tm) => {
self.client.write_all(&tm.packet)?; if SPP_CLIENT_WIRETAPPING_TX {
log::debug!(
"SPP TCP TX {}: {:x?}",
tm.packet.len(),
tm.packet.as_slice()
);
}
self.client.as_mut().unwrap().write_all(&tm.packet)?;
} }
Err(e) => match e { Err(e) => match e {
mpsc::TryRecvError::Empty => break, mpsc::TryRecvError::Empty => break,
@ -138,12 +148,21 @@ impl TcpSppClient {
Ok(()) Ok(())
} }
pub fn check_conn_status(&mut self) -> io::Result<bool> { pub fn check_conn_status(&mut self) -> io::Result<()> {
match self.client.peer_addr() { match self.client.as_mut().unwrap().peer_addr() {
Ok(_) => Ok(true), Ok(_) => {
if self.connection == ConnectionStatus::Unknown
|| self.connection == ConnectionStatus::TryingReconnect
{
self.connection = ConnectionStatus::Connected;
}
Ok(())
}
Err(e) => { Err(e) => {
if e.kind() == io::ErrorKind::NotConnected { if e.kind() == io::ErrorKind::NotConnected {
return Ok(false); log::warn!("lost connection, or do not have one");
self.connection = ConnectionStatus::LostConnection;
return Ok(());
} }
Err(e) Err(e)
} }
@ -154,7 +173,7 @@ impl TcpSppClient {
let mut dummy = 0; let mut dummy = 0;
if SPP_CLIENT_WIRETAPPING_RX { if SPP_CLIENT_WIRETAPPING_RX {
log::debug!( log::debug!(
"received {} bytes on TCP client: {:x?}", "SPP TCP RX {} bytes: {:x?}",
read_bytes, read_bytes,
&self.read_buf[..read_bytes] &self.read_buf[..read_bytes]
); );

View File

@ -232,7 +232,7 @@ fn main() {
.spawn(move || { .spawn(move || {
info!("Running TCP SPP client"); info!("Running TCP SPP client");
loop { loop {
let result = tcp_spp_client.periodic_operation(); let result = tcp_spp_client.operation();
if let Err(e) = result { if let Err(e) = result {
log::error!("TCP SPP client error: {}", e); log::error!("TCP SPP client error: {}", e);
} }

View File

@ -42,7 +42,6 @@ pub struct TestCustomServiceWrapper {
EcssTcInVecConverter, EcssTcInVecConverter,
VerificationReporter, VerificationReporter,
>, >,
// pub test_srv_event_sender: mpsc::Sender<EventMessageU32>,
} }
impl TestCustomServiceWrapper { impl TestCustomServiceWrapper {