pub mod all_tags;
pub mod errors;
use crate::client::LabjackClient;
use crate::client::LabjackInteractions;
use crate::helpers::bit_manipulation::{be_bytes_to_u16_array, u8_to_u16_vec};
use crate::modbus_feedback::mbfb::ModbusFeedbackFrame;
use crate::Error;
use crate::Result;
use bytes::{Buf, Bytes, BytesMut};
use derive_builder::Builder;
use enum_dispatch::enum_dispatch;
use std::cmp;
use std::marker::PhantomData;
use std::str;
use tokio::time::timeout;
use tokio_modbus::client::Writer;
use tokio_modbus::prelude::Reader;
pub struct CanRead;
pub struct CanWrite;
pub struct CannotRead;
pub struct CannotWrite;
pub struct LabjackTag<T, R, W> {
pub address: u16,
_phantom_data: PhantomData<(T, R, W)>, }
impl<T, R, W> LabjackTag<T, R, W> {
pub const fn new(address: u16) -> LabjackTag<T, R, W> {
LabjackTag::<T, R, W> {
address,
_phantom_data: PhantomData,
}
}
}
impl<W> LabjackTag<u64, CanRead, W> {
pub async fn read(self, client: &mut LabjackClient) -> Result<u64> {
let result = timeout(
client.command_response_timeout,
client.context.read_input_registers(self.address, 4),
)
.await??;
match result {
Ok(data) => {
Ok((u64::from(data[0]) << 48)
| (u64::from(data[1]) << 32)
| (u64::from(data[2]) << 16)
| u64::from(data[3]))
}
Err(e) => Err(client.detailed_error_from_exception_code(e).await),
}
}
}
impl<R> LabjackTag<f32, R, CanWrite> {
pub async fn write(self, client: &mut LabjackClient, val: f32) -> Result<()> {
let result = timeout(
client.command_response_timeout,
client
.context
.write_multiple_registers(self.address, &be_bytes_to_u16_array(val.to_be_bytes())),
)
.await??;
match result {
Ok(res) => Ok(res),
Err(e) => Err(client.detailed_error_from_exception_code(e).await),
}
}
}
impl<W> LabjackTag<f32, CanRead, W> {
pub async fn read(self, client: &mut LabjackClient) -> Result<f32> {
let result = timeout(
client.command_response_timeout,
client.context.read_input_registers(self.address, 2),
)
.await??;
match result {
Ok(data) => {
let combined_value = (u32::from(data[0]) << 16) | u32::from(data[1]);
Ok(f32::from_bits(combined_value))
}
Err(e) => Err(client.detailed_error_from_exception_code(e).await),
}
}
}
impl<R> LabjackTag<i32, R, CanWrite> {
pub async fn write(self, client: &mut LabjackClient, val: i32) -> Result<()> {
let result = timeout(
client.command_response_timeout,
client
.context
.write_multiple_registers(self.address, &be_bytes_to_u16_array(val.to_be_bytes())),
)
.await??;
match result {
Ok(res) => Ok(res),
Err(e) => Err(client.detailed_error_from_exception_code(e).await),
}
}
}
impl<W> LabjackTag<i32, CanRead, W> {
pub async fn read(self, client: &mut LabjackClient) -> Result<i32> {
let result = timeout(
client.command_response_timeout,
client.context.read_input_registers(self.address, 2),
)
.await??;
match result {
Ok(data) => {
let combined_value = (u32::from(data[0]) << 16) | u32::from(data[1]);
Ok(i32::from_be_bytes(combined_value.to_be_bytes()))
}
Err(e) => Err(client.detailed_error_from_exception_code(e).await),
}
}
}
impl<R> LabjackTag<u32, R, CanWrite> {
pub async fn write(self, client: &mut LabjackClient, val: u32) -> Result<()> {
let result = timeout(
client.command_response_timeout,
client
.context
.write_multiple_registers(self.address, &be_bytes_to_u16_array(val.to_be_bytes())),
)
.await??;
match result {
Ok(res) => Ok(res),
Err(e) => Err(client.detailed_error_from_exception_code(e).await),
}
}
}
impl<W> LabjackTag<u32, CanRead, W> {
pub async fn read(self, client: &mut LabjackClient) -> Result<u32> {
let result = timeout(
client.command_response_timeout,
client.context.read_input_registers(self.address, 2),
)
.await??;
match result {
Ok(data) => {
Ok((u32::from(data[0]) << 16) | u32::from(data[1]))
}
Err(e) => Err(client.detailed_error_from_exception_code(e).await),
}
}
}
impl<W> LabjackTag<u16, CanRead, W> {
pub async fn read(self, client: &mut LabjackClient) -> Result<u16> {
let result = timeout(
client.command_response_timeout,
client.context.read_input_registers(self.address, 1),
)
.await??;
match result {
Ok(data) => Ok(data[0]),
Err(e) => Err(client.detailed_error_from_exception_code(e).await),
}
}
}
impl<R> LabjackTag<u16, R, CanWrite> {
pub async fn write(self, client: &mut LabjackClient, val: u16) -> Result<()> {
let result = timeout(
client.command_response_timeout,
client.context.write_single_register(self.address, val),
)
.await??;
match result {
Ok(res) => Ok(res),
Err(e) => Err(client.detailed_error_from_exception_code(e).await),
}
}
}
impl<W> LabjackTag<Bytes, CanRead, W> {
pub async fn read(self, client: &mut LabjackClient, num_bytes: u32) -> Result<Bytes> {
const MAX_BYTES_PER_CALL: u16 = 1020;
const MAX_REGISTERS: u8 = 255;
let mut total_bytes_to_read = num_bytes;
let mut data_bytes = BytesMut::with_capacity(num_bytes as usize);
while total_bytes_to_read > 0 {
let cur_num_bytes_to_read =
cmp::min(total_bytes_to_read, MAX_BYTES_PER_CALL.into()) as u16;
let mut total_registers_to_read = (cur_num_bytes_to_read + 1) / 2;
let mut addresses: Vec<u16> = Vec::new();
let mut register_counts: Vec<u8> = Vec::new();
while total_registers_to_read > 0 {
let cur_num_registers_to_read =
cmp::min(total_registers_to_read, MAX_REGISTERS.into());
addresses.push(self.address);
register_counts.push(cur_num_registers_to_read as u8);
total_registers_to_read -= cur_num_registers_to_read;
}
let mut mbfb = ModbusFeedbackFrame::new_read_frame(&addresses, ®ister_counts);
let result =
timeout(client.command_response_timeout, client.read_mbfb(&mut mbfb)).await??;
tracing::debug!("total num bytes read from read_mbfb: {}", result.len());
total_bytes_to_read = total_bytes_to_read.saturating_sub(result.len() as u32);
tracing::debug!("Still need to read {total_bytes_to_read} bytes");
data_bytes.extend_from_slice(&result);
}
if num_bytes % 2 != 0 {
data_bytes.truncate(data_bytes.len() - 1);
}
Ok(data_bytes.freeze())
}
pub async fn read_string(self, client: &mut LabjackClient, len: u32) -> Result<String> {
let mut bytes = self.read(client, len).await?;
bytes.truncate(bytes.len() - 1);
let str_slice = str::from_utf8(&bytes)?;
Ok(str_slice.to_string())
}
pub async fn read_file(self, client: &mut LabjackClient, len: u32) -> Result<String> {
let bytes = self.read(client, len).await?;
let str_slice = str::from_utf8(&bytes)?;
Ok(str_slice.to_string())
}
}
impl<R> LabjackTag<Bytes, R, CanWrite> {
pub async fn write(self, client: &mut LabjackClient, val: Bytes) -> Result<()> {
let result = timeout(
client.command_response_timeout,
client
.context
.write_multiple_registers(self.address, &u8_to_u16_vec(&val)),
)
.await??;
match result {
Ok(res) => Ok(res),
Err(e) => Err(client.detailed_error_from_exception_code(e).await),
}
}
}
#[enum_dispatch]
pub trait Addressable {
fn register_count(&self) -> u8;
fn address(&self) -> u16;
}
#[enum_dispatch]
pub trait Readable: Addressable {
fn hydrate(&self, bytes: &mut Bytes) -> HydratedTagValue;
}
impl<R, W> Addressable for LabjackTag<u64, R, W> {
fn register_count(&self) -> u8 {
4
}
fn address(&self) -> u16 {
self.address
}
}
impl<W> Readable for LabjackTag<u64, CanRead, W> {
fn hydrate(&self, bytes: &mut Bytes) -> HydratedTagValue {
HydratedTagValue::U64(bytes.get_u64())
}
}
impl<R, W> Addressable for LabjackTag<f32, R, W> {
fn register_count(&self) -> u8 {
2
}
fn address(&self) -> u16 {
self.address
}
}
impl<W> Readable for LabjackTag<f32, CanRead, W> {
fn hydrate(&self, bytes: &mut Bytes) -> HydratedTagValue {
HydratedTagValue::F32(bytes.get_f32())
}
}
impl<R, W> Addressable for LabjackTag<i32, R, W> {
fn register_count(&self) -> u8 {
2
}
fn address(&self) -> u16 {
self.address
}
}
impl<W> Readable for LabjackTag<i32, CanRead, W> {
fn hydrate(&self, bytes: &mut Bytes) -> HydratedTagValue {
HydratedTagValue::I32(bytes.get_i32())
}
}
impl<R, W> Addressable for LabjackTag<u32, R, W> {
fn register_count(&self) -> u8 {
2
}
fn address(&self) -> u16 {
self.address
}
}
impl<W> Readable for LabjackTag<u32, CanRead, W> {
fn hydrate(&self, bytes: &mut Bytes) -> HydratedTagValue {
HydratedTagValue::U32(bytes.get_u32())
}
}
impl<R, W> Addressable for LabjackTag<u16, R, W> {
fn register_count(&self) -> u8 {
1
}
fn address(&self) -> u16 {
self.address
}
}
impl<W> Readable for LabjackTag<u16, CanRead, W> {
fn hydrate(&self, bytes: &mut Bytes) -> HydratedTagValue {
HydratedTagValue::U16(bytes.get_u16())
}
}
#[derive(Debug, PartialEq)]
pub enum HydratedTagValue {
U64(u64),
F32(f32),
I32(i32),
U32(u32),
U16(u16),
}
impl TryInto<f32> for &HydratedTagValue {
type Error = Error;
fn try_into(self) -> Result<f32> {
match self {
&HydratedTagValue::F32(val) => Ok(val),
_ => Err(Error::Other(format!("Expected F32, got {:?}", self))),
}
}
}
impl TryInto<i32> for &HydratedTagValue {
type Error = Error;
fn try_into(self) -> Result<i32> {
match self {
&HydratedTagValue::I32(val) => Ok(val),
_ => Err(Error::Other(format!("Expected I32, got {:?}", self))),
}
}
}
impl TryInto<u64> for &HydratedTagValue {
type Error = Error;
fn try_into(self) -> Result<u64> {
match self {
&HydratedTagValue::U64(val) => Ok(val),
_ => Err(Error::Other(format!("Expected U64, got {:?}", self))),
}
}
}
impl TryInto<u32> for &HydratedTagValue {
type Error = Error;
fn try_into(self) -> Result<u32> {
match self {
&HydratedTagValue::U32(val) => Ok(val),
_ => Err(Error::Other(format!("Expected U32, got {:?}", self))),
}
}
}
impl TryInto<u16> for &HydratedTagValue {
type Error = Error;
fn try_into(self) -> Result<u16> {
match self {
&HydratedTagValue::U16(val) => Ok(val),
_ => Err(Error::Other(format!("Expected U16, got {:?}", self))),
}
}
}
#[enum_dispatch(Addressable)]
pub enum WritableLabjackTag {
F32WriteOnly(LabjackTag<f32, CannotRead, CanWrite>),
F32ReadWrite(LabjackTag<f32, CanRead, CanWrite>),
I32WriteOnly(LabjackTag<i32, CannotRead, CanWrite>),
I32ReadWrite(LabjackTag<i32, CanRead, CanWrite>),
U32WriteOnly(LabjackTag<u32, CannotRead, CanWrite>),
U32ReadWrite(LabjackTag<u32, CanRead, CanWrite>),
U16WriteOnly(LabjackTag<u16, CannotRead, CanWrite>),
U16ReadWrite(LabjackTag<u16, CanRead, CanWrite>),
}
#[enum_dispatch(Addressable)]
#[enum_dispatch(Readable)]
pub enum ReadableLabjackTag {
U64ReadOnly(LabjackTag<u64, CanRead, CannotWrite>),
U64ReadWrite(LabjackTag<u64, CanRead, CanWrite>),
F32ReadOnly(LabjackTag<f32, CanRead, CannotWrite>),
F32ReadWrite(LabjackTag<f32, CanRead, CanWrite>),
I32ReadOnly(LabjackTag<i32, CanRead, CannotWrite>),
I32ReadWrite(LabjackTag<i32, CanRead, CanWrite>),
U32ReadOnly(LabjackTag<u32, CanRead, CannotWrite>),
U32ReadWrite(LabjackTag<u32, CanRead, CanWrite>),
U16ReadOnly(LabjackTag<u16, CanRead, CannotWrite>),
U16ReadWrite(LabjackTag<u16, CanRead, CanWrite>),
}
#[derive(Builder, Debug, PartialEq)]
pub struct StreamConfig {
#[builder(default = 1000.0)]
pub scan_rate: f32,
pub num_addresses: u32,
#[builder(default = 512)]
pub samples_per_packet: u32,
#[builder(default = 0.0)]
pub settling_us: f32,
#[builder(default = 0)]
pub resolution_index: u32,
#[builder(default = 0)]
pub buffer_size_bytes: u32,
#[builder(default = 1)]
pub auto_target: u32,
#[builder(default = 0)]
pub num_scans: u32,
}