The RMP Motion Controller APIs
GcodeMotion.cs
using RSI.RapidCode.dotNET; // Import our RapidCode Library.
using NUnit.Framework;
using System;
using System.Linq;
using System.Threading;
using System.IO;
using System.Runtime.InteropServices;
#if DOXYGEN // RSI internal documentation use only
#endif
{
public override void Execute(GcodeCallbackData data)
{
Console.WriteLine("G-Code Callback executed: " + data.LineNumber + " " + data.LineText);
// if you want to nofity the Gcode object that there's an error processing, set its error details:
// data.UserError.number = RSIErrorMessage.RSI_ERROR_MESSAGE_DYNAMIC;
// data.UserError.text = "This is an error from the callback.";
}
}
[TestFixture]
[Category("Software")]
public class GcodeMotion : SampleAppTestBase
{
[SetUp]
public void Setup()
{
jointsMultiAxis?.AxisRemoveAll();
}
[TearDown]
public void Teardown()
{
robot?.EStop();
robot?.MotionDoneWait();
robot?.ClearFaults();
Robot.RobotDelete(controller, robot);
robot = null;
jointsMultiAxis?.Abort();
Thread.Sleep(50);
jointsMultiAxis?.ClearFaults();
}
private void VerifyModel(KinematicModel model, string expectedName, LinearUnits expectedUnits = LinearUnits.None)
{
var actualUnits = model.UnitsGet();
Assert.That(actualUnits, Is.EqualTo(expectedUnits), $"Expected model units to be '{expectedUnits}' but was '{actualUnits}' instead!");
var actualName = model.NameGet();
Assert.That(actualName, Is.EqualTo(expectedName), $"Expected model name to be '{expectedName}' but was '{actualName}' instead!");
}
[Test]
public void GcodeBasic()
{
string gcodeProgram = "G91; Sets the programming mode to RELATIVE\n" +
"G64; Turns off exact stop mode(Default)\n" +
"G1 X1.0 Y0.0 Z0.0 A1.0 F60.0; Move on USERUNIT in positive x direction at 60in/min. Moves Free axis A to position 1.0.\n" +
"G3 X1 Y1 I0 J1; Counter clockwise arc with a center point of 0,1,0 and end point of 1,1,0 relative to the current position\n" +
"M80; Show how to use an M-code with GcodeCallback!\n";
// We assume the axes have been confgured and homed properly prior this this program.
const string xLabel = "X-Axis";
const string yLabel = "Y-Axis";
const string zLabel = "Z-Axis";
const string aLabel = "A-Axis";
x_axis.UserLabelSet(xLabel);
y_axis.UserLabelSet(yLabel);
z_axis.UserLabelSet(zLabel);
a_axis.UserLabelSet(aLabel);
// The joint index of each axis is the index within the MultiAxis object.
// "X-Axis" has joint index 0
// "Y-Axis" has joint index 1
// "Z-Axis" has joint index 2
// "A-Axis" has joint index 3
Axis[] axes = new Axis[] { x_axis, y_axis, z_axis, a_axis };
jointsMultiAxis.AxesAdd(axes, axes.Length);
jointsMultiAxis.ClearFaults();
jointsMultiAxis.AmpEnableSet(true);
const string modelName = "RSI_XYZA";
const double scaling = 1.0, offset = 0.0;
LinearModelBuilder builder = new LinearModelBuilder(modelName);
builder.JointAdd(new LinearJointMapping(0, CartesianAxis.X) { ExpectedLabel = xLabel, Scaling = scaling, Offset = offset });
builder.JointAdd(new LinearJointMapping(1, CartesianAxis.Y) { ExpectedLabel = yLabel, Scaling = scaling, Offset = offset });
builder.JointAdd(new LinearJointMapping(2, CartesianAxis.Z) { ExpectedLabel = zLabel, Scaling = scaling, Offset = offset });
builder.FreeAxisAdd(new ModelAxisMapping(3) { ExpectedLabel = aLabel, Scaling = scaling, Offset = offset }); // Add a free axis.
// Create a Robot object with a multi axis containing all joints and the kinematic type
robot = Robot.RobotCreate(controller, jointsMultiAxis, builder, MotionController.AxisFrameBufferSizeDefault);
robot.Gcode.AccelerationRateSet(1000); // UserUnits per minute squared
// The free axis index here refers to the index in the RobotPosition freeAxes array. We use index 0 here to refer to the first position in that array.
robot.Gcode.FreeAxisLetterSet('A', 0);
robot.Gcode.CallbackRegister(callback); // Register a callback to be called when the Gcode encounters any M-code command
try
{
robot.Gcode.Load(gcodeProgram); // Loads in the string and prepairs it for execution
}
catch (Exception e)
{
if (robot.Gcode.SyntaxErrorLineNumberGet() != -1)
{
// LineNumberGet retusns positive when a gcode errors exists on a known line. Or if the exception is of type PathLine or GcodeLine
Console.WriteLine("Error on line: " + robot.Gcode.SyntaxErrorLineNumberGet() + "Details: " + e.Message);
}
else
{
Console.WriteLine("Error: " + e.Message);
}
}
// print some details about the upcoming motion
Console.WriteLine("G-Code Line Count: " + robot.Gcode.LineCountGet());
Console.WriteLine("G-Code Error Log Count: " + robot.Gcode.ErrorLogCountGet());
Console.WriteLine("G-code estimated run time: " + robot.Gcode.DurationGet() + " seconds");
robot.Gcode.Run(); // Starts the motion. Calling with the false arguement for non blocking behavior
Int64 activeLineNumber = 0;
do
{
Thread.Sleep(200);
if (activeLineNumber != robot.Gcode.ExecutingLineNumberGet()) // only write if we are on a new line
{
activeLineNumber = robot.Gcode.ExecutingLineNumberGet();
Console.WriteLine("G-Code Line Number: " + activeLineNumber);
}
} while (robot.Gcode.IsRunning());
Assert.That(robot.Gcode.ErrorLogCountGet(), Is.EqualTo(0), "Gcode Error Log Count is not zero. first error: " + robot.Gcode.ErrorLogGet().Message);
Assert.AreEqual(gcodeProgram.Count(c => c.Equals('\n')), robot.Gcode.LineCountGet());
VerifyModel(robot.ModelGet(), modelName);
}
[Test]
public void ChangingUnits()
{
const string xLabel = "X-Axis";
const string yLabel = "Y-Axis";
const string zLabel = "Z-Axis";
const string aLabel = "A-Axis";
const string bLabel = "B-Axis";
const string cLabel = "C-Axis";
x_axis.UserLabelSet(xLabel);
y_axis.UserLabelSet(yLabel);
z_axis.UserLabelSet(zLabel);
a_axis.UserLabelSet(aLabel);
b_axis.UserLabelSet(bLabel);
c_axis.UserLabelSet(cLabel);
// The joint index of each axis is the index within the MultiAxis object.
// "X-Axis" has joint index 0
// "Y-Axis" has joint index 1
// "Z-Axis" has joint index 2
// "A-Axis" has joint index 3
// "B-Axis" has joint index 4
// "C-Axis" has joint index 5
Axis[] axes = new Axis[] { x_axis, y_axis, z_axis, a_axis, b_axis, c_axis };
jointsMultiAxis.AxesAdd(axes, axes.Length);
jointsMultiAxis.ClearFaults();
const string modelName = "RSI_XYZABC_Centimeters";
const LinearUnits units = LinearUnits.Centimeters;
const double scaling = 1.0, offset = 0.0;
LinearModelBuilder builder = new LinearModelBuilder(modelName);
builder.UnitsSet(units);
builder.JointAdd(new LinearJointMapping(0, CartesianAxis.X) { ExpectedLabel = xLabel, Scaling = scaling, Offset = offset });
builder.JointAdd(new LinearJointMapping(1, CartesianAxis.Y) { ExpectedLabel = yLabel, Scaling = scaling, Offset = offset });
builder.JointAdd(new LinearJointMapping(2, CartesianAxis.Z) { ExpectedLabel = zLabel, Scaling = scaling, Offset = offset });
builder.JointAdd(new LinearJointMapping(3, CartesianAxis.Roll) { ExpectedLabel = aLabel, Scaling = scaling, Offset = offset });
builder.JointAdd(new LinearJointMapping(4, CartesianAxis.Pitch) { ExpectedLabel = bLabel, Scaling = scaling, Offset = offset });
builder.JointAdd(new LinearJointMapping(5, CartesianAxis.Yaw) { ExpectedLabel = cLabel, Scaling = scaling, Offset = offset });
// Create a Robot object with a multi axis containing all joints and the kinematic type
robot = Robot.RobotCreate(controller, jointsMultiAxis, builder, MotionController.AxisFrameBufferSizeDefault);
//NOTE: to use the above kinematic model you must have a gantry (linear 1:1 kinematics) and each linear axis must have its user units scaled to millimeters
//This will return none. A gcode unit hasn't been established to it will use path units. If path units are not set it will use user units.
Console.WriteLine(robot.Gcode.UnitsGet());
robot.Gcode.AccelerationRateSet(10); //Sets the G-Code acceleration to 10 Centimeters(USER UNITS) per MINUTE sqd
robot.Gcode.FeedRateSet(10); //Sets the G-Code velocity to 10 Centimeters(USER UNITS) per MINUTE
robot.Gcode.UnitsSet(LinearUnits.Inches);//This is the same as executing the G-Code Line G20
Console.WriteLine(robot.Gcode.UnitsGet());//This will return Inches as you just set.
robot.Gcode.AccelerationRateSet(10); //Sets the G-Code acceleration to 10 Inches per MINUTE sqd
robot.Gcode.FeedRateSet(10); //Sets the G-Code velocity to 10 Inches per MINUTE
VerifyModel(robot.ModelGet(), modelName, units);
}
}
static void CheckErrors(RapidCodeObject rsiObject)
Check if the RapidCodeObject has any errors.
Helper Functions for checking logged creation errors, starting the network, etc.
void UserLabelSet(const char *const userLabel)
Set the axis User defined Label.
Represents a single axis of motion control. This class provides an interface for commanding motion,...
Definition rsi.h:5664
int32_t LineCountGet()=0
Get the number of lines in the last loaded G-Code program.
int32_t SyntaxErrorLineNumberGet()=0
Gets the line number of any errors in the G-Code syntax.
int32_t ExecutingLineNumberGet()=0
Get the currently executing line number.
LinearUnits UnitsGet()=0
Get the currently active unit as set by G20/G21.
void AccelerationRateSet(double programmingUnitsPerMinuteSquared)=0
Sets the target acceleration for the machine (units/minute^2). Should be set apprpriately based on yo...
void UnitsSet(LinearUnits units)=0
Set the currently active unit (same as calling G20/G21)
double DurationGet()=0
Get the time duration required to run the loaded G-Code program in seconds.
void FreeAxisLetterSet(const char gcodeLetter, const int freeAxisIndex)=0
Map a letter in a Gcode file to a free axis. It will be used to specify the free axis' motion.
void FeedRateSet(double programmingUnitsPerMinute)=0
Sets the target feed rate for the machine (units/minute).
void Run()=0
Run the loaded G-Code file (or string). The behavior is non-blocking. Use Robot.MotionDoneWait() to b...
Handles callbacks for M-codes within a G-code file.
void CallbackRegister(Cartesian::GcodeCallback *callback)=0
G-code callback register.
bool IsRunning()=0
Returns true if a Gcode program is executing, false otherwise.
void UnitsSet(LinearUnits units)
Set the units the built model will use.
void FreeAxisAdd(const ModelAxisMapping &freeAxis)
Map a free-axis in the kinematic model.
Describes the mathematical kinematic model of a robot.
The Builder for a linear kinematic model. Constructs a single Linear Kinematic Model to use when crea...
void JointAdd(const LinearJointMapping &joint)
adds a joint to the model using the configuration specified within the LinearJoint structure.
uint64_t MotionDoneWait()=0
Waits for a move to complete.
static Robot * RobotCreate(MotionController *controller, MultiAxis *multiAxis, KinematicModelBuilder *builder, uint32_t motionFrameBufferSize)
Create a Robot object to use G-Code, path motion, etc.
void EStop()=0
Commands a joint EStop and clears the loaded moves.
Represents a collection of joints in Cartesian space with forward and inverse kinematics....
void ClearFaults()=0
Clears the MultiAxis fault then the Robot's error bit.
const KinematicModel & ModelGet()=0
Get the model this robot was created with Visit our Topic Page for more information.
static void RobotDelete(MotionController *controller, Robot *robot)
Delete a Robot.
RSI::RapidCode::Cartesian::Gcode * Gcode
An object to load and run Gcode files.
static constexpr int32_t AxisFrameBufferSizeDefault
The default value of the AxisFrameBufferSize, also the minimum allowable value.
Definition rsi.h:814
Represents the RMP soft motion controller. This class provides an interface to general controller con...
Definition rsi.h:762
void AxisRemoveAll()
Remove all axes from a MultiAxis group.s.
void AxesAdd(Axis **axes, int32_t axisCount)
void ClearFaults()
Clear all faults for an Axis or MultiAxis.
void AmpEnableSet(bool enable)
Enable all amplifiers.
void Abort()
Abort an axis.
const RsiError *const ErrorLogGet()
Get the next RsiError in the log.
int32_t ErrorLogCountGet()
Get the number of software errors in the error log.
make a Callback class and register it with the Gcode object
CartesianAxis
This enum specifies which Cartesian axis a LinearJointMapping maps a robot joint to.
LinearUnits
Unit types. For G-code use.
The Cartesian namespace.
const char * LineText
The actual line content from the G-code file.
int32_t LineNumber
The line number in the G-code file where the M-code is encountered.
Holds data for the G-code M-code callback mechanism.
Data for adding joint or free-axis mappings when building a kinematic model.