Compare commits
20 Commits
satrs-mib-
...
satrs-v0.2
Author | SHA1 | Date | |
---|---|---|---|
bfaddd0ebb | |||
423a068736 | |||
8022af1bf2 | |||
acd2260dfd | |||
e5ee698dc4 | |||
e8907c74d4
|
|||
536051e05b | |||
701db659e9 | |||
4b8e54b91b
|
|||
870d60cfd6 | |||
9e62e4292c
|
|||
b2e77fbc09 | |||
5371928496
|
|||
31cddbd99b | |||
7c00e13e70 | |||
aa72063454 | |||
7b37b76695 | |||
ea5d95c12d | |||
c62adbb300 | |||
4a27d2605d
|
@ -103,7 +103,9 @@ class PusHandler(GenericApidHandlerBase):
|
|||||||
|
|
||||||
def handle_tm(self, apid: int, packet: bytes, _user_args: Any):
|
def handle_tm(self, apid: int, packet: bytes, _user_args: Any):
|
||||||
try:
|
try:
|
||||||
pus_tm = PusTelemetry.unpack(packet, time_reader=CdsShortTimestamp.empty())
|
pus_tm = PusTelemetry.unpack(
|
||||||
|
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||||
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
_LOGGER.warning("Could not generate PUS TM object from raw data")
|
_LOGGER.warning("Could not generate PUS TM object from raw data")
|
||||||
_LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}")
|
_LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}")
|
||||||
@ -111,7 +113,7 @@ class PusHandler(GenericApidHandlerBase):
|
|||||||
service = pus_tm.service
|
service = pus_tm.service
|
||||||
if service == 1:
|
if service == 1:
|
||||||
tm_packet = Service1Tm.unpack(
|
tm_packet = Service1Tm.unpack(
|
||||||
data=packet, params=UnpackParams(CdsShortTimestamp.empty(), 1, 2)
|
data=packet, params=UnpackParams(CdsShortTimestamp.TIMESTAMP_SIZE, 1, 2)
|
||||||
)
|
)
|
||||||
res = self.verif_wrapper.add_tm(tm_packet)
|
res = self.verif_wrapper.add_tm(tm_packet)
|
||||||
if res is None:
|
if res is None:
|
||||||
@ -128,7 +130,9 @@ class PusHandler(GenericApidHandlerBase):
|
|||||||
elif service == 3:
|
elif service == 3:
|
||||||
_LOGGER.info("No handling for HK packets implemented")
|
_LOGGER.info("No handling for HK packets implemented")
|
||||||
_LOGGER.info(f"Raw packet: 0x[{packet.hex(sep=',')}]")
|
_LOGGER.info(f"Raw packet: 0x[{packet.hex(sep=',')}]")
|
||||||
pus_tm = PusTelemetry.unpack(packet, time_reader=CdsShortTimestamp.empty())
|
pus_tm = PusTelemetry.unpack(
|
||||||
|
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||||
|
)
|
||||||
if pus_tm.subservice == 25:
|
if pus_tm.subservice == 25:
|
||||||
if len(pus_tm.source_data) < 8:
|
if len(pus_tm.source_data) < 8:
|
||||||
raise ValueError("No addressable ID in HK packet")
|
raise ValueError("No addressable ID in HK packet")
|
||||||
@ -136,7 +140,7 @@ class PusHandler(GenericApidHandlerBase):
|
|||||||
_LOGGER.info(json_str)
|
_LOGGER.info(json_str)
|
||||||
elif service == 5:
|
elif service == 5:
|
||||||
tm_packet = PusTelemetry.unpack(
|
tm_packet = PusTelemetry.unpack(
|
||||||
packet, time_reader=CdsShortTimestamp.empty()
|
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||||
)
|
)
|
||||||
src_data = tm_packet.source_data
|
src_data = tm_packet.source_data
|
||||||
event_u32 = EventU32.unpack(src_data)
|
event_u32 = EventU32.unpack(src_data)
|
||||||
@ -145,7 +149,7 @@ class PusHandler(GenericApidHandlerBase):
|
|||||||
_LOGGER.info("Received test event")
|
_LOGGER.info("Received test event")
|
||||||
elif service == 17:
|
elif service == 17:
|
||||||
tm_packet = Service17Tm.unpack(
|
tm_packet = Service17Tm.unpack(
|
||||||
packet, time_reader=CdsShortTimestamp.empty()
|
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||||
)
|
)
|
||||||
if tm_packet.subservice == 2:
|
if tm_packet.subservice == 2:
|
||||||
self.file_logger.info("Received Ping Reply TM[17,2]")
|
self.file_logger.info("Received Ping Reply TM[17,2]")
|
||||||
@ -162,7 +166,7 @@ class PusHandler(GenericApidHandlerBase):
|
|||||||
f"The service {service} is not implemented in Telemetry Factory"
|
f"The service {service} is not implemented in Telemetry Factory"
|
||||||
)
|
)
|
||||||
tm_packet = PusTelemetry.unpack(
|
tm_packet = PusTelemetry.unpack(
|
||||||
packet, time_reader=CdsShortTimestamp.empty()
|
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||||
)
|
)
|
||||||
self.raw_logger.log_tm(pus_tm)
|
self.raw_logger.log_tm(pus_tm)
|
||||||
|
|
||||||
@ -197,15 +201,15 @@ class TcHandler(TcHandlerBase):
|
|||||||
_LOGGER.info(log_entry.log_str)
|
_LOGGER.info(log_entry.log_str)
|
||||||
|
|
||||||
def queue_finished_cb(self, info: ProcedureWrapper):
|
def queue_finished_cb(self, info: ProcedureWrapper):
|
||||||
if info.proc_type == TcProcedureType.DEFAULT:
|
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
||||||
def_proc = info.to_def_procedure()
|
def_proc = info.to_tree_commanding_procedure()
|
||||||
_LOGGER.info(f"Queue handling finished for command {def_proc.cmd_path}")
|
_LOGGER.info(f"Queue handling finished for command {def_proc.cmd_path}")
|
||||||
|
|
||||||
def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
|
def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
|
||||||
q = self.queue_helper
|
q = self.queue_helper
|
||||||
q.queue_wrapper = wrapper.queue_wrapper
|
q.queue_wrapper = wrapper.queue_wrapper
|
||||||
if info.proc_type == TcProcedureType.DEFAULT:
|
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
||||||
def_proc = info.to_def_procedure()
|
def_proc = info.to_tree_commanding_procedure()
|
||||||
assert def_proc.cmd_path is not None
|
assert def_proc.cmd_path is not None
|
||||||
pus_tc.pack_pus_telecommands(q, def_proc.cmd_path)
|
pus_tc.pack_pus_telecommands(q, def_proc.cmd_path)
|
||||||
|
|
||||||
@ -256,6 +260,7 @@ def main():
|
|||||||
while True:
|
while True:
|
||||||
state = tmtc_backend.periodic_op(None)
|
state = tmtc_backend.periodic_op(None)
|
||||||
if state.request == BackendRequest.TERMINATION_NO_ERROR:
|
if state.request == BackendRequest.TERMINATION_NO_ERROR:
|
||||||
|
tmtc_backend.close_com_if()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif state.request == BackendRequest.DELAY_IDLE:
|
elif state.request == BackendRequest.DELAY_IDLE:
|
||||||
_LOGGER.info("TMTC Client in IDLE mode")
|
_LOGGER.info("TMTC Client in IDLE mode")
|
||||||
@ -270,6 +275,7 @@ def main():
|
|||||||
elif state.request == BackendRequest.CALL_NEXT:
|
elif state.request == BackendRequest.CALL_NEXT:
|
||||||
pass
|
pass
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
tmtc_backend.close_com_if()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
|||||||
tmtccmd == 8.0.0rc1
|
tmtccmd == 8.0.0rc2
|
||||||
# -e git+https://github.com/robamu-org/tmtccmd@97e5e51101a08b21472b3ddecc2063359f7e307a#egg=tmtccmd
|
# -e git+https://github.com/robamu-org/tmtccmd@97e5e51101a08b21472b3ddecc2063359f7e307a#egg=tmtccmd
|
@ -1,3 +1,4 @@
|
|||||||
|
use std::time::Duration;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashSet, VecDeque},
|
collections::{HashSet, VecDeque},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
@ -139,7 +140,9 @@ impl<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>
|
|||||||
|
|
||||||
pub fn periodic_operation(&mut self) {
|
pub fn periodic_operation(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
let result = self.0.handle_all_connections(None);
|
let result = self
|
||||||
|
.0
|
||||||
|
.handle_all_connections(Some(Duration::from_millis(400)));
|
||||||
match result {
|
match result {
|
||||||
Ok(_conn_result) => (),
|
Ok(_conn_result) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -114,6 +114,7 @@ impl<
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
@ -182,7 +183,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transactions() {
|
fn test_transactions() {
|
||||||
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), 0);
|
let sock_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
|
||||||
let test_receiver = TestSender::default();
|
let test_receiver = TestSender::default();
|
||||||
// let tc_queue = test_receiver.tc_vec.clone();
|
// let tc_queue = test_receiver.tc_vec.clone();
|
||||||
let udp_tc_server =
|
let udp_tc_server =
|
||||||
@ -200,8 +201,8 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let client = UdpSocket::bind("127.0.0.1:0").expect("Connecting to UDP server failed");
|
let client = UdpSocket::bind("127.0.0.1:0").expect("Connecting to UDP server failed");
|
||||||
let client_addr = client.local_addr().unwrap();
|
let client_addr = client.local_addr().unwrap();
|
||||||
client.connect(server_addr).unwrap();
|
println!("{}", server_addr);
|
||||||
client.send(&ping_tc).unwrap();
|
client.send_to(&ping_tc, server_addr).unwrap();
|
||||||
udp_dyn_server.periodic_operation();
|
udp_dyn_server.periodic_operation();
|
||||||
{
|
{
|
||||||
let mut queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow_mut();
|
let mut queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow_mut();
|
||||||
|
@ -8,6 +8,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
# [v0.2.0-rc.4] 2024-04-23
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- The `parse_for_ccsds_space_packets` method now expects a non-mutable slice and does not copy
|
||||||
|
broken tail packets anymore. It also does not expect a mutable `next_write_idx` argument anymore.
|
||||||
|
Instead, a `ParseResult` structure is returned which contains the `packets_found` and an
|
||||||
|
optional `incomplete_tail_start` value.
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- `parse_for_ccsds_space_packets` did not detect CCSDS space packets at the buffer end with the
|
||||||
|
smallest possible size of 7 bytes.
|
||||||
|
- TCP server component now re-registers the internal `mio::Poll` object if the client reset
|
||||||
|
the connection unexpectedly. Not doing so prevented the server from functioning properly
|
||||||
|
after a re-connect.
|
||||||
|
|
||||||
|
# [v0.2.0-rc.3] 2024-04-17
|
||||||
|
|
||||||
|
docs-rs hotfix 2
|
||||||
|
|
||||||
|
# [v0.2.0-rc.2] 2024-04-17
|
||||||
|
|
||||||
|
docs-rs hotfix
|
||||||
|
|
||||||
# [v0.2.0-rc.1] 2024-04-17
|
# [v0.2.0-rc.1] 2024-04-17
|
||||||
|
|
||||||
- `spacepackets` v0.11
|
- `spacepackets` v0.11
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "satrs"
|
name = "satrs"
|
||||||
version = "0.2.0-rc.1"
|
version = "0.2.0-rc.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.61"
|
rust-version = "1.71.1"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
description = "A framework to build software for remote systems"
|
description = "A framework to build software for remote systems"
|
||||||
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
|
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
|
||||||
@ -126,4 +126,4 @@ doc-images = []
|
|||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"]
|
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]
|
||||||
|
@ -21,6 +21,13 @@ pub trait SpacePacketValidator {
|
|||||||
fn validate(&self, sp_header: &SpHeader, raw_buf: &[u8]) -> SpValidity;
|
fn validate(&self, sp_header: &SpHeader, raw_buf: &[u8]) -> SpValidity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
|
pub struct ParseResult {
|
||||||
|
pub packets_found: u32,
|
||||||
|
/// If an incomplete space packet was found, its start index is indicated by this value.
|
||||||
|
pub incomplete_tail_start: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
/// This function parses a given buffer for tightly packed CCSDS space packets. It uses the
|
/// This function parses a given buffer for tightly packed CCSDS space packets. It uses the
|
||||||
/// [spacepackets::SpHeader] of the CCSDS packets and a user provided [SpacePacketValidator]
|
/// [spacepackets::SpHeader] of the CCSDS packets and a user provided [SpacePacketValidator]
|
||||||
/// to check whether a received space packet is relevant for processing.
|
/// to check whether a received space packet is relevant for processing.
|
||||||
@ -41,35 +48,29 @@ pub trait SpacePacketValidator {
|
|||||||
/// 3. [SpValidity::Skip]: The parser skips the packet using the packet length determined from the
|
/// 3. [SpValidity::Skip]: The parser skips the packet using the packet length determined from the
|
||||||
/// space packet header.
|
/// space packet header.
|
||||||
pub fn parse_buffer_for_ccsds_space_packets<SendError>(
|
pub fn parse_buffer_for_ccsds_space_packets<SendError>(
|
||||||
buf: &mut [u8],
|
buf: &[u8],
|
||||||
packet_validator: &(impl SpacePacketValidator + ?Sized),
|
packet_validator: &(impl SpacePacketValidator + ?Sized),
|
||||||
sender_id: ComponentId,
|
sender_id: ComponentId,
|
||||||
packet_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
|
packet_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
|
||||||
next_write_idx: &mut usize,
|
) -> Result<ParseResult, SendError> {
|
||||||
) -> Result<u32, SendError> {
|
let mut parse_result = ParseResult::default();
|
||||||
*next_write_idx = 0;
|
|
||||||
let mut packets_found = 0;
|
|
||||||
let mut current_idx = 0;
|
let mut current_idx = 0;
|
||||||
let buf_len = buf.len();
|
let buf_len = buf.len();
|
||||||
loop {
|
loop {
|
||||||
if current_idx + 7 >= buf.len() {
|
if current_idx + 7 > buf.len() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let sp_header = SpHeader::from_be_bytes(&buf[current_idx..]).unwrap().0;
|
let sp_header = SpHeader::from_be_bytes(&buf[current_idx..]).unwrap().0;
|
||||||
// let packet_id = u16::from_be_bytes(buf[current_idx..current_idx + 2].try_into().unwrap());
|
|
||||||
match packet_validator.validate(&sp_header, &buf[current_idx..]) {
|
match packet_validator.validate(&sp_header, &buf[current_idx..]) {
|
||||||
SpValidity::Valid => {
|
SpValidity::Valid => {
|
||||||
let packet_size = sp_header.total_len();
|
let packet_size = sp_header.total_len();
|
||||||
if (current_idx + packet_size) <= buf_len {
|
if (current_idx + packet_size) <= buf_len {
|
||||||
packet_sender
|
packet_sender
|
||||||
.send_packet(sender_id, &buf[current_idx..current_idx + packet_size])?;
|
.send_packet(sender_id, &buf[current_idx..current_idx + packet_size])?;
|
||||||
packets_found += 1;
|
parse_result.packets_found += 1;
|
||||||
} else {
|
} else {
|
||||||
// Move packet to start of buffer if applicable.
|
// Move packet to start of buffer if applicable.
|
||||||
if current_idx > 0 {
|
parse_result.incomplete_tail_start = Some(current_idx);
|
||||||
buf.copy_within(current_idx.., 0);
|
|
||||||
*next_write_idx = buf.len() - current_idx;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
current_idx += packet_size;
|
current_idx += packet_size;
|
||||||
continue;
|
continue;
|
||||||
@ -83,14 +84,14 @@ pub fn parse_buffer_for_ccsds_space_packets<SendError>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(packets_found)
|
Ok(parse_result)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
ecss::{tc::PusTcCreator, WritablePusPacket},
|
ecss::{tc::PusTcCreator, WritablePusPacket},
|
||||||
CcsdsPacket, PacketId, SpHeader,
|
CcsdsPacket, PacketId, PacketSequenceCtrl, PacketType, SequenceFlags, SpHeader,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{encoding::tests::TcCacher, ComponentId};
|
use crate::{encoding::tests::TcCacher, ComponentId};
|
||||||
@ -136,17 +137,15 @@ mod tests {
|
|||||||
.write_to_bytes(&mut buffer)
|
.write_to_bytes(&mut buffer)
|
||||||
.expect("writing packet failed");
|
.expect("writing packet failed");
|
||||||
let tc_cacher = TcCacher::default();
|
let tc_cacher = TcCacher::default();
|
||||||
let mut next_write_idx = 0;
|
|
||||||
let parse_result = parse_buffer_for_ccsds_space_packets(
|
let parse_result = parse_buffer_for_ccsds_space_packets(
|
||||||
&mut buffer,
|
&buffer,
|
||||||
&SimpleVerificator::default(),
|
&SimpleVerificator::default(),
|
||||||
PARSER_ID,
|
PARSER_ID,
|
||||||
&tc_cacher,
|
&tc_cacher,
|
||||||
&mut next_write_idx,
|
|
||||||
);
|
);
|
||||||
assert!(parse_result.is_ok());
|
assert!(parse_result.is_ok());
|
||||||
let parsed_packets = parse_result.unwrap();
|
let parse_result = parse_result.unwrap();
|
||||||
assert_eq!(parsed_packets, 1);
|
assert_eq!(parse_result.packets_found, 1);
|
||||||
let mut queue = tc_cacher.tc_queue.borrow_mut();
|
let mut queue = tc_cacher.tc_queue.borrow_mut();
|
||||||
assert_eq!(queue.len(), 1);
|
assert_eq!(queue.len(), 1);
|
||||||
let packet_with_sender = queue.pop_front().unwrap();
|
let packet_with_sender = queue.pop_front().unwrap();
|
||||||
@ -167,17 +166,15 @@ mod tests {
|
|||||||
.write_to_bytes(&mut buffer[packet_len_ping..])
|
.write_to_bytes(&mut buffer[packet_len_ping..])
|
||||||
.expect("writing packet failed");
|
.expect("writing packet failed");
|
||||||
let tc_cacher = TcCacher::default();
|
let tc_cacher = TcCacher::default();
|
||||||
let mut next_write_idx = 0;
|
|
||||||
let parse_result = parse_buffer_for_ccsds_space_packets(
|
let parse_result = parse_buffer_for_ccsds_space_packets(
|
||||||
&mut buffer,
|
&buffer,
|
||||||
&SimpleVerificator::default(),
|
&SimpleVerificator::default(),
|
||||||
PARSER_ID,
|
PARSER_ID,
|
||||||
&tc_cacher,
|
&tc_cacher,
|
||||||
&mut next_write_idx,
|
|
||||||
);
|
);
|
||||||
assert!(parse_result.is_ok());
|
assert!(parse_result.is_ok());
|
||||||
let parsed_packets = parse_result.unwrap();
|
let parse_result = parse_result.unwrap();
|
||||||
assert_eq!(parsed_packets, 2);
|
assert_eq!(parse_result.packets_found, 2);
|
||||||
let mut queue = tc_cacher.tc_queue.borrow_mut();
|
let mut queue = tc_cacher.tc_queue.borrow_mut();
|
||||||
assert_eq!(queue.len(), 2);
|
assert_eq!(queue.len(), 2);
|
||||||
let packet_with_addr = queue.pop_front().unwrap();
|
let packet_with_addr = queue.pop_front().unwrap();
|
||||||
@ -205,18 +202,12 @@ mod tests {
|
|||||||
.write_to_bytes(&mut buffer[packet_len_ping..])
|
.write_to_bytes(&mut buffer[packet_len_ping..])
|
||||||
.expect("writing packet failed");
|
.expect("writing packet failed");
|
||||||
let tc_cacher = TcCacher::default();
|
let tc_cacher = TcCacher::default();
|
||||||
let mut next_write_idx = 0;
|
|
||||||
let verificator = SimpleVerificator::new_with_second_id();
|
let verificator = SimpleVerificator::new_with_second_id();
|
||||||
let parse_result = parse_buffer_for_ccsds_space_packets(
|
let parse_result =
|
||||||
&mut buffer,
|
parse_buffer_for_ccsds_space_packets(&buffer, &verificator, PARSER_ID, &tc_cacher);
|
||||||
&verificator,
|
|
||||||
PARSER_ID,
|
|
||||||
&tc_cacher,
|
|
||||||
&mut next_write_idx,
|
|
||||||
);
|
|
||||||
assert!(parse_result.is_ok());
|
assert!(parse_result.is_ok());
|
||||||
let parsed_packets = parse_result.unwrap();
|
let parse_result = parse_result.unwrap();
|
||||||
assert_eq!(parsed_packets, 2);
|
assert_eq!(parse_result.packets_found, 2);
|
||||||
let mut queue = tc_cacher.tc_queue.borrow_mut();
|
let mut queue = tc_cacher.tc_queue.borrow_mut();
|
||||||
assert_eq!(queue.len(), 2);
|
assert_eq!(queue.len(), 2);
|
||||||
let packet_with_addr = queue.pop_front().unwrap();
|
let packet_with_addr = queue.pop_front().unwrap();
|
||||||
@ -242,24 +233,24 @@ mod tests {
|
|||||||
.write_to_bytes(&mut buffer[packet_len_ping..])
|
.write_to_bytes(&mut buffer[packet_len_ping..])
|
||||||
.expect("writing packet failed");
|
.expect("writing packet failed");
|
||||||
let tc_cacher = TcCacher::default();
|
let tc_cacher = TcCacher::default();
|
||||||
let mut next_write_idx = 0;
|
|
||||||
let verificator = SimpleVerificator::new_with_second_id();
|
let verificator = SimpleVerificator::new_with_second_id();
|
||||||
let parse_result = parse_buffer_for_ccsds_space_packets(
|
let parse_result = parse_buffer_for_ccsds_space_packets(
|
||||||
&mut buffer[..packet_len_ping + packet_len_action - 4],
|
&buffer[..packet_len_ping + packet_len_action - 4],
|
||||||
&verificator,
|
&verificator,
|
||||||
PARSER_ID,
|
PARSER_ID,
|
||||||
&tc_cacher,
|
&tc_cacher,
|
||||||
&mut next_write_idx,
|
|
||||||
);
|
);
|
||||||
assert!(parse_result.is_ok());
|
assert!(parse_result.is_ok());
|
||||||
let parsed_packets = parse_result.unwrap();
|
let parse_result = parse_result.unwrap();
|
||||||
assert_eq!(parsed_packets, 1);
|
assert_eq!(parse_result.packets_found, 1);
|
||||||
|
assert!(parse_result.incomplete_tail_start.is_some());
|
||||||
|
let incomplete_tail_idx = parse_result.incomplete_tail_start.unwrap();
|
||||||
|
assert_eq!(incomplete_tail_idx, packet_len_ping);
|
||||||
|
|
||||||
let queue = tc_cacher.tc_queue.borrow();
|
let queue = tc_cacher.tc_queue.borrow();
|
||||||
assert_eq!(queue.len(), 1);
|
assert_eq!(queue.len(), 1);
|
||||||
// The broken packet was moved to the start, so the next write index should be after the
|
// The broken packet was moved to the start, so the next write index should be after the
|
||||||
// last segment missing 4 bytes.
|
// last segment missing 4 bytes.
|
||||||
assert_eq!(next_write_idx, packet_len_action - 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -273,19 +264,36 @@ mod tests {
|
|||||||
let tc_cacher = TcCacher::default();
|
let tc_cacher = TcCacher::default();
|
||||||
|
|
||||||
let verificator = SimpleVerificator::new_with_second_id();
|
let verificator = SimpleVerificator::new_with_second_id();
|
||||||
let mut next_write_idx = 0;
|
|
||||||
let parse_result = parse_buffer_for_ccsds_space_packets(
|
let parse_result = parse_buffer_for_ccsds_space_packets(
|
||||||
&mut buffer[..packet_len_ping - 4],
|
&buffer[..packet_len_ping - 4],
|
||||||
&verificator,
|
&verificator,
|
||||||
PARSER_ID,
|
PARSER_ID,
|
||||||
&tc_cacher,
|
&tc_cacher,
|
||||||
&mut next_write_idx,
|
|
||||||
);
|
);
|
||||||
assert_eq!(next_write_idx, 0);
|
|
||||||
assert!(parse_result.is_ok());
|
assert!(parse_result.is_ok());
|
||||||
let parsed_packets = parse_result.unwrap();
|
let parse_result = parse_result.unwrap();
|
||||||
assert_eq!(parsed_packets, 0);
|
assert_eq!(parse_result.packets_found, 0);
|
||||||
let queue = tc_cacher.tc_queue.borrow();
|
let queue = tc_cacher.tc_queue.borrow();
|
||||||
assert_eq!(queue.len(), 0);
|
assert_eq!(queue.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_smallest_packet() {
|
||||||
|
let ccsds_header_only = SpHeader::new(
|
||||||
|
PacketId::new(PacketType::Tc, true, TEST_APID_0),
|
||||||
|
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 0),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
let mut buf: [u8; 7] = [0; 7];
|
||||||
|
ccsds_header_only
|
||||||
|
.write_to_be_bytes(&mut buf)
|
||||||
|
.expect("writing failed");
|
||||||
|
let verificator = SimpleVerificator::default();
|
||||||
|
let tc_cacher = TcCacher::default();
|
||||||
|
let parse_result =
|
||||||
|
parse_buffer_for_ccsds_space_packets(&buf, &verificator, PARSER_ID, &tc_cacher);
|
||||||
|
assert!(parse_result.is_ok());
|
||||||
|
let parse_result = parse_result.unwrap();
|
||||||
|
assert_eq!(parse_result.packets_found, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,6 @@ use mio::{Events, Interest, Poll, Token};
|
|||||||
use socket2::{Domain, Socket, Type};
|
use socket2::{Domain, Socket, Type};
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
// use std::net::TcpListener;
|
|
||||||
// use std::net::{SocketAddr, TcpStream};
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use crate::tmtc::{PacketSenderRaw, PacketSource};
|
use crate::tmtc::{PacketSenderRaw, PacketSource};
|
||||||
@ -244,13 +242,16 @@ impl<
|
|||||||
// Create a poll instance.
|
// Create a poll instance.
|
||||||
let poll = Poll::new()?;
|
let poll = Poll::new()?;
|
||||||
// Create storage for events.
|
// Create storage for events.
|
||||||
let events = Events::with_capacity(10);
|
let events = Events::with_capacity(32);
|
||||||
let listener: std::net::TcpListener = socket.into();
|
let listener: std::net::TcpListener = socket.into();
|
||||||
let mut mio_listener = TcpListener::from_std(listener);
|
let mut mio_listener = TcpListener::from_std(listener);
|
||||||
|
|
||||||
// Start listening for incoming connections.
|
// Start listening for incoming connections.
|
||||||
poll.registry()
|
poll.registry().register(
|
||||||
.register(&mut mio_listener, Token(0), Interest::READABLE)?;
|
&mut mio_listener,
|
||||||
|
Token(0),
|
||||||
|
Interest::READABLE | Interest::WRITABLE,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
id: cfg.id,
|
id: cfg.id,
|
||||||
@ -280,11 +281,11 @@ impl<
|
|||||||
self.listener.local_addr()
|
self.listener.local_addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This call is used to handle the next connection to a client. Right now, it performs
|
/// This call is used to handle all connection from clients. Right now, it performs
|
||||||
/// the following steps:
|
/// the following steps:
|
||||||
///
|
///
|
||||||
/// 1. It calls the [std::net::TcpListener::accept] method internally using the blocking API
|
/// 1. It calls the [std::net::TcpListener::accept] method until a client connects. An optional
|
||||||
/// until a client connects.
|
/// timeout can be specified for non-blocking acceptance.
|
||||||
/// 2. It reads all the telecommands from the client and parses all received data using the
|
/// 2. It reads all the telecommands from the client and parses all received data using the
|
||||||
/// user specified [TcpTcParser].
|
/// user specified [TcpTcParser].
|
||||||
/// 3. After reading and parsing all telecommands, it sends back all telemetry using the
|
/// 3. After reading and parsing all telecommands, it sends back all telemetry using the
|
||||||
@ -317,11 +318,17 @@ impl<
|
|||||||
loop {
|
loop {
|
||||||
match self.listener.accept() {
|
match self.listener.accept() {
|
||||||
Ok((stream, addr)) => {
|
Ok((stream, addr)) => {
|
||||||
self.handle_accepted_connection(stream, addr)?;
|
if let Err(e) = self.handle_accepted_connection(stream, addr) {
|
||||||
|
self.reregister_poll_interest()?;
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
handled_connections += 1;
|
handled_connections += 1;
|
||||||
}
|
}
|
||||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
|
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
|
||||||
Err(err) => return Err(TcpTmtcError::Io(err)),
|
Err(err) => {
|
||||||
|
self.reregister_poll_interest()?;
|
||||||
|
return Err(TcpTmtcError::Io(err));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -331,6 +338,14 @@ impl<
|
|||||||
Ok(ConnectionResult::AcceptTimeout)
|
Ok(ConnectionResult::AcceptTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reregister_poll_interest(&mut self) -> io::Result<()> {
|
||||||
|
self.poll.registry().reregister(
|
||||||
|
&mut self.listener,
|
||||||
|
Token(0),
|
||||||
|
Interest::READABLE | Interest::WRITABLE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_accepted_connection(
|
fn handle_accepted_connection(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut stream: TcpStream,
|
mut stream: TcpStream,
|
||||||
|
@ -26,14 +26,19 @@ impl<T: SpacePacketValidator, TmError, TcError: 'static> TcpTcParser<TmError, Tc
|
|||||||
next_write_idx: &mut usize,
|
next_write_idx: &mut usize,
|
||||||
) -> Result<(), TcpTmtcError<TmError, TcError>> {
|
) -> Result<(), TcpTmtcError<TmError, TcError>> {
|
||||||
// Reader vec full, need to parse for packets.
|
// Reader vec full, need to parse for packets.
|
||||||
conn_result.num_received_tcs += parse_buffer_for_ccsds_space_packets(
|
let parse_result = parse_buffer_for_ccsds_space_packets(
|
||||||
&mut tc_buffer[..current_write_idx],
|
&tc_buffer[..current_write_idx],
|
||||||
self,
|
self,
|
||||||
sender_id,
|
sender_id,
|
||||||
tc_sender,
|
tc_sender,
|
||||||
next_write_idx,
|
|
||||||
)
|
)
|
||||||
.map_err(|e| TcpTmtcError::TcError(e))?;
|
.map_err(|e| TcpTmtcError::TcError(e))?;
|
||||||
|
if let Some(broken_tail_start) = parse_result.incomplete_tail_start {
|
||||||
|
// Copy broken tail to front of buffer.
|
||||||
|
tc_buffer.copy_within(broken_tail_start..current_write_idx, 0);
|
||||||
|
*next_write_idx = current_write_idx - broken_tail_start;
|
||||||
|
}
|
||||||
|
conn_result.num_received_tcs += parse_result.packets_found;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,9 @@ use crate::{
|
|||||||
use satrs_shared::res_code::ResultU16;
|
use satrs_shared::res_code::ResultU16;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
|
||||||
pub use std_mod::*;
|
pub use std_mod::*;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use alloc_mod::*;
|
pub use alloc_mod::*;
|
||||||
|
|
||||||
@ -65,7 +63,6 @@ impl GenericActionReplyPus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
|
||||||
pub mod alloc_mod {
|
pub mod alloc_mod {
|
||||||
use crate::{
|
use crate::{
|
||||||
action::ActionRequest,
|
action::ActionRequest,
|
||||||
@ -127,7 +124,6 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
|
||||||
pub mod std_mod {
|
pub mod std_mod {
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
@ -13,10 +13,8 @@ use crate::pus::verification::TcStateToken;
|
|||||||
use crate::pus::EcssTmSender;
|
use crate::pus::EcssTmSender;
|
||||||
use crate::pus::EcssTmtcError;
|
use crate::pus::EcssTmtcError;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
|
||||||
pub use alloc_mod::*;
|
pub use alloc_mod::*;
|
||||||
#[cfg(feature = "heapless")]
|
#[cfg(feature = "heapless")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))]
|
|
||||||
pub use heapless_mod::*;
|
pub use heapless_mod::*;
|
||||||
|
|
||||||
/// This trait allows the PUS event manager implementation to stay generic over various types
|
/// This trait allows the PUS event manager implementation to stay generic over various types
|
||||||
@ -44,7 +42,6 @@ pub mod heapless_mod {
|
|||||||
use crate::events::LargestEventRaw;
|
use crate::events::LargestEventRaw;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))]
|
|
||||||
// TODO: After a new version of heapless is released which uses hash32 version 0.3, try using
|
// TODO: After a new version of heapless is released which uses hash32 version 0.3, try using
|
||||||
// regular Event type again.
|
// regular Event type again.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -368,7 +368,6 @@ pub mod alloc_mod {
|
|||||||
/// [DynClone] allows cloning the trait object as long as the boxed object implements
|
/// [DynClone] allows cloning the trait object as long as the boxed object implements
|
||||||
/// [Clone].
|
/// [Clone].
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
|
||||||
pub trait EcssTmSenderExt: EcssTmSender + Downcast + DynClone {
|
pub trait EcssTmSenderExt: EcssTmSender + Downcast + DynClone {
|
||||||
// Remove this once trait upcasting coercion has been implemented.
|
// Remove this once trait upcasting coercion has been implemented.
|
||||||
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
|
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
|
||||||
@ -409,7 +408,6 @@ pub mod alloc_mod {
|
|||||||
/// [DynClone] allows cloning the trait object as long as the boxed object implements
|
/// [DynClone] allows cloning the trait object as long as the boxed object implements
|
||||||
/// [Clone].
|
/// [Clone].
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
|
||||||
pub trait EcssTcSenderExt: EcssTcSender + Downcast + DynClone {}
|
pub trait EcssTcSenderExt: EcssTcSender + Downcast + DynClone {}
|
||||||
|
|
||||||
/// Blanket implementation for all types which implement [EcssTcSender] and are clonable.
|
/// Blanket implementation for all types which implement [EcssTcSender] and are clonable.
|
||||||
@ -429,7 +427,6 @@ pub mod alloc_mod {
|
|||||||
/// [DynClone] allows cloning the trait object as long as the boxed object implements
|
/// [DynClone] allows cloning the trait object as long as the boxed object implements
|
||||||
/// [Clone].
|
/// [Clone].
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
|
||||||
pub trait EcssTcReceiverExt: EcssTcReceiver + Downcast {}
|
pub trait EcssTcReceiverExt: EcssTcReceiver + Downcast {}
|
||||||
|
|
||||||
/// Blanket implementation for all types which implement [EcssTcReceiver] and are clonable.
|
/// Blanket implementation for all types which implement [EcssTcReceiver] and are clonable.
|
||||||
@ -551,7 +548,6 @@ pub mod alloc_mod {
|
|||||||
>
|
>
|
||||||
{
|
{
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
|
||||||
pub fn new_from_now(
|
pub fn new_from_now(
|
||||||
active_request_map: ActiveRequestMap,
|
active_request_map: ActiveRequestMap,
|
||||||
fail_data_buf_size: usize,
|
fail_data_buf_size: usize,
|
||||||
@ -638,7 +634,6 @@ pub mod alloc_mod {
|
|||||||
|
|
||||||
/// Update the current time used for timeout checks based on the current OS time.
|
/// Update the current time used for timeout checks based on the current OS time.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
|
||||||
pub fn update_time_from_now(&mut self) -> Result<(), std::time::SystemTimeError> {
|
pub fn update_time_from_now(&mut self) -> Result<(), std::time::SystemTimeError> {
|
||||||
self.current_time = UnixTimestamp::from_now()?;
|
self.current_time = UnixTimestamp::from_now()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -653,7 +648,6 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
|
||||||
pub mod std_mod {
|
pub mod std_mod {
|
||||||
use crate::pool::{
|
use crate::pool::{
|
||||||
PoolAddr, PoolError, PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool,
|
PoolAddr, PoolError, PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool,
|
||||||
|
@ -26,11 +26,9 @@ pub enum Subservice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
|
||||||
pub mod alloc_mod {}
|
pub mod alloc_mod {}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
|
||||||
pub mod std_mod {}
|
pub mod std_mod {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -423,7 +423,6 @@ pub mod alloc_mod {
|
|||||||
|
|
||||||
/// Like [Self::new], but sets the `init_current_time` parameter to the current system time.
|
/// Like [Self::new], but sets the `init_current_time` parameter to the current system time.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
|
||||||
pub fn new_with_current_init_time(time_margin: Duration) -> Result<Self, SystemTimeError> {
|
pub fn new_with_current_init_time(time_margin: Duration) -> Result<Self, SystemTimeError> {
|
||||||
Ok(Self::new(UnixTime::now()?, time_margin))
|
Ok(Self::new(UnixTime::now()?, time_margin))
|
||||||
}
|
}
|
||||||
@ -667,7 +666,6 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
|
||||||
pub fn update_time_from_now(&mut self) -> Result<(), SystemTimeError> {
|
pub fn update_time_from_now(&mut self) -> Result<(), SystemTimeError> {
|
||||||
self.current_time = UnixTime::now()?;
|
self.current_time = UnixTime::now()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -98,18 +98,11 @@ pub use crate::seq_count::SeqCountProviderSimple;
|
|||||||
pub use spacepackets::ecss::verification::*;
|
pub use spacepackets::ecss::verification::*;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "alloc")))]
|
|
||||||
pub use alloc_mod::*;
|
pub use alloc_mod::*;
|
||||||
|
|
||||||
use crate::request::Apid;
|
use crate::request::Apid;
|
||||||
use crate::ComponentId;
|
use crate::ComponentId;
|
||||||
|
|
||||||
/*
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "std")))]
|
|
||||||
pub use std_mod::*;
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// This is a request identifier as specified in 5.4.11.2 c. of the PUS standard.
|
/// This is a request identifier as specified in 5.4.11.2 c. of the PUS standard.
|
||||||
///
|
///
|
||||||
/// This field equivalent to the first two bytes of the CCSDS space packet header.
|
/// This field equivalent to the first two bytes of the CCSDS space packet header.
|
||||||
|
Reference in New Issue
Block a user