pub const IBGTL: u8 = 0x01;
pub const IBSDC: u8 = 0x04;
pub const IBGET: u8 = 0x08;
pub const IBTCT: u8 = 0x09;
pub const IBLLO: u8 = 0x11;
pub const IBDCL: u8 = 0x14;
pub const IBSPE: u8 = 0x18;
pub const IBSPD: u8 = 0x19;
pub const IBUNL: u8 = 0x3f;
pub const IBUNT: u8 = 0x5f;
pub const LADBASE: u8 = 0x20;
pub const TADBASE: u8 = 0x40;
pub const SADBASE: u8 = 0x60;
pub const NUM_GPIB_ADDRESSES: i32 = 32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum GpibUniversalCommand {
DeviceClear = 1,
LocalLockout = 2,
SerialPollDisable = 3,
SerialPollEnable = 4,
Unlisten = 5,
Untalk = 6,
}
impl GpibUniversalCommand {
pub fn from_menu(menu: i32) -> Option<Self> {
match menu {
1 => Some(Self::DeviceClear),
2 => Some(Self::LocalLockout),
3 => Some(Self::SerialPollDisable),
4 => Some(Self::SerialPollEnable),
5 => Some(Self::Unlisten),
6 => Some(Self::Untalk),
_ => None,
}
}
pub fn cmd_byte(self) -> u8 {
match self {
Self::DeviceClear => IBDCL,
Self::LocalLockout => IBLLO,
Self::SerialPollDisable => IBSPD,
Self::SerialPollEnable => IBSPE,
Self::Unlisten => IBUNL,
Self::Untalk => IBUNT,
}
}
}
pub fn universal_cmd_byte(menu: i32) -> u8 {
GpibUniversalCommand::from_menu(menu).map_or(0, GpibUniversalCommand::cmd_byte)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum GpibAddressedCommand {
GroupExecuteTrigger = 1,
GoToLocal = 2,
SelectedDeviceClear = 3,
TakeControl = 4,
SerialPoll = 5,
}
impl GpibAddressedCommand {
pub fn from_menu(menu: i32) -> Option<Self> {
match menu {
1 => Some(Self::GroupExecuteTrigger),
2 => Some(Self::GoToLocal),
3 => Some(Self::SelectedDeviceClear),
4 => Some(Self::TakeControl),
5 => Some(Self::SerialPoll),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GpibAddressedRequest {
Frame(Vec<u8>),
SerialPoll,
}
pub fn addressed_frame(cmd_byte: u8, addr: i32, take_control: bool) -> Vec<u8> {
let addr_byte = (addr as u8).wrapping_add(if take_control { TADBASE } else { LADBASE });
let mut frame = vec![IBUNT, IBUNL, addr_byte, cmd_byte];
if !take_control {
frame.push(IBUNT);
frame.push(IBUNL);
}
frame
}
pub fn addressed_request(menu: i32, addr: i32) -> GpibAddressedRequest {
match GpibAddressedCommand::from_menu(menu) {
Some(GpibAddressedCommand::SerialPoll) => GpibAddressedRequest::SerialPoll,
Some(GpibAddressedCommand::TakeControl) => {
GpibAddressedRequest::Frame(addressed_frame(IBTCT, addr, true))
}
Some(GpibAddressedCommand::GroupExecuteTrigger) => {
GpibAddressedRequest::Frame(addressed_frame(IBGET, addr, false))
}
Some(GpibAddressedCommand::GoToLocal) => {
GpibAddressedRequest::Frame(addressed_frame(IBGTL, addr, false))
}
Some(GpibAddressedCommand::SelectedDeviceClear) => {
GpibAddressedRequest::Frame(addressed_frame(IBSDC, addr, false))
}
None => GpibAddressedRequest::Frame(addressed_frame(0, addr, false)),
}
}
pub fn gpib_port_capabilities() -> Vec<super::Capability> {
use super::Capability::*;
vec![
OctetRead, OctetWrite, Gpib, Int32Read, Int32Write, Flush, Connect,
]
}
pub fn int32_write_not_supported() -> crate::error::AsynError {
crate::error::AsynError::Status {
status: crate::error::AsynStatus::Error,
message: "write is not supported".into(),
}
}
pub fn int32_read_not_supported() -> crate::error::AsynError {
crate::error::AsynError::Status {
status: crate::error::AsynStatus::Error,
message: "read is not supported".into(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn universal_cmd_bytes_match_the_c_switch() {
assert_eq!(universal_cmd_byte(1), 0x14, "DCL");
assert_eq!(universal_cmd_byte(2), 0x11, "LLO");
assert_eq!(universal_cmd_byte(3), 0x19, "SPD");
assert_eq!(universal_cmd_byte(4), 0x18, "SPE");
assert_eq!(universal_cmd_byte(5), 0x3f, "UNL");
assert_eq!(universal_cmd_byte(6), 0x5f, "UNT");
}
#[test]
fn an_out_of_menu_universal_command_is_the_zero_byte() {
assert_eq!(universal_cmd_byte(99), 0);
}
#[test]
fn addressed_frames_match_the_c_acmd_buffer() {
assert_eq!(
addressed_request(1, 5),
GpibAddressedRequest::Frame(vec![0x5f, 0x3f, 0x20 + 5, 0x08, 0x5f, 0x3f]),
"GET"
);
assert_eq!(
addressed_request(2, 5),
GpibAddressedRequest::Frame(vec![0x5f, 0x3f, 0x20 + 5, 0x01, 0x5f, 0x3f]),
"GTL"
);
assert_eq!(
addressed_request(3, 5),
GpibAddressedRequest::Frame(vec![0x5f, 0x3f, 0x20 + 5, 0x04, 0x5f, 0x3f]),
"SDC"
);
}
#[test]
fn take_control_uses_the_talk_address_and_stops_at_four_bytes() {
assert_eq!(
addressed_request(4, 5),
GpibAddressedRequest::Frame(vec![0x5f, 0x3f, 0x40 + 5, 0x09]),
"TCT"
);
}
#[test]
fn serial_poll_is_a_sequence_not_a_frame() {
assert_eq!(addressed_request(5, 5), GpibAddressedRequest::SerialPoll);
}
#[test]
fn an_out_of_menu_addressed_command_still_frames_the_address() {
assert_eq!(
addressed_request(99, 7),
GpibAddressedRequest::Frame(vec![0x5f, 0x3f, 0x20 + 7, 0x00, 0x5f, 0x3f])
);
}
}