#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
const PK_VERSION: &str = env!("CARGO_PKG_VERSION");
#[cfg(all(
any(feature = "tokio-runtime", feature = "smol-runtime"),
not(feature = "std")
))]
compile_error!("Enabling 'tokio-runtime' or 'smol-runtime' requires the 'std' feature.");
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::{
boxed::Box,
string::{String, ToString},
vec,
vec::Vec,
};
#[cfg(not(feature = "std"))]
extern crate core as std;
use std::cell::{Cell, RefCell};
use std::ops::Add;
use std::pin::Pin;
use std::task::Poll;
use std::time::Duration;
pub mod types;
use types::{Command, Operation, Role, Stage, Status};
#[doc(hidden)]
#[cfg(feature = "doc")]
pub mod doc_util;
mod util;
#[cfg_attr(docsrs, doc(cfg(feature = "embassy-runtime")))]
#[cfg(feature = "embassy-runtime")]
pub use util::async_adapters::embassy as embassy_adapter;
#[cfg_attr(docsrs, doc(cfg(all(feature = "std", feature = "smol-runtime"))))]
#[cfg(all(feature = "std", feature = "smol-runtime"))]
pub use util::async_adapters::smol as smol_adapter;
#[cfg(all(feature = "std", feature = "tokio-runtime"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "std", feature = "tokio-runtime"))))]
pub use util::async_adapters::tokio as tokio_adapter;
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub use util::{PkHashmapMethod, PkHashmapVariable, PkPromise, msg_id};
pub trait PkVariableAccessor {
fn get(&self, key: String) -> Option<Vec<u8>>;
fn set(&self, key: String, value: Vec<u8>) -> Result<(), String>;
}
pub trait Pollable {
fn poll(&self) -> std::task::Poll<Result<Option<Vec<u8>>, String>>;
}
pub trait PkMethodAccessor {
fn call(&self, key: String, param: Vec<u8>) -> Result<Pin<Box<dyn Pollable>>, String>;
}
pub trait PkInstant
where
Self: Sized,
{
fn now() -> Self;
fn elapsed(&self) -> Duration;
}
#[cfg(feature = "std")]
impl PkInstant for std::time::Instant {
fn now() -> Self {
std::time::Instant::now()
}
fn elapsed(&self) -> Duration {
std::time::Instant::elapsed(self)
}
}
#[cfg(feature = "embassy-time")]
#[cfg_attr(docsrs, doc(cfg(feature = "embassy-time")))]
#[derive(Clone, Copy, PartialOrd, PartialEq, Eq, Ord)]
pub struct EmbassyInstant(embassy_time::Instant);
#[cfg(feature = "embassy-time")]
impl EmbassyInstant {
fn into_inner(self) -> embassy_time::Instant {
self.0
}
}
#[cfg(feature = "embassy-time")]
impl Add<Duration> for EmbassyInstant {
type Output = EmbassyInstant;
fn add(self, rhs: Duration) -> Self::Output {
EmbassyInstant(self.0 + embassy_time::Duration::from_millis(rhs.as_millis() as u64))
}
}
#[cfg(feature = "embassy-time")]
impl From<embassy_time::Instant> for EmbassyInstant {
fn from(inst: embassy_time::Instant) -> Self {
EmbassyInstant(inst)
}
}
#[cfg(feature = "embassy-time")]
impl PkInstant for EmbassyInstant {
fn now() -> Self {
embassy_time::Instant::now().into()
}
fn elapsed(&self) -> core::time::Duration {
let now = embassy_time::Instant::now();
if now >= self.into_inner() {
(now - self.into_inner()).into()
} else {
core::time::Duration::from_secs(0)
}
}
}
#[derive(Clone)]
pub struct PkCommandConfig {
ack_timeout: Duration,
inter_command_timeout: Duration,
await_interval: Duration,
packet_limit: u64,
pk_version: &'static str,
}
impl PkCommandConfig {
pub fn default(packet_limit: u64) -> Self {
PkCommandConfig {
ack_timeout: Duration::from_millis(100),
inter_command_timeout: Duration::from_millis(500),
await_interval: Duration::from_millis(300),
packet_limit,
pk_version: PK_VERSION,
}
}
pub fn new(
ack_timeout: u64,
inter_command_timeout: u64,
await_interval: u64,
packet_limit: u64,
) -> Self {
PkCommandConfig {
ack_timeout: Duration::from_millis(ack_timeout),
inter_command_timeout: Duration::from_millis(inter_command_timeout),
await_interval: Duration::from_millis(await_interval),
packet_limit,
pk_version: PK_VERSION,
}
}
}
pub struct PkCommand<VA, MA, Instant>
where
VA: PkVariableAccessor,
MA: PkMethodAccessor,
Instant: PkInstant + Add<Duration, Output = Instant> + PartialOrd + Copy,
{
stage: Cell<Stage>,
status: Cell<Status>,
role: Cell<Role>,
last_sent_command: RefCell<Command>,
last_sent_msg_id: Cell<u16>,
last_received_msg_id: Cell<u16>,
data_param: RefCell<Vec<u8>>,
data_return: RefCell<Vec<u8>>,
sending_data_progress: Cell<u64>,
root_operation: Cell<Operation>,
root_object: RefCell<Option<String>>,
command_buffer: RefCell<Command>,
command_processed: Cell<bool>,
last_command_time: Cell<Instant>,
device_op_pending: Cell<bool>,
device_await_deadline: Cell<Option<Instant>>,
config: PkCommandConfig,
variable_accessor: VA,
method_accessor: MA,
pending_pollable: RefCell<Option<Pin<Box<dyn Pollable>>>>,
device_should_return: Cell<bool>, }
impl<
VA: PkVariableAccessor,
MA: PkMethodAccessor,
Instant: PkInstant + Add<Duration, Output = Instant> + PartialOrd + Copy,
> PkCommand<VA, MA, Instant>
{
pub fn incoming_command(&self, command_bytes: Vec<u8>) -> Result<(), &'static str> {
match Command::parse(&command_bytes) {
Ok(parsed_command) => {
self.command_buffer.replace(parsed_command);
self.command_processed.set(false);
self.last_command_time.replace(Instant::now());
Ok(())
}
Err(e) => Err(e),
}
}
fn slice_data(&self, role: Role) -> Result<(Vec<u8>, bool), &'static str> {
match role {
Role::Device => {
let data = self.data_return.borrow();
if data.is_empty() {
return Err("No return data to slice.");
}
let start = self.sending_data_progress.get() as usize;
let end =
std::cmp::min(start + (self.config.packet_limit - 14) as usize, data.len());
let is_last_packet = end == data.len();
self.sending_data_progress.set(end as u64);
Ok((data[start..end].to_vec(), is_last_packet))
}
Role::Host => {
let data = self.data_param.borrow();
if data.is_empty() {
return Err("No parameter data to slice.");
}
let start = self.sending_data_progress.get() as usize;
let end =
std::cmp::min(start + (self.config.packet_limit - 14) as usize, data.len());
let is_last_packet = end == data.len();
self.sending_data_progress.set(end as u64);
Ok((data[start..end].to_vec(), is_last_packet))
}
Role::Idle => Err("Cannot slice data in Idle role."),
}
}
pub fn poll(&self) -> Option<Command> {
let next_msg_id_for_send = || util::msg_id::increment(self.last_received_msg_id.get());
let send = move |command: Command| -> Option<Command> {
self.last_command_time.set(Instant::now());
self.last_sent_msg_id.set(command.msg_id);
self.last_sent_command.replace(command.clone());
self.status.set(Status::AwaitingAck);
Some(command)
};
let reset_transaction_state = || {
self.stage.set(Stage::Idle);
self.status.set(Status::Other);
self.role.set(Role::Idle);
self.data_param.replace(vec![]);
self.data_return.replace(vec![]);
self.sending_data_progress.set(0);
self.device_op_pending.set(false);
self.device_await_deadline.set(None);
self.pending_pollable.replace(None); self.device_should_return.set(false);
};
let ack = move |msg_id: u16, operation: Operation| -> Option<Command> {
self.last_command_time.set(Instant::now());
Some(Command {
msg_id,
operation: Operation::Acknowledge,
object: Some(operation.to_name().to_string()),
data: None,
})
};
let err = |msg: &'static str| -> Option<Command> {
self.status.set(Status::AwaitingErrAck);
let command = Command {
msg_id: 0,
operation: Operation::Error,
object: Some(String::from("ERROR")),
data: Some(msg.as_bytes().to_vec()),
};
self.last_command_time.set(Instant::now());
self.last_sent_msg_id.set(command.msg_id);
self.last_sent_command.replace(command.clone());
Some(command)
};
match self.command_processed.get() {
true => {
if self.stage.get() == Stage::Idle {
return None;
}
if self.stage.get() == Stage::Started
&& self.role.get() == Role::Host
&& self.status.get() != Status::AwaitingAck
{
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Start,
object: None,
data: None,
});
}
if self.role.get() == Role::Device
&& self.device_op_pending.get()
&& self.stage.get() == Stage::SendingResponse
{
if self.status.get() == Status::AwaitingAck {
} else if self.status.get() == Status::AwaitingErrAck {
} else {
let mut pollable_store = self.pending_pollable.borrow_mut();
if let Some(pinned_pollable) = pollable_store.as_mut() {
match pinned_pollable.as_mut().poll() {
Poll::Ready(result) => {
pollable_store.take(); self.device_op_pending.set(false);
self.device_await_deadline.set(None);
match result {
Ok(data_opt) => {
self.data_return.replace(data_opt.unwrap_or_default());
self.sending_data_progress.set(0);
let rturn_object_name =
if self.data_return.borrow().is_empty() {
String::from("EMPTY")
} else {
Operation::Invoke.to_name().to_string()
};
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Return,
object: Some(rturn_object_name),
data: None,
});
}
Err(_) => {
reset_transaction_state();
return err("INVOK operation failed");
}
}
}
Poll::Pending => {
if Instant::now()
>= self
.device_await_deadline
.get()
.unwrap_or(Instant::now())
{
self.device_await_deadline
.set(Some(Instant::now() + self.config.await_interval));
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Await,
object: None,
data: None,
});
}
}
}
} else {
reset_transaction_state();
return err("Internal: Device op pending but no pollable.");
}
}
} if self.device_should_return.get() {
self.sending_data_progress.set(0); self.device_should_return.set(false);
match self.root_operation.get() {
Operation::GetVersion => {
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Return,
object: Some(self.root_operation.get().to_name().to_string()),
data: None,
});
}
Operation::RequireVariable => {
if self.data_return.borrow().is_empty() {
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Return,
object: Some(String::from("EMPTY")),
data: None,
});
}
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Return,
object: Some(self.root_operation.get().to_name().to_string()),
data: None,
});
}
Operation::SendVariable => {
self.data_return.replace(vec![]); return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Return,
object: Some(Operation::Empty.to_name().to_string()),
data: None,
});
}
Operation::Invoke => {
}
_ => {
panic!("Not a root operation");
}
}
}
let elapsed_ms = self.last_command_time.get().elapsed();
match self.status.get() {
Status::AwaitingAck | Status::AwaitingErrAck => {
if elapsed_ms >= self.config.ack_timeout {
return Some(self.last_sent_command.borrow().clone());
}
}
_ => {
if self.stage.get() != Stage::Idle
&& !self.device_op_pending.get()
&& elapsed_ms >= self.config.inter_command_timeout
{
reset_transaction_state(); return err("Operation timed out");
}
}
}
}
false => {
self.command_processed.set(true);
self.last_received_msg_id
.set(self.command_buffer.borrow().msg_id); let recv = self.command_buffer.borrow();
if recv.operation == Operation::Error {
reset_transaction_state();
return ack(0, Operation::Error);
} else if self.status.get() == Status::AwaitingErrAck {
if recv.operation == Operation::Acknowledge
&& Some(String::from("ERROR")) == recv.object
{
self.status.set(Status::Other);
self.root_operation.set(Operation::Empty);
self.stage.set(Stage::Idle);
self.role.set(Role::Idle);
return None;
} else {
return err("Should be ACKNO ERROR");
}
}
match self.stage.get() {
Stage::Idle => {
if recv.operation != Operation::Start {
return err("not in a chain");
}
self.role.set(Role::Device);
self.stage.set(Stage::Started);
self.status.set(Status::Other); return ack(recv.msg_id, recv.operation);
}
Stage::Started => {
match self.role.get() {
Role::Host => {
if recv.operation == Operation::Acknowledge {
self.status.set(Status::Other);
self.stage.set(Stage::RootOperationAssigned);
return send(Command {
msg_id: next_msg_id_for_send(),
operation: self.root_operation.get(),
object: self.root_object.borrow().clone(),
data: None,
});
}
}
Role::Device => {
if recv.operation.is_root() {
self.root_operation.set(recv.operation);
if (recv.operation == Operation::RequireVariable
|| recv.operation == Operation::SendVariable
|| recv.operation == Operation::Invoke)
&& recv.object.is_none()
{
reset_transaction_state();
return err(
"Operation requires an object but none was provided.",
);
}
self.root_object.replace(recv.object.clone());
self.stage.set(Stage::RootOperationAssigned);
return ack(recv.msg_id, recv.operation);
} else {
return err("not a root operation");
}
}
_ => {
panic!("Role cannot be Idle if Stage is Started")
}
}
}
Stage::RootOperationAssigned => {
match self.role.get() {
Role::Host => {
if recv.operation == Operation::Acknowledge {
self.status.set(Status::Other);
self.stage.set(Stage::SendingParameter);
if self.data_param.borrow().is_empty() {
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Empty,
object: None,
data: None,
});
} else {
match self.slice_data(Role::Host) {
Ok((data_chunk, _is_last)) => {
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Data,
object: Some(
self.root_operation
.get()
.to_name()
.to_string(),
),
data: Some(data_chunk),
});
}
Err(e) => {
reset_transaction_state();
return err(e);
}
}
}
} else {
return err("Should be ACKNO");
}
}
Role::Device => {
if recv.operation == Operation::Empty {
self.stage.set(Stage::SendingParameter);
return ack(recv.msg_id, recv.operation);
} else if recv.operation == Operation::Data {
self.stage.set(Stage::SendingParameter);
{
self.data_param
.borrow_mut()
.append(&mut recv.data.as_ref().unwrap().clone());
}
return ack(recv.msg_id, recv.operation);
} else {
return err("Should be EMPTY or DATA");
}
}
_ => {
panic!("Role cannot be Idle if Stage is RootOperationAssigned")
}
}
}
Stage::SendingParameter => {
match self.role.get() {
Role::Host => {
if recv.operation != Operation::Acknowledge {
return err("Host expected ACKNO in SendingParameter stage");
}
self.status.set(Status::Other);
let last_sent_op;
{
last_sent_op = self.last_sent_command.borrow().operation;
}
match last_sent_op {
Operation::Empty => {
self.stage.set(Stage::ParameterSent);
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::EndTransaction,
object: None,
data: None,
});
}
Operation::Data => {
let param_data_len = self.data_param.borrow().len() as u64;
if self.sending_data_progress.get() < param_data_len {
let (data_chunk, _is_last) =
match self.slice_data(Role::Host) {
Ok(d) => d,
Err(e) => {
reset_transaction_state();
return err(e);
}
};
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Data,
object: Some(
self.root_operation.get().to_name().to_string(),
),
data: Some(data_chunk),
});
} else {
self.stage.set(Stage::ParameterSent);
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::EndTransaction,
object: None,
data: None,
});
}
}
_ => {
return err(
"Host received ACKNO for unexpected command in SendingParameter stage",
);
}
}
}
Role::Device => {
if recv.operation == Operation::Data {
if let Some(ref data_vec) = recv.data {
self.data_param.borrow_mut().extend_from_slice(data_vec);
}
return ack(recv.msg_id, recv.operation);
} else if recv.operation == Operation::EndTransaction {
self.stage.set(Stage::ParameterSent);
return ack(recv.msg_id, recv.operation);
} else {
return err(
"Device expected DATA or ENDTR in SendingParameter stage",
);
}
}
Role::Idle => {
panic!("Role cannot be Idle if Stage is SendingParameter")
}
}
}
Stage::ParameterSent => {
match self.role.get() {
Role::Host => match recv.operation {
Operation::Acknowledge => {
self.status.set(Status::Other); if Some(String::from("ENDTR")) == recv.object {
return send(Command {
msg_id: util::msg_id::increment(recv.msg_id),
operation: Operation::Query,
object: None,
data: None,
});
} else if Some(String::from("QUERY")) == recv.object {
return None;
} else {
return err(
"Host: Unexpected ACK object in ParameterSent stage",
);
}
}
Operation::Await => {
return ack(recv.msg_id, recv.operation);
}
Operation::Return => {
if Some(String::from("EMPTY")) == recv.object
|| Some(self.root_operation.get().to_name().to_string())
== recv.object
{
self.stage.set(Stage::SendingResponse);
return ack(recv.msg_id, recv.operation);
}
}
_ => {
return err("Should be ACKNO, AWAIT or RETURN");
}
},
Role::Device => {
if recv.operation == Operation::Query {
match self.root_operation.get() {
Operation::GetVersion => {
self.data_return.replace(
self.config.pk_version.as_bytes().to_vec(),
);
self.stage.set(Stage::SendingResponse);
}
Operation::RequireVariable => {
let key = match self
.root_object
.borrow()
.as_ref()
.cloned()
{
Some(k) => k,
None => {
reset_transaction_state();
return err(
"Internal: Missing object name for REQUV.",
);
}
};
self.data_return.replace(
self.variable_accessor.get(key).unwrap_or(vec![]),
);
self.stage.set(Stage::SendingResponse);
}
Operation::SendVariable => {
let key = match self
.root_object
.borrow()
.as_ref()
.cloned()
{
Some(k) => k,
None => {
reset_transaction_state();
return err(
"Internal: Missing object name for SENDV.",
);
}
};
self.data_return.replace(
if let Err(e) = self
.variable_accessor
.set(key, self.data_param.borrow().clone())
{
e.as_bytes().to_vec()
} else {
vec![]
},
);
self.stage.set(Stage::SendingResponse); }
Operation::Invoke => {
self.device_op_pending.set(true);
self.device_await_deadline.set(Some(
Instant::now() + self.config.await_interval,
));
let method_name = match self
.root_object
.borrow()
.as_ref()
.cloned()
{
Some(name) => name,
None => {
reset_transaction_state();
return err(
"Internal: Missing method name for INVOK",
);
}
};
match self
.method_accessor
.call(method_name, self.data_param.borrow().clone())
{
Ok(pollable) => {
self.pending_pollable.replace(Some(pollable));
}
Err(_) => {
reset_transaction_state();
return err(
"Failed to initiate INVOK operation",
);
}
}
}
_ => {
reset_transaction_state();
return err("Not a root operation");
}
}
self.stage.set(Stage::SendingResponse);
self.device_should_return.set(true);
return ack(recv.msg_id, recv.operation);
}
}
Role::Idle => {
panic!("Role cannot be Idle if Stage is ParameterSent")
}
}
}
Stage::SendingResponse => {
match self.role.get() {
Role::Host => {
if recv.operation == Operation::Data {
if let Some(ref data_vec) = recv.data {
self.data_return.borrow_mut().extend_from_slice(data_vec);
}
return ack(recv.msg_id, recv.operation);
} else if recv.operation == Operation::EndTransaction {
let endtr_ack = ack(recv.msg_id, recv.operation);
self.stage.set(Stage::Idle);
self.status.set(Status::Other); return endtr_ack;
} else {
return err(
"Host expected SDATA or ENDTR in SendingResponse stage",
);
}
}
Role::Device => {
if recv.operation != Operation::Acknowledge {
return err("Device expected ACKNO in SendingResponse stage");
}
self.status.set(Status::Other);
let last_sent_op;
{
last_sent_op = self.last_sent_command.borrow().operation;
}
match last_sent_op {
Operation::Return => {
let return_data_len =
self.data_return.borrow().len() as u64;
if return_data_len == 0 {
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::EndTransaction,
object: None,
data: None,
});
} else {
let (data_chunk, _) =
match self.slice_data(Role::Device) {
Ok(d) => d,
Err(e) => {
reset_transaction_state();
return err(e);
}
};
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Data,
object: Some(
self.root_operation.get().to_name().to_string(),
),
data: Some(data_chunk),
});
}
}
Operation::Data => {
if self.sending_data_progress.get()
< self.data_return.borrow().len() as u64
{
let (data_chunk, _) =
match self.slice_data(Role::Device) {
Ok(d) => d,
Err(e) => {
reset_transaction_state();
return err(e);
}
};
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::Data,
object: Some(
self.root_operation.get().to_name().to_string(),
),
data: Some(data_chunk),
});
} else {
return send(Command {
msg_id: next_msg_id_for_send(),
operation: Operation::EndTransaction,
object: None,
data: None,
});
}
}
Operation::EndTransaction => {
self.role.set(Role::Idle);
self.stage.set(Stage::Idle);
return None;
}
Operation::Await => {
return None;
}
_ => {
return err(
"Device received ACKNO for unexpected command in SendingResponse stage",
);
}
}
}
_ => {
panic!("Role cannot be Idle if Stage is SendingResponse")
}
}
}
}
}
}
None
}
pub fn perform(
&self,
operation: Operation,
object: Option<String>,
data: Option<Vec<u8>>,
) -> Result<(), &'static str> {
if operation.is_root()
&& self.stage.get() == Stage::Idle
&& self.status.get() == Status::Other
&& self.role.get() == Role::Idle
{
self.root_operation.set(operation);
self.root_object.replace(object);
self.data_param.replace(data.unwrap_or(vec![]));
self.role.set(Role::Host);
self.stage.set(Stage::Started);
self.status.set(Status::Other);
Ok(())
} else if !operation.is_root() {
Err("Cannot initiate a non-root operation")
} else {
Err("Cannot initiate an operation when the transaction is in progress")
}
}
fn reset_transaction_state(&self) {
self.stage.set(Stage::Idle);
self.status.set(Status::Other);
self.role.set(Role::Idle);
self.data_param.borrow_mut().clear();
self.data_return.borrow_mut().clear();
self.sending_data_progress.set(0);
self.device_op_pending.set(false);
self.device_await_deadline.set(None);
self.pending_pollable.borrow_mut().take(); }
pub fn is_complete(&self) -> bool {
self.stage.get() == Stage::Idle
}
pub fn get_return_data(&self) -> Option<Vec<u8>> {
if self.stage.get() == Stage::Idle && self.role.get() == Role::Host {
let data = self.data_return.borrow().clone();
self.reset_transaction_state();
if data.is_empty() {
None
} else {
Some(data.clone())
}
} else {
None }
}
pub fn wait_for_complete_and<F>(&self, callback: F) -> bool
where
F: FnOnce(Option<Vec<u8>>),
{
if self.stage.get() == Stage::Idle {
let data = self.data_return.borrow().clone();
self.reset_transaction_state();
callback(if data.is_empty() { None } else { Some(data) });
true
} else {
false
}
}
pub fn new(config: PkCommandConfig, variable_accessor: VA, method_accessor: MA) -> Self {
PkCommand {
stage: Cell::new(Stage::Idle),
status: Cell::new(Status::Other),
role: Cell::new(Role::Idle),
last_sent_command: RefCell::new(Command {
msg_id: 0,
operation: Operation::Empty,
object: None,
data: None,
}),
last_sent_msg_id: Cell::new(0),
last_received_msg_id: Cell::new(0),
data_param: RefCell::new(vec![]),
data_return: RefCell::new(vec![]),
sending_data_progress: Cell::new(0),
root_operation: Cell::new(Operation::Empty),
root_object: RefCell::new(None),
command_buffer: RefCell::new(Command {
msg_id: 0,
operation: Operation::Empty,
object: None,
data: None,
}),
command_processed: Cell::new(true),
last_command_time: Cell::new(Instant::now()),
device_op_pending: Cell::new(false),
device_await_deadline: Cell::new(None),
config,
variable_accessor,
method_accessor,
pending_pollable: RefCell::new(None),
device_should_return: Cell::new(false),
}
}
}