4using System.Threading.Tasks;
6using RSI.RapidCode.dotNET;
7using System.Diagnostics;
13 [Category(
"Software")]
14 class RapidSequencerAPIExample : SampleAppTestBase
17 static string samplesDirectory = TestContext.CurrentContext.TestDirectory +
@"\examples\rapidsequencer\";
19 static string samplesDirectory = TestContext.CurrentContext.TestDirectory +
@"\..\examples\rapidsequencer\";
22 string multipleTasksSampleFile = samplesDirectory +
"MultipleTasks.sq";
24 int GLOBAL_TEST_LOOPS;
26 public String _nodeName;
27 public String _rmpNodeName;
28 public RSI.RapidSequencer.Platform _platform;
30 public String workingDir = TestContext.CurrentContext.TestDirectory;
33 public ulong timeoutMs;
35 private void ShutdownSequencers()
45 string runShell(
string cmd,
string cmdArgs)
47 Process cliProcess =
new Process()
49 StartInfo =
new ProcessStartInfo(cmd, cmdArgs)
51 UseShellExecute =
false,
52 RedirectStandardOutput =
true
56 string cliOut = cliProcess.StandardOutput.ReadToEnd();
57 cliProcess.WaitForExit();
63 bool hasValipIP(
string ifconfigOutput)
65 string[] invalidIPs = {
"0.0.0.0",
"127.0.0.1" };
66 System.Text.RegularExpressions.Regex ipv4Regex =
new System.Text.RegularExpressions.Regex(
@"inet\s+\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b");
68 System.Text.RegularExpressions.Match ipv4Match = ipv4Regex.Match(ifconfigOutput);
70 while (ipv4Match.Success)
72 string ipv4String = ipv4Match.Groups[1].Value;
73 if (!invalidIPs.Contains(ipv4String))
78 ipv4Match = ipv4Match.NextMatch();
83 void ConfigureINtimeNetworkStack()
85 string intime_base = Environment.ExpandEnvironmentVariables(
"%intime%");
86 string piperta = intime_base +
"bin\\piperta";
87 string ifconfig =
"-node " + this._nodeName +
" \"" + intime_base +
"network7\\ifconfig\"";
88 string netload =
"-node " + this._nodeName +
" \"" + intime_base +
"network7\\netload\"";
90 string ifconfigOut = runShell(piperta, ifconfig);
91 bool validIP = hasValipIP(ifconfigOut);
97 runShell(piperta, netload);
99 ifconfigOut = runShell(piperta, ifconfig);
101 validIP = hasValipIP(ifconfigOut);
103 Assert.IsTrue(validIP,
"ifconfig did not return a valid IP address (even after netload)!");
107 public void SetupFixture()
111 ConfigureINtimeNetworkStack();
113 ShutdownSequencers();
115 if (controller !=
null)
120 controller = MotionController.
CreateFromSoftware(TestContext.CurrentContext.TestDirectory);
121 int frameSendReceiveMicroseconds = 0;
124 for (
int index = 0; index < controller.
AxisCountGet(); index++)
126 AxisSetup(controller.
AxisGet(index));
135 private void FastWaitForDone(Axis axis)
137 const int MAX_LOOPS = 100;
139 while (!axis.MotionDoneGet())
143 if (loops > MAX_LOOPS)
144 Assert.Fail(
"Failed waiting for motion done (fast) on Axis " + axis.NumberGet());
148 public static UInt64 DedidcatedInputAddressGet(Axis axis)
151 var dedicatedInPointerAddress = axis.AddressGet(
RSIAxisAddressType.RSIAxisAddressTypeDEDICATED_INPUTS_POINTER);
152 var dedicatedInPointer = axis.rsiControl.MemoryGet(dedicatedInPointerAddress);
153 var dedicatedInAddress = axis.rsiControl.HostAddressGet((uint)dedicatedInPointer);
154 return dedicatedInAddress;
157 public void AxisSetup(Axis axis)
159 while (axis.ErrorLogCountGet() > 0)
161 axis.ErrorLogClear();
163 axis.ThrowExceptions(
true);
165 const double USER_UNITS = 1000.0;
166 axis.UserUnitsSet(USER_UNITS);
168 axis.InterruptEnableSet(
false);
170 const double ACCELERATION = 1000000;
171 axis.EStopDecelerationSet(ACCELERATION);
174 axis.AmpFaultActionSet(
RSIAction.RSIActionNONE);
175 axis.HardwareNegLimitActionSet(
RSIAction.RSIActionNONE);
176 axis.HardwareNegLimitTriggerStateSet(
true);
177 axis.HardwarePosLimitActionSet(
RSIAction.RSIActionNONE);
178 axis.HardwarePosLimitTriggerStateSet(
true);
179 axis.ErrorLimitActionSet(
RSIAction.RSIActionNONE);
181 const double COARSE_POSITION_PHANTOM = Double.MaxValue / 10;
182 axis.PositionToleranceCoarseSet(COARSE_POSITION_PHANTOM);
184 const double FINE_POSITION_PHANTOM = Double.MaxValue / 10;
185 axis.PositionToleranceFineSet(FINE_POSITION_PHANTOM);
186 ulong dedicatedInAddress = DedidcatedInputAddressGet(axis);
187 axis.rsiControl.MemorySet(dedicatedInAddress, 0);
191 FastWaitForDone(axis);
193 Assert.That(axis.StateGet(), Is.EqualTo(
RSIState.RSIStateIDLE));
195 axis.CommandPositionSet(0);
196 axis.CommandPositionDirectSet(0);
197 axis.MotionCamRepeatStop();
198 axis.DefaultVelocitySet(Constants.VELOCITY);
199 axis.DefaultAccelerationSet(Constants.ACCELERATION);
200 axis.DefaultDecelerationSet(Constants.ACCELERATION);
201 axis.DefaultJerkPercentSet(Constants.JERK_PERCENT);
202 axis.BacklashWidthSet(0.0);
203 axis.BacklashRateSet(0.0);
206 axis.GearingDisable();
211 axis.MotionAttributeMaskDefaultSet();
214 Assert.That(axis.StateGet(), Is.EqualTo(
RSIState.RSIStateIDLE),
"Axis " + axis.NumberGet() +
" could not clear faults in AxisSetup. Source is: " + axis.SourceNameGet(axis.SourceGet()));
215 Assert.That(axis.CommandPositionGet(), Is.EqualTo(0),
"Axis " + axis.NumberGet() +
" command position is not zero.");
217 axis.FeedRateSet(1.0);
220 axis.EStopDecelerationSet(10000.0);
221 axis.EStopJerkPercentSet(50.0);
222 axis.TriggeredModifyDecelerationSet(10000.0);
223 axis.TriggeredModifyJerkPercentSet(50.0);
224 axis.SoftwareNegLimitActionSet(
RSIAction.RSIActionE_STOP);
225 axis.SoftwarePosLimitActionSet(
RSIAction.RSIActionE_STOP);
226 axis.SoftwareNegLimitTriggerValueSet(-1125899906842620);
227 axis.SoftwarePosLimitTriggerValueSet(1125899906842620);
228 axis.ErrorLimitTriggerValueSet(1000);
230 axis.FilterDualLoopSet((Axis)axis,
RSIMotorFeedback.RSIMotorFeedbackPRIMARY,
false);
232 axis.HomeOffsetSet(0.0);
235 axis.AmpEnableSet(
true);
239 public void TeardownFixture()
241 ShutdownSequencers();
245 public void TearDownSequencers()
247 ShutdownSequencers();
253 this._platform = platform;
255 this._nodeName =
"NodeB";
256 this._rmpNodeName =
"NodeA";
258 GLOBAL_TEST_LOOPS = loops;
266 public void TwoTasksSimultaneously()
268 Assert.That(this._platform, Is.EqualTo(
RSI.
RapidSequencer.Platform.INtime),
"Timing tests are only valid on real-time operating systems!");
276 string rmpPath = TestContext.CurrentContext.TestDirectory;
277 sequencer1.
EngineStart(this._rmpNodeName, rmpPath);
278 sequencer2.
EngineStart(this._rmpNodeName, rmpPath);
281 sequencer1.
Compile(multipleTasksSampleFile,
"RunEverySample");
282 sequencer2.
Compile(multipleTasksSampleFile,
"RunEvery50Samples");
285 string runEverySampleTaskId = sequencer1.
RunAsync();
286 string runEvery50SamplesTaskId = sequencer2.
RunAsync();
291 for (
int i = 0; i < GLOBAL_TEST_LOOPS; i++)
300 Assert.That(loopCout.Value, Is.EqualTo(1),
"This must be 1 if the program is running every sample. Failed on loop " + i);
302 Assert.That(loopCout50.Value, Is.EqualTo(50),
"This must be 50 if the program is running every 50 samples. Failed on loop " + i);
308 sequencer1.
TaskStop(runEverySampleTaskId);
309 sequencer2.
TaskStop(runEvery50SamplesTaskId);
314 private void RunFunctionAndTestGlobal(
string functionName,
string globalName,
int expectedValue)
316 Assert.That(this._platform, Is.EqualTo(
RSI.
RapidSequencer.Platform.INtime),
"Timing tests are only valid on real-time operating systems!");
320 string rmpPath = TestContext.CurrentContext.TestDirectory;
323 sequencer.
Compile(multipleTasksSampleFile, functionName);
325 string taskId = sequencer.
RunAsync();
329 NUnit.Framework.Constraints.IResolveConstraint constraint;
332 constraint = Is.EqualTo(expectedValue);
336 constraint = Is.GreaterThanOrEqualTo(expectedValue);
339 for (
int i = 0; i < GLOBAL_TEST_LOOPS; i++)
343 Assert.That(globalValue.Value, constraint,
"This must be " + expectedValue +
" if the program is running every " + expectedValue +
" samples. Failed on loop #" + i);
351 public void RunEverySample()
353 RunFunctionAndTestGlobal(
"RunEverySample",
"loopCounterDelta", 1);
357 public void RunEvery50Samples()
359 RunFunctionAndTestGlobal(
"RunEvery50Samples",
"loopCounterDelta50", 50);
365 public void BasicGlobalData()
379 string rmpPath = TestContext.CurrentContext.TestDirectory;
383 sequencer.
Compile(samplesDirectory +
"BasicGlobalData.sq",
"main");
386 string taskId = sequencer.
RunAsync();
394 string globalIntName =
"globalInt";
395 int expectedIntValue = 2;
403 int intValue = globalInt.Value;
406 Assert.That(intValue, Is.EqualTo(expectedIntValue));
413 string globalDoubleName =
"globalDouble";
414 double expectedDoubleValue = globalInt.Value * 3.14159;
431 double doubleValue = globalDouble.Value;
434 Assert.That(doubleValue, Is.EqualTo(expectedDoubleValue));
437 Assert.That(globalDouble.Type, Is.EqualTo(
RSI.
RapidSequencer.
SequencerGlobal.DataType.Double), $
"Global variable {globalDoubleName} was not of type double!");
444 StringAssert.Contains($
"{expectedDoubleValue}", output, $
"Sequencer program output does not contain expected value {expectedDoubleValue}. Output: {output}");
452 public void MoveSCurve()
454 string fileNameString =
"MoveSCurve.sq";
459 string rmpPath = TestContext.CurrentContext.TestDirectory;
462 string entryPoint =
"main";
463 sequencer.
Compile(samplesDirectory + fileNameString, entryPoint);
465 string taskId = sequencer.
Run();
470 double expectedFinalPosition = 1.0;
471 Console.WriteLine($
"output: {output}");
472 StringAssert.Contains($
"{expectedFinalPosition}", output, $
"Sequencer program axis move did not reach final destination: {expectedFinalPosition}. Output: {output}");
Axis * AxisGet(int32_t axisNumber)
AxisGet returns a pointer to an Axis object and initializes its internals.
void MemorySet(uint64_t address, int32_t data)
Write a value to controller memory.
uint64_t AddressGet(RSIControllerAddressType type)
Get the an address for some location on the MotionController.
static MotionController * CreateFromSoftware()
Initialize and start the RMP EtherCAT controller.
void Delete(void)
Delete the MotionController and all its objects.
int32_t AxisCountGet()
Get the number of axes processing.
void InterruptEnableSet(bool enable)
Control interrupts for this class.
string TaskOutputGet(string taskId)
Gets the string output of the specified task.
string RunAsync(ulong[] breakpoints=null)
Run the last compiled script. Does not wait for the script to finish excecution. Returns the ID of th...
void TaskStop(string taskId)
Stops the specified task.
CompileSuccess Compile(string path, string entryPoint=DEFAULT_ENTRY_POINT, string exceptionHandler="")
Compiles the script at the given path. If successful, saves this compilation result to run at a later...
void GlobalVariableSet(string name, SequencerGlobal.DataType type, dynamic value)
Sets the value of the specified global variable.
string Run(ulong[] breakpoints=null)
Run the last compiled script. Waits for the script to finish execution. Returns the ID of the resulti...
void EngineStart(string rmpNode="NodeA", string rmpPath="")
Starts the runtime of the RapidSequencer process.
SequencerGlobal GlobalVariableGet(string name)
Gets the value of the global variable with the given name.
static RapidSequencer Create(Platform platform, string sequencerNodeName, string rmpNodeName, string executablePath, int grpcPort=DEFAULT_GRPC_PORT, string friendlyName="RapidServer", ulong timeoutMs=DEFAULT_TIMEOUT_MS, int discoveryPort=DEFAULT_DISCOVER_PORT)
Creates a RapidSequencer process on the given platform at the specified port if one does not already ...
The RapidSequencerFactory provides static methods for creating RapidSequencer processes or discoverin...
An object for interacting with a RapidSequencer process.
RSIFilterAlgorithm
Filter algorithms.
RSIControllerAddressType
Used to get firmware address used in User Limits, Sequencers, etc.
RSIAction
Action to perform on an Axis.
RSIAxisAddressType
Used to get firmware address used in User Limits, Sequencers, etc.
RSIMotionHoldType
Types for MotionHold attribute.
RSIMotorFeedback
Encoders per motor.
Structure for describing a global tag. Contains information about the type, name, and value of the ta...