Skip to content

Command Streaming

Streams provide a way to execute a sequence of actions on the device without a communication delay. An example of an action sequence may be:

  • Move two axes to coordinates (10 cm, 20 cm)
  • Wait 1 second
  • Set digital output 1 to high
  • Move single axis by 5 cm
  • Wait until digital input 1 is low
  • Do a circular movement with radius 1 cm

Stream movement differs from successive command execution because stream movement transitions smoothly between each path segment. In addition, stream movement includes methods for multi-axis movement, such as moving in lines, arcs, or circles. You can read more about interpolated, multi-axis motion in this article.

Much of the presented API's behavior originates in the underlying Zaber ASCII Protocol. Refer to the stream section in the ASCII Protocol Manual for a detailed explanation of the behaviour.

Checking if your device supports streams

You may check if the device supports streams by checking if the stream.numstreams setting has a non-zero value.

num_streams = device.settings.get('stream.numstreams')
print('Number of streams possible:', num_streams)
const numStreams = device.settings.get('stream.numstreams');
console.log(`Number of streams possible: ${numStreams}`);
var numStreams = device.Settings.Get("stream.numstreams");
Console.WriteLine($"Number of streams possible: {numStreams}");
double numStreams = device.getSettings().get("stream.numstreams");
System.out.println("Number of streams possible: " + numStreams);
numStreams = device.getSettings().get("stream.numstreams");
fprintf('Number of streams possible: %d\n', numStreams);
double numStreams = device.getSettings().get("stream.numstreams");
std::cout << "Number of streams possible: " << numStreams << std::endl;

Setup

To start using streams, get a stream from the device. In this example we get the first stream.

stream = device.get_stream(1)
const stream = device.getStream(1);
var stream = device.GetStream(1);
Stream stream = device.getStream(1);
stream = device.getStream(1);
Stream stream = device.getStream(1);

The instance returned represents a handle for stream number 1 on the device.

The stream needs to be set up before it can be used. Once the stream is set up in either “Live” mode or “Store” mode, the methods of this instance allow you to append stream actions onto the stream queue.

Live Mode

When a stream is in Live mode, actions in the stream's queue will immediately execute. To set up a stream in Live mode, call the @method SetupLive method with an arguments containing the axis numbers of the axes that the stream will target. You can provide a variable number of arguments (axes numbers).

stream.setup_live(1, 2)
await stream.setupLive(1, 2);
stream.SetupLive(1, 2);
stream.setupLive(1, 2);
stream.setupLive([1, 2]);
stream.setupLive(1, 2);

Store Mode

Streams set up in store mode do not execute the actions in their queue. Instead, they store them to a stream buffer on the device. Once some actions are stored, stream buffers can be played back by Live streams via the @method call method, so that the stored actions are added to the Live stream's queue.

Stored actions may also be set up to trigger if a device reaches a condition, such as when an I/O port equals a value. Read more about triggers here.

Buffers on a device are identified by numbers starting from 1. The total number of buffers available is determined by stream.numbufs setting. Since Store streams cannot be set up to use non-empty stream buffers, it is recommended to erase the buffer before setting up.

See the following code for an example of setting up a stream in Store mode:

stream_buffer = device.get_stream_buffer(1)
stream_buffer.erase()

# set up stream to store actions to stream buffer 1 and
# to use the first two axes for unit conversion
stream.setup_store(stream_buffer, 1, 2)

# append stream actions...
const streamBuffer = device.getStreamBuffer(1);
await streamBuffer.erase();

// set up stream to store actions to stream buffer 1 and
// to use the first two axes for unit conversion
await stream.setupStore(streamBuffer, 1, 2);

// append stream actions...
var streamBuffer = device.GetStreamBuffer(1);
streamBuffer.Erase();

// set up stream to store actions to stream buffer 1 and
// to use the first two axes for unit conversion
stream.SetupStore(streamBuffer, 1, 2);

// append stream actions...
StreamBuffer streamBuffer = device.getStreamBuffer(1);
streamBuffer.erase();

// set up stream to store actions to stream buffer 1 and
// to use the first two axes for unit conversion
stream.setupStore(streamBuffer, 1, 2);

// append stream actions...
streamBuffer = device.getStreamBuffer(1);
streamBuffer.erase();

% set up stream to store actions to stream buffer 1 and
% to use the first two axes for unit conversion
stream.setupStore(streamBuffer, [1, 2]);

% append stream actions...
StreamBuffer streamBuffer = device.getStreamBuffer(1);
streamBuffer.erase();

// set up stream to store actions to stream buffer 1 and
// to use the first two axes for unit conversion
stream.setupStore(streamBuffer, 1, 2);

// append stream actions...

Once you have appended some actions to the stream buffer, you can print it out, or call it from a live stream. Before calling the stream buffer ensure that the live stream is set up on the same set of axes as the original store stream. If the axes are different, the unit conversions applied when storing the commands may result in incorrect motion. You can ignore this condition if the conversion factors of your axes are the same (e.g. the peripherals are the same product).

content = stream_buffer.get_content()
print(content)
# example output:
# ['setup store 1 2',
# 'line abs 1000 100',
# 'wait 1000',
# 'line abs 20000 29999',
# 'setup disable']

# change the streams mode from Store to Live
stream.disable()
stream.setup_live(1, 2)

# the stream will now execute the stored actions
stream.call(stream_buffer)
console.log('Stream buffer content:');
console.log(await streamBuffer.getContent());
// example output:
// ['setup store 1 2',
// 'line abs 1000 100',
// 'wait 1000',
// 'line abs 20000 29999',
// 'setup disable']

await stream.disable();
await stream.setupLive(1, 2);

await stream.call(streamBuffer);
var content = streamBuffer.GetContent();
Console.WriteLine("[{0}]", string.Join(",\n", content));
// example output:
// ['setup store 1 2',
// 'line abs 1000 100',
// 'wait 1000',
// 'line abs 20000 29999',
// 'setup disable']

stream.Disable();
stream.SetupLive(1, 2);

stream.Call(streamBuffer);
String[] content = streamBuffer.getContent();
System.out.println(Arrays.toString(content));
// example output:
// ['setup store 1 2',
// 'line abs 1000 100',
// 'wait 1000',
// 'line abs 20000 29999',
// 'setup disable']

stream.disable();
stream.setupLive(1, 2);

stream.call(streamBuffer);
content = streamBuffer.getContent();
for i=1:content.size
    fprintf("%s,\n", content(i));
end
% example output:
% ['setup store 1 2',
% 'line abs 1000 100',
% 'wait 1000',
% 'line abs 20000 29999',
% 'setup disable']

stream.disable();
stream.setupLive([1, 2]);

stream.call(streamBuffer);
std::vector<std::string> content = streamBuffer.getContent();
for (auto line : content) {
    std::cout << line << std::endl;
}
// example output:
// 'setup store 1 2',
// 'line abs 1000 100',
// 'wait 1000',
// 'line abs 20000 29999',
// 'setup disable'

stream.disable();
stream.setupLive(1, 2);

stream.call(streamBuffer);

Note that the actions printed are in native units and their format is described in the stream buffer print command documentation in the ASCII Protocol Manual.

Store (Arbitrary axes)

There is a variant of Store mode that the library provides, called StoreArbitrary mode. StoreArbitrary mode works as Store mode, but it does not target specific axes on the device. Streams setup in this mode only require that the number of axes in the stream be specified.

See the following code for an example:

stream_buffer = device.get_stream_buffer(1)
stream_buffer.erase()
# set up a StoreArbitrary stream for any two axes
stream.setup_store_arbitrary(stream_buffer, 2)
const streamBuffer = device.getStreamBuffer(1);
await streamBuffer.erase();
// set up a StoreArbitrary stream for any two axes
await stream.setupStoreArbitrary(streamBuffer, 2);
var streamBuffer = device.GetStreamBuffer(1);
streamBuffer.Erase();
// set up a StoreArbitrary stream for any two axes
stream.SetupStoreArbitrary(streamBuffer, 2);
StreamBuffer streamBuffer = device.getStreamBuffer(1);
streamBuffer.erase();
// set up a StoreArbitrary stream for any two axes
stream.setupStoreArbitrary(streamBuffer, 2);
streamBuffer = device.getStreamBuffer(1);
streamBuffer.erase();
% set up a StoreArbitrary stream for any two axes
stream.setupStoreArbitrary(streamBuffer, 2);
StreamBuffer streamBuffer = device.getStreamBuffer(1);
// set up a StoreArbitrary stream for any two axes
streamBuffer.erase();

stream.setupStoreArbitrary(streamBuffer, 2);

The benefit of StoreArbitrary mode is that you can later call the stream buffer from any live stream, provided it has same the number of axes.

The downside of this mode is that it does not allow for unit conversion, as unit conversion is usually dependent on the conversion factor of a specific axis on the device.

Movement

Stream movement actions can be relative or absolute. If a movement is relative, the device treats the current position of the axes as the origin. If a movement is absolute, the device treats the position zero of the axes as origin.

Parameters for stream movement actions make use of Measurement objects. Measurement objects are simple objects which contains a @prop Value field as well as an optional @prop Unit field.

The next couple of examples show how to do stream movement.

Line Movement

stream.line_absolute(
    Measurement(29.0047, Units.LENGTH_MILLIMETRES),
    Measurement(40.49, Units.LENGTH_MILLIMETRES)
)

stream.line_relative(
    Measurement(0),
    Measurement(50.5, Units.LENGTH_MILLIMETRES)
)

# or, to write less, iterate over an array of points and convert to cm once
path_in_cm = [(0.00, 3.00), (2.25, 7.10), (5.35, 0.15), (1.45, 10.20), (9.00, 9.00)]
for point in path_in_cm:
    stream.line_absolute(
        Measurement(point[0], Units.LENGTH_CENTIMETRES),
        Measurement(point[1], Units.LENGTH_CENTIMETRES)
    )
await stream.lineAbsolute({
  value: 29.0047, unit: Length.mm,
}, {
  value: 40.49, unit: Length.mm,
});

await stream.lineRelative({
  value: 0,
}, {
  value: 50.5, unit: Length.mm,
});

// or, to write less, iterate over an array of points and convert to cm once
const pathInCm = [[0.00, 3.00], [2.25, 7.10], [5.35, 0.15], [1.45, 10.20], [9.00, 9.00]];
for (const point of pathInCm) {
  await stream.lineAbsolute(
    { value: point[0], unit: Length.cm },
    { value: point[1], unit: Length.cm },
  );
}
stream.LineAbsolute(
    new Measurement { Value = 29.0047, Unit = Units.Length_Millimetres },
    new Measurement { Value = 40.49, Unit = Units.Length_Millimetres }
);

stream.LineRelative(
    new Measurement { Value = 0 },
    new Measurement { Value = 50.5, Unit = Units.Length_Millimetres }
);

// or, to write less, iterate over an array of points and convert to cm once
double[,] pathInCm = new double[,] { { 0.00, 3.00 }, { 2.25, 7.10 }, { 5.35, 0.15 }, { 1.45, 10.20 }, { 9.00, 9.00 } };
for (int i = 0; i < pathInCm.GetLength(0); i++)
{
    stream.LineAbsolute(
        new Measurement { Value = pathInCm[i, 0], Unit = Units.Length_Centimetres },
        new Measurement { Value = pathInCm[i, 1], Unit = Units.Length_Centimetres }
    );
}
stream.lineAbsolute(
    new Measurement(29.0047, Units.LENGTH_MILLIMETRES),
    new Measurement(40.49, Units.LENGTH_MILLIMETRES)
);
stream.lineRelative(
    new Measurement(0),
    new Measurement(50.5, Units.LENGTH_MILLIMETRES)
);

// or, to write less, iterate over an array of points and convert to cm once
double[][] pathInCm = { {0.00, 3.00}, {2.25, 7.10}, {5.35, 0.15}, {1.45, 10.20}, {9.00, 9.00} };
for (double[] point : pathInCm) {
    stream.lineAbsolute(
        new Measurement(point[0], Units.LENGTH_CENTIMETRES),
        new Measurement(point[1], Units.LENGTH_CENTIMETRES)
    );
}
stream.lineAbsolute([
    Measurement(29.0047, Units.LENGTH_MILLIMETRES)
    Measurement(40.49, Units.LENGTH_MILLIMETRES)
]);
stream.lineRelative([
    Measurement(0)
    Measurement(50.5, Units.LENGTH_MILLIMETRES)
]);

% or, to write less, iterate over an array of points and convert to cm once
points = [0.00 3.00; 2.25 7.10; 5.35 0.15; 1.45 10.20; 9.00 9.00];
for row=1:size(points,1)
    stream.lineAbsolute([
        Measurement(points(row, 1), Units.LENGTH_CENTIMETRES)
        Measurement(points(row, 2), Units.LENGTH_CENTIMETRES)
    ]);
end
stream.lineAbsolute(
    Measurement(29.0047, Units::LENGTH_MILLIMETRES),
    Measurement(40.49, Units::LENGTH_MILLIMETRES)
);
stream.lineAbsolute(
    Measurement(0),
    Measurement(50.5, Units::LENGTH_MILLIMETRES)
);

// or, to write less, iterate over an array of points and convert to cm once
double pathInCm[5][2] = { {0.00, 3.00}, {2.25, 7.10}, {5.35, 0.15}, {1.45, 10.20}, {9.00, 9.00} };
for (double* point : pathInCm) {
    stream.lineAbsolute(
        Measurement(point[0], Units::LENGTH_CENTIMETRES),
        Measurement(point[1], Units::LENGTH_CENTIMETRES)
    );
}

Note that line movement methods accept a variable number of arguments and therefore support motion on different numbers of axes.

Circular Movement

# assuming current position is (9cm, 9cm)

# do a clockwise rotation with radius 4, starting at 0 degrees
circle_center_abs = (  # X,Y absolute position of circle's center
    Measurement(5, Units.LENGTH_CENTIMETRES),
    Measurement(9, Units.LENGTH_CENTIMETRES)
)
stream.circle_absolute(RotationDirection.CW, circle_center_abs[0], circle_center_abs[1])

# do a counter-clockwise rotation with radius 4, starting at 0 degrees
circle_center_rel = (  # X,Y relative position of circle's center
    Measurement(-4, Units.LENGTH_CENTIMETRES),
    Measurement(0, Units.LENGTH_CENTIMETRES)
)
stream.circle_relative(RotationDirection.CCW, circle_center_rel[0], circle_center_rel[1])
// assuming current position is (9cm, 9cm)

// do a clockwise rotation with radius 4, starting at 0 degrees
const circleCenterAbs = [ // X,Y absolute position of circle's center
  { value: 5, unit: Length.cm },
  { value: 9, unit: Length.cm },
];
await stream.circleAbsolute(RotationDirection.CW, circleCenterAbs[0], circleCenterAbs[1]);

// do a counter-clockwise rotation with radius 4, starting at 0 degrees
const circleCenterRel = [ // X,Y relative position of circle's center
  { value: -4, unit: Length.cm },
  { value: 0, unit: Length.cm },
];
await stream.circleRelative(RotationDirection.CCW, circleCenterRel[0], circleCenterRel[1]);
// assuming current position is (9cm, 9cm)

// do a clockwise rotation with radius 4, starting at 0 degrees
var circleCenterAbs = new Measurement[] // X,Y absolute position of circle's center
{
    new Measurement { Value = 5, Unit = Units.Length_Centimetres },
    new Measurement { Value = 9, Unit = Units.Length_Centimetres }
};
stream.CircleAbsolute(RotationDirection.CW, circleCenterAbs[0], circleCenterAbs[1]);

// do a counter-clockwise rotation with radius 4, starting at 0 degrees
var circleCenterRel = new Measurement[] // X,Y relative position of circle's center
{
    new Measurement { Value = -4, Unit = Units.Length_Centimetres },
    new Measurement { Value = 0, Unit = Units.Length_Centimetres }
};
stream.CircleRelative(RotationDirection.CCW, circleCenterRel[0],
// assuming current position is (9cm, 9cm)

// do a clockwise rotation with radius 4, starting at 0 degrees
Measurement[] circleCenterAbs = { // X,Y absolute position of circle's center
    new Measurement(5, Units.LENGTH_CENTIMETRES),
    new Measurement(9, Units.LENGTH_CENTIMETRES),
};
stream.circleAbsolute(RotationDirection.CW, circleCenterAbs[0], circleCenterAbs[1]);

// do a counter-clockwise rotation with radius 4, starting at 0 degrees
Measurement[] circleCenterRel = { // X,Y relative position of circle's center
    new Measurement(-4, Units.LENGTH_CENTIMETRES),
    new Measurement(0, Units.LENGTH_CENTIMETRES),
};
stream.circleRelative(RotationDirection.CCW, circleCenterRel[0], circleCenterRel[1]);
% assuming current position is (9cm, 9cm)

% do a clockwise rotation with radius 4, starting at 0 degrees
circleCenterAbs = [  % X,Y absolute position of circle's center
    Measurement(5, Units.LENGTH_CENTIMETRES)
    Measurement(9, Units.LENGTH_CENTIMETRES)
];
stream.circleAbsolute(RotationDirection.CW, circleCenterAbs(1), circleCenterAbs(2));

% do a counter-clockwise rotation with radius 4, starting at 0 degrees
circleCenterRel = [  % X,Y relative position of circle's center
    Measurement(-4, Units.LENGTH_CENTIMETRES)
    Measurement(0, Units.LENGTH_CENTIMETRES)
];
stream.circleRelative(RotationDirection.CCW, circleCenterRel(1), circleCenterRel(2));
// assuming current position is (9cm, 9cm)

// do a clockwise rotation with radius 4, starting at 0 degrees
Measurement circleCenterAbs[] = { // X,Y absolute position of circle's center
    Measurement(5, Units::LENGTH_CENTIMETRES),
    Measurement(9, Units::LENGTH_CENTIMETRES),
};
stream.circleAbsolute(RotationDirection::CW, circleCenterAbs[0], circleCenterAbs[1]);

// do a counter-clockwise rotation with radius 4, starting at 0 degrees
Measurement circleCenterRel[] = { // X,Y relative position of circle's center
    Measurement(-4, Units::LENGTH_CENTIMETRES),
    Measurement(0, Units::LENGTH_CENTIMETRES),
};
stream.circleRelative(RotationDirection::CCW, circleCenterRel[0], circleCenterRel[1]);

Arc Movement

# assuming current position is (9cm, 9cm)

arc_circle_center_rel = (  # X,Y relative position of arc's center
    Measurement(-4, Units.LENGTH_CENTIMETRES),
    Measurement(0, Units.LENGTH_CENTIMETRES)
)
arc_end_rel = (  # X,Y relative position of arc's end
    Measurement(-8, Units.LENGTH_CENTIMETRES),
    Measurement(0, Units.LENGTH_CENTIMETRES)
)
# move from 0 degrees to 180 degrees of circle with radius 4
stream.arc_relative(
    RotationDirection.CCW,
    arc_circle_center_rel[0],
    arc_circle_center_rel[1],
    arc_end_rel[0],
    arc_end_rel[1]
)

arc_circle_center_abs = (  # X,Y absolute position of arc's center
    Measurement(5, Units.LENGTH_CENTIMETRES),
    Measurement(9, Units.LENGTH_CENTIMETRES)
)
arc_end_abs = (  # X,Y absolute position of arc's end
    Measurement(9, Units.LENGTH_CENTIMETRES),
    Measurement(9, Units.LENGTH_CENTIMETRES)
)
# backtrace last arc movement by moving from 180 degrees to 0 degrees
stream.arc_absolute(
    RotationDirection.CW,
    arc_circle_center_abs[0],
    arc_circle_center_abs[1],
    arc_end_abs[0],
    arc_end_abs[1]
)
// assuming current position is (9cm, 9cm)

const arcCircleCenterRel = [ // X,Y relative position of arc's center
  { value: -4, unit: Length.cm },
  { value: 0, unit: Length.cm },
];
const arcEndRel = [ // X,Y relative position of arc's end
  { value: -8, unit: Length.cm },
  { value: 0, unit: Length.cm },
];
// move from 0 degrees to 180 degrees of circle with radius 4
await stream.arcRelative(
  RotationDirection.CCW,
  arcCircleCenterRel[0], arcCircleCenterRel[1],
  arcEndRel[0], arcEndRel[1],
);

const arcCircleCenterAbs = [ // X,Y absolute position of arc's center
  { value: 5, unit: Length.cm },
  { value: 9, unit: Length.cm },
];
const arcEndAbs = [ // X,Y absolute position of arc's end
  { value: 9, unit: Length.cm },
  { value: 9, unit: Length.cm },
];
// backtrace last arc movement by moving from 180 degrees to 0 degrees
await stream.arcAbsolute(
  RotationDirection.CW,
  arcCircleCenterAbs[0], arcCircleCenterAbs[1],
  arcEndAbs[0], arcEndAbs[1],
);
// assuming current position is (9cm, 9cm)

var arcCircleCenterRel = new Measurement[] // X,Y relative position of arc's center
{
    new Measurement { Value = -4, Unit = Units.Length_Centimetres },
    new Measurement { Value = 0, Unit = Units.Length_Centimetres }
};
var arcEndRel = new Measurement[] // X,Y relative position of arc's end
{
    new Measurement { Value = -8, Unit = Units.Length_Centimetres },
    new Measurement { Value = 0, Unit = Units.Length_Centimetres }
};
// move from 0 degrees to 180 degrees of circle with radius 4
stream.ArcRelative(
    RotationDirection.CCW,
    arcCircleCenterRel[0], arcCircleCenterRel[1],
    arcEndRel[0], arcEndRel[1]
);

var arcCircleCenterAbs = new Measurement[] // X,Y absolute position of arc's center
{
    new Measurement { Value = 5, Unit = Units.Length_Centimetres },
    new Measurement { Value = 9, Unit = Units.Length_Centimetres }
};
var arcEndAbs = new Measurement[] // X,Y absolute position of arc's end
{
    new Measurement { Value = 9, Unit = Units.Length_Centimetres },
    new Measurement { Value = 9, Unit = Units.Length_Centimetres }
};
// backtrace last arc movement by moving from 180 degrees to 0 degrees
stream.ArcRelative(
    RotationDirection.CW,
    arcCircleCenterAbs[0], arcCircleCenterAbs[1],
    arcEndAbs[0], arcEndAbs[1]
);
// assuming current position is (9cm, 9cm)

Measurement[] arcCircleCenterRel = { // X,Y relative position of arc's center
    new Measurement(-4, Units.LENGTH_CENTIMETRES),
    new Measurement(0, Units.LENGTH_CENTIMETRES),
};
Measurement[] arcEndRel = { // X,Y relative position of arc's end
    new Measurement(-8, Units.LENGTH_CENTIMETRES),
    new Measurement(0, Units.LENGTH_CENTIMETRES),
};
// move from 0 degrees to 180 degrees of circle with radius 4
stream.arcRelative(
    RotationDirection.CCW,
    arcCircleCenterRel[0], arcCircleCenterRel[1],
    arcEndRel[0], arcEndRel[1]
);

Measurement[] arcCircleCenterAbs = { // X,Y absolute position of arc's center
    new Measurement(5, Units.LENGTH_CENTIMETRES),
    new Measurement(9, Units.LENGTH_CENTIMETRES),
};
Measurement[] arcEndAbs = { // X,Y absolute position of arc's end
    new Measurement(9, Units.LENGTH_CENTIMETRES),
    new Measurement(9, Units.LENGTH_CENTIMETRES),
};
// backtrace last arc movement by moving from 180 degrees to 0 degrees
stream.arcAbsolute(
    RotationDirection.CW,
    arcCircleCenterAbs[0], arcCircleCenterAbs[1],
    arcEndAbs[0], arcEndAbs[1]
);
% assuming current position is (9cm, 9cm)

arcCircleCenterRel = [ % X,Y relative position of arc's center
    Measurement(-4, Units.LENGTH_CENTIMETRES)
    Measurement(0, Units.LENGTH_CENTIMETRES)
];
arcEndRel = [ % X,Y relative position of arc's end
    Measurement(-8, Units.LENGTH_CENTIMETRES)
    Measurement(0, Units.LENGTH_CENTIMETRES)
];
% move from 0 degrees to 180 degrees of circle with radius 4
stream.arcRelative( ...
    RotationDirection.CCW, ...
    arcCircleCenterRel(1), arcCircleCenterRel(2), ...
    arcEndRel(1), arcEndRel(2));

arcCircleCenterAbs = [ % X,Y absolute position of arc's center
    Measurement(5, Units.LENGTH_CENTIMETRES)
    Measurement(9, Units.LENGTH_CENTIMETRES)
];
arcEndAbs = [ % X,Y absolute position of arc's end
    Measurement(9, Units.LENGTH_CENTIMETRES)
    Measurement(9, Units.LENGTH_CENTIMETRES)
];
% backtrace last arc movement by moving from 180 degrees to 0 degrees
stream.arcAbsolute( ...
    RotationDirection.CW, ...
    arcCircleCenterAbs(1), arcCircleCenterAbs(2), ...
    arcEndAbs(1), arcEndAbs(2));
// assuming current position is (9cm, 9cm)

Measurement arcCircleCenterRel[] = { // X,Y relative position of arc's center
        Measurement(-4, Units::LENGTH_CENTIMETRES),
        Measurement(0, Units::LENGTH_CENTIMETRES),
};
Measurement arcEndRel[] = { // X,Y relative position of arc's end
        Measurement(-8, Units::LENGTH_CENTIMETRES),
        Measurement(0, Units::LENGTH_CENTIMETRES),
};
// move from 0 degrees to 180 degrees of circle with radius 4
stream.arcRelative(
        RotationDirection::CCW,
        arcCircleCenterRel[0], arcCircleCenterRel[1],
        arcEndRel[0], arcEndRel[1]
);

Measurement arcCircleCenterAbs[] = { // X,Y absolute position of arc's center
        Measurement(5, Units::LENGTH_CENTIMETRES),
        Measurement(9, Units::LENGTH_CENTIMETRES),
};
Measurement arcEndAbs[] = { // X,Y absolute position of arc's end
        Measurement(9, Units::LENGTH_CENTIMETRES),
        Measurement(9, Units::LENGTH_CENTIMETRES),
};
// backtrace last arc movement by moving from 180 degrees to 0 degrees
stream.arcAbsolute(
        RotationDirection::CW,
        arcCircleCenterAbs[0], arcCircleCenterAbs[1],
        arcEndAbs[0], arcEndAbs[1]
);

Target Specific Axes

For all movement methods, you may also specify a subset of axes which the movement should target. This is useful if, for instance, you want to do an arc or circular movement on a stream set up for more than two axes, or if you do not want to specify all the points for a line movement.

To do this, call the on version of the method, specifying the axes to target as the first parameter. Note that these axes are zero-based indices into the streams axes, not axis numbers on the device.

Below is an example of using the on version of the line method.

# move only the first axis in the stream
stream.line_absolute_on([0], [Measurement(1, Units.LENGTH_CENTIMETRES)])
// move only the first axis in the stream
await stream.lineAbsoluteOn([0], [{ value: 1, unit: Length.cm }]);
// move only the first axis in the stream
stream.LineAbsoluteOn(
    new int[] { 0 },
    new Measurement[] { new Measurement { Value = 1, Unit = Units.Length_Centimetres } }
);
// move only the first axis in the stream
stream.lineAbsoluteOn(
    new int[] {0},
    new Measurement[] { new Measurement(1, Units.LENGTH_CENTIMETRES) }
);
% move only the first axis in the stream
axes = [ 0 ];
endpoints = javaArray('zaber.motion.Measurement', 1);
endpoints(1) = Measurement(1, Units.LENGTH_CENTIMETRES);
stream.lineAbsoluteOn(axes, endpoints);
// move only the first axis in the stream
stream.lineAbsoluteOn({ 0 }, { Measurement(1, Units::LENGTH_CENTIMETRES) });

Movement Settings

You may set the maximum speed, maximum tangential acceleration, and maximum centripetal acceleration of a stream. Setting these settings is also considered an action by the stream, so it will append the action to the action queue.

The device will always move such that it maintains the conditions of the movement settings. For example, if an action increases the maximum speed the device will accelerate to that maxspeed after executing that action. When an action decreases the maximum speed, the device will slow down to that speed before executing the action.

Note that the units are converted to the device native units using the first axis of the stream. If the other axes have different conversion factors, the settings will not apply correctly.

See the stream set command documentation in the ASCII Protocol Manual for a detailed description of each setting.

Max Speed

To set the maximum speed use the @method setMaxSpeed method.

stream.set_max_speed(0.5, Units.VELOCITY_CENTIMETRES_PER_SECOND)
await stream.setMaxSpeed(0.5, Velocity.MILLIMETRES_PER_SECOND);
stream.SetMaxSpeed(0.5, Units.Velocity_MillimetresPerSecond);
stream.setMaxSpeed(0.5, Units.VELOCITY_MILLIMETRES_PER_SECOND);
stream.setMaxSpeed(0.5, Units.VELOCITY_MILLIMETRES_PER_SECOND);
stream.setMaxSpeed(0.5, Units::VELOCITY_MILLIMETRES_PER_SECOND);

Max Tangential Acceleration

The maximum tangential acceleration controls the maximum acceleration and deceleration used for linear segments as well as the actual tangential acceleration and deceleration for curve segments. To set it use the @method setMaxTangentialAcceleration method.

stream.set_max_tangential_acceleration(5, Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED)
await stream.setMaxTangentialAcceleration(5, Acceleration.CENTIMETRES_PER_SECOND_SQUARED);
stream.SetMaxTangentialAcceleration(5, Units.Acceleration_CentimetresPerSecondSquared);
stream.setMaxTangentialAcceleration(5, Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED);
stream.setMaxTangentialAcceleration(5, Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED);
stream.setMaxTangentialAcceleration(5, Units::ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED);

Max Centripetal Acceleration

The maximum centripetal acceleration controls the maximum centripetal acceleration and deceleration used for curves (including sharp corners between path segments). To set it use the @method setMaxCentripetalAcceleration method.

stream.set_max_centripetal_acceleration(5, Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED)
await stream.setMaxCentripetalAcceleration(5, Acceleration.CENTIMETRES_PER_SECOND_SQUARED);
stream.SetMaxCentripetalAcceleration(5, Units.Acceleration_CentimetresPerSecondSquared);
stream.setMaxCentripetalAcceleration(5, Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED);
stream.setMaxCentripetalAcceleration(5, Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED);
stream.setMaxCentripetalAcceleration(5, Units::ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED);

Waiting

Streams can wait between actions.

stream.wait(2, Units.TIME_SECONDS)
await stream.wait(2, Time.SECONDS);
stream.Wait(2, Units.Time_Seconds);
stream.wait(2, Units.TIME_SECONDS);
stream.wait(2, Units.TIME_SECONDS);
stream.wait(2, Units::TIME_SECONDS);

I/O

Streams can set the digital or analog output ports of a device as a stream action. You can also wait until digital or analog input ports of a device satisfy a certain condition.

# wait for digital input to become high
stream.wait_digital_input(1, True)
# wait for digital input to become low
stream.wait_digital_input(1, False)

stream.set_digital_output(1, True)
stream.toggle_digital_output(2)

stream.set_analog_output(1, 0.42) # in Volts
# condition can be one of '!=', '<=', '>=', '==', '>', '<'
stream.wait_analog_input(1, '>=', 0.50)
// wait for digital input to become high
await stream.waitDigitalInput(1, true);
// wait for digital input to become low
await stream.waitDigitalInput(1, false);

await stream.setDigitalOutput(1, true);
await stream.toggleDigitalOutput(1);

await stream.setAnalogOutput(1, 0.42);
// condition can be one of '!=', '<=', '>=', '==', '>', '<'
await stream.waitAnalogInput(1, '>=', 0.50); // in Volts
// wait for digital input to become high
stream.WaitDigitalInput(1, true);
// wait for digital input to become low
stream.WaitDigitalInput(1, false);

stream.SetDigitalOutput(1, true);
stream.ToggleDigitalOutput(1);

stream.SetAnalogOutput(1, 0.42);
// condition can be one of '!=', '<=', '>=', '==', '>', '<'
stream.WaitAnalogInput(1, ">=", 0.50); // in Volts
// wait for digital input to become high
stream.waitDigitalInput(1, true);
// wait for digital input to become low
stream.waitDigitalInput(1, false);

stream.setDigitalOutput(1, true);
stream.toggleDigitalOutput(1);

stream.setAnalogOutput(1, 0.42);
// condition can be one of '!=', '<=', '>=', '==', '>', '<'
stream.waitAnalogInput(1, ">=", 0.50); // in Volts
% wait for digital input to become high
stream.waitDigitalInput(1, true);
% wait for digital input to become low
stream.waitDigitalInput(1, false);

stream.setDigitalOutput(1, true);
stream.toggleDigitalOutput(1);

stream.setAnalogOutput(1, 0.42);
% condition can be one of '!=', '<=', '>=', '==', '>', '<'
stream.waitAnalogInput(1, ">=", 0.50); % in Volts
// wait for digital input to become high
stream.waitDigitalInput(1, true);
// wait for digital input to become low
stream.waitDigitalInput(1, false);

stream.setDigitalOutput(1, true);
stream.toggleDigitalOutput(1);

stream.setAnalogOutput(1, 0.42);
// condition can be one of '!=', '<=', '>=', '==', '>', '<'
stream.waitAnalogInput(1, ">=", 0.50); // in Volts

Corking

Corking a stream blocks the execution of commands in a Live stream's action queue. Execution resumes when the stream is uncorked, or when the number of queued actions reaches its limit.

Corking is useful when you are doing a large number of fast actions, such as moving in small incremements or interacting with I/O multiple times. If actions are executed faster than they are received, the action queue will empty and may cause discontinuities in motion. This may also cause irregularities in timing of actions such as setting I/O, or waiting for small intervals.

Corking reduces the likelihood of running out of actions by first populating the action queue on the device, and then telling the device to execute it.

# block action execution
stream.cork()

# ... add some actions to queue

# unblock action execution
stream.uncork()
// block action execution
await stream.cork();

// ... add some actions to queue

// unblock action execution
await stream.uncork();
// block action execution
stream.Cork();

// ... add some actions to queue

// unblock action execution
stream.Uncork();
// block action execution
stream.cork();

// ... add some actions to queue

// unblock action execution
stream.uncork();
% block action execution
stream.cork();

% ... add some actions to queue

% unblock action execution
stream.uncork();
// block action execution
stream.cork();

// ... add some actions to queue

// unblock action execution
stream.uncork();

Note that the stream must be idle when issuing the cork command.

See the stream fifo command documentation in the ASCII Protocol Manual for more details.

Stream Information

You can get information about a stream, such as textual representation, axes, and movement settings. See the following example:

print(repr(stream))
print(stream.axes)

print(stream.get_max_speed(Units.VELOCITY_CENTIMETRES_PER_SECOND))
print(stream.get_max_tangential_acceleration(Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED))
print(stream.get_max_centripetal_acceleration(Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED))
console.log(stream.toString());
console.log(stream.axes);

console.log(await stream.getMaxSpeed(Velocity.CENTIMETRES_PER_SECOND));
console.log(await stream.getMaxTangentialAcceleration(Acceleration.CENTIMETRES_PER_SECOND_SQUARED));
console.log(await stream.getMaxCentripetalAcceleration(Acceleration.CENTIMETRES_PER_SECOND_SQUARED));
Console.WriteLine(stream.ToString());
Console.WriteLine(string.Join<StreamAxisDefinition>(", ", stream.Axes));

Console.WriteLine(stream.GetMaxSpeed(Units.Velocity_CentimetresPerSecond));
Console.WriteLine(stream.GetMaxTangentialAcceleration(Units.Acceleration_CentimetresPerSecondSquared));
Console.WriteLine(stream.GetMaxCentripetalAcceleration(Units.Acceleration_CentimetresPerSecondSquared));
System.out.println(stream.toString());
System.out.println(Arrays.toString(stream.getAxes()));

System.out.println(stream.getMaxSpeed(Units.VELOCITY_CENTIMETRES_PER_SECOND));
System.out.println(stream.getMaxTangentialAcceleration(Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED));
System.out.println(stream.getMaxCentripetalAcceleration(Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED));
fprintf("%s\n", stream.toString());
fprintf("%d\n", stream.getAxes().size);
fprintf("%f\n",stream.getMaxSpeed(Units.VELOCITY_CENTIMETRES_PER_SECOND));
fprintf("%f\n",stream.getMaxTangentialAcceleration(Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED));
fprintf("%f\n",stream.getMaxCentripetalAcceleration(Units.ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED));
std::cout << stream.toString() << std::endl;
std::cout << stream.getAxes().size() << std::endl;

std::cout << stream.getMaxSpeed(Units::VELOCITY_CENTIMETRES_PER_SECOND) << std::endl;
std::cout << stream.getMaxTangentialAcceleration(Units::ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED) << std::endl;
std::cout << stream.getMaxCentripetalAcceleration(Units::ACCELERATION_CENTIMETRES_PER_SECOND_SQUARED) << std::endl;

Is Busy

A stream is busy if it is currently executing actions in its action queue. You can check if a Live stream is busy through the @method isBusy method.

The @method waitUntilIdle method waits until the stream becomes idle.

Here is an example of their usage:

if stream.is_busy():
    print("Stream is busy.")

stream.wait_until_idle()
if (await stream.isBusy()) {
  console.log('Stream is busy.');
}
await stream.waitUntilIdle();
if (stream.IsBusy())
{
   Console.WriteLine("Stream is busy.");
}
stream.WaitUntilIdle();
if (stream.isBusy()) {
    System.out.println("Stream is busy.");
}
stream.waitUntilIdle();
if stream.isBusy()
    fprintf("Stream is busy.")
end

stream.waitUntilIdle();
if (stream.isBusy()) {
      std::cout << "Stream is busy." << std::endl;
}
stream.waitUntilIdle();

Disabling

To disable a stream that has been setup call the @method disable method.

stream.disable()
await stream.disable();
stream.Disable();
stream.disable();
stream.disable();
stream.disable();

Lockstep

On devices with firmware version 7 and higher, the streams can also include axes in lockstep groups. In the following example the stream is set up on a lockstep group with number 1 and a single physical axis with number 3.

stream.setup_live_composite(
    StreamAxisDefinition(1, StreamAxisType.LOCKSTEP),
    StreamAxisDefinition(3, StreamAxisType.PHYSICAL)
)
stream.setupLiveComposite(
  { axisNumber: 1, axisType: StreamAxisType.LOCKSTEP },
  { axisNumber: 3, axisType: StreamAxisType.PHYSICAL },
);
stream.SetupLiveComposite(
    new StreamAxisDefinition { AxisNumber = 1, AxisType = StreamAxisType.Lockstep },
    new StreamAxisDefinition { AxisNumber = 3, AxisType = StreamAxisType.Physical }
);
stream.setupLiveComposite(
    new StreamAxisDefinition(1, StreamAxisType.LOCKSTEP),
    new StreamAxisDefinition(3, StreamAxisType.PHYSICAL)
);
stream.setupLiveComposite([
    StreamAxisDefinition(1, StreamAxisType.LOCKSTEP),
    StreamAxisDefinition(3, StreamAxisType.PHYSICAL)
]);
stream.setupLiveComposite(
    StreamAxisDefinition(1, StreamAxisType::LOCKSTEP),
    StreamAxisDefinition(3, StreamAxisType::PHYSICAL)
);

See the Lockstep how-to guide for more information about lockstep groups.

Resources