APIs, concepts, guides, and more
rapidcoderemote-quickstart.cs
/* 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);
ControllerShutdown(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}");
}
// 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");
}
#pragma warning disable CS8321 // local function is declared but never used (purposely not called in this sample yet, but included for documentation purposes)
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
{
Index = Constants.AXIS_0_INDEX,
Config = new AxisConfig
{
UserUnits = 1000,
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
{
Index = Constants.AXIS_0_INDEX,
Action = new AxisAction
{
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");
// wait for motion to complete
System.Threading.Thread.Sleep(1000);
// 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
{
Index = Constants.AXIS_0_INDEX,
Config = new AxisConfig
{
UserUnits = 1000,
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
{
Index = Constants.AXIS_0_INDEX,
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;
}