camera_tests #13
201
Cargo.lock
generated
201
Cargo.lock
generated
@ -304,6 +304,95 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
@ -326,6 +415,20 @@ version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "homedir"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22074da8bba2ef26fc1737ae6c777b5baab5524c2dc403b5c6a76166766ccda5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"nix",
|
||||
"serde",
|
||||
"widestring",
|
||||
"windows-sys 0.48.0",
|
||||
"wmi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
@ -410,6 +513,15 @@ version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
@ -422,6 +534,19 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodrop"
|
||||
version = "0.1.14"
|
||||
@ -482,6 +607,7 @@ dependencies = [
|
||||
"derive-new",
|
||||
"env_logger",
|
||||
"fern",
|
||||
"homedir",
|
||||
"humantime",
|
||||
"lazy_static",
|
||||
"log",
|
||||
@ -516,6 +642,18 @@ version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.1.0"
|
||||
@ -704,6 +842,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.6.14"
|
||||
@ -932,6 +1079,24 @@ version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
@ -941,6 +1106,28 @@ dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@ -1098,6 +1285,20 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wmi"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f0a4062ca522aad4705a2948fd4061b3857537990202a8ddd5af21607f79a"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"futures",
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.32"
|
||||
|
@ -19,6 +19,7 @@ num_enum = "0.7"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
mio = "0.8"
|
||||
homedir = "0.2"
|
||||
socket2 = "0.5"
|
||||
|
||||
[dependencies.satrs]
|
||||
|
31
pytmtc/camera_params.py
Normal file
31
pytmtc/camera_params.py
Normal file
@ -0,0 +1,31 @@
|
||||
import struct
|
||||
from serde import Model, fields
|
||||
|
||||
from common import EXPERIMENT_APID, UniqueId, make_addressable_id
|
||||
|
||||
class CameraParameters(Model):
|
||||
R: fields.Int()
|
||||
G: fields.Int()
|
||||
B: fields.Int()
|
||||
N: fields.Int()
|
||||
P: fields.Bool()
|
||||
E: fields.Int()
|
||||
W: fields.Int()
|
||||
|
||||
def serialize_for_uplink(self) -> bytearray:
|
||||
return self.to_json().encode('utf-8')
|
||||
|
||||
# Example serialization
|
||||
data = bytearray(make_addressable_id(EXPERIMENT_APID, UniqueId.CameraHandler))
|
||||
params = CameraParameters(8, 8, 8, 1, True, 200, 1000)
|
||||
serialized = params.to_json().encode('utf-8')
|
||||
byte_string = bytearray(struct.pack('!{}s'.format(len(serialized)), serialized))
|
||||
print(byte_string)
|
||||
print(params.serialize_for_uplink())
|
||||
data.extend(params.serialize_for_uplink())
|
||||
print(data)
|
||||
|
||||
# Example deserialization
|
||||
data = '{"R": 100, "G": 150, "B": 200, "N": 3, "P": true, "E": 10, "W": 20}'
|
||||
deserialized_params = CameraParameters.from_json(data)
|
||||
print(deserialized_params)
|
@ -3,13 +3,30 @@ from __future__ import annotations
|
||||
import dataclasses
|
||||
import enum
|
||||
import struct
|
||||
|
||||
from serde import Model, fields
|
||||
|
||||
EXPERIMENT_ID = 278
|
||||
EXPERIMENT_APID = 1024 + EXPERIMENT_ID
|
||||
|
||||
|
||||
class UniqueId(enum.IntEnum):
|
||||
|
||||
Controller = 0
|
||||
PusEventManagement = 1
|
||||
PusRouting = 2
|
||||
PusTest = 3
|
||||
PusAction = 4
|
||||
PusMode = 5
|
||||
PusHk = 6
|
||||
UdpServer = 7
|
||||
TcpServer = 8
|
||||
TcpSppClient = 9
|
||||
PusScheduler = 10
|
||||
CameraHandler = 11
|
||||
|
||||
|
||||
class EventSeverity(enum.IntEnum):
|
||||
|
||||
INFO = 0
|
||||
LOW = 1
|
||||
MEDIUM = 2
|
||||
@ -43,6 +60,13 @@ class AcsHkIds(enum.IntEnum):
|
||||
|
||||
|
||||
def make_addressable_id(target_id: int, unique_id: int) -> bytes:
|
||||
byte_string = bytearray(struct.pack("!I", target_id))
|
||||
byte_string.extend(struct.pack("!I", unique_id))
|
||||
byte_string = bytearray(struct.pack("!I", unique_id))
|
||||
# byte_string = bytearray(struct.pack("!I", target_id))
|
||||
# byte_string.extend(struct.pack("!I", unique_id))
|
||||
return byte_string
|
||||
|
||||
|
||||
def make_addressable_id_with_action_id(unique_id: int, action_id: int) -> bytes:
|
||||
byte_string = bytearray(struct.pack("!I", unique_id))
|
||||
byte_string.extend(struct.pack("!I", action_id))
|
||||
return byte_string
|
||||
|
@ -9,6 +9,10 @@ from tmtccmd.pus.tc.s200_fsfw_mode import Mode
|
||||
from tmtccmd.tmtc import DefaultPusQueueHelper
|
||||
from tmtccmd.pus.s11_tc_sched import create_time_tagged_cmd
|
||||
from tmtccmd.pus.s200_fsfw_mode import Subservice as ModeSubservice
|
||||
from serde import Model, fields
|
||||
|
||||
from camera_params import CameraParameters
|
||||
from common import EXPERIMENT_APID, UniqueId, make_addressable_id, make_addressable_id_with_action_id
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -65,6 +69,38 @@ def create_cmd_definition_tree() -> CmdTreeNode:
|
||||
)
|
||||
root_node.add_child(scheduler_node)
|
||||
|
||||
action_node = CmdTreeNode("action", "Action Node")
|
||||
cam_node = CmdTreeNode(
|
||||
"take_image", "Take Image with IMS Imager"
|
||||
)
|
||||
cam_node.add_child(
|
||||
CmdTreeNode(
|
||||
"default_single", "Default Single Image Camera Parameters"
|
||||
)
|
||||
)
|
||||
cam_node.add_child(
|
||||
CmdTreeNode(
|
||||
"balanced_single", "Balanced Single Image Camera Parameters"
|
||||
)
|
||||
)
|
||||
cam_node.add_child(
|
||||
CmdTreeNode(
|
||||
"default_single_flatsat", "Default Single Image Camera Parameters for use on FlatSat"
|
||||
)
|
||||
)
|
||||
cam_node.add_child(
|
||||
CmdTreeNode(
|
||||
"balanced_single_flatsat", "Balanced Single Image Camera Parameters for use on FlatSat"
|
||||
)
|
||||
)
|
||||
cam_node.add_child(
|
||||
CmdTreeNode(
|
||||
"custom_params", "Custom Camera Parameters as specified from file"
|
||||
)
|
||||
)
|
||||
action_node.add_child(cam_node)
|
||||
root_node.add_child(action_node)
|
||||
|
||||
return root_node
|
||||
|
||||
|
||||
@ -97,6 +133,26 @@ def pack_pus_telecommands(q: DefaultPusQueueHelper, cmd_path: str):
|
||||
)
|
||||
if cmd_path_list[0] == "acs":
|
||||
assert len(cmd_path_list) >= 2
|
||||
if cmd_path_list[0] == "action":
|
||||
assert len(cmd_path_list)>= 2
|
||||
if cmd_path_list[1] == "take_image":
|
||||
assert len(cmd_path_list)>= 3
|
||||
q.add_log_cmd("Sending PUS take image action request with " + cmd_path_list[2] + " params.")
|
||||
if cmd_path_list[2] == "default_single":
|
||||
data = make_addressable_id_with_action_id(UniqueId.CameraHandler, 1)
|
||||
if cmd_path_list[2] == "balanced_single":
|
||||
data = make_addressable_id_with_action_id(UniqueId.CameraHandler, 2)
|
||||
if cmd_path_list[2] == "default_single_flatsat":
|
||||
data = make_addressable_id_with_action_id(UniqueId.CameraHandler, 3)
|
||||
if cmd_path_list[2] == "balanced_single_flatsat":
|
||||
data = make_addressable_id_with_action_id(UniqueId.CameraHandler, 4)
|
||||
if cmd_path_list[2] == "custom":
|
||||
data = make_addressable_id_with_action_id(UniqueId.CameraHandler, 5)
|
||||
params = CameraParameters(8, 8, 8, 1, True, 200, 1000)
|
||||
bytes = params.serialize_for_uplink()
|
||||
data.extend(bytes)
|
||||
print(data.hex(sep=","))
|
||||
return q.add_pus_tc(PusTelecommand(service=8, subservice=128, apid=EXPERIMENT_APID, app_data=data))
|
||||
|
||||
|
||||
def handle_set_mode_cmd(
|
||||
|
@ -147,6 +147,12 @@ class PusHandler(GenericApidHandlerBase):
|
||||
_LOGGER.info(f"Received event packet. Event: {event_u32}")
|
||||
if event_u32.group_id == 0 and event_u32.unique_id == 0:
|
||||
_LOGGER.info("Received test event")
|
||||
elif service == 8:
|
||||
if pus_tm.subservice == 130:
|
||||
_LOGGER.info(f"Received Action Data Reply TM[8,130]")
|
||||
reply = pus_tm.source_data
|
||||
reply = reply[6:]
|
||||
_LOGGER.info(f"Data Reply Content: " + reply.decode('utf-8'))
|
||||
elif service == 17:
|
||||
tm_packet = Service17Tm.unpack(
|
||||
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||
|
@ -35,4 +35,12 @@ def tc_definitions() -> TmtcDefinitionWrapper:
|
||||
info="PUS Service 11 TC Scheduling",
|
||||
op_code_entry=srv_11,
|
||||
)
|
||||
srv_8 = OpCodeEntry()
|
||||
srv_8.add("pic", "Action Request Image")
|
||||
defs.add_service(
|
||||
name=CoreServiceList.SERVICE_8,
|
||||
info="PUS Service 8 Action",
|
||||
op_code_entry=srv_8,
|
||||
|
||||
)
|
||||
return defs
|
||||
|
@ -3,13 +3,12 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use satrs::spacepackets::PacketId;
|
||||
use satrs_mib::res_code::ResultU16Info;
|
||||
use satrs_mib::resultcode;
|
||||
use std::env;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub const STOP_FILE_NAME: &str = "stop-experiment";
|
||||
pub const CONFIG_FILE_NAME: &str = "exp278.toml";
|
||||
pub const HOME_FOLER_EXPERIMENT: &str = "/home/exp278";
|
||||
pub const HOME_FOLDER_EXPERIMENT: &str = "/home/exp278";
|
||||
pub const LOG_FOLDER: &str = "logs";
|
||||
|
||||
pub const OBSW_SERVER_ADDR: Ipv4Addr = Ipv4Addr::UNSPECIFIED;
|
||||
@ -42,12 +41,17 @@ pub enum GroupId {
|
||||
|
||||
lazy_static! {
|
||||
pub static ref HOME_PATH: PathBuf = {
|
||||
let home_path_default = env::var("HOME").expect("HOME env variable not set");
|
||||
let mut home_path = PathBuf::new();
|
||||
home_path.push(if Path::new(HOME_FOLER_EXPERIMENT).exists() {
|
||||
HOME_FOLER_EXPERIMENT
|
||||
let home_path_default = homedir::get_my_home()
|
||||
.expect("Getting home dir from OS failed.")
|
||||
.expect("No home dir found.");
|
||||
|
||||
home_path.push(if Path::new(HOME_FOLDER_EXPERIMENT).exists() {
|
||||
HOME_FOLDER_EXPERIMENT
|
||||
} else {
|
||||
&home_path_default
|
||||
home_path_default
|
||||
.to_str()
|
||||
.expect("Error converting to string.")
|
||||
});
|
||||
home_path
|
||||
};
|
||||
@ -239,6 +243,7 @@ pub mod components {
|
||||
TcpServer = 8,
|
||||
TcpSppClient = 9,
|
||||
PusScheduler = 10,
|
||||
CameraHandler = 11,
|
||||
}
|
||||
|
||||
pub const CONTROLLER_ID: UniqueApidTargetId =
|
||||
@ -263,6 +268,8 @@ pub mod components {
|
||||
UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::TcpServer as u32);
|
||||
pub const TCP_SPP_CLIENT: UniqueApidTargetId =
|
||||
UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::TcpSppClient as u32);
|
||||
pub const CAMERA_HANDLER: UniqueApidTargetId =
|
||||
UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::CameraHandler as u32);
|
||||
}
|
||||
|
||||
pub mod tasks {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::pus::action::send_data_reply;
|
||||
/// Device handler implementation for the IMS-100 Imager used on the OPS-SAT mission.
|
||||
///
|
||||
/// from the [OPSSAT Experimenter Wiki](https://opssat1.esoc.esa.int/projects/experimenter-information/wiki/Camera_Introduction):
|
||||
@ -26,17 +27,23 @@
|
||||
/// see also https://opssat1.esoc.esa.int/dmsf/files/6/view
|
||||
use crate::requests::CompositeRequest;
|
||||
use derive_new::new;
|
||||
use log::debug;
|
||||
use log::{debug, info};
|
||||
use ops_sat_rs::TimeStampHelper;
|
||||
use satrs::action::{ActionRequest, ActionRequestVariant};
|
||||
use satrs::hk::HkRequest;
|
||||
use satrs::pus::action::{ActionReplyPus, ActionReplyVariant};
|
||||
use satrs::pus::EcssTmtcError;
|
||||
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
||||
use satrs::tmtc::PacketAsVec;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::Error;
|
||||
use std::process::Command;
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use std::process::{Command, Output};
|
||||
use std::sync::mpsc;
|
||||
|
||||
// const IMS_TESTAPP: &str = "scripts/ims100_testapp";
|
||||
const IMS_TESTAPP: &str = "ims100_testapp";
|
||||
|
||||
const DEFAULT_SINGLE_CAM_PARAMS: CameraPictureParameters = CameraPictureParameters {
|
||||
R: 8,
|
||||
G: 8,
|
||||
@ -77,8 +84,12 @@ const BALANCED_SINGLE_FLATSAT_CAM_PARAMS: CameraPictureParameters = CameraPictur
|
||||
W: 1000,
|
||||
};
|
||||
|
||||
// TODO copy as action
|
||||
// TODO ls -l via cfdp
|
||||
// TODO howto downlink
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CameraActionIds {
|
||||
pub enum CameraActionId {
|
||||
DefaultSingle = 1,
|
||||
BalancedSingle = 2,
|
||||
DefaultSingleFlatSat = 3,
|
||||
@ -86,25 +97,25 @@ pub enum CameraActionIds {
|
||||
CustomParameters = 5,
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for CameraActionIds {
|
||||
impl TryFrom<u32> for CameraActionId {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
value if value == CameraActionIds::DefaultSingle as u32 => {
|
||||
Ok(CameraActionIds::DefaultSingle)
|
||||
value if value == CameraActionId::DefaultSingle as u32 => {
|
||||
Ok(CameraActionId::DefaultSingle)
|
||||
}
|
||||
value if value == CameraActionIds::BalancedSingle as u32 => {
|
||||
Ok(CameraActionIds::BalancedSingle)
|
||||
value if value == CameraActionId::BalancedSingle as u32 => {
|
||||
Ok(CameraActionId::BalancedSingle)
|
||||
}
|
||||
value if value == CameraActionIds::DefaultSingleFlatSat as u32 => {
|
||||
Ok(CameraActionIds::DefaultSingleFlatSat)
|
||||
value if value == CameraActionId::DefaultSingleFlatSat as u32 => {
|
||||
Ok(CameraActionId::DefaultSingleFlatSat)
|
||||
}
|
||||
value if value == CameraActionIds::BalancedSingleFlatSat as u32 => {
|
||||
Ok(CameraActionIds::BalancedSingleFlatSat)
|
||||
value if value == CameraActionId::BalancedSingleFlatSat as u32 => {
|
||||
Ok(CameraActionId::BalancedSingleFlatSat)
|
||||
}
|
||||
value if value == CameraActionIds::CustomParameters as u32 => {
|
||||
Ok(CameraActionIds::CustomParameters)
|
||||
value if value == CameraActionId::CustomParameters as u32 => {
|
||||
Ok(CameraActionId::CustomParameters)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
@ -113,7 +124,7 @@ impl TryFrom<u32> for CameraActionIds {
|
||||
|
||||
// TODO what happens if limits are exceded
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Serialize, Deserialize, new)]
|
||||
#[derive(Debug, Serialize, Deserialize, new)]
|
||||
pub struct CameraPictureParameters {
|
||||
pub R: u8,
|
||||
pub G: u8,
|
||||
@ -124,20 +135,89 @@ pub struct CameraPictureParameters {
|
||||
pub W: u32, // wait time between pictures in ms, max: 40000
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(new)]
|
||||
pub enum CameraError {
|
||||
TakeImageError,
|
||||
NoDataSent,
|
||||
VariantNotImplemented,
|
||||
DeserializeError,
|
||||
ListFileError,
|
||||
IoError(std::io::Error),
|
||||
EcssTmtcError(EcssTmtcError),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for CameraError {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
Self::IoError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EcssTmtcError> for CameraError {
|
||||
fn from(value: EcssTmtcError) -> Self {
|
||||
Self::EcssTmtcError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CameraError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
CameraError::TakeImageError => {
|
||||
write!(f, "Error taking image.")
|
||||
}
|
||||
CameraError::NoDataSent => {
|
||||
write!(f, "No data sent.")
|
||||
}
|
||||
CameraError::VariantNotImplemented => {
|
||||
write!(f, "Request variant not implemented.")
|
||||
}
|
||||
CameraError::DeserializeError => {
|
||||
write!(f, "Unable to deserialize parameters.")
|
||||
}
|
||||
CameraError::ListFileError => {
|
||||
write!(f, "Error listing image files.")
|
||||
}
|
||||
CameraError::IoError(io_error) => {
|
||||
write!(f, "{}", io_error)
|
||||
}
|
||||
CameraError::EcssTmtcError(ecss_tmtc_error) => {
|
||||
write!(f, "{}", ecss_tmtc_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct IMS100BatchHandler {
|
||||
id: UniqueApidTargetId,
|
||||
// mode_interface: MpscModeLeafInterface,
|
||||
composite_request_receiver: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
||||
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
||||
// hk_reply_sender: mpsc::Sender<GenericMessage<HkReply>>,
|
||||
tm_sender: mpsc::Sender<PacketAsVec>,
|
||||
tm_tx: mpsc::Sender<PacketAsVec>,
|
||||
action_reply_tx: mpsc::Sender<GenericMessage<ActionReplyPus>>,
|
||||
stamp_helper: TimeStampHelper,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(dead_code)]
|
||||
impl IMS100BatchHandler {
|
||||
pub fn new(
|
||||
id: UniqueApidTargetId,
|
||||
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
||||
tm_tx: mpsc::Sender<PacketAsVec>,
|
||||
action_reply_tx: mpsc::Sender<GenericMessage<ActionReplyPus>>,
|
||||
stamp_helper: TimeStampHelper,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
composite_request_rx,
|
||||
tm_tx,
|
||||
action_reply_tx,
|
||||
stamp_helper,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn periodic_operation(&mut self) {
|
||||
self.stamp_helper.update_from_now();
|
||||
// Handle requests.
|
||||
@ -147,7 +227,7 @@ impl IMS100BatchHandler {
|
||||
|
||||
pub fn handle_composite_requests(&mut self) {
|
||||
loop {
|
||||
match self.composite_request_receiver.try_recv() {
|
||||
match self.composite_request_rx.try_recv() {
|
||||
Ok(ref msg) => match &msg.message {
|
||||
CompositeRequest::Hk(hk_request) => {
|
||||
self.handle_hk_request(&msg.requestor_info, hk_request);
|
||||
@ -181,22 +261,21 @@ impl IMS100BatchHandler {
|
||||
|
||||
pub fn handle_action_request(
|
||||
&mut self,
|
||||
_requestor_info: &MessageMetadata,
|
||||
requestor_info: &MessageMetadata,
|
||||
action_request: &ActionRequest,
|
||||
) -> std::io::Result<()> {
|
||||
let param = match CameraActionIds::try_from(action_request.action_id).unwrap() {
|
||||
CameraActionIds::DefaultSingle => DEFAULT_SINGLE_CAM_PARAMS,
|
||||
CameraActionIds::BalancedSingle => BALANCED_SINGLE_CAM_PARAMS,
|
||||
CameraActionIds::DefaultSingleFlatSat => DEFAULT_SINGLE_FLATSAT_CAM_PARAMS,
|
||||
CameraActionIds::BalancedSingleFlatSat => BALANCED_SINGLE_FLATSAT_CAM_PARAMS,
|
||||
CameraActionIds::CustomParameters => match &action_request.variant {
|
||||
ActionRequestVariant::NoData => return Err(Error::other("No Data sent!")),
|
||||
) -> Result<(), CameraError> {
|
||||
let param =
|
||||
match CameraActionId::try_from(action_request.action_id).expect("Invalid action id") {
|
||||
CameraActionId::DefaultSingle => DEFAULT_SINGLE_CAM_PARAMS,
|
||||
CameraActionId::BalancedSingle => BALANCED_SINGLE_CAM_PARAMS,
|
||||
CameraActionId::DefaultSingleFlatSat => DEFAULT_SINGLE_FLATSAT_CAM_PARAMS,
|
||||
CameraActionId::BalancedSingleFlatSat => BALANCED_SINGLE_FLATSAT_CAM_PARAMS,
|
||||
CameraActionId::CustomParameters => match &action_request.variant {
|
||||
ActionRequestVariant::NoData => return Err(CameraError::NoDataSent),
|
||||
ActionRequestVariant::StoreData(_) => {
|
||||
// let param = serde_json::from_slice()
|
||||
// TODO implement non dynamic version
|
||||
return Err(Error::other(
|
||||
"Static parameter transfer not implemented yet!",
|
||||
));
|
||||
return Err(CameraError::VariantNotImplemented);
|
||||
}
|
||||
ActionRequestVariant::VecData(data) => {
|
||||
let param: serde_json::Result<CameraPictureParameters> =
|
||||
@ -204,18 +283,28 @@ impl IMS100BatchHandler {
|
||||
match param {
|
||||
Ok(param) => param,
|
||||
Err(_) => {
|
||||
return Err(Error::other("Unable to deserialize parameters"));
|
||||
return Err(CameraError::DeserializeError);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::other("Invalid Action Request Variant!")),
|
||||
_ => return Err(CameraError::VariantNotImplemented),
|
||||
},
|
||||
};
|
||||
self.take_picture(param)
|
||||
let output = self.take_picture(param)?;
|
||||
debug!("Sending action reply!");
|
||||
send_data_reply(self.id, output.stdout, &self.stamp_helper, &self.tm_tx)?;
|
||||
self.action_reply_tx
|
||||
.send(GenericMessage::new(
|
||||
*requestor_info,
|
||||
ActionReplyPus::new(action_request.action_id, ActionReplyVariant::Completed),
|
||||
))
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn take_picture(&mut self, param: CameraPictureParameters) -> std::io::Result<()> {
|
||||
let mut cmd = Command::new("ims100_testapp");
|
||||
pub fn take_picture(&mut self, param: CameraPictureParameters) -> Result<Output, CameraError> {
|
||||
info!("Taking image!");
|
||||
let mut cmd = Command::new(IMS_TESTAPP);
|
||||
cmd.arg("-R")
|
||||
.arg(¶m.R.to_string())
|
||||
.arg("-G")
|
||||
@ -237,12 +326,23 @@ impl IMS100BatchHandler {
|
||||
.arg(¶m.E.to_string())
|
||||
.arg("-w")
|
||||
.arg(¶m.W.to_string());
|
||||
|
||||
let output = cmd.output()?;
|
||||
|
||||
debug!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
debug!("Imager Output: {}", String::from_utf8_lossy(&output.stdout));
|
||||
|
||||
Ok(())
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn list_current_images(&self) -> Result<Vec<String>, CameraError> {
|
||||
let output = Command::new("ls").arg("-l").arg("*.png").output()?;
|
||||
|
||||
if output.status.success() {
|
||||
let output_str = String::from_utf8(output.stdout).unwrap();
|
||||
let files: Vec<String> = output_str.lines().map(|s| s.to_string()).collect();
|
||||
Ok(files)
|
||||
} else {
|
||||
Err(CameraError::ListFileError)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -255,7 +355,7 @@ impl IMS100BatchHandler {
|
||||
P: &str,
|
||||
E: &str,
|
||||
W: &str,
|
||||
) -> std::io::Result<()> {
|
||||
) -> Result<(), CameraError> {
|
||||
let mut cmd = Command::new("ims100_testapp");
|
||||
cmd.arg("-R")
|
||||
.arg(R)
|
||||
@ -287,8 +387,85 @@ impl IMS100BatchHandler {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::handlers::camera::{
|
||||
CameraActionId, CameraPictureParameters, IMS100BatchHandler,
|
||||
DEFAULT_SINGLE_FLATSAT_CAM_PARAMS,
|
||||
};
|
||||
use crate::requests::CompositeRequest;
|
||||
use ops_sat_rs::config::components::CAMERA_HANDLER;
|
||||
use ops_sat_rs::TimeStampHelper;
|
||||
use satrs::action::{ActionRequest, ActionRequestVariant};
|
||||
use satrs::pus::action::ActionReplyPus;
|
||||
use satrs::request::{GenericMessage, MessageMetadata};
|
||||
use satrs::tmtc::PacketAsVec;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
fn create_handler() -> (
|
||||
IMS100BatchHandler,
|
||||
Sender<GenericMessage<CompositeRequest>>,
|
||||
Receiver<PacketAsVec>,
|
||||
Receiver<GenericMessage<ActionReplyPus>>,
|
||||
) {
|
||||
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
||||
let (tm_tx, tm_rx) = mpsc::channel();
|
||||
let (action_reply_tx, action_reply_rx) = mpsc::channel();
|
||||
let time_helper = TimeStampHelper::default();
|
||||
let cam_handler: IMS100BatchHandler = IMS100BatchHandler::new(
|
||||
CAMERA_HANDLER,
|
||||
composite_request_rx,
|
||||
tm_tx,
|
||||
action_reply_tx,
|
||||
time_helper,
|
||||
);
|
||||
(cam_handler, composite_request_tx, tm_rx, action_reply_rx)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_crc() {
|
||||
// TODO
|
||||
fn command_line_execution() {
|
||||
let (mut cam_handler, req_tx, tm_rx, action_reply_rx) = create_handler();
|
||||
cam_handler
|
||||
.take_picture(DEFAULT_SINGLE_FLATSAT_CAM_PARAMS)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_and_deserialize_command() {
|
||||
let data = serde_json::to_string(&DEFAULT_SINGLE_FLATSAT_CAM_PARAMS).unwrap();
|
||||
println!("{}", data);
|
||||
let param: CameraPictureParameters = serde_json::from_str(&data).unwrap();
|
||||
println!("{:?}", param);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_action_req() {
|
||||
let (mut cam_handler, req_tx, tm_rx, action_reply_rx) = create_handler();
|
||||
|
||||
let data = serde_json::to_string(&DEFAULT_SINGLE_FLATSAT_CAM_PARAMS).unwrap();
|
||||
let req = ActionRequest::new(
|
||||
CameraActionId::CustomParameters as u32,
|
||||
ActionRequestVariant::VecData(data.as_bytes().to_vec()),
|
||||
);
|
||||
|
||||
cam_handler
|
||||
.handle_action_request(&MessageMetadata::new(1, 1), &req)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_action_req_channel() {
|
||||
let (mut cam_handler, req_tx, tm_rx, action_reply_rx) = create_handler();
|
||||
|
||||
let data = serde_json::to_string(&DEFAULT_SINGLE_FLATSAT_CAM_PARAMS).unwrap();
|
||||
let req = ActionRequest::new(
|
||||
CameraActionId::CustomParameters as u32,
|
||||
ActionRequestVariant::VecData(data.as_bytes().to_vec()),
|
||||
);
|
||||
let req = CompositeRequest::Action(req);
|
||||
req_tx
|
||||
.send(GenericMessage::new(MessageMetadata::new(1, 1), req))
|
||||
.unwrap();
|
||||
|
||||
cam_handler.periodic_operation();
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use satrs::spacepackets::time::TimeWriter;
|
||||
|
||||
pub mod config;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TimeStampHelper {
|
||||
stamper: CdsTime,
|
||||
time_stamp: [u8; 7],
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use ops_sat_rs::config::LOG_FOLDER;
|
||||
|
||||
@ -6,6 +6,9 @@ pub fn setup_logger() -> Result<(), fern::InitError> {
|
||||
if !Path::new(LOG_FOLDER).exists() && std::fs::create_dir_all(LOG_FOLDER).is_err() {
|
||||
eprintln!("Failed to create log folder '{}'", LOG_FOLDER);
|
||||
}
|
||||
let mut path_buf = PathBuf::from(LOG_FOLDER);
|
||||
path_buf.push("output.log");
|
||||
println!("{:?}", path_buf);
|
||||
fern::Dispatch::new()
|
||||
.format(move |out, message, record| {
|
||||
out.finish(format_args!(
|
||||
@ -18,11 +21,7 @@ pub fn setup_logger() -> Result<(), fern::InitError> {
|
||||
})
|
||||
.level(log::LevelFilter::Debug)
|
||||
.chain(std::io::stdout())
|
||||
.chain(fern::log_file(format!(
|
||||
"{}/output_{}.log",
|
||||
LOG_FOLDER,
|
||||
humantime::format_rfc3339_seconds(std::time::SystemTime::now())
|
||||
))?)
|
||||
.chain(fern::log_file(path_buf.as_os_str())?)
|
||||
.apply()?;
|
||||
Ok(())
|
||||
}
|
||||
|
45
src/main.rs
45
src/main.rs
@ -6,6 +6,7 @@ use std::{
|
||||
};
|
||||
|
||||
use log::info;
|
||||
use ops_sat_rs::config::components::CAMERA_HANDLER;
|
||||
use ops_sat_rs::config::{
|
||||
cfg_file::create_app_config,
|
||||
components::{CONTROLLER_ID, TCP_SERVER, TCP_SPP_CLIENT, UDP_SERVER},
|
||||
@ -14,8 +15,10 @@ use ops_sat_rs::config::{
|
||||
VALID_PACKET_ID_LIST,
|
||||
};
|
||||
use ops_sat_rs::config::{tasks::FREQ_MS_UDP_TMTC, OBSW_SERVER_ADDR, SERVER_PORT};
|
||||
use ops_sat_rs::TimeStampHelper;
|
||||
use satrs::hal::std::{tcp_server::ServerConfig, udp_server::UdpTcServer};
|
||||
|
||||
use crate::handlers::camera::IMS100BatchHandler;
|
||||
use crate::pus::{
|
||||
hk::create_hk_service, mode::create_mode_service, scheduler::create_scheduler_service,
|
||||
PusTcDistributor, PusTcMpscRouter,
|
||||
@ -67,12 +70,16 @@ fn main() {
|
||||
let (_pus_mode_reply_tx, pus_mode_reply_rx) = mpsc::channel();
|
||||
let (controller_composite_tx, controller_composite_rx) = mpsc::channel();
|
||||
// let (controller_action_reply_tx, controller_action_reply_rx) = mpsc::channel();
|
||||
let (camera_composite_tx, camera_composite_rx) = mpsc::channel();
|
||||
|
||||
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
||||
let mut request_map = GenericRequestRouter::default();
|
||||
request_map
|
||||
.composite_router_map
|
||||
.insert(CONTROLLER_ID.id(), controller_composite_tx);
|
||||
request_map
|
||||
.composite_router_map
|
||||
.insert(CAMERA_HANDLER.id(), camera_composite_tx);
|
||||
|
||||
let pus_router = PusTcMpscRouter {
|
||||
test_tc_sender: pus_test_tx,
|
||||
@ -166,7 +173,7 @@ fn main() {
|
||||
|
||||
let mut controller = ExperimentController::new(
|
||||
controller_composite_rx,
|
||||
pus_action_reply_tx,
|
||||
pus_action_reply_tx.clone(),
|
||||
stop_signal.clone(),
|
||||
);
|
||||
|
||||
@ -180,6 +187,18 @@ fn main() {
|
||||
)
|
||||
.expect("creating TCP SPP client failed");
|
||||
|
||||
let timestamp_helper = TimeStampHelper::default();
|
||||
let mut camera_handler: IMS100BatchHandler = IMS100BatchHandler::new(
|
||||
CAMERA_HANDLER,
|
||||
camera_composite_rx,
|
||||
tm_funnel_tx.clone(),
|
||||
pus_action_reply_tx.clone(),
|
||||
timestamp_helper,
|
||||
);
|
||||
|
||||
// Main Task Thread Definitions
|
||||
|
||||
// Main Experiment Control Task
|
||||
info!("Starting CTRL task");
|
||||
let ctrl_stop_signal = stop_signal.clone();
|
||||
let jh_ctrl_thread = thread::Builder::new()
|
||||
@ -193,6 +212,7 @@ fn main() {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// TMTC and UDP Task
|
||||
info!("Starting TMTC and UDP task");
|
||||
let tmtc_stop_signal = stop_signal.clone();
|
||||
let jh_udp_tmtc = thread::Builder::new()
|
||||
@ -210,6 +230,7 @@ fn main() {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// TCP Server Task
|
||||
let tcp_server_stop_signal = stop_signal.clone();
|
||||
info!("Starting TCP server task");
|
||||
let jh_tcp_server = thread::Builder::new()
|
||||
@ -225,6 +246,7 @@ fn main() {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// TCP SPP Client Task
|
||||
// We could also move this to the existing TCP server thread, but we would have to adapt
|
||||
// the server code for this so we do not block anymore and we pause manually if both the client
|
||||
// and server are IDLE and have nothing to do..
|
||||
@ -246,6 +268,7 @@ fn main() {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// TM Funnel Task
|
||||
info!("Starting TM funnel task");
|
||||
let funnel_stop_signal = stop_signal.clone();
|
||||
let jh_tm_funnel = thread::Builder::new()
|
||||
@ -258,7 +281,8 @@ fn main() {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
info!("Starting PUS handlers thread");
|
||||
// PUS Handler Task
|
||||
info!("Starting PUS handlers task");
|
||||
let pus_stop_signal = stop_signal.clone();
|
||||
let jh_pus_handler = thread::Builder::new()
|
||||
.name("ops-sat pus".to_string())
|
||||
@ -271,6 +295,20 @@ fn main() {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Camera Handler Task
|
||||
info!("Starting camera handler task");
|
||||
let camera_stop_signal = stop_signal.clone();
|
||||
let jh_camera_handler = thread::Builder::new()
|
||||
.name("ops-sat camera".to_string())
|
||||
.spawn(move || loop {
|
||||
camera_handler.periodic_operation();
|
||||
if camera_stop_signal.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Join Threads
|
||||
jh_ctrl_thread
|
||||
.join()
|
||||
.expect("Joining Controller thread failed");
|
||||
@ -289,4 +327,7 @@ fn main() {
|
||||
jh_pus_handler
|
||||
.join()
|
||||
.expect("Joining PUS handlers thread failed");
|
||||
jh_camera_handler
|
||||
.join()
|
||||
.expect("Joining camera handler thread failed");
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use log::{error, warn};
|
||||
use log::{debug, error, warn};
|
||||
use ops_sat_rs::config::components::PUS_ACTION_SERVICE;
|
||||
use ops_sat_rs::config::tmtc_err;
|
||||
use ops_sat_rs::TimeStampHelper;
|
||||
use satrs::action::{ActionRequest, ActionRequestVariant};
|
||||
use satrs::params::WritableToBeBytes;
|
||||
use satrs::pus::action::{
|
||||
@ -13,11 +14,13 @@ use satrs::pus::verification::{
|
||||
use satrs::pus::{
|
||||
ActiveRequestProvider, EcssTcAndToken, EcssTcInVecConverter, EcssTmSender, EcssTmtcError,
|
||||
GenericConversionError, PusPacketHandlerResult, PusReplyHandler, PusServiceHelper,
|
||||
PusTcToRequestConverter,
|
||||
PusTcToRequestConverter, PusTmVariant,
|
||||
};
|
||||
use satrs::request::{GenericMessage, UniqueApidTargetId};
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||
use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket};
|
||||
use satrs::spacepackets::SpHeader;
|
||||
use satrs::tmtc::PacketAsVec;
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
@ -29,6 +32,8 @@ use super::{
|
||||
PusTargetedRequestService, TargetedPusService,
|
||||
};
|
||||
|
||||
pub const DATA_REPLY: u8 = 130;
|
||||
|
||||
pub struct ActionReplyHandler {
|
||||
fail_data_buf: [u8; 128],
|
||||
}
|
||||
@ -270,6 +275,26 @@ impl TargetedPusService for ActionServiceWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_data_reply<TmSender: EcssTmSender>(
|
||||
apid_target: UniqueApidTargetId,
|
||||
reply_data: Vec<u8>,
|
||||
stamp_helper: &TimeStampHelper,
|
||||
tm_sender: &TmSender,
|
||||
) -> Result<(), EcssTmtcError> {
|
||||
let sp_header = SpHeader::new_from_apid(apid_target.apid);
|
||||
let sec_header = PusTmSecondaryHeader::new(8, DATA_REPLY, 0, 0, stamp_helper.stamp());
|
||||
let mut data = Vec::new();
|
||||
data.extend(apid_target.apid.to_be_bytes());
|
||||
data.extend(apid_target.unique_id.to_be_bytes());
|
||||
data.extend(reply_data);
|
||||
debug!(
|
||||
"{}",
|
||||
String::from_utf8(data.clone()[6..].to_vec()).expect("Error decoding data reply.")
|
||||
);
|
||||
let data_reply_tm = PusTmCreator::new(sp_header, sec_header, &data, true);
|
||||
tm_sender.send_tm(apid_target.id(), PusTmVariant::Direct(data_reply_tm))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use satrs::pus::test_util::{
|
||||
@ -432,6 +457,7 @@ mod tests {
|
||||
.send(EcssTcAndToken::new(
|
||||
TcInMemory::Vec(PacketAsVec::new(
|
||||
self.service.service_helper.id(),
|
||||
//tc.to_vec().unwrap().into(),
|
||||
tc.to_vec().unwrap(),
|
||||
)),
|
||||
accepted_token,
|
||||
|
Loading…
Reference in New Issue
Block a user