use crate::{
errors::{CatBridgeError, NetworkError},
fsemul::sdio::{
SDIO_DATA_STREAMS,
data_stream::DataStream,
errors::SdioNetworkError,
proto::{
SDIO_BLOCK_SIZE,
message::{
SdioControlMessageRequest, SdioControlTelnetChannel, SdioControlTelnetMessage,
},
read::SdioControlReadRequest,
},
},
net::client::{TCPClient, models::RequestStreamEvent},
};
use bytes::Bytes;
use std::{net::SocketAddr, sync::Arc, time::Duration};
use tokio::{net::TcpListener, sync::RwLock};
use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
const DEFAULT_SEND_TIMEOUT: Duration = Duration::from_secs(5 * 60);
#[derive(Debug)]
pub struct SdioClient {
data_listener: Arc<RwLock<TcpListener>>,
response_timeout: Duration,
underlying_client: TCPClient,
}
impl SdioClient {
pub async fn new(
bind_address_control: SocketAddr,
bind_address_data: SocketAddr,
trace_during_debug: bool,
) -> Result<Self, CatBridgeError> {
let mut client = TCPClient::new("sdio", 512_usize, (None, None), trace_during_debug);
let data = Arc::new(RwLock::new(
TcpListener::bind(bind_address_data)
.await
.map_err(NetworkError::IO)?,
));
client.bind(bind_address_control).await?;
let cloned_data_start = data.clone();
#[cfg(debug_assertions)]
let copied_trace: bool = trace_during_debug;
client.set_on_stream_begin(move |event: RequestStreamEvent<()>| async move {
let sid = event.stream_id();
let (connection, server_location) = {
let guard = cloned_data_start.write().await;
guard.accept().await
}
.map_err(NetworkError::IO)?;
connection.set_nodelay(true).map_err(NetworkError::IO)?;
let client_location = connection.local_addr().map_err(NetworkError::IO)?;
_ = SDIO_DATA_STREAMS
.insert_async(
sid,
DataStream::from_stream(
client_location,
server_location,
connection,
#[cfg(debug_assertions)]
copied_trace,
)?,
)
.await;
Ok::<bool, CatBridgeError>(true)
})?;
client.set_on_stream_end(|event: RequestStreamEvent<()>| async move {
SDIO_DATA_STREAMS.remove_async(&event.stream_id()).await;
Ok(())
})?;
Ok(Self {
data_listener: data,
response_timeout: DEFAULT_SEND_TIMEOUT,
underlying_client: client,
})
}
#[must_use]
pub const fn get_response_timeout(&self) -> Duration {
self.response_timeout
}
pub async fn send_telnet_cafe_os(&self, message: String) -> Result<(), CatBridgeError> {
for char_chunk in message.bytes().collect::<Vec<u8>>().chunks(499) {
let msg_chunk = unsafe { String::from_utf8_unchecked(char_chunk.to_vec()) };
self.underlying_client
.send(
SdioControlMessageRequest::new(vec![SdioControlTelnetMessage::new(
msg_chunk,
SdioControlTelnetChannel::CafeOS,
)?]),
None,
)
.await?;
}
Ok(())
}
pub async fn send_telnet_dkm(&self, message: String) -> Result<(), CatBridgeError> {
for char_chunk in message.bytes().collect::<Vec<u8>>().chunks(499) {
let msg_chunk = unsafe { String::from_utf8_unchecked(char_chunk.to_vec()) };
self.underlying_client
.send(
SdioControlMessageRequest::new(vec![SdioControlTelnetMessage::new(
msg_chunk,
SdioControlTelnetChannel::DevkitMsg,
)?]),
None,
)
.await?;
}
Ok(())
}
pub async fn send_telnet_sysconfigtool(&self, message: String) -> Result<(), CatBridgeError> {
for char_chunk in message.bytes().collect::<Vec<u8>>().chunks(499) {
let msg_chunk = unsafe { String::from_utf8_unchecked(char_chunk.to_vec()) };
self.underlying_client
.send(
SdioControlMessageRequest::new(vec![SdioControlTelnetMessage::new(
msg_chunk,
SdioControlTelnetChannel::SysConfigTool,
)?]),
None,
)
.await?;
}
Ok(())
}
pub async fn send_raw_telnet_message(
&self,
messages: Vec<SdioControlTelnetMessage>,
) -> Result<(), CatBridgeError> {
for message in messages {
self.underlying_client
.send(SdioControlMessageRequest::new(vec![message]), None)
.await?;
}
Ok(())
}
pub async fn read(&self, lba: u32, blocks: u32, channel: u32) -> Result<Bytes, CatBridgeError> {
let (primary_stream_id, _req_id, _) = self
.underlying_client
.send(SdioControlReadRequest::new(lba, blocks, channel)?, None)
.await?;
let Some(data_stream) = SDIO_DATA_STREAMS.get_async(&primary_stream_id).await else {
return Err(SdioNetworkError::DataStreamMissing(primary_stream_id).into());
};
Ok(data_stream
.recv(usize::try_from(blocks).unwrap_or(usize::MAX) * SDIO_BLOCK_SIZE)
.await?)
}
}
const SDIO_CLIENT_FIELDS: &[NamedField<'static>] = &[
NamedField::new("data_listener"),
NamedField::new("underlying_client"),
];
impl Structable for SdioClient {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static("SdioClient", Fields::Named(SDIO_CLIENT_FIELDS))
}
}
impl Valuable for SdioClient {
fn as_value(&self) -> Value<'_> {
Value::Structable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
visitor.visit_named_fields(&NamedValues::new(
SDIO_CLIENT_FIELDS,
&[
Valuable::as_value(&format!("{:?}", self.data_listener)),
Valuable::as_value(&self.underlying_client),
],
));
}
}