Get width and height from an image in Bevy - bevy

I am fairly new to Bevy and rust. I want to load an png image and get the width and height of it. The code below does not print "found resource ...".
fn setup( mut commands: Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<Image>>) {
let texture_handle = asset_server.load("board.png");
//materials.add(texture_handle.clone().into()); //Gives error: the trait `From<Handle<_>>` is not implemented for `bevy::prelude::Image`
commands.spawn().insert_bundle(SpriteBundle {
texture: texture_handle.clone(),
..Default::default()
}).insert(BackgroundSprite);
if let Some(image) = materials.get(texture_handle) {
print!("found resource with width and height: [{},{}]", image.texture_descriptor.size.width, image.texture_descriptor.size.height);
}
}

I figured it out after some help on the Bevy Discord help channle.
Resources are loaded asynchronously and has to be accessed at a later point. See AssetEvent example here
I am a beginner when it comes to Rust so I wont say this is the way to do it. But here is my result:
#[derive(Component)]
pub struct BackgroundHandle {
handle: Handle<Image>,
}
#[derive(Component)]
pub struct BackgroundSpriteSize {
width: u32,
height: u32,
}
fn setup( mut commands: Commands,
mut app_state: ResMut<State<BoardState>>,
asset_server: Res<AssetServer>) {
let texture_handle = asset_server.load("board.png");
commands.spawn().insert_bundle(SpriteBundle {
texture: texture_handle.clone(),
..Default::default()
}).insert(BackgroundSpriteBundle);
commands.insert_resource(BackgroundHandle {
handle: texture_handle,
});
app_state.set(BoardState::Initializing);
}
fn setupBounds(mut commands: Commands,
mut app_state: ResMut<State<BoardState>>,
mut ev_asset: EventReader<AssetEvent<Image>>,
assets: Res<Assets<Image>>,
bg: Res<BackgroundHandle>) {
for ev in ev_asset.iter() {
match ev {
AssetEvent::Created { handle } => {
if *handle == bg.handle {
let img = assets.get(bg.handle.clone()).unwrap();
let bg_size = BackgroundSpriteSize {
width: img.texture_descriptor.size.width,
height: img.texture_descriptor.size.height,
};
commands.insert_resource(bg_size);
app_state.set(BoardState::Initialized);
}
},
_ => {
}
}
}
}

Related

Generic data in compose ui state

I'm getting data from Marvel API, so the main screen you have different kinds of categories (Characters, Events, Comics etc.) When the user clicks on one of the categories, the app navigates to a list of the related data.
So I want this screen to hold different kinds of data (categories) without using a different screen for each one. Is this the best approach? and how can I do that?
code:
#kotlinx.serialization.Serializable
data class MarvelResponse(
val data:Data
)
#kotlinx.serialization.Serializable
data class Data(
var characters:List<Character>,
var series:List<Series>,
var stories:List<Story>,
var events:List<Event>,
var comics:List<Comic>,
var cartoons:List<Cartoon>
)
class DetailsViewModel #Inject constructor(
private val useCase: UseCase,
savedStateHandle: SavedStateHandle
) : ViewModel() {
private val _uiState = mutableStateOf<Resource<Any>>(Resource.Loading())
val uiState = _uiState
private fun getData(category: String) {
when (category) {
"Characters" -> {
getCharacters()
}
"Comics" -> {
getComics()
}
"Series" -> {
//
}
"Stories" -> {
//
}
}
}
private fun getCharacters() {
viewModelScope.launch {
val charactersResponse = useCase.getCharactersUseCase()
_uiState.value = Resource.Success(charactersResponse)
}
}
..........
fun Details(
vm: DetailsViewModel = hiltViewModel(),
navController:NavHostController
) {
Scaffold(
topBar = {
TopAppBar(
navigationIcon = {
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(imageVector = Icons.Default.ArrowBack, contentDescription = null)
}
},
title = { Text(text = "Back") }
)
}
) { paddingValues ->
DetailsVerticalGrid(state, modifier = Modifier.padding(paddingValues))
}
}
#ExperimentalMaterialApi
#ExperimentalComposeUiApi
#Composable
fun DetailsVerticalGrid(
data: List<Any>,
modifier: Modifier = Modifier
) {
LazyVerticalGrid(
columns = GridCells.Adaptive(30.dp),
modifier = modifier
) {
items(data.size) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
data.forEach {
DetailsGridItemCard(
image = "",
title = it.title
) {
}
}
}
}
}
}
Of course the above code will not work, I want it to work with any type of data, using a state that holds the data according to the category selected. How can I achieve that?

Implement async socket recvmsg for RX and TX timestamping in Rust

Objective
This project is a test for using AsyncFd (from tokio) to wrap a socket file descriptor.
Code example
Socket file:
use nix::{
cmsg_space,
sys::{
socket::{ControlMessageOwned, MsgFlags, SockaddrIn},
time::TimeSpec,
},
};
use tokio::io::unix::{AsyncFd, TryIoError};
use std::{
io::{IoSlice, IoSliceMut},
marker::PhantomData,
os::unix::prelude::{AsRawFd, RawFd},
};
pub struct AsyncSocket<'a, T: 'a> {
inner: AsyncFd<RawFd>,
phantom: PhantomData<&'a T>,
}
impl<'a, T> AsyncSocket<'a, T> {
pub fn new(fd: RawFd) -> tokio::io::Result<Self> {
Ok(Self {
// inner: AsyncFd::new(fd)?,
inner: AsyncFd::new(fd)?,
phantom: PhantomData,
})
}
pub async fn write_to(
&'a self,
buffer: &'a [IoSlice<'_>; 1],
socket_address: &SockaddrIn,
) -> Result<usize, TryIoError> {
let mut guard = self.inner.writable().await.unwrap();
let flags = MsgFlags::empty();
let cmsgs = &mut [];
match guard.try_io(|inner| {
match nix::sys::socket::sendmsg(
inner.as_raw_fd(),
buffer,
cmsgs,
flags,
Some(socket_address),
) {
Ok(read_bytes) => Ok(read_bytes),
Err(would_block) => Err(std::io::Error::from_raw_os_error(would_block as i32)),
}
}) {
Ok(res) => match res {
Ok(read_bytes) => Ok(read_bytes),
Err(e) => {
eprintln!("Error {}", e);
Ok(0)
}
},
Err(e) => Err(e),
}
}
pub async fn read(
&'a self,
buffer: &'a mut [IoSliceMut<'_>; 1],
flags: MsgFlags,
) -> Result<usize, TryIoError> {
buffer[0].fill(0);
let mut guard = self.inner.readable().await.unwrap();
match guard.try_io(|inner| {
let sys_time = nix::time::clock_gettime(nix::time::ClockId::CLOCK_REALTIME).unwrap();
println!("Real clock {:?}", sys_time);
println!("FLAG = {:?}", flags);
match nix::sys::socket::recvmsg::<()>(
inner.as_raw_fd(),
buffer,
Some(&mut cmsg_space!(
nix::sys::socket::MsgFlags,
nix::sys::socket::TimestampingFlag,
nix::sys::socket::SockFlag
)),
flags,
) {
Ok(result) => {
let mut ts = TimeSpec::new(0, 0);
let mut _thw = TimeSpec::new(0, 0);
let control_messages: Vec<ControlMessageOwned> = result.cmsgs().collect();
println!("Control message length = {}", control_messages.len());
for c in control_messages {
match c {
ControlMessageOwned::ScmTimestampsns(timestamps) => {
_thw = timestamps.hw_raw;
ts = timestamps.system;
println!("Timestamps {:?}", timestamps);
}
ControlMessageOwned::ScmRights(_) => println!("ScmRights"),
ControlMessageOwned::ScmCredentials(_) => println!("ScmCredentials"),
ControlMessageOwned::ScmTimestamp(_) => println!("ScmTimestamp"),
ControlMessageOwned::ScmTimestampns(_) => println!("ScmTimestampns"),
ControlMessageOwned::Ipv4PacketInfo(_) => println!("Ipv4PacketInfo"),
ControlMessageOwned::Ipv6PacketInfo(_) => println!("Ipv6PacketInfo"),
ControlMessageOwned::Ipv4OrigDstAddr(_) => println!("Ipv4OrigDstAddr"),
ControlMessageOwned::Ipv6OrigDstAddr(_) => println!("Ipv6OrigDstAddr"),
ControlMessageOwned::UdpGroSegments(_) => println!("UdpGroSegments"),
ControlMessageOwned::RxqOvfl(_) => println!("RxqOvfl"),
ControlMessageOwned::Ipv4RecvErr(a, b) => {
println!("Received ipv4 Err {:?} from {:?}", a, b);
}
ControlMessageOwned::Ipv6RecvErr(_, _) => println!("Ipv6RecvErr"),
_ => println!("Other"),
}
}
let soft_diff = diff_systime(ts, sys_time);
// let hw_diff = diff_systime(thw, sys_time);
if soft_diff != sys_time {
let delta = std::time::Duration::from(soft_diff).as_micros();
println!("Soft Delta is {}", delta);
}
// } else if hw_diff != sys_time {
// // let delta = std::time::Duration::from(hw_diff).as_micros();
// // println!("Hard Delta is {}", delta);
// // }
return Ok(result.bytes);
}
Err(errno) => {
match errno {
nix::errno::Errno::EAGAIN => println!("EAGAIN Error"),
_ => println!("Other error {:?}", errno),
}
let error = std::io::Error::from_raw_os_error(errno as i32);
Err(error)
}
}
}) {
Ok(res) => match res {
Ok(read_bytes) => Ok(read_bytes),
Err(_e) => {
println!("Error from socket {:?}", std::io::Error::last_os_error());
Ok(0)
}
},
Err(e) => {
println!("Guard error {:?}", std::io::Error::last_os_error());
Err(e)
}
}
// }
}
}
impl<'a, T> AsRawFd for AsyncSocket<'a, T> {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
impl<'a, T> Drop for AsyncSocket<'a, T> {
fn drop(&mut self) {
let fd = self.inner.as_raw_fd();
unsafe { nix::libc::close(fd) };
}
}
fn diff_systime(first: TimeSpec, second: TimeSpec) -> TimeSpec {
if second > first {
second - first
} else {
first - second
}
}
Error.rs
#[derive(thiserror::Error, Debug)]
pub enum LibError {
AddrParseError(#[from] std::net::AddrParseError),
#[error(transparent)]
IO(#[from] std::io::Error),
OSError(#[from] nix::Error),
}
impl std::fmt::Display for LibError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", &self)
}
}
Main.rs:
use std::{
io::{IoSlice, IoSliceMut},
str::FromStr,
sync::{
atomic::{AtomicU8, Ordering},
Arc,
},
time::Duration,
};
mod error;
mod socket;
use nix::sys::socket::{
bind, setsockopt,
sockopt::{self},
AddressFamily, MsgFlags, SockFlag, SockProtocol, SockType, SockaddrIn, TimestampingFlag,
};
use socket::AsyncSocket;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let local_sock_addr = SockaddrIn::from_str("0.0.0.0:6790").unwrap();
let local_sock_addr1 = SockaddrIn::from_str("192.168.1.84:44581").unwrap();
let send_sock_addr = SockaddrIn::from_str("192.168.1.123:6790").unwrap();
let rsock = nix::sys::socket::socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::all(),
SockProtocol::Udp,
)?;
let ssock = nix::sys::socket::socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::all(),
SockProtocol::Udp,
)?;
// let sock_txtime = sock_txtime {
// clockid: nix::time::ClockId::CLOCK_MONOTONIC.as_raw(),
// flags: SOF_TXTIME_REPORT_ERRORS,
// };
setsockopt(rsock, sockopt::Timestamping, &TimestampingFlag::all())?;
setsockopt(ssock, sockopt::Timestamping, &TimestampingFlag::all())?;
// setsockopt(ssock, sockopt::ReuseAddr, &true)?;
// setsockopt(rsock, sockopt::ReuseAddr, &true)?;
// setsockopt(ssock, sockopt::TxTime, &sock_txtime)?;
bind(ssock, &local_sock_addr1)?;
bind(rsock, &local_sock_addr)?;
let recv_socket: AsyncSocket<i32> = AsyncSocket::new(rsock)?;
let send_socket: AsyncSocket<i32> = AsyncSocket::new(ssock)?;
let atomic_i = Arc::new(AtomicU8::new(1));
let mut read_buf = [0u8; 1024];
let mut iov2 = [IoSliceMut::new(&mut read_buf)];
// let mut rbuf1 = [0u8; 1024];
let mut rbuf2 = [0u8; 1024];
// let mut iov3 = [IoSliceMut::new(&mut rbuf1)];
let mut iov4 = [IoSliceMut::new(&mut rbuf2)];
loop {
tokio::select! {
read = recv_socket.read(&mut iov2, MsgFlags::empty()) => {
match read {
Ok(v) => {
println!("Recv sock Received {} bytes in mes {:?}", v, iov2[0].iter().take(v).collect::<Vec<&u8>>());
let i = atomic_i.load(Ordering::Relaxed);
let sbuf: Vec<u8> = (1u8..=i).map(|el| el).collect();
let iov1 = [IoSlice::new(&mut sbuf.as_slice())];
tokio::time::sleep(Duration::from_millis(15)).await;
let _ = recv_socket.write_to(&iov1, &local_sock_addr1).await;
},
Err(e) => println!("Recv Err {:?}", e),
}
},
_tick = tokio::time::sleep(Duration::from_millis(500)) => {
// println!("Tick");
let i = atomic_i.load(Ordering::Relaxed);
if i == 3 {
continue;
// In case you want the sending to last forever
// atomic_i
// .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |n| Some(n - n))
// .unwrap();
// break;
}
let sbuf: Vec<u8> = (1u8..=i).map(|el| el).collect();
let iov1 = [IoSlice::new(&mut sbuf.as_slice())];
let _ = send_socket.write_to(&iov1, &send_sock_addr).await;
// Calling read here results in a deadlock
println!("Message {} sent", i);
atomic_i.fetch_add(1, Ordering::Relaxed);
},
read2 = send_socket.read(&mut iov4, MsgFlags::empty()) => {
match read2 {
Ok(v) => {
println!("Send sock Received {} bytes in mes {:?}", v, iov4[0].iter().take(v).collect::<Vec<&u8>>());
// This second read call is done to retrieve any messages present in the Error queue (timestamps are there)
// match send_socket.read(&mut iov3, MsgFlags::MSG_ERRQUEUE).await {
// Ok(v) => println!("Send sock Received from Error queue {} bytes in mes {:?}", v, iov3[0].iter().take(v).collect::<Vec<&u8>>()),
// Err(e) => println!("Send Err {:?}", e),
// }
},
Err(e) => println!("Send Err {:?}", e),
}
},
// Adding this entry results in very inconsistent behavior for receiving Tx timestamps
// read1 = send_socket.read(&mut iov3, MsgFlags::MSG_ERRQUEUE) => {
// match read1 {
// Ok(v) => println!("Send sock Received from Error queue {} bytes in mes {:?}", v, iov3[0].iter().take(v).collect::<Vec<&u8>>()),
// Err(e) => println!("Send Err {:?}", e),
// }
// },
}
println!("\n")
}
// Ok(())
}
Cargo.toml:
[package]
name = "socket-timestamp-test"
version = "0.1.0"
edition = "2021"
[dependencies]
bytes = "1.3.0"
futures = "0.3.25"
log = "0.4.17"
mio = { version = "0.8.5", features = ["os-ext"] }
nix = { version = "0.26.1", features = ["socket"] }
num_enum = "0.5.7"
thiserror = "1.0.37"
tokio = { version = "1.2", features = [
"sync",
"net",
"macros",
"rt-multi-thread",
"time",
] }
Motivation
The motivation is to be able to access configuration that is not present in common user network libraries (such as tokio, std, socket2, smoltcp, etc), such as retrieving timestamp messages for RX and TX packets.
These values can be retrieved with a call to libc's recvmsg in a correctly configured socket (configured with SOF timestamping flags).
Tx timestamps can be retrieved from the Socket Error Queue, by accessing the socket's control messages.
Current problems
Tx timestamps are stored (when MSG_ERRQUEUE is used) in the error queue, but polling of the socket only happens when a message is received buffer. This results in the following behavior:
Socket 1 sends message
Timestamping stuff is queued
... no polling is triggered and the message is not retrieved
Socket 1 receives the response
If recvmsg is called with MSG_ERRQUEUE:
Socket is polled until all timestamps from the sent messages are received
If recvmsg is called without MSG_ERRQUEUE:
Socket is polled and receives the payload of the response, but no data from error queue is read (which is the expected behavior)
If two calls to recvmsg are placed in the select! closure, it results in inconsistent behavior where sometimes you get error queued messages but sometimes you don't. This means that a second another call to recvmsg is required to receive the remaining Tx timestamps.
Intended overall behavior
If socket is configured to generate TX timestamps, and the Error queue has received such information, the async runtime should automatically poll the results.
When data comes in the normal recv buffer, the async runtime should also poll these results.
Possible solutions
One way to fix it would be to poll the socket with recvmsg(...MSG_ERRQUEUE) after sending the packet. But this is cumbersome and due to scheduling, the recvmsg call can't be done immediately after the send. Ideally I would like polling to happen when there is a message in the recv buffer and when there is no message in the recv buffer, but there is a queued entry in the Error queue.
Another solution would be to place a recvmsg with MSG_ERRQUEUE right after the recvmsg (without MSG_ERRQUE). The problem here is that if the socket has not received anything, we won't poll the error queue as well.
Request for comments
I would like help in figuring out how to retrieve TX timestamps without having to manually call recvmsg (...MSG_ERRQUEUE) to get it.
I'm open to suggestions on different approaches to the problem, i.e., without tokio AsyncFd.

Crypto-js: how to use aes ecb mode with 256bit

my code:
function AesDecrypt(word, keyIn) {
let decrypt = CryptoJS.AES.decrypt(word, keyIn, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return decrypt.toString(CryptoJS.enc.Utf8);
}
function AesEncrypt(word, keyIn) {
let encrypted = CryptoJS.AES.encrypt(word, keyIn, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}
when using:
let msg = {
'OS': 'iOS'
}
msg = JSON.stringify(msg)
let key = 'FE7A45426AFF5D14E52897E134F5CC33'
const aes_msg = AesEncrypt(msg, key)
# U2FsdGVkX1/QZRpRuRajXR7UdoXxxYR/lcyoYItxTrI=
const msg_d = AesDecrypt(aes_msg, key)
# {"OS":"iOS"}
as above, problem solved but it conflicts with online-AES. i do ont know how and where it goes wrong.

alternative to using 'await' with lazy_static! macro in rust?

I want to use Async MongoDB in a project.
I don't want to pass around the client because it would need to go around multiple tasks and threads. So I kept a static client using lazy_static. However, I can't use await in the initialization block.
What can I do to work around this?
Suggestions for doing it without lazy_static are also welcome.
use std::env;
use futures::stream::StreamExt;
use mongodb::{
bson::{doc, Bson},
options::ClientOptions,
Client,
};
lazy_static! {
static ref MONGO: Option<Client> = {
if let Ok(token) = env::var("MONGO_AUTH") {
if let Ok(client_options) = ClientOptions::parse(&token).await
^^^^^
{
if let Ok(client) = Client::with_options(client_options) {
return Some(client);
}
}
}
return None;
};
}
I went with this approach based on someone's suggestion in rust forums.
static MONGO: OnceCell<Client> = OnceCell::new();
static MONGO_INITIALIZED: OnceCell<tokio::sync::Mutex<bool>> = OnceCell::new();
pub async fn get_mongo() -> Option<&'static Client> {
// this is racy, but that's OK: it's just a fast case
let client_option = MONGO.get();
if let Some(_) = client_option {
return client_option;
}
// it hasn't been initialized yet, so let's grab the lock & try to
// initialize it
let initializing_mutex = MONGO_INITIALIZED.get_or_init(|| tokio::sync::Mutex::new(false));
// this will wait if another task is currently initializing the client
let mut initialized = initializing_mutex.lock().await;
// if initialized is true, then someone else initialized it while we waited,
// and we can just skip this part.
if !*initialized {
// no one else has initialized it yet, so
if let Ok(token) = env::var("MONGO_AUTH") {
if let Ok(client_options) = ClientOptions::parse(&token).await {
if let Ok(client) = Client::with_options(client_options) {
if let Ok(_) = MONGO.set(client) {
*initialized = true;
}
}
}
}
}
drop(initialized);
MONGO.get()
}
However I can't use await in the initialization block.
You can skirt this with futures::executor::block_on
use once_cell::sync::Lazy;
// ...
static PGCLIENT: Lazy<Client> = Lazy::new(|| {
let client: Client = futures::executor::block_on(async {
let (client, connection) = tokio_postgres::connect(
"postgres:///?user=ecarroll&port=5432&host=/run/postgresql",
NoTls,
)
.await
.unwrap();
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("connection error: {}", e);
}
});
client
});
client
});
What we have is a non-async closure blocking in a single thread until the resolution of the future.
Create a new runtime from tokio::runtime::Runtime and use block_on to block the current thread until completion.
// database.rs
use tokio::runtime::Runtime;
use mongodb::Client;
pub fn connect_sync() -> Client {
Runtime::new().unwrap().block_on(async {
Client::with_uri_str("mongodb://localhost:27017").await.unwrap()
})
}
// main.rs
mod database;
lazy_static! {
static ref CLIENT: mongodb::Client = database::connect_sync();
}
#[actix_web::main]
async fn main() {
let collection = &CLIENT.database("db_name").collection("coll_name");
// ...
}
Use the async_once crate.
use async_once::AsyncOnce;
use lazy_static::lazy_static;
use mongodb::Client;
lazy_static! {
static ref CLIENT: AsyncOnce<Client> = AsyncOnce::new(async {
Client::with_uri_str(std::env::var("MONGO_URL").expect("MONGO_URL not set"))
.await
.unwrap()
});
}
then
CLIENT.get().await;

IONIC [native-file, image-picker] does not work writeFile on iOS

There is such a method:
public loadGallery():void{
let options ={
maximumImagesCount: 20,
width: 500,
height: 500,
quality: 75
};
this.imagePicker.getPictures(options).then((result) => {
for (let i = 0; i < result.length; i++) {
this.imgList.push({'win':{id: result[i], title: result[i]}});
}
let builder = new xml2js.Builder();
let xml = builder.buildObject(this.imgList);
this.file.writeFile(this.file.dataDirectory,'mywins.xml', xml, {replace: true});
},
function(errmsg){
console.log('---->'+ errmsg)
});
}
On Android it works, on iOS it saves only at the first start, at the following only adds to the array when you shoot out the photo, but the file does not overwrite.