Merge remote-tracking branch 'origin/main' into can_pus_handler
This commit is contained in:
commit
02b4a51457
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -58,9 +58,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.15.4"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bus"
|
||||
@ -81,9 +81,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.91"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd97381a8cc6493395a5afc4c691c1084b3768db713b73aa215217aa245d153"
|
||||
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@ -119,9 +119,9 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.2.0"
|
||||
version = "3.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2b432c56615136f8dba245fed7ec3d5518c500a31108661067e61e72fe7e6bc"
|
||||
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
@ -371,7 +371,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "ops-sat-rs"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"derive-new",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ops-sat-rs"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@ -19,6 +19,7 @@ num_enum = "0.7"
|
||||
version = "0.2.0-rc.0"
|
||||
git = "https://egit.irs.uni-stuttgart.de/rust/sat-rs.git"
|
||||
branch = "main"
|
||||
features = ["test_util"]
|
||||
|
||||
[dependencies.satrs-mib]
|
||||
version = "0.1.1"
|
||||
|
13
README.md
13
README.md
@ -11,16 +11,29 @@ This is the primary repository for the ESA OPS-SAT experiment.
|
||||
|
||||
## Build
|
||||
|
||||
You might need to set the [`CROSS_CONTAINER_ENGINE`](https://github.com/cross-rs/cross/wiki/FAQ#explicitly-choose-the-container-engine)
|
||||
and [`CROSS_ROOTLESS_CONTAINER_ENGINE`](https://github.com/cross-rs/cross/blob/main/docs/environment_variables.md#configuring-cross-with-environment-variables)
|
||||
variables manually before calling cross.
|
||||
|
||||
### Debug Build
|
||||
|
||||
```sh
|
||||
cross build
|
||||
```
|
||||
|
||||
### Release Build
|
||||
|
||||
```sh
|
||||
cross build --release
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
The [wiki](https://opssat1.esoc.esa.int/projects/experimenter-information/wiki)
|
||||
appears to be a useful source for documentation.
|
||||
|
||||
- [OBSW documents](https://opssat1.esoc.esa.int/projects/experimenter-information/dmsf?folder_id=7)
|
||||
- [Software Integration Process](https://opssat1.esoc.esa.int/dmsf/files/34/view)
|
||||
- [Cross-compiling SEPP](https://opssat1.esoc.esa.int/projects/experimenter-information/wiki/Cross-compiling_SEPP_application)
|
||||
- [TMTC infrastructure](https://opssat1.esoc.esa.int/projects/experimenter-information/wiki/Live_TM_TC_data)
|
||||
- [Submitting an Experiment](https://opssat1.esoc.esa.int/projects/experimenter-information/wiki/Building_and_submitting_your_application_to_ESOC)
|
||||
|
0
ops-sat-rs-tmtc/main.py → pytmtc/main.py
Normal file → Executable file
0
ops-sat-rs-tmtc/main.py → pytmtc/main.py
Normal file → Executable file
@ -1,10 +1,10 @@
|
||||
use lazy_static::lazy_static;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use satrs::spacepackets::{PacketId, PacketType};
|
||||
use satrs_mib::res_code::ResultU16Info;
|
||||
use satrs_mib::resultcode;
|
||||
use std::{collections::HashSet, net::Ipv4Addr};
|
||||
use strum::IntoEnumIterator;
|
||||
use satrs_mib::resultcode;
|
||||
use satrs_mib::res_code::ResultU16Info;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
|
||||
pub const OBSW_SERVER_ADDR: Ipv4Addr = Ipv4Addr::UNSPECIFIED;
|
||||
pub const SERVER_PORT: u16 = 7301;
|
||||
@ -41,8 +41,8 @@ pub enum GroupId {
|
||||
}
|
||||
|
||||
pub mod tmtc_err {
|
||||
use satrs::res_code::ResultU16;
|
||||
use super::*;
|
||||
use satrs::res_code::ResultU16;
|
||||
|
||||
#[resultcode]
|
||||
pub const INVALID_PUS_SERVICE: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 0);
|
||||
@ -60,7 +60,7 @@ pub mod tmtc_err {
|
||||
pub const REQUEST_TIMEOUT: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 6);
|
||||
|
||||
#[resultcode(
|
||||
info = "Not enough data inside the TC application data field. Optionally includes: \
|
||||
info = "Not enough data inside the TC application data field. Optionally includes: \
|
||||
8 bytes of failure data containing 2 failure parameters, \
|
||||
P1 (u32 big endian): Expected data length, P2: Found data length"
|
||||
)]
|
||||
|
@ -2,10 +2,9 @@ use std::net::{SocketAddr, UdpSocket};
|
||||
use std::sync::mpsc;
|
||||
|
||||
use log::{info, warn};
|
||||
use satrs::pus::{PusTmAsVec, PusTmInPool};
|
||||
use satrs::pus::PusTmAsVec;
|
||||
use satrs::{
|
||||
hal::std::udp_server::{ReceiveResult, UdpTcServer},
|
||||
pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
|
||||
tmtc::CcsdsError,
|
||||
};
|
||||
|
||||
@ -90,7 +89,7 @@ mod tests {
|
||||
},
|
||||
tmtc::ReceivesTcCore,
|
||||
};
|
||||
use satrs_example::config::{components, OBSW_SERVER_ADDR};
|
||||
use ops_sat_rs::config::{components, OBSW_SERVER_ADDR};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use satrs::spacepackets::time::cds::CdsTime;
|
||||
use satrs::spacepackets::time::TimeWriter;
|
||||
|
||||
|
29
src/main.rs
29
src/main.rs
@ -6,6 +6,7 @@ use std::{
|
||||
};
|
||||
|
||||
use log::info;
|
||||
use ops_sat_rs::config::tasks::FREQ_MS_PUS_STACK;
|
||||
use ops_sat_rs::config::{
|
||||
tasks::FREQ_MS_UDP_TMTC, OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT,
|
||||
};
|
||||
@ -13,27 +14,25 @@ use satrs::{
|
||||
hal::std::{tcp_server::ServerConfig, udp_server::UdpTcServer},
|
||||
tmtc::CcsdsDistributor,
|
||||
};
|
||||
use ops_sat_rs::config::tasks::FREQ_MS_PUS_STACK;
|
||||
|
||||
use crate::{
|
||||
tmtc::ccsds::CcsdsReceiver,
|
||||
logger::setup_logger,
|
||||
interface::tcp::{SyncTcpTmSource, TcpTask},
|
||||
tmtc::PusTcSourceProviderDynamic,
|
||||
interface::udp::{DynamicUdpTmHandler, UdpTmtcServer},
|
||||
};
|
||||
use crate::pus::{PusReceiver, PusTcMpscRouter};
|
||||
use crate::pus::stack::PusStack;
|
||||
use crate::pus::test::create_test_service_dynamic;
|
||||
use crate::requests::GenericRequestRouter;
|
||||
use crate::pus::{PusReceiver, PusTcMpscRouter};
|
||||
use crate::tmtc::tm_funnel::TmFunnelDynamic;
|
||||
use crate::tmtc::TcSourceTaskDynamic;
|
||||
use crate::{
|
||||
interface::tcp::{SyncTcpTmSource, TcpTask},
|
||||
interface::udp::{DynamicUdpTmHandler, UdpTmtcServer},
|
||||
logger::setup_logger,
|
||||
tmtc::ccsds::CcsdsReceiver,
|
||||
tmtc::PusTcSourceProviderDynamic,
|
||||
};
|
||||
|
||||
mod logger;
|
||||
mod tmtc;
|
||||
mod requests;
|
||||
mod pus;
|
||||
mod interface;
|
||||
mod logger;
|
||||
mod pus;
|
||||
mod requests;
|
||||
mod tmtc;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {
|
||||
@ -134,7 +133,7 @@ fn main() {
|
||||
tcp_ccsds_distributor,
|
||||
PACKET_ID_VALIDATOR.clone(),
|
||||
)
|
||||
.expect("tcp server creation failed");
|
||||
.expect("tcp server creation failed");
|
||||
|
||||
let mut tm_funnel = TmFunnelDynamic::new(sync_tm_tcp_source, tm_funnel_rx, tm_server_tx);
|
||||
|
||||
|
@ -1,8 +1,12 @@
|
||||
pub mod test;
|
||||
pub mod stack;
|
||||
pub mod test;
|
||||
|
||||
use crate::requests::GenericRequestRouter;
|
||||
use crate::tmtc::MpscStoreAndSendError;
|
||||
use log::warn;
|
||||
use ops_sat_rs::config::components::PUS_ROUTING_SERVICE;
|
||||
use ops_sat_rs::config::{tmtc_err, CustomPusServiceId};
|
||||
use ops_sat_rs::TimeStampHelper;
|
||||
use satrs::pus::verification::{
|
||||
self, FailParams, TcStateAccepted, TcStateStarted, VerificationReporter,
|
||||
VerificationReporterCfg, VerificationReportingProvider, VerificationToken,
|
||||
@ -18,12 +22,8 @@ use satrs::request::{Apid, GenericMessage, MessageMetadata};
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::PusServiceId;
|
||||
use satrs::ComponentId;
|
||||
use ops_sat_rs::config::{tmtc_err, CustomPusServiceId};
|
||||
use ops_sat_rs::TimeStampHelper;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::mpsc::{self, Sender};
|
||||
use ops_sat_rs::config::components::PUS_ROUTING_SERVICE;
|
||||
use crate::tmtc::MpscStoreAndSendError;
|
||||
|
||||
// pub mod action;
|
||||
// pub mod event;
|
||||
@ -34,6 +34,7 @@ use crate::tmtc::MpscStoreAndSendError;
|
||||
// pub mod test;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum HandlingStatus {
|
||||
Empty,
|
||||
HandledOne,
|
||||
@ -185,6 +186,7 @@ pub trait TargetedPusService {
|
||||
/// 1. [Self::handle_one_tc] which tries to poll and handle one TC packet, covering steps 1-5.
|
||||
/// 2. [Self::check_one_reply] which tries to poll and handle one reply, covering step 6.
|
||||
/// 3. [Self::check_for_request_timeouts] which checks for request timeouts, covering step 7.
|
||||
#[allow(dead_code)]
|
||||
pub struct PusTargetedRequestService<
|
||||
TcReceiver: EcssTcReceiverCore,
|
||||
TmSender: EcssTmSenderCore,
|
||||
@ -207,6 +209,7 @@ pub struct PusTargetedRequestService<
|
||||
phantom: std::marker::PhantomData<(RequestType, ActiveRequestInfo, ReplyType)>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl<
|
||||
TcReceiver: EcssTcReceiverCore,
|
||||
TmSender: EcssTmSenderCore,
|
||||
@ -437,6 +440,7 @@ where
|
||||
|
||||
/// Generic timeout handling: Handle the verification failure with a dedicated return code
|
||||
/// and also log the error.
|
||||
#[allow(dead_code)]
|
||||
pub fn generic_pus_request_timeout_handler(
|
||||
sender: &(impl EcssTmSenderCore + ?Sized),
|
||||
active_request: &(impl ActiveRequestProvider + Debug),
|
||||
|
@ -1,11 +1,11 @@
|
||||
// use crate::pus::mode::ModeServiceWrapper;
|
||||
use crate::pus::test::TestCustomServiceWrapper;
|
||||
use crate::pus::HandlingStatus;
|
||||
use derive_new::new;
|
||||
use satrs::{
|
||||
pus::{EcssTcInMemConverter, EcssTmSenderCore},
|
||||
spacepackets::time::{cds, TimeWriter},
|
||||
};
|
||||
use crate::pus::HandlingStatus;
|
||||
use crate::pus::test::TestCustomServiceWrapper;
|
||||
|
||||
// use super::{
|
||||
// action::ActionServiceWrapper, event::EventServiceWrapper, hk::HkServiceWrapper,
|
||||
|
@ -1,29 +1,25 @@
|
||||
use crate::pus::create_verification_reporter;
|
||||
use log::{info, warn};
|
||||
use satrs::event_man::{EventMessage, EventMessageU32};
|
||||
use satrs::pool::SharedStaticMemoryPool;
|
||||
use ops_sat_rs::config::components::PUS_TEST_SERVICE;
|
||||
use ops_sat_rs::config::tmtc_err;
|
||||
// use satrs::event_man::{EventMessage, EventMessageU32};
|
||||
use satrs::pus::test::PusService17TestHandler;
|
||||
use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider};
|
||||
use satrs::pus::EcssTcInSharedStoreConverter;
|
||||
use satrs::pus::{
|
||||
EcssTcAndToken, EcssTcInMemConverter, EcssTcInVecConverter, EcssTmSenderCore, MpscTcReceiver,
|
||||
MpscTmAsVecSender, MpscTmInSharedPoolSenderBounded, PusPacketHandlerResult, PusServiceHelper,
|
||||
PusTmAsVec, PusTmInPool, TmInSharedPoolSender,
|
||||
MpscTmAsVecSender, PusPacketHandlerResult, PusServiceHelper, PusTmAsVec,
|
||||
};
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::PusPacket;
|
||||
use satrs::spacepackets::time::cds::CdsTime;
|
||||
use satrs::spacepackets::time::TimeWriter;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::Sender;
|
||||
use ops_sat_rs::config::components::PUS_TEST_SERVICE;
|
||||
use ops_sat_rs::config::tmtc_err;
|
||||
|
||||
pub fn create_test_service_dynamic(
|
||||
tm_funnel_tx: mpsc::Sender<PusTmAsVec>,
|
||||
// event_sender: mpsc::Sender<EventMessageU32>,
|
||||
pus_test_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
) -> TestCustomServiceWrapper<Sender<PusTmAsVec>, EcssTcInVecConverter> {
|
||||
) -> TestCustomServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
|
||||
let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new(
|
||||
PUS_TEST_SERVICE.id(),
|
||||
pus_test_rx,
|
||||
|
@ -2,6 +2,8 @@ use std::collections::HashMap;
|
||||
use std::sync::mpsc;
|
||||
|
||||
use log::warn;
|
||||
use ops_sat_rs::config::components::PUS_ROUTING_SERVICE;
|
||||
use ops_sat_rs::config::tmtc_err;
|
||||
use satrs::action::ActionRequest;
|
||||
use satrs::hk::HkRequest;
|
||||
use satrs::mode::ModeRequest;
|
||||
@ -14,8 +16,6 @@ use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::PusPacket;
|
||||
use satrs::ComponentId;
|
||||
use ops_sat_rs::config::components::PUS_ROUTING_SERVICE;
|
||||
use ops_sat_rs::config::tmtc_err;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
@ -41,6 +41,8 @@ impl Default for GenericRequestRouter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl GenericRequestRouter {
|
||||
pub(crate) fn handle_error_generic(
|
||||
&self,
|
||||
|
@ -1,9 +1,9 @@
|
||||
use ops_sat_rs::config::components::Apid;
|
||||
use ops_sat_rs::config::APID_VALIDATOR;
|
||||
use satrs::pus::ReceivesEcssPusTc;
|
||||
use satrs::spacepackets::{CcsdsPacket, SpHeader};
|
||||
use satrs::tmtc::{CcsdsPacketHandler, ReceivesCcsdsTc};
|
||||
use satrs::ValidatorU16Id;
|
||||
use ops_sat_rs::config::components::Apid;
|
||||
use ops_sat_rs::config::APID_VALIDATOR;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CcsdsReceiver<
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::pus::PusReceiver;
|
||||
use satrs::pool::{StoreAddr, StoreError};
|
||||
use satrs::pus::{EcssTcAndToken, MpscTmAsVecSender};
|
||||
use satrs::spacepackets::ecss::PusPacket;
|
||||
use satrs::{
|
||||
pus::ReceivesEcssPusTc,
|
||||
spacepackets::{ecss::tc::PusTcReader, SpHeader},
|
||||
tmtc::ReceivesCcsdsTc,
|
||||
};
|
||||
use std::sync::mpsc::{self, SendError, Sender, TryRecvError};
|
||||
use satrs::pool::{StoreAddr, StoreError};
|
||||
use satrs::pus::{EcssTcAndToken, MpscTmAsVecSender};
|
||||
use satrs::spacepackets::ecss::PusPacket;
|
||||
use crate::pus::PusReceiver;
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod tm_funnel;
|
||||
|
@ -4,16 +4,14 @@ use std::{
|
||||
};
|
||||
|
||||
use log::info;
|
||||
use satrs::pus::{PusTmAsVec, PusTmInPool};
|
||||
use satrs::pus::PusTmAsVec;
|
||||
use satrs::{
|
||||
pool::PoolProvider,
|
||||
seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProviderCore},
|
||||
spacepackets::{
|
||||
ecss::{tm::PusTmZeroCopyWriter, PusPacket},
|
||||
time::cds::MIN_CDS_FIELD_LEN,
|
||||
CcsdsPacket,
|
||||
},
|
||||
tmtc::tm_helper::SharedTmPool,
|
||||
};
|
||||
|
||||
use crate::interface::tcp::SyncTcpTmSource;
|
||||
@ -22,6 +20,7 @@ use crate::interface::tcp::SyncTcpTmSource;
|
||||
pub struct CcsdsSeqCounterMap {
|
||||
apid_seq_counter_map: HashMap<u16, CcsdsSimpleSeqCountProvider>,
|
||||
}
|
||||
|
||||
impl CcsdsSeqCounterMap {
|
||||
pub fn get_and_increment(&mut self, apid: u16) -> u16 {
|
||||
self.apid_seq_counter_map
|
||||
@ -75,53 +74,6 @@ impl TmFunnelCommon {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TmFunnelStatic {
|
||||
common: TmFunnelCommon,
|
||||
shared_tm_store: SharedTmPool,
|
||||
tm_funnel_rx: mpsc::Receiver<PusTmInPool>,
|
||||
tm_server_tx: mpsc::SyncSender<PusTmInPool>,
|
||||
}
|
||||
|
||||
impl TmFunnelStatic {
|
||||
pub fn new(
|
||||
shared_tm_store: SharedTmPool,
|
||||
sync_tm_tcp_source: SyncTcpTmSource,
|
||||
tm_funnel_rx: mpsc::Receiver<PusTmInPool>,
|
||||
tm_server_tx: mpsc::SyncSender<PusTmInPool>,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: TmFunnelCommon::new(sync_tm_tcp_source),
|
||||
shared_tm_store,
|
||||
tm_funnel_rx,
|
||||
tm_server_tx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operation(&mut self) {
|
||||
if let Ok(pus_tm_in_pool) = self.tm_funnel_rx.recv() {
|
||||
// Read the TM, set sequence counter and message counter, and finally update
|
||||
// the CRC.
|
||||
let shared_pool = self.shared_tm_store.clone_backing_pool();
|
||||
let mut pool_guard = shared_pool.write().expect("Locking TM pool failed");
|
||||
let mut tm_copy = Vec::new();
|
||||
pool_guard
|
||||
.modify(&pus_tm_in_pool.store_addr, |buf| {
|
||||
let zero_copy_writer = PusTmZeroCopyWriter::new(buf, MIN_CDS_FIELD_LEN)
|
||||
.expect("Creating TM zero copy writer failed");
|
||||
self.common.apply_packet_processing(zero_copy_writer);
|
||||
tm_copy = buf.to_vec()
|
||||
})
|
||||
.expect("Reading TM from pool failed");
|
||||
self.tm_server_tx
|
||||
.send(pus_tm_in_pool)
|
||||
.expect("Sending TM to server failed");
|
||||
// We could also do this step in the update closure, but I'd rather avoid this, could
|
||||
// lead to nested locking.
|
||||
self.common.sync_tm_tcp_source.add_tm(&tm_copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TmFunnelDynamic {
|
||||
common: TmFunnelCommon,
|
||||
tm_funnel_rx: mpsc::Receiver<PusTmAsVec>,
|
||||
|
Loading…
Reference in New Issue
Block a user