APIs, concepts, guides, and more
rapidcoderemote-quickstart.cs
Note
See Quick Start 📜 for a detailed explanation of this sample code.
Warning
This is a sample program to assist in the integration of the RMP motion controller with your application. It may not contain all of the logic and safety features that your application requires. We recommend that you wire an external hardware emergency stop (e-stop) button for safety when using our code sample apps. Doing so will help ensure the safety of you and those around you and will prevent potential injury or damage.

The sample apps assume that the system (network, axes, I/O) are configured prior to running the code featured in the sample app. See the Configuration page for more information.
/* This sample application demonstrates how to use RapidCodeRemote (gRPC) to communicate with a RapidServer.
It shows server startup, connection, axis status checking, point-to-point motion, and streaming motion.
RapidCodeRemote allows you to control RMP hardware remotely over a network using gRPC.
*/
#:package Google.Protobuf@3.33.0
#:package Grpc.Net.Client@2.71.0
using Grpc.Net.Client;
using Google.Protobuf;
using RSI.RapidCodeRemote;
using RSI.RapidServer;
using static RSI.RapidCodeRemote.RMPService;
using static RSI.RapidServer.ServerControlService;
using System.Diagnostics;
/*
┌──────┐
│ MAIN │
└──────┘
*/
Console.WriteLine("📜 RapidCodeRemote: Quick Start");
// start the rapidserver
Process serverProcess = ServerStart();
// create grpc channel and clients
var address = $"http://{Constants.RAPIDSERVER_IP}:{Constants.RAPIDSERVER_PORT}";
var channel = GrpcChannel.ForAddress(address); // grpc channel used to communicate with the server
var serverClient = new ServerControlServiceClient(channel); // grpc client for using the ServerControlService
var rmpClient = new RMPServiceClient(channel); // grpc client for using the RMPService
try
{
ServerInfoGet(serverClient);
ControllerCreate(rmpClient);
AxisCountSet(rmpClient);
AxisStatusGet(rmpClient);
AxisMotionPointToPoint(rmpClient);
AxisMotionStreaming(rmpClient);
}
finally
{
// shutdown and dispose the channel
await channel.ShutdownAsync();
channel.Dispose();
// kill the rapidserver process
if (serverProcess != null && !serverProcess.HasExited)
{
serverProcess.Kill();
serverProcess.WaitForExit();
Console.WriteLine("Stopped RapidServer");
}
}
Console.WriteLine("\n✅ RapidCodeRemote Quick Start Complete");
/*
┌─────────┐
│ METHODS │
└─────────┘
*/
// request and display basic information about the server
void ServerInfoGet(ServerControlServiceClient client)
{
Console.WriteLine("\n 📜 Server Info");
ServerGetInfoRequest infoRequest = new ServerGetInfoRequest();
ServerGetInfoResponse infoResponse = client.GetInfo(infoRequest);
CheckErrors(infoResponse.Header);
Console.WriteLine($"Connected to server named '{infoResponse.Name}' with ID: {infoResponse.Id:X}");
Console.WriteLine($"Server version: {infoResponse.Version}");
}
// create the MotionController on the server
void ControllerCreate(RMPServiceClient client)
{
Console.WriteLine("\n 📜 Controller Create");
MotionControllerResponse motionControllerResponse = client.MotionController(new MotionControllerRequest
{
Action = new MotionControllerAction { Create = new MotionControllerCreationParameters() }
});
CheckErrors(motionControllerResponse.Header);
Console.WriteLine("Motion Controller created successfully");
}
// set the axis count so we have phantom axes to work with
void AxisCountSet(RMPServiceClient client)
{
Console.WriteLine("\n 📜 Axis Count");
MotionControllerResponse motionControllerResponse = client.MotionController(new MotionControllerRequest
{
Config = new MotionControllerConfig { AxisCount = Constants.AXIS_COUNT }
});
CheckErrors(motionControllerResponse.Header);
Console.WriteLine($"Axis count set to: {motionControllerResponse.Config.AxisCount}");
}
// check the state of the axis at index 0
void AxisStatusGet(RMPServiceClient client)
{
Console.WriteLine("\n 📜 Axis Status");
AxisResponse axisResponse = client.Axis(new AxisRequest { Index = Constants.AXIS_0_INDEX });
CheckErrors(axisResponse.Header);
Console.WriteLine($"Axis {Constants.AXIS_0_INDEX} state: {axisResponse.Status.State}");
}
#pragma warning disable CS8321 // local function is declared but never used (purposely not called in this sample yet, but included for documentation purposes)
// shutdown the motion controller
void ControllerShutdown(RMPServiceClient client)
{
Console.WriteLine("\n 📜 Controller Shutdown");
client.MotionController(new MotionControllerRequest
{
Action = new MotionControllerAction { Shutdown = new MotionControllerAction.Types.Shutdown() }
});
Console.WriteLine("Motion Controller shut down successfully");
}
// start the EtherCAT network
void NetworkStart(bool discoverBeforeStart = false)
{
Console.WriteLine("\n 📜 Network Start");
var action = new NetworkAction();
if (discoverBeforeStart)
action.DiscoverAndStart = new NetworkAction.Types.DiscoverAndStart();
else
action.Start = new NetworkAction.Types.Start();
NetworkResponse networkResponse = rmpClient.Network(new NetworkRequest
{
Action = action
});
CheckErrors(networkResponse.Header);
}
#pragma warning restore CS8321
// demonstrate a simple point-to-point move
void AxisMotionPointToPoint(RMPServiceClient client)
{
Console.WriteLine("\n 📜 Axis Motion: Point-to-Point");
// configure the axis for phantom use
AxisResponse axisResponse = client.Axis(new AxisRequest
{
Config = new AxisConfig
{
ErrorLimit = new AxisConfig.Types.ErrorLimit { Action = RSIAction.None } // ignore position errors for phantom axis
}
});
CheckErrors(axisResponse.Header);
// clear any faults and set initial position
axisResponse = client.Axis(new AxisRequest
{
Action = new AxisAction
{
Abort = new AxisAction.Types.Abort(),
ClearFaults = new AxisAction.Types.ClearFaults(),
PositionSet = new AxisAction.Types.PositionSet { Position = 0 }
}
});
CheckErrors(axisResponse.Header);
// command a point-to-point move from position 0 to position 10
var moveAction = new AxisAction
{
Move = new AxisAction.Types.Move
{
PointToPoint = new AxisMovePointToPoint
{
Acceleration = 100,
Deceleration = 100,
Velocity = 10,
Position = 10,
JerkPercent = 50
}
}
};
axisResponse = client.Axis(new AxisRequest { Index = Constants.AXIS_0_INDEX, Action = moveAction });
CheckErrors(axisResponse.Header);
Console.WriteLine($"Commanded move to position 10");
Console.WriteLine("Waiting for axis to stop moving...");
while (true)
{
axisResponse = client.Axis(new AxisRequest { Index = Constants.AXIS_0_INDEX });
CheckErrors(axisResponse.Header);
if (axisResponse.Status.State != RSIState.Moving)
break;
System.Threading.Thread.Sleep(100);
}
// check final position
axisResponse = client.Axis(new AxisRequest { Index = Constants.AXIS_0_INDEX });
CheckErrors(axisResponse.Header);
Console.WriteLine($"Final command position: {axisResponse.Status.Position.Command}");
}
// demonstrate streaming motion with continuously added points
void AxisMotionStreaming(RMPServiceClient client)
{
Console.WriteLine("\n 📜 Axis Motion: Streaming");
// configure the axis
AxisResponse axisResponse = client.Axis(new AxisRequest
{
Config = new AxisConfig
{
ErrorLimit = new AxisConfig.Types.ErrorLimit { Action = RSIAction.None }
}
});
CheckErrors(axisResponse.Header);
// wait for any previous motion to complete
Console.WriteLine("Waiting for axis to stop moving...");
while (true)
{
axisResponse = client.Axis(new AxisRequest { Index = Constants.AXIS_0_INDEX });
CheckErrors(axisResponse.Header);
if (axisResponse.Status.State != RSIState.Moving)
break;
System.Threading.Thread.Sleep(100);
}
// clear faults and set initial position
axisResponse = client.Axis(new AxisRequest
{
Action = new AxisAction
{
ClearFaults = new AxisAction.Types.ClearFaults(),
PositionSet = new AxisAction.Types.PositionSet { Position = 0 }
}
});
CheckErrors(axisResponse.Header);
// get initial motion id
int originalMotionId = (int)axisResponse.Status.MotionId;
// create first batch of streaming points (sine wave pattern)
var streamingMove = new MoveStreaming();
int numPoints = 100;
double amplitude = 1.0;
for (int i = 0; i < numPoints; i++)
{
double position = Math.Sin(i / (double)numPoints * 2 * Math.PI) * amplitude;
streamingMove.Positions.Add(position);
streamingMove.Times.Add(1.0 / numPoints);
}
streamingMove.EmptyCount = 50;
streamingMove.Retain = false;
streamingMove.Final = false;
// send first streaming move
var moveAction = new AxisAction
{
Move = new AxisAction.Types.Move { Streaming = streamingMove }
};
axisResponse = client.Axis(new AxisRequest { Index = Constants.AXIS_0_INDEX, Action = moveAction });
CheckErrors(axisResponse.Header);
Console.WriteLine("Sent first streaming move batch");
// add additional batches while motion executes
int previousMotionId = (int)axisResponse.Status.MotionId - 1;
for (int batch = 2; batch <= 5; batch++)
{
// wait for previous batch to start executing
while (true)
{
axisResponse = client.Axis(new AxisRequest { Index = Constants.AXIS_0_INDEX });
CheckErrors(axisResponse.Header);
if (axisResponse.Status.MotionIdExecuting >= previousMotionId)
break;
System.Threading.Thread.Sleep(10);
}
// create next batch with increasing amplitude
streamingMove = new MoveStreaming();
amplitude = batch;
for (int i = 0; i < numPoints; i++)
{
double position = Math.Sin(i / (double)numPoints * 2 * Math.PI) * amplitude;
streamingMove.Positions.Add(position);
streamingMove.Times.Add(1.0 / numPoints);
}
streamingMove.EmptyCount = 50;
streamingMove.Retain = false;
streamingMove.Final = (batch == 5); // mark last batch as final
moveAction = new AxisAction
{
Move = new AxisAction.Types.Move { Streaming = streamingMove }
};
axisResponse = client.Axis(new AxisRequest { Index = Constants.AXIS_0_INDEX, Action = moveAction });
CheckErrors(axisResponse.Header);
previousMotionId = (int)axisResponse.Status.MotionId - 1;
Console.WriteLine($"Sent streaming batch {batch} (amplitude={amplitude}, final={streamingMove.Final})");
}
// wait for motion to complete
Console.WriteLine("Waiting for streaming motion to complete...");
while (true)
{
// get motion status
axisResponse = client.Axis(new AxisRequest { Index = Constants.AXIS_0_INDEX });
CheckErrors(axisResponse.Header);
if (axisResponse.Status.State != RSIState.Moving)
break;
System.Threading.Thread.Sleep(100);
}
Console.WriteLine($"Streaming motion complete. Final state: {axisResponse.Status.State}");
Console.WriteLine($"Final command position: {axisResponse.Status.Position.Command}");
}
/*
┌─────────┐
│ HELPERS │
└─────────┘
*/
// check response header for errors and throw if any exist
void CheckErrors(RSI.RapidServer.ResponseHeader header)
{
if (header.Errors.Count > 0)
{
string message = "RPC had errors:\n";
foreach (var error in header.Errors)
{
message += $" {error.Message}\n";
}
throw new Exception(message);
}
}
// start the rapidserver process
Process ServerStart()
{
// set sample config params
string serverProcessName = "rapidserver";
// get the path to the rapidserver executable
string serverPath;
#if WINDOWS
serverPath = Path.Combine(Constants.RMP_WINDOWS_PATH, serverProcessName + ".exe");
#elif LINUX
serverPath = Path.Combine(Constants.RMP_LINUX_PATH, serverProcessName);
#endif
// start the rapidserver process
string cmdArgs = $"-grpc_port {Constants.RAPIDSERVER_PORT}";
Process serverProcess = Process.Start(serverPath, cmdArgs);
if (serverProcess == null || serverProcess.HasExited)
{
throw new Exception("Failed to start RapidServer process!");
}
Console.WriteLine($"Started RapidServer (PID: {serverProcess.Id}) on port {Constants.RAPIDSERVER_PORT}");
// give the server time to initialize
Thread.Sleep(1000);
return serverProcess;
}
Constants used in the C# sample apps.
Definition _constants.cs:3
const double AXIS_0_USER_UNITS
Default: 1.
Definition _constants.cs:17
const int AXIS_COUNT
Default: 6.
Definition _constants.cs:10
const int AXIS_0_INDEX
Default: 0.
Definition _constants.cs:11
const string RMP_LINUX_PATH
Default: /rsi.
Definition _constants.cs:5
const string RMP_WINDOWS_PATH
Default: .....
Definition _constants.cs:4
This namespace provides static methods and constants for user configuration in RMP applications....
Definition config.h:25
void CheckErrors(RapidCodeObject *rsiObject, const std::source_location &location=std::source_location::current())
Checks for errors in the given RapidCodeObject and throws an exception if any non-warning errors are ...
Definition helpers.h:32
void NetworkStart(MotionController *controller)
[CheckErrors]
Definition helpers.h:73