Module binary

Source
Available on crate feature binary only.
Expand description

Types and traits for communicating with Zaber products with Zaber’s Binary protocol.

The binary protocol is deprecated and Zaber recommends the ascii protocol instead.

§Communicating with a Device

All communication with Zaber products starts with a Port, which can be either a serial or TCP port:

let mut port = Port::open_serial("/dev/ttyUSB0")?;
// OR
let mut port = Port::open_tcp("192.168.0.1:55550")?;

You can then transmit a command to and receive a reply from devices connected to that port with the tx_recv method, or any of the other tx_recv* methods. Commands are constructed as tuples:

  • Commands that do not require data take the form (u8, Command), where
    • u8 is the device address and
    • Command is some type that implements Command (the constants in the command module are highly recommended but you can also use a u8).
// Send the Home command to all devices (address 0)
use zproto::binary::command::HOME;
let reply = port.tx_recv((0, HOME))?;
  • Commands that do require data take a form similar to above, (u8, Command, Data), where u8 and Command are the same and Data is some type that implements Data. This includes types like i32, bool, command, and other types.
// Move device 1 to the absolute position 10,000.
use zproto::binary::command::*;
let reply = port.tx_recv((1, MOVE_ABSOLUTE, 10000))?;
// OR
let reply = port.tx_recv((0, RETURN_SETTING, SET_TARGET_SPEED))?;

If the response is an ERROR or is otherwise unexpected (e.g., from a different device) an error will be returned.

NOTE: Address aliases other than 0 (all devices) are presently not supported. Responses to such messages will be treated as unexpected by the tx_recv* methods.

§Other Port Methods

tx_recv works great when you need to send one command and receive one response. But, when you have a chain of devices, you may need to read a response from all devices in the chain. When you know the number of devices in the chain, you can use tx_recv_n to read n responses to a command. If you don’t know the number of devices in the chain you can use tx_recv_until_timeout to read as many responses as possible.

Sometimes you only want to transmit or receive data, but not both. In those cases the tx, recv, recv_n, and recv_until_timeout are very helpful.

Finally, a common pattern is to poll a device until it is in a desired state, which you can do with the poll_until and poll_until_idle methods.

§Reading Data

Reading data from a response is as simple as calling data() on the response. If the command was sent using the strongly typed commands in the command module, the library will pick the correct type conversion for you at compile time.

use zproto::binary::command::*;
let reply = port.tx_recv((0, RETURN_SETTING, SET_TARGET_SPEED))?;
let speed = reply.data()?; // `speed` is an `i32`
let reply = port.tx_recv((0, RETURN_SETTING, SET_HOME_STATUS))?;
let homed = reply.data()?;  // `homed` is a `bool`.

§Sending a Port between threads

By default a Port does not implement Send, so cannot be sent to another thread. If you’re application requires this, use the Port::try_into_send method to convert it to one that can be. Doing so places Send bounds on any packet handlers.

let sendable_port = Port::open_serial("...")?
    .try_into_send()
    .unwrap();

§Type Safety

The library uses Rust’s type system and the strongly-typed commands in the command module to help enforce proper message structure and avoid run-time bugs. For instance, not passing data to commands that require data, or passing the wrong data type, will result in a compile time error.

use zproto::binary::command::*;
let reply = port.tx_recv((0, MOVE_ABSOLUTE))?;  // ERROR: the data field is missing!
let reply = port.tx_recv((0, MOVE_ABSOLUTE, true))?;  // ERROR: the data has the incorrect type!
let reply = port.tx_recv((0, RESET));  // ERROR: Devices do not respond to the RESET command!

To do this, each command in the command module is given a unique type that implement a set of traits. These tell the compiler what kind of data the command takes, what kind of data it returns, and if it returns a response at all. However, this means that the commands cannot be swapped out for one another at run time or stored in a collection.

// This will not compile because SET_TARGET_SPEED and SET_ACCELERATION
// are different types.
use zproto::binary::command::*;
let commands = [(0, SET_TARGET_SPEED, 10000), (0, SET_ACCELERATION, 5000)];

To accommodate situations like this, the library provides “untyped” versions of the command in the command::untyped module. The commands in that module are simply u8s and can be used anywhere their strongly-typed counterparts can.

// This works now.
use zproto::binary::command::untyped::*;
let commands = [(0, SET_TARGET_SPEED, 10000), (0, SET_ACCELERATION, 5000)];

However, you will not have any of the nice compile time type safety or automatic type conversion – you will have to pick the types yourself.

use zproto::binary::command::untyped::*;
let reply = port.tx_recv((0, RETURN_SETTING, SET_TARGET_SPEED))?;
let speed: i32 = reply.data()?; // Notice that the type must be specified.
// The compiler cannot tell if you have picked the wrong type so this will
// compile, but the data conversion will fail at run time.
let speed: bool = reply.data()?;

§Handling Compile-time Errors

The traits in this crate are named so that their meaning is hopefully clear, but, nevertheless, the compiler errors can sometimes be confusing. Here are some errors that you might see and suggestions for correcting them.

§TakesData is not Satisfied
the trait `TakesData<{integer}>` is not implemented for ...

There are two reasons for this error:

  1. The command does not take any data argument. Try restructuring the message from (address, command, data) to (address, command).
  2. The command requires a data argument with a different type. Consult the documentation for the command and use the data type listed there.
§TakesNoData is not Satisfied
the trait `TakesNoData` is not implemented for ...

The command likely requires a data argument. Try restructuring the message from (address, command) to (address, command, data).

§ElicitsResponse is not Satisfied
the trait `ElicitsResponse` is not implemented for ...

This means the command does not elicit a response from a device and the Port::tx_recv family of functions would eventually time out waiting for one. Try transmitting the command without reading a reply with tx instead.

Modules§

command
Binary commands.
handlers
Event handlers for ports.
traits
Traits encoding information about Binary commands/messages and the types that can generate them.

Structs§

IoStates
The state of multiple digital I/O channels.
Message
A Binary Protocol message.
OpenSerialOptions
Options for configuring and opening a serial port.
OpenTcpOptions
Options for configuring and opening a TCP port.
Port
A Port for transmitting and receiving Zaber Binary protocol messages.
Version
Represents a version of firmware.

Enums§

Direction
The direction a packet was sent.
Status
The status of a device.

Type Aliases§

SendPort
The type of Port that implements Send.