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