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)
Configure the RMP firmware control loop frequency.
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:800
RapidCodeOS * OS
Provides access to operating system (Windows) features.
Definition rsi.h:3836
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:962
int32_t CpuAffinity
[Linux] Indicate the CPU core on which the RMP and RMPNetwork processes run.
Definition rsi.h:1014
static constexpr uint32_t PathLengthMaximum
MotionController::CreationParameters Maximum string buffer length.
Definition rsi.h:882
CreationParameters for MotionController::Create.
Definition rsi.h:866