#![cfg(target_os = "windows")]
use std::convert::TryInto;
use std::default::Default;
use std::error::Error;
use std::ffi::CStr;
use std::fmt::Display;
use std::fmt;
use std::os::windows::raw::HANDLE;
use std::ptr::null;
use std::slice::from_raw_parts;
use std::time::Duration;
use libc::c_char;
use libc::c_void;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::handleapi::CloseHandle;
use winapi::um::minwinbase::LPSECURITY_ATTRIBUTES;
use winapi::um::synchapi::{CreateEventW,WaitForSingleObject,ResetEvent};
use serde::{Serialize, Deserialize};
const DATA_EVENT_NAME: &'static str = "Local\\IRSDKDataValidEvent";
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct Header {
pub version: i32,
pub status: i32,
pub tick_rate: i32,
pub session_info_version: i32,
pub session_info_length: i32,
pub session_info_offset: i32,
pub n_vars: i32,
pub header_offset: i32,
pub n_buffers: i32,
pub buffer_length: i32,
pub padding: [u32; 2],
buffers: [ValueBuffer; 4],
}
pub struct Blocking {
origin: *const c_void,
values: Vec<ValueHeader>,
header: Header,
event_handle: HANDLE
}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
struct ValueBuffer {
pub ticks: i32,
pub offset: i32,
pub padding: [u32; 2],
}
#[derive(Clone)]
#[repr(C)]
struct ValueHeader {
pub value_type: i32,
pub offset: i32,
pub count: i32,
pub count_as_time: bool,
_pad: [u8; 3],
_name: [c_char; ValueHeader::MAX_VAR_NAME_LENGTH],
_description: [c_char; ValueHeader::MAX_VAR_DESCRIPTION_LENGTH],
_unit: [c_char; ValueHeader::MAX_VAR_NAME_LENGTH],
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ValueDescription {
pub value: Value,
pub count: usize,
pub count_as_time: bool,
pub name: String,
pub description: String,
pub unit: String
}
#[derive(Debug, Default)]
pub struct Sample {
tick: i32,
buffer: Vec<u8>,
values: Vec<ValueHeader>
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub enum Value {
CHAR(u8),
BOOL(bool),
INT(i32),
BITS(u32),
FLOAT(f32),
DOUBLE(f64),
UNKNOWN(()),
IntVec(Vec<i32>),
FloatVec(Vec<f32>),
BoolVec(Vec<bool>)
}
impl From<i32> for Value {
fn from(v: i32) -> Value {
match v {
0 => Value::CHAR(0x0),
1 => Value::BOOL(false),
2 => Value::INT(0),
3 => Value::BITS(0x00),
4 => Value::FLOAT(0.0),
5 => Value::DOUBLE(0.0),
_ => Value::UNKNOWN(())
}
}
}
impl Value {
pub fn size(&self) -> usize {
match self {
Self::CHAR(_) | Self::BOOL(_) | Self::BoolVec(_) => 1,
Self::INT(_) | Self::BITS(_) | Self::FLOAT(_) | Self::IntVec(_) | Self::FloatVec(_) => 4,
Self::DOUBLE(_) => 8,
Self::UNKNOWN(_) => 1
}
}
}
impl TryInto<i32> for Value {
type Error = &'static str;
fn try_into(self) -> Result<i32, Self::Error> {
match self {
Self::INT(n) => Ok(n),
_ => Err("Value is not a signed 4-byte integer")
}
}
}
impl TryInto<u32> for Value {
type Error = &'static str;
fn try_into(self) -> Result<u32, Self::Error> {
match self {
Self::INT(n) => Ok(n as u32),
Self::BITS(n) => Ok(n),
_ => Err("Value is not a 4-byte integer")
}
}
}
impl TryInto<f32> for Value {
type Error = &'static str;
fn try_into(self) -> Result<f32, Self::Error> {
match self {
Self::FLOAT(n) => Ok(n),
_ => Err("Value is not a float")
}
}
}
impl TryInto<f64> for Value {
type Error = &'static str;
fn try_into(self) -> Result<f64, Self::Error> {
match self {
Self::DOUBLE(n) => Ok(n),
Self::FLOAT(f) => Ok(f as f64),
_ => Err("Value is not a float or double")
}
}
}
impl Into<bool> for Value {
fn into(self) -> bool {
match self {
Self::BOOL(b) => b,
_ => false
}
}
}
impl Into<Vec<bool>> for Value {
fn into(self) -> Vec<bool> {
match self {
Self::BoolVec(b) => b,
_ => vec![false]
}
}
}
impl ValueHeader {
const MAX_VAR_NAME_LENGTH: usize = 32;
const MAX_VAR_DESCRIPTION_LENGTH: usize = 64;
pub fn name(&self) -> String {
let name = unsafe { CStr::from_ptr(self._name.as_ptr()) };
name.to_string_lossy().to_string()
}
pub fn description(&self) -> String {
let description = unsafe { CStr::from_ptr(self._description.as_ptr()) };
description.to_string_lossy().to_string()
}
pub fn unit(&self) -> String {
let unit = unsafe { CStr::from_ptr(self._unit.as_ptr()) };
unit.to_string_lossy().to_string()
}
}
struct VarDescription([c_char; ValueHeader::MAX_VAR_DESCRIPTION_LENGTH]);
impl Clone for VarDescription {
fn clone(&self) -> Self {
let mut new = VarDescription([0; ValueHeader::MAX_VAR_DESCRIPTION_LENGTH]);
for i in 1 .. ValueHeader::MAX_VAR_DESCRIPTION_LENGTH {
new.0[i] = self.0[i];
}
new
}
}
impl Default for ValueHeader {
fn default() -> Self {
ValueHeader {
value_type: 0,
offset: 0,
count: 0,
count_as_time: false,
_pad: [0; 3],
_name: [0; ValueHeader::MAX_VAR_NAME_LENGTH],
_unit: [0; ValueHeader::MAX_VAR_NAME_LENGTH],
_description: [0; ValueHeader::MAX_VAR_DESCRIPTION_LENGTH],
}
}
}
impl fmt::Debug for ValueHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ValueHeader(name=\"{}\", type={}, count={}, offset={})",
self.name(),
self.value_type,
self.count,
self.offset
)
}
}
impl Header {
fn latest_buffer(&self) -> (i32, ValueBuffer) {
let mut latest_tick: i32 = 0;
let mut buffer = self.buffers[0];
for b in self.buffers.iter() {
if b.ticks > latest_tick {
buffer = *b;
latest_tick = b.ticks;
}
}
return (latest_tick, buffer);
}
fn var_buffer(&self, lb: ValueBuffer, from_loc: *const c_void) -> &[u8] {
let sz = self.buffer_length as usize;
let buffer_loc = from_loc as usize + lb.offset as usize;
unsafe { from_raw_parts(buffer_loc as *const u8, sz) }
}
fn get_var_header(&self, from_loc: *const c_void) -> &[ValueHeader] {
let n_vars = self.n_vars as usize;
let header_loc = from_loc as usize + self.header_offset as usize;
let content = unsafe { from_raw_parts(header_loc as *const ValueHeader, n_vars) };
content.clone()
}
pub fn telemetry(
&self,
from_loc: *const c_void,
) -> Result<Sample, Box<dyn std::error::Error>> {
let (tick, vbh) = self.latest_buffer();
let value_buffer = self.var_buffer(vbh, from_loc);
let value_header = self.get_var_header(from_loc);
Ok(Sample::new(tick, value_header.to_vec(), value_buffer.to_vec()))
}
}
impl Sample {
fn new(tick: i32, header: Vec<ValueHeader>, buffer: Vec<u8>) -> Self {
Sample {
tick: tick,
values: header,
buffer: buffer,
}
}
fn header_for(&self, name: &'static str) -> Option<ValueHeader> {
for v in self.values.iter() {
if v.name() == name {
return Some(v.clone());
}
}
None
}
pub fn has(&self, name: &'static str) -> bool {
match self.header_for(name) {
Some(_) => true,
None => false
}
}
pub fn all(&self) -> Vec<ValueDescription> {
let r = self.values.iter().map( |v| {
let val = self.value(v);
ValueDescription{
name: v.name().to_owned(),
description: v.description().to_owned(),
unit: v.unit().to_owned(),
count: v.count as usize,
count_as_time: v.count_as_time,
value: val
}
});
r.collect::<Vec<ValueDescription>>()
}
pub fn get(&self, name: &'static str) -> Result<Value, String> {
match self.header_for(name) {
None => Err(format!("No value '{}' found", name)),
Some(vh) => Ok(self.value(&vh))
}
}
fn value(&self, vh: &ValueHeader) -> Value {
let vs = vh.offset as usize;
let vt = Value::from(vh.value_type);
let vz = vt.size();
let ve = vs + vz;
let vc = vh.count as usize;
let raw_val = &self.buffer[vs..ve];
let v: Value;
v = match vt {
Value::INT(_) => {
if vc == 1 {
Value::INT( i32::from_le_bytes( raw_val.try_into().unwrap() ))
} else {
let mut values: Vec<i32> = Vec::with_capacity(vc);
for i in 0..vc-1 {
values.push(i32::from_le_bytes( self.buffer[vs+vz*i .. vs+vz*(i+1) ].try_into().unwrap() ));
}
Value::IntVec(values)
}
},
Value::FLOAT(_) => {
if vc == 1 {
Value::FLOAT( f32::from_le_bytes( raw_val.try_into().unwrap() ))
} else {
let mut values: Vec<f32> = Vec::with_capacity(vc);
for i in 0 .. vc-1 {
values.push(f32::from_le_bytes( self.buffer[vs+vz*i .. vs+vz*(i+1) ].try_into().unwrap() ));
}
Value::FloatVec(values)
}
},
Value::DOUBLE(_) => Value::DOUBLE( f64::from_le_bytes( raw_val.try_into().unwrap() )),
Value::BITS(_) => Value::BITS( u32::from_le_bytes( raw_val.try_into().unwrap() )),
Value::CHAR(_) => Value::CHAR(raw_val[0] as u8),
Value::BOOL(_) => {
if vc == 1 {
Value::BOOL(raw_val[0] > 0)
} else {
let mut values: Vec<bool> = Vec::with_capacity(vc);
for i in 0..vc-1 {
values.push(self.buffer[vs + i] > 0);
}
Value::BoolVec(values)
}
}
_ => unimplemented!()
};
v
}
}
#[derive(Debug)]
pub enum TelemetryError {
ABANDONED,
TIMEOUT(usize),
UNKNOWN(u32)
}
impl Display for TelemetryError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ABANDONED => write!(f, "{}", "Abandoned"),
Self::TIMEOUT(ms) => write!(f, "Timeout after {}ms", ms),
Self::UNKNOWN(v) => write!(f, "Unknown error code = {:x?}", v)
}
}
}
impl Error for TelemetryError {
}
impl Blocking {
pub fn new(location: *const c_void, head: Header) -> std::io::Result<Self> {
let values = head.get_var_header(location).to_vec();
let mut event_name: Vec<u16> = DATA_EVENT_NAME.encode_utf16().collect();
event_name.push(0);
let sc: LPSECURITY_ATTRIBUTES = unsafe { std::mem::zeroed() };
let handle: HANDLE = unsafe { CreateEventW(sc, 0, 0, event_name.as_ptr()) };
if null() == handle {
let errno: i32 = unsafe { GetLastError() as i32 };
return Err(std::io::Error::from_raw_os_error(errno));
}
Ok(Blocking{
origin: location,
header: head,
values: values,
event_handle: handle
})
}
pub fn close(&self) -> std::io::Result<()> {
if self.event_handle.is_null() {
return Ok(());
}
let succ = unsafe { CloseHandle(self.event_handle) };
if succ == 0 {
let err: i32 = unsafe { GetLastError() as i32 };
return Err(std::io::Error::from_raw_os_error(err));
}
if self.origin.is_null() {
return Ok(());
}
let succ = unsafe { CloseHandle(self.origin as HANDLE) };
if succ == 0 {
let err: i32 = unsafe { GetLastError() as i32 };
Err(std::io::Error::from_raw_os_error(err))
} else {
Ok(())
}
}
pub fn sample(&self, timeout: Duration) -> Result<Sample, Box<dyn Error>> {
let wait_time: u32 = match timeout.as_millis().try_into() {
Ok(v) => v,
Err(e) => return Err(Box::new(e))
};
let signal = unsafe { WaitForSingleObject(self.event_handle, wait_time) };
match signal {
0x80 => Err(Box::new(TelemetryError::ABANDONED)),
0x102 => Err(Box::new(TelemetryError::TIMEOUT(wait_time as usize))),
0xFFFFFFFF => {
let errno = unsafe { GetLastError() as i32 };
Err(Box::new(std::io::Error::from_raw_os_error(errno)))
},
0x00 => {
unsafe { ResetEvent(self.event_handle) };
self.header.telemetry(self.origin)
}
_ => Err(Box::new(TelemetryError::UNKNOWN(signal as u32)))
}
}
}