The RMP Motion Controller APIs
SyncInterrupt.cpp
1
18#ifdef __INTIME__ // INTIME!
19#define NOMINMAX
20#include "rt.h"
21#elif _WIN32 // Windows
22#define NOMINMAX
23#include "Windows.h"
24#include <tchar.h>
25#include <bitset>
26#else // linux
27#include <unistd.h>
28#include <sys/resource.h>
29#endif //
30
31#include <inttypes.h>
32
33#include <vector>
34#include <thread>
35#include <stdio.h>
36#include <sstream>
37#include <atomic>
38#include <cstring>
39#include <cstdio>
40
41#include "rsi.h"
42#include "HelperFunctionsCpp.h"
43#include "SyncInterrupt.h"
44
45// Changeable Constants
46constexpr int32_t SYNC_INTERRUPT_EXIT_ERROR = 5;
47// thread priorities
48constexpr int32_t SLOW_LOOP_MILLISECONDS = 50;
49
50constexpr bool PRINT_DEFAULT = true;
51constexpr int32_t PRINT_FREQUENCY_MS_DEFAULT = 50;
52constexpr int32_t TIMEOUT_DEFAULT = -1;
53
54
55#if defined(__INTIME__)
56constexpr int32_t LOWEST_PRIORITY = 200;
57constexpr int32_t LOW_PRIORITY = 150;
58constexpr int32_t HIGH_PRIORITY = 0;
59
60constexpr int32_t SAMPLERATE_DEFAULT = Hz_4000;
61constexpr int32_t SYNCPERIOD_DEFAULT = sync_1;
62constexpr int32_t SYNCPERIODTHRESHOLD_DEFAULT = threshold_65;
63#elif defined(_WIN32)
64constexpr int32_t LOWEST_PRIORITY = THREAD_PRIORITY_BELOW_NORMAL;
65constexpr int32_t LOW_PRIORITY = THREAD_PRIORITY_NORMAL;
66constexpr int32_t HIGH_PRIORITY = THREAD_PRIORITY_TIME_CRITICAL;
67
68constexpr int32_t SAMPLERATE_DEFAULT = Hz_1000;
69constexpr int32_t SYNCPERIOD_DEFAULT = sync_1;
70constexpr int32_t SYNCPERIODTHRESHOLD_DEFAULT = threshold_250;
71#else // Linux
72constexpr int32_t LOWEST_PRIORITY = 0;
73constexpr int32_t LOW_PRIORITY = 5;
74constexpr int32_t HIGH_PRIORITY = 35;
75constexpr int32_t HIGH_BUT_NOT_TOO_HIGH_LINUX_NICE = -5;
76constexpr int32_t RMP_NICE_LINUX = -19;
77
78constexpr int32_t SAMPLERATE_DEFAULT = Hz_4000;
79constexpr int32_t SYNCPERIOD_DEFAULT = sync_1;
80constexpr int32_t SYNCPERIODTHRESHOLD_DEFAULT = threshold_65;
81
82static const std::string& GetExePath()
83{
84 static constexpr int PATH_MAX = 255;
85 static std::string exe_string;
86 if (exe_string.empty())
87 {
88 char buf[PATH_MAX];
89 ssize_t len = ::readlink("/proc/self/exe", buf, sizeof(buf));
90 if (len == -1 || len == sizeof(buf))
91 len = 0;
92 buf[len] = '\0';
93 std::string buf_string(buf);
94 exe_string = buf_string.substr(0, buf_string.find_last_of("\\/") + 1);
95 }
96 return exe_string;
97}
98#endif
99
100
101// RapidCode objects
103
104// Instantiate variables
105// shared variables
106uint32_t cpuFrequency;
107int32_t currentPerformanceCounter;
108int32_t previousPerformanceCounter;
109int32_t deltaPerformanceCounter;
110int32_t syncInterruptIterations;
111double deltaMicroseconds;
112int32_t syncInterruptSampleCounter;
113int32_t lastSyncInterruptSampleCounter;
114
115int32_t returnCode;
116std::atomic_bool readyToCleanup;
117
118// Configurable
119int32_t sampleRate; // hz
120int32_t syncPeriod; // interrupt every MotionController SYNC_PERIOD samples
121int32_t printFrequencyMS;
122bool print;
123int32_t timeout_mS;
124int32_t syncPeriodThreshold_uS;
125int32_t process_cpu;
126
127#if !defined(RSI_TEST)
128
129class BufferedDataImpl : public StatisticsBuffer
130{
131public:
132 double mean, count;
133 BufferedDataImpl() : StatisticsBuffer() { this->Reset(); }
134 virtual void Init() override { this->Reset(); }
135 virtual void Reset() override { mean = 0; count = 0; }
136 virtual void AddData(const double& datum) override
137 {
138 this->StatisticsBuffer::AddData(datum);
139
140 double delta = datum - this->mean;
141 this->mean += delta / (++this->count);
142 }
143};
144
145BufferedDataImpl buffered_data_impl;
146StatisticsBuffer& buffered_stats = buffered_data_impl;
147
148void PrintTimingInfo()
149{
150 // && iterations to wait until we start looping.
151 // short circuit on bPrint
152 if (print && syncInterruptIterations)
153 {
154 printf("\t\t%ld\t|\t%8.1lfus\t|\t%8.1lfus\t|\t%8.1lfus\t|\t%8.1lfus\r",
155 syncInterruptIterations, deltaMicroseconds,
156 buffered_data_impl.min, buffered_data_impl.max, buffered_data_impl.mean);
157 }
158}
159void printTimingHeaderString()
160{
161 printf("Number Processed\t|\tDeltaT (uS)\t|\tMin (uS)\t|\tMax (uS)\t|\tMean (uS)\n");
162}
163
164void StatisticsThread() { return; }
165#endif
166
167void SyncInterruptMainLoopThread()
168{
169 const double cpuPeriod = 1.0 / cpuFrequency;
170 const double cpuPeriod_uS = cpuPeriod * MICROSECONDS_PER_SECOND;
171 double sample_frequency = 1.0 / sampleRate; // seconds per sample
172
173 // sync_period conversions
174 // sample_frequency * 1e0 = seconds per sample
175 // sample_frequency * 1e3 = milliseconds per sample
176 // sample_frequency * 1e6 = microseconds per sample
177 double sync_period_us = syncPeriod * sample_frequency * MICROSECONDS_PER_SECOND;
178
179 double threshold_low_us, threshold_high_us;
180 threshold_low_us = sync_period_us - syncPeriodThreshold_uS;
181 threshold_high_us = sync_period_us + syncPeriodThreshold_uS;
182 printf("Threshold Set [%8.1lf %8.1lf]\n", threshold_low_us, threshold_high_us);
183 printf("Sync Period Set %i (~%.1lf us).\n", syncPeriod, syncPeriod * sample_frequency * MICROSECONDS_PER_SECOND);
184
185 buffered_stats.Init();
186
187 // configure a Sync interrupt every syncPeriod samples
188 controller->SyncInterruptPeriodSet(syncPeriod);
189
190 // enable controller interrupts
191 controller->SyncInterruptEnableSet(true);
192
193 lastSyncInterruptSampleCounter = controller->SyncInterruptWait();
194 previousPerformanceCounter = controller->OS->PerformanceTimerCountGet();
195
196
197 // polling threads will set this false
198 while (!readyToCleanup)
199 {
200 // Wait for the interrupt
201 syncInterruptSampleCounter = controller->SyncInterruptWait();
202
203 // Calculate metrics and make sure that we are within our tolerances
204
205 // On all systems this should wake up within the sample of the interrupt.
206 // So the current sample should equal the last sample + our sync period
207 //
208 // On Real Time systems, the actual measured time between periods should be
209 // no greater than the period plus some defined maximum.
210 // This value is system specific.
211 // On non-RT systems, the actual measured time between periods should be on
212 // average the period, with most close to and some greater than the period.
213 // There is no maximum.
214 currentPerformanceCounter = controller->OS->PerformanceTimerCountGet();
215 deltaPerformanceCounter = currentPerformanceCounter - previousPerformanceCounter;
216 deltaMicroseconds = deltaPerformanceCounter * cpuPeriod_uS;
217
218 // add data to a rotating circular buffer for stats thread can calc
219 buffered_stats.AddData(deltaMicroseconds);
220
221 // Check if our absolute delta time (us) is within our threshold
222 if (deltaMicroseconds < threshold_low_us || threshold_high_us < deltaMicroseconds)
223 {
224 printf("\n");
225 printf("Sync Interrupt exceeded range of [%8.1lf %8.1lf] : %8.1lf\n", threshold_low_us, threshold_high_us, deltaMicroseconds);
226 PrintTimingInfo();
227 printf("\n");
228 returnCode = SYNC_INTERRUPT_EXIT_ERROR;
229 readyToCleanup = true;
230 break;
231 }
232 // check this before syncInterruptSampleCounter != (lastSyncInterruptSampleCounter + syncPeriod)
233 if (syncInterruptSampleCounter == lastSyncInterruptSampleCounter)
234 {
235 printf("\n");
236 printf("Sync Interrupt Got a double sample. syncCounter %ld, lastSyncCounter %ld, deltaT %8.1lf\n",
237 syncInterruptSampleCounter, lastSyncInterruptSampleCounter, deltaMicroseconds);
238 PrintTimingInfo();
239 printf("\n");
240 returnCode = SYNC_INTERRUPT_EXIT_ERROR;
241 readyToCleanup = true;
242 break;
243 }
244 // check if we were delayed between getting the interrupt and getting the sample counter
245 // Or if we completely missed a sample
246 if (syncInterruptSampleCounter != (lastSyncInterruptSampleCounter + syncPeriod))
247 {
248 printf("\n");
249 printf("Sync Interrupt missed a sample. syncCounter %ld, lastSyncCounter %ld\n", syncInterruptSampleCounter, lastSyncInterruptSampleCounter);
250 PrintTimingInfo();
251 printf("\n");
252 returnCode = SYNC_INTERRUPT_EXIT_ERROR;
253 readyToCleanup = true;
254 break;
255 }
256
257
258 // Do your calculations HERE!
259
260
261
262
263
264 // set current to last for next loop
265 previousPerformanceCounter = currentPerformanceCounter;
266 lastSyncInterruptSampleCounter = syncInterruptSampleCounter;
267 ++syncInterruptIterations;
268 }
269
270 // turn off Sync Interrupt
271 controller->SyncInterruptEnableSet(false);
272
273 buffered_stats.Reset();
274
275 return;
276}
277
278void PrinterThread()
279{
280 while (!readyToCleanup && syncInterruptIterations == 0)
281 {
282 std::this_thread::sleep_for(std::chrono::milliseconds(SLOW_LOOP_MILLISECONDS));
283 }
284 printTimingHeaderString();
285 do
286 {
287 std::this_thread::sleep_for(std::chrono::milliseconds(printFrequencyMS));
288 PrintTimingInfo();
289 } while (!readyToCleanup);
290}
291
292void KeyPressExitThread()
293{
294 // wait for someone to press a key
295 while (controller->OS->KeyGet((int32_t)RSIWait::RSIWaitPOLL) < 0 && !readyToCleanup)
296 {
297 std::this_thread::sleep_for(std::chrono::milliseconds(SLOW_LOOP_MILLISECONDS));
298 }
299 readyToCleanup = true;
300}
301
302void TimeoutThread()
303{
304 if (timeout_mS < 0)
305 {
306 return;
307 }
308 std::chrono::milliseconds chrono_timeout(timeout_mS);
309 std::chrono::time_point start = std::chrono::high_resolution_clock::now();
310 std::chrono::nanoseconds duration;
311 do
312 {
313 std::this_thread::sleep_for(std::chrono::milliseconds(SLOW_LOOP_MILLISECONDS));
314 duration = std::chrono::high_resolution_clock::now() - start;
315 } while (duration < chrono_timeout && !readyToCleanup);
316
317 readyToCleanup = true; // set in case we timed out!
318
319 return;
320}
321
322void SystemEventHandlerThread()
323{
324#if __INTIME__
325 EVENTINFO intimeEventInfo;
326 // wait for notification and check
327 while (!RtNotifyEvent(RT_SYSTEM_NOTIFICATIONS | RT_EXIT_NOTIFICATIONS,
328 SLOW_LOOP_MILLISECONDS, &intimeEventInfo) && !readyToCleanup)
329 {
330 if (GetLastRtError())
331 {
332 continue;
333 } // else E_OK
334
335 switch (intimeEventInfo.dwNotifyType)
336 {
337 case TERMINATE:
338 case NT_HOST_SHUTDOWN_PENDING:
339 case KERNEL_STOPPING:
340 case KERNEL_SHUTDOWN_PENDING:
341 case RT_CLIENT_DOWN:
342 case RT_CLIENT_UP:
343 case NT_HOST_DOWN:
344 case NT_HOST_UP:
345 case NT_BLUESCREEN:
346 readyToCleanup = true;
347 break;
348 }
349 }
350#elif _WIN32
351#else
352
353#endif
354 return;
355}
356
357
358
359// Raises the process base priority to realtime.
360// If you successfully raise the priority to realtime...
361// ALL THREADS RUN WITH A BASE PRIORITY OF RT.
362void RaiseProcessPriorityClass(int cpuAffinity, const char* const threadName)
363{
364#if defined(__INTIME__)
365#elif defined (_WIN32)
366 DWORD dwPriClass;
367 HANDLE hProcess = GetCurrentProcess();
368
369 // Display priority class
370 dwPriClass = GetPriorityClass(hProcess);
371 _tprintf(TEXT("Current priority class is 0x%x\n"), dwPriClass);
372
373 if (!SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS))
374 {
375 _tprintf(TEXT("Failed to set to REALTIME_PRIORITY_CLASS (%d)\n"), GetLastError());
376 }
377
378 // Display priority class
379 dwPriClass = GetPriorityClass(hProcess);
380 _tprintf(TEXT("Current priority class is 0x%x\n"), dwPriClass);
381
382 DWORD_PTR affinityMask = 1 << cpuAffinity;
383 BOOL success = SetProcessAffinityMask(hProcess, affinityMask);
384
385 wchar_t threadNameW[512];
386 swprintf_s(threadNameW, 512, L"%S", threadName);
387
388 SetThreadDescription(GetCurrentThread(), threadNameW);
389#else // linux
390
391 auto native_thread = pthread_self();
392 // set this low for main
393 struct sched_param thread_schedule;
394 thread_schedule.sched_priority = LOW_PRIORITY;
395 pthread_setschedparam(native_thread, SCHED_OTHER, &thread_schedule);
396
397 cpu_set_t affinityMask;
398 CPU_ZERO(&affinityMask);
399 CPU_SET(cpuAffinity, &affinityMask);
400 if (pthread_setaffinity_np(native_thread, sizeof(affinityMask), &affinityMask))
401 {
402 printf("Failed to set CPU affinity with errno %i", errno);
403 }
404 //if (sched_setaffinity(getpid(), sizeof(affinityMask), &affinityMask))
405 //{
406 // printf("Failed to set CPU affinity with errno %i", errno);
407 //}
408 pthread_setname_np(native_thread, threadName);
409#endif
410}
411
412template<class _Fp>
413std::thread CreateThreadAndSetPriority(_Fp&& __f, int32_t priority, const char* const threadName)
414{
415 std::thread thread = std::thread(__f);
416 auto native_thread = thread.native_handle();
417#if __INTIME__
418 // std::thread puts the restult of CreateRtThread into std::thread::__t_ of type native_handle(=void*)
419 // std::thread::native_handle returns std::thread::__t_
420 //RTHANDLE native_thread = reinterpret_cast<RTHANDLE>(thread.native_handle());
421 SetRtThreadPriority(reinterpret_cast<RTHANDLE>(native_thread), priority);
422#elif _WIN32
423 // crank up the thread priority
424 //HANDLE currentThreadHandle = thread.native_handle();
425 // Set the thread priority to time critical
426 SetThreadPriority(native_thread, priority);
427 int actualPriority = GetThreadPriority(native_thread);
428
429 std::stringstream id_ss;
430 id_ss << std::this_thread::get_id();
431 std::string tid_string = id_ss.str();
432
433 printf("Tried to set thread %s to priority %i. Is Actually %i\n",
434 tid_string.c_str(), priority, actualPriority
435 );
436
437 wchar_t threadNameW[512];
438 swprintf_s(threadNameW, 512, L"%S", threadName);
439
440 SetThreadDescription(native_thread, threadNameW);
441#else
442 //pthread_t native_thread = thread.native_handle();
443 struct sched_param thread_schedule;
444 thread_schedule.sched_priority = priority;
445 int sched;
446 switch(priority)
447 {
448 case HIGH_PRIORITY:
449 sched = SCHED_FIFO;
450 break;
451 default: // LOWEST_PRIORITY, LOW_PRIORITY
452 sched = SCHED_OTHER;
453 break;
454 }
455
456 pthread_setschedparam(native_thread, sched, &thread_schedule);
457 pthread_setname_np(native_thread, threadName);
458#endif
459
460 return thread;
461}
462
463void ParseSetGlobalArgs(int32_t argc, char* argv[], MotionController::CreationParameters &params)
464{
465 print = PRINT_DEFAULT;
466 timeout_mS = TIMEOUT_DEFAULT;
467 printFrequencyMS = PRINT_FREQUENCY_MS_DEFAULT;
468
469 sampleRate = SAMPLERATE_DEFAULT;
470 syncPeriod = SYNCPERIOD_DEFAULT;
471 syncPeriodThreshold_uS = SYNCPERIODTHRESHOLD_DEFAULT;
472#if __INTIME__
473#elif _WIN32
474 DWORD_PTR dwProcessAffinity, dwSystemAffinity;
475 GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity);
476 int64_t cpu_count = std::bitset<64>(dwProcessAffinity).count();
477 process_cpu = static_cast<decltype(process_cpu)>(cpu_count);
478#else // Linux
479 // syncPeriodThreshold_uS = syncPeriod * (1.0e6 / sampleRate); // 1/hz = seconds -> us = *1e6
480 // we want to be able to use cat /sys/devices/system/cpu/online
481 // sched_getaffinity(0,...) only shows cpus available for the scheduler....
482 cpu_set_t affinityMask;
483 CPU_ZERO(&affinityMask);
484 sched_getaffinity(0, sizeof(affinityMask), &affinityMask);
485 int32_t cpu_count = CPU_COUNT(&affinityMask); // 0 indexed
486 int32_t rmp_cpu = 3;
487 process_cpu = rmp_cpu - 1;
488
489 params.CpuAffinity = rmp_cpu;
490 const auto rmpPath = GetExePath();
491 std::snprintf(params.RmpPath, MotionController::CreationParameters::PathLengthMaximum, rmpPath.c_str());
492 // params.nicePriority = RMP_NICE_LINUX;
493#endif
494
495 const char* strarg_samplerate = "-SampleRate";
496 const char* strarg_syncperiod = "-SyncPeriod";
497 const char* strarg_printfreq = "-PrintFrequencyMS";
498 const char* strarg_timeoutms = "-Timeoutms";
499 const char* strarg_print = "-Print";
500 const char* strarg_thresholdus = "-Thresholdus";
501 const char* strarg_rmppath = "-RmpPath";
502 for (int i = 1; i < argc; ++i)
503 {
504 if (std::strncmp(argv[i], strarg_samplerate, sizeof(strarg_samplerate)) == 0)
505 {
506 if ((i + 1) < argc && argv[i+1][0] != '-')
507 {
508 sampleRate = strtol(argv[++i], nullptr, 10);
509 }
510 }
511 else if (std::strncmp(argv[i], strarg_syncperiod, sizeof(strarg_syncperiod)) == 0)
512 {
513 if ((i + 1) < argc && argv[i + 1][0] != '-')
514 {
515 syncPeriod = strtol(argv[++i], nullptr, 10);
516 }
517 }
518 else if (std::strncmp(argv[i], strarg_printfreq, sizeof(strarg_printfreq)) == 0)
519 {
520 if ((i + 1) < argc && argv[i + 1][0] != '-')
521 {
522 printFrequencyMS = strtol(argv[++i], nullptr, 10);
523 }
524 }
525 else if (std::strncmp(argv[i], strarg_timeoutms, sizeof(strarg_timeoutms)) == 0)
526 {
527 if ((i + 1) < argc)
528 {
529 timeout_mS = strtol(argv[++i], nullptr, 10);
530 }
531 }
532 else if (std::strncmp(argv[i], strarg_print, sizeof(strarg_print)) == 0)
533 {
534 if ((i + 1) < argc && argv[i + 1][0] != '-')
535 {
536 char* bVal = argv[++i];
537 if (std::strncmp(bVal, "t", sizeof("t")) || std::strncmp(bVal, "true", sizeof("true")))
538 {
539 print = true;
540 }
541 else if (std::strncmp(bVal, "f", sizeof("f")) || std::strncmp(bVal, "false", sizeof("false")))
542 {
543 print = true;
544 }
545 }
546 else // flag present and no t/f set
547 {
548 print = true;
549 }
550 }
551 else if (std::strncmp(argv[i], strarg_thresholdus, sizeof(strarg_thresholdus)) == 0)
552 {
553 if ((i + 1) < argc && argv[i + 1][0] != '-')
554 {
555 int parsed_val = strtol(argv[++i], nullptr, 10);
556 if (-1 < parsed_val)
557 {
558 syncPeriodThreshold_uS = parsed_val;
559 }
560 }
561 }
562 else if (std::strncmp(argv[i], strarg_rmppath, sizeof(strarg_rmppath)) == 0)
563 {
564 if ((i + 1) < argc)
565 {
566 std::string newRmpPath(argv[++i]);
568 }
569 }
570 }
571}
572
573// Necessary to build correctly on linux, as the SyncInterrupt is expected to be the entry point to SampleAppsCPP
574#ifdef __linux__
575int main(int argc, char* argv[])
576#else
577int32_t SyncInterruptMain(int32_t argc, char* argv[])
578#endif
579{
581 ParseSetGlobalArgs(argc, argv, params);
582
583 // Zero initialize variables
584 previousPerformanceCounter = 0;
585 syncInterruptIterations = 0;
586 returnCode = 0; // OK!
587
588 try
589 {
590 printf("Hello, RapidCodeRT!\n");
591
592 // create and Initialize MotionController class.
593
594 std::cout << "params.rmpPath: " << params.RmpPath << std::endl;
595 controller = MotionController::Create(&params);
597
598 printf("Serial Number: %d \n", controller->SerialNumberGet());
599
600 controller->SampleRateSet(sampleRate);
601 printf("Sample Rate: %8.0lf \n", controller->SampleRateGet());
602
603 // disable the service thread if using Controller Sync Interrupt
604 controller->ServiceThreadEnableSet(false);
605
606 // Get CPU frequency from Operating System performance counter
607 cpuFrequency = controller->OS->PerformanceTimerFrequencyGet();
608 printf("CPU Frequency is: %u Hz\n", cpuFrequency);
609
610 // start all threads
611 std::vector<std::thread> threads;
612
613 // raise the base priority of the process. Be careful with this...
614 RaiseProcessPriorityClass(process_cpu, "SyncInterruptMainThread");
615
616 // start 3 slow polling threads
617 readyToCleanup = false; // set because the pollers check this
618 threads.push_back(CreateThreadAndSetPriority(&SystemEventHandlerThread, LOWEST_PRIORITY, "SystemEventThread"));
619 threads.push_back(CreateThreadAndSetPriority(&TimeoutThread, LOWEST_PRIORITY, "TimeoutThread"));
620 threads.push_back(CreateThreadAndSetPriority(&KeyPressExitThread, LOWEST_PRIORITY, "KeypressThread"));
621 threads.push_back(CreateThreadAndSetPriority(&PrinterThread, LOW_PRIORITY, "PrinterThread"));
622 threads.push_back(CreateThreadAndSetPriority(&StatisticsThread, LOW_PRIORITY, "StatisticsThread"));
623
624 // run the high priority loop
625 threads.push_back(CreateThreadAndSetPriority(&SyncInterruptMainLoopThread, HIGH_PRIORITY, "SyncInterruptThread"));
626
627 // Wait for all of our working threads to finish!
628 for (auto& thread : threads)
629 {
630 if (thread.joinable())
631 {
632 thread.join();
633 }
634 }
635 printf("\n");//flush
636
637 if (controller != nullptr)
638 {
639 // Delete the controller to clean up all RapidCodeRT objects
640 controller->Delete();
641 }
642 }
643 catch (std::exception const& e)
644 {
645 printf("\n%s\n", e.what());
646 }
647
648 return returnCode;
649}
650
651
652
653
static void CheckErrors(RapidCodeObject *rsiObject)
Checks for errors in the given RapidCodeObject and throws an exception if any non-warning errors are ...
void SampleRateSet(double sampleRate)
void ServiceThreadEnableSet(bool enable)
Enable or disable the service thread.
void SyncInterruptEnableSet(bool enable)
Configure Sync (periodic) interrupts for the controller.
static MotionController * Create()
Initialize and start the RMP EtherCAT controller.
void SyncInterruptPeriodSet(uint32_t samples)
Configure the period for the Sync Interrupt on the controller.
void Delete(void)
Delete the MotionController and all its objects.
int32_t SyncInterruptWait()
Suspend the current thread until an interrupt arrives from the controller.
uint32_t SerialNumberGet(void)
Get the controller's serial number.
Represents the RMP soft motion controller. This class provides an interface to general controller con...
Definition rsi.h:762
RapidCodeOS * OS
Provides access to operating system (Windows) features.
Definition rsi.h:3736
int32_t KeyGet(int32_t milliseconds)
Wait for a key to be pressed and return its value.
int32_t PerformanceTimerFrequencyGet()
Gets the frequency of the performance counter.
int32_t PerformanceTimerCountGet()
Gets the current high performance counter value.
char RmpPath[PathLengthMaximum]
Location of the RMP firmware executable, license, and associated binaries and resources.
Definition rsi.h:909
int32_t CpuAffinity
[Linux] Indicate the CPU core on which the RMP and RMPNetwork processes run.
Definition rsi.h:961
static constexpr uint32_t PathLengthMaximum
MotionController::CreationParameters Maximum string buffer length.
Definition rsi.h:836
CreationParameters for MotionController::Create.
Definition rsi.h:826