mod errors;
use std::{collections::BTreeMap, error::Error, ffi::CStr, sync::mpsc::channel, thread};
use {
libc::{c_char, c_int, c_uchar, size_t},
log,
memchr::memchr,
};
use kpal_plugin::{error_codes::*, Val};
use kpal_plugin::{ATTRIBUTE_PRE_INIT_FALSE, ATTRIBUTE_PRE_INIT_TRUE, INIT_PHASE, RUN_PHASE};
use super::{
messaging::{Receiver, Transmitter},
Plugin,
};
use crate::{
constants::*,
models::{Attribute, Model, Peripheral},
};
pub use errors::ExecutorError;
use errors::{
AdvancePhaseError, CountError, IdsError, InitError, NameError, PreInitError, SetValueError,
ValueError,
};
pub struct Executor {
pub plugin: Plugin,
pub rx: Receiver,
pub tx: Transmitter,
phase: i32,
}
impl Executor {
pub fn new(plugin: Plugin) -> Executor {
let (tx, rx) = channel();
let phase = INIT_PHASE;
Executor {
plugin,
rx,
tx,
phase,
}
}
pub fn run(mut self, mut peripheral: Peripheral) {
thread::spawn(move || -> Result<(), ExecutorError> {
log::info!("Spawning new thread for plugin: {:?}", self.plugin);
loop {
log::debug!("Checking for messages for plugin: {}", peripheral.id());
let msg = self.rx.recv().map_err(|e| {
ExecutorError::new(
"Failed to read from plugin's channel".to_string(),
500,
Some(Box::new(e)),
)
})?;
msg.handle(&mut self, &mut peripheral);
}
});
}
pub fn attribute_count(&self) -> Result<usize, ExecutorError> {
let mut count: usize = 0;
let result = unsafe {
(self.plugin.vtable.attribute_count)(self.plugin.plugin_data, &mut count as *mut size_t)
};
if result == PLUGIN_OK {
Ok(count)
} else {
Err(CountError("Could not determine the number of attributes".to_string()).into())
}
}
pub fn attribute_ids(&self) -> Result<Vec<usize>, ExecutorError> {
let num_attributes = self
.attribute_count()
.map_err(|_| IdsError("Could not determine the number of attributes".to_string()))?;
let mut ids = vec![0usize; num_attributes];
let result = unsafe {
(self.plugin.vtable.attribute_ids)(self.plugin.plugin_data, ids.as_mut_ptr(), ids.len())
};
if result == PLUGIN_OK {
Ok(ids)
} else {
Err(IdsError("Could not determine the attribute IDs".to_string()).into())
}
}
pub fn attribute_name(&self, id: size_t) -> Result<String, ExecutorError> {
let mut name = [0u8; ATTRIBUTE_NAME_BUFFER_LENGTH];
let result = unsafe {
(self.plugin.vtable.attribute_name)(
self.plugin.plugin_data,
id,
&mut name[0] as *mut c_uchar,
ATTRIBUTE_NAME_BUFFER_LENGTH,
)
};
if result == PLUGIN_OK {
let name = match memchr(0, &name)
.ok_or("could not find null byte")
.and_then(|null_byte| {
CStr::from_bytes_with_nul(&name[..=null_byte])
.map_err(|_| "could not convert name from C string")
})
.map(|name| name.to_string_lossy().into_owned())
{
Ok(name) => name,
Err(err) => {
log::error!("{}", err);
String::from("Unknown")
}
};
log::debug!("Received name: {:?}", name);
Ok(name)
} else if result == ATTRIBUTE_DOES_NOT_EXIST {
log::debug!("Attribute does not exist: {}", result);
let msg = unsafe {
self.error_message(result)
.unwrap_or_else(|_| String::from(""))
};
Err(NameError::DoesNotExist(msg).into())
} else {
log::error!(
"Received error code while getting attribute name: {}",
result
);
let msg = unsafe {
self.error_message(result)
.unwrap_or_else(|_| String::from(""))
};
Err(NameError::Failure(msg).into())
}
}
pub fn attribute_pre_init(&self, id: size_t) -> Result<bool, ExecutorError> {
let mut pre_init: c_char = 0;
let result = unsafe {
(self.plugin.vtable.attribute_pre_init)(
self.plugin.plugin_data,
id,
&mut pre_init as *mut c_char,
)
};
if result == PLUGIN_OK {
log::debug!("Received pre-init status: {}", pre_init);
if pre_init == ATTRIBUTE_PRE_INIT_TRUE {
Ok(true)
} else if pre_init == ATTRIBUTE_PRE_INIT_FALSE {
Ok(false)
} else {
Err(PreInitError::Failure(
"Could not determine error message from plugin".to_string(),
)
.into())
}
} else if result == ATTRIBUTE_DOES_NOT_EXIST {
log::debug!("Attribute does not exist: {}", result);
let msg = unsafe {
self.error_message(result).unwrap_or_else(|_| {
String::from("Could not determine error message from plugin")
})
};
Err(PreInitError::DoesNotExist(msg).into())
} else {
log::error!(
"Received error code while determining whether the attribute is pre-init: {}",
result
);
let msg = unsafe {
self.error_message(result).unwrap_or_else(|_| {
String::from("Could not determine error message from plugin")
})
};
Err(PreInitError::Failure(msg).into())
}
}
pub fn attribute_value(&self, id: size_t, value: &mut Val) -> Result<(), ExecutorError> {
let result = unsafe {
(self.plugin.vtable.attribute_value)(
self.plugin.plugin_data,
id,
value as *mut Val,
self.phase,
)
};
if result == PLUGIN_OK {
log::debug!("Received value: {:?}", value);
Ok(())
} else if result == ATTRIBUTE_DOES_NOT_EXIST {
log::debug!("Attribute does not exist: {}", result);
let msg = unsafe {
self.error_message(result)
.unwrap_or_else(|_| String::from(""))
};
Err(ValueError::DoesNotExist(msg).into())
} else {
log::error!(
"Received error code while fetching attribute value: {}",
result
);
let msg = unsafe {
self.error_message(result)
.unwrap_or_else(|_| String::from(""))
};
Err(ValueError::Failure(msg).into())
}
}
pub fn set_attribute_value(&self, id: size_t, value: &Val) -> Result<(), ExecutorError> {
let result = unsafe {
(self.plugin.vtable.set_attribute_value)(
self.plugin.plugin_data,
id,
value as *const Val,
self.phase,
)
};
if result == PLUGIN_OK {
log::debug!("Set value: {:?}", value);
Ok(())
} else if result == ATTRIBUTE_DOES_NOT_EXIST {
log::debug!("Attribute does not exist: {}", id);
let msg = unsafe {
self.error_message(result)
.unwrap_or_else(|_| String::from(""))
};
Err(SetValueError::DoesNotExist(msg).into())
} else if result == ATTRIBUTE_IS_NOT_SETTABLE {
log::debug!("Attribute is not settable: {}", id);
let msg = unsafe {
self.error_message(result)
.unwrap_or_else(|_| String::from(""))
};
Err(SetValueError::NotSettable(msg).into())
} else {
log::error!(
"Received error code while setting attribute value: {}",
result
);
let msg = unsafe {
self.error_message(result)
.unwrap_or_else(|_| String::from(""))
};
Err(SetValueError::Failure(msg).into())
}
}
unsafe fn error_message(&self, error_code: c_int) -> Result<String, ExecutorError> {
let msg_p = (self.plugin.vtable.error_message_ns)(error_code) as *const c_char;
let msg = if msg_p.is_null() {
return Err(ExecutorError::new(
"An unrecognized error code was provided to the plugin".to_string(),
500,
None,
));
} else {
CStr::from_ptr(msg_p).to_str()?.to_owned()
};
Ok(msg)
}
pub fn advance(&mut self) -> Result<i32, ExecutorError> {
if self.phase == INIT_PHASE {
self.phase = RUN_PHASE;
return Ok(self.phase);
}
Err(AdvancePhaseError(self.phase).into())
}
pub fn discover_attributes(&mut self) -> Option<BTreeMap<usize, Attribute>> {
let ids = match self.attribute_ids() {
Ok(ids) => ids,
Err(e) => {
log::error!("Could not discover plugin attributes: {:?}", e);
return None;
}
};
let mut value = Val::Int(0);
let mut attrs: BTreeMap<usize, Attribute> = BTreeMap::new();
for id in ids {
match self.attribute_value(id, &mut value) {
Ok(_) => (),
Err(err) => {
log::error!("Could not discover value of attribute {}: {:?}", id, err);
continue;
}
};
let name = match self.attribute_name(id) {
Ok(name) => name,
Err(err) => {
log::error!("Could not discover name of attribute {}: {:?}", id, err);
continue;
}
};
let pre_init = match self.attribute_pre_init(id) {
Ok(pre_init) => pre_init,
Err(err) => {
log::error!(
"Could not discover pre_init status of attribute {}: {:?}",
id,
err
);
continue;
}
};
let new_attr = match Attribute::new(value.clone(), id, name, pre_init) {
Ok(new_attr) => new_attr,
Err(err) => {
log::error!("Could not create new attribute: {:?}", err);
continue;
}
};
attrs.insert(id, new_attr);
}
if attrs.is_empty() {
None
} else {
Some(attrs)
}
}
pub fn init(&self) -> Result<(), ExecutorError> {
let result = unsafe { (self.plugin.vtable.plugin_init)(self.plugin.plugin_data) };
if result == PLUGIN_OK {
log::debug!("Plugin's initialzation routine ran successfully.");
Ok(())
} else {
log::error!(
"Received error code while initialzing the plugin: {}",
result
);
let msg = unsafe {
self.error_message(result)
.unwrap_or_else(|_| String::from(""))
};
Err(InitError(msg).into())
}
}
pub fn sync(&mut self, peripheral: &Peripheral) -> Result<(), ExecutorError> {
for attr in peripheral.attributes().values() {
let value = attr.to_value()?;
let val = value.as_val();
if let Err(err) = self.set_attribute_value(attr.id(), &val) {
println!("{:?}", err.source());
let source = match err.source() {
Some(source) => source,
None => return Err(err),
};
let side: &SetValueError = match source.downcast_ref() {
Some(side) => side,
None => return Err(err),
};
match side {
SetValueError::NotSettable(_) => {
log::debug!("Skipping synchronization of attribute: {}", attr.id());
continue;
}
_ => return Err(err),
}
};
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::boxed::Box;
use libc::{c_int, c_uchar, size_t};
use kpal_plugin::{Phase, Plugin, PluginData, VTable, Val};
use crate::models::Peripheral as ModelPeripheral;
type AttributeName = extern "C" fn(*const PluginData, size_t, *mut c_uchar, size_t) -> c_int;
type AttributeValue = extern "C" fn(*const PluginData, size_t, *mut Val, Phase) -> c_int;
#[test]
fn test_advance() {
let (plugin, _) = set_up();
let mut executor = Executor::new(plugin);
assert_eq!(INIT_PHASE, executor.phase);
let mut result = executor.advance();
assert!(result.is_ok());
assert_eq!(RUN_PHASE, executor.phase);
result = executor.advance();
assert!(result.is_err());
assert_eq!(RUN_PHASE, executor.phase);
}
#[test]
fn test_error_message() {
let (plugin, _) = set_up();
let executor = Executor::new(plugin);
let msg = unsafe { executor.error_message(0) };
assert_eq!("foo", msg.unwrap());
}
#[test]
fn test_attribute_count() {
let (plugin, _) = set_up();
let executor = Executor::new(plugin);
let count = if let Ok(count) = executor.attribute_count() {
count
} else {
panic!("Could not obtain attribute count")
};
assert_eq!(1, count);
}
#[test]
fn test_attribute_ids() {
let (plugin, _) = set_up();
let executor = Executor::new(plugin);
let ids = if let Ok(ids) = executor.attribute_ids() {
ids
} else {
panic!("Could not obtain attribute ids")
};
assert_eq!(1, ids.len());
assert_eq!(0, ids[0]);
}
#[test]
fn test_attribute_name() {
let (mut plugin, _) = set_up();
let cases: Vec<(Result<String, ExecutorError>, AttributeName)> = vec![
(Ok(String::from("")), attribute_name_ok),
(
Err(NameError::DoesNotExist(String::from("foo")).into()),
attribute_name_does_not_exist,
),
(
Err(NameError::Failure(String::from("foo")).into()),
attribute_name_failure,
),
];
let mut result: Result<String, ExecutorError>;
let mut executor: Executor;
for (expected, case) in cases {
plugin.vtable.attribute_name = case;
executor = Executor::new(plugin.clone());
result = executor.attribute_name(0);
match (expected, result) {
(Ok(exp), Ok(res)) => assert_eq!(exp, res),
(Err(exp), Err(res)) => assert_eq!(exp, res),
_ => panic!("Result types differ"),
}
}
tear_down(plugin);
}
#[test]
fn test_attribute_value() {
let (mut plugin, _) = set_up();
let cases: Vec<(Result<(), ExecutorError>, AttributeValue)> = vec![
(Ok(()), attribute_value_ok),
(
Err(ValueError::DoesNotExist(String::from("foo")).into()),
attribute_value_does_not_exist,
),
(
Err(ValueError::Failure(String::from("foo")).into()),
attribute_value_failure,
),
];
let mut executor: Executor;
let mut value = Val::Int(0);
let mut result: Result<(), ExecutorError>;
for (expected, case) in cases {
plugin.vtable.attribute_value = case;
executor = Executor::new(plugin.clone());
result = executor.attribute_value(0, &mut value);
assert_eq!(expected, result);
}
tear_down(plugin);
}
#[test]
fn test_discover_attributes() {
let (plugin, _) = set_up();
let mut executor = Executor::new(plugin);
let attribute = Attribute::Int {
id: 0,
name: String::from("bar"),
pre_init: true,
value: 42,
};
let attrs = executor.discover_attributes().unwrap();
assert_eq!(&attribute, attrs.get(&0).unwrap());
}
fn set_up() -> (Plugin, ModelPeripheral) {
let plugin_data = Box::into_raw(Box::new(MockPluginData {})) as *mut PluginData;
let vtable = VTable {
plugin_free: def_peripheral_free,
plugin_init: def_plugin_init,
error_message_ns: def_error_message,
attribute_count: def_attribute_count,
attribute_ids: def_attribute_ids,
attribute_name: def_attribute_name,
attribute_pre_init: def_attribute_pre_init,
attribute_value: def_attribute_value,
set_attribute_value: def_set_attribute_value,
};
let plugin = Plugin {
plugin_data,
vtable,
};
let model: ModelPeripheral =
serde_json::from_str(r#"{"name":"foo","library_id":0}"#).unwrap();
(plugin, model)
}
fn tear_down(plugin: Plugin) {
unsafe { Box::from_raw(plugin.plugin_data) };
}
struct MockPluginData {}
extern "C" fn def_peripheral_free(_: *mut PluginData) {}
extern "C" fn def_plugin_init(_: *mut PluginData) -> c_int {
0
}
extern "C" fn def_error_message(_: c_int) -> *const c_uchar {
b"foo\0" as *const c_uchar
}
extern "C" fn def_attribute_count(_: *const PluginData, count: *mut size_t) -> c_int {
unsafe { *count = 1 };
PLUGIN_OK
}
extern "C" fn def_attribute_ids(
_: *const PluginData,
buffer: *mut size_t,
_length: size_t,
) -> c_int {
unsafe {
let ids: &[usize] = &[0usize];
let buffer = std::slice::from_raw_parts_mut(buffer, 1);
buffer[0..1].copy_from_slice(ids);
};
PLUGIN_OK
}
extern "C" fn def_attribute_name(
_: *const PluginData,
id: size_t,
buffer: *mut c_uchar,
_: size_t,
) -> c_int {
if id == 0 {
unsafe {
let string: &[u8] = b"bar\0";
let buffer = std::slice::from_raw_parts_mut(buffer, ATTRIBUTE_NAME_BUFFER_LENGTH);
buffer[0..4].copy_from_slice(string);
};
PLUGIN_OK
} else {
ATTRIBUTE_DOES_NOT_EXIST
}
}
extern "C" fn def_attribute_pre_init(_: *const PluginData, _: size_t, _: *mut c_char) -> c_int {
PLUGIN_OK
}
extern "C" fn def_attribute_value(
_: *const PluginData,
id: size_t,
value: *mut Val,
_: Phase,
) -> c_int {
if id == 0 {
unsafe { *value = Val::Int(42) };
PLUGIN_OK
} else {
ATTRIBUTE_DOES_NOT_EXIST
}
}
extern "C" fn def_set_attribute_value(
_: *mut PluginData,
_: size_t,
_: *const Val,
_: Phase,
) -> c_int {
0
}
extern "C" fn attribute_name_ok(
_: *const PluginData,
_: size_t,
_: *mut c_uchar,
_: size_t,
) -> c_int {
PLUGIN_OK
}
extern "C" fn attribute_name_does_not_exist(
_: *const PluginData,
_: size_t,
_: *mut c_uchar,
_: size_t,
) -> c_int {
ATTRIBUTE_DOES_NOT_EXIST
}
extern "C" fn attribute_name_failure(
_: *const PluginData,
_: size_t,
_: *mut c_uchar,
_: size_t,
) -> c_int {
999
}
extern "C" fn attribute_value_ok(
_: *const PluginData,
_: size_t,
_: *mut Val,
_: Phase,
) -> c_int {
PLUGIN_OK
}
extern "C" fn attribute_value_does_not_exist(
_: *const PluginData,
_: size_t,
_: *mut Val,
_: Phase,
) -> c_int {
ATTRIBUTE_DOES_NOT_EXIST
}
extern "C" fn attribute_value_failure(
_: *const PluginData,
_: size_t,
_: *mut Val,
_: Phase,
) -> c_int {
999
}
}