6.6.5 **minimal_instruction_sample.cpp** --------------------------------------------- This example (``5_minimal_instruction_sample.cpp``) demonstrates a **step-by-step instruction flow** for controlling the robot using the API: - register monitoring callbacks, - open/close the controller connection, - request **control authority**, - turn **Servo On** and check system/version, - switch **robot mode/system**, - execute a simple **joint motion** (``movej``), - test **inverse kinematics** (``ikin``, ``ikin_norm``), - and send/stop a **DRL script**. Each function is mapped to a numeric key (1~9), so users can trigger individual operations one by one and observe how the API behaves in real time. This example is located in the following path: |br| ``API-DRFL/example/Linux_64/5_minimal_instruction_sample.cpp`` **See the example on the** `Doosan Robotics API-DRFL GitHub `_. **Watch the full walk-through on the** `Doosan Robotics Official YouTube `_. .. raw:: html
Setup & Global Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The example uses a global controller object and several flags to track control status. .. code-block:: cpp #include "../../include/DRFLEx.h" using namespace DRAFramework; CDRFLEx Drfl; bool g_bHasControlAuthority = FALSE; bool g_TpInitailizingComplted = FALSE; bool g_mStat = FALSE; bool g_Stop = FALSE; bool moving = FALSE; A DRL script string and helper flags are also defined. .. code-block:: cpp string strDrl = "\r\n\ loop = 0\r\n\ while loop < 1003:\r\n\ movej(posj(10,10,10,10,10,10), vel=60, acc=60)\r\n\ movej(posj(0,0,0,0,0,0), vel=60, acc=60)\r\n\ loop+=1\r\n"; bool bAlterFlag = FALSE; These globals store the DRFLEx instance, track whether control authority has been granted and whether an alarm was logged, and hold a DRL script. No menu key uses the global ``strDrl``; key ``8`` declares its own local ``strDrl``. Keyboard Menu & Main Loop ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The main loop reads a key from the console and executes a corresponding instruction block. .. code-block:: cpp bool bLoop = TRUE; while (bLoop) { g_mStat = false; g_Stop = false; #ifdef __XENO__ unsigned long overrun = 0; const double tick = 1000000; // 1ms rt_task_set_periodic(nullptr, TM_NOW, tick); if (rt_task_wait_period(&overrun) == -ETIMEDOUT) { std::cout << __func__ << ": \x1B[37m\x1B[41mover-runs: " << overrun << "\x1B[0m\x1B[0K" << std::endl; } #else std::this_thread::sleep_for(std::chrono::microseconds(1000)); #endif // __XENO__ #if 0 static char ch = '0'; if (ch == '7') ch = '0'; else if (ch == '0') ch = '7'; #else cout << "\n\033[1;33minput key : \033[0m"; // char ch = _getch(); char ch; cin >> ch; // cout << ch << endl; #endif switch (ch) { case 'q': bLoop = FALSE; Drfl.close_connection(); std::cout << "Close Connection !! " << std::endl; break; // ... default: break; } // Sleep(100); this_thread::sleep_for(std::chrono::milliseconds(100)); } Drfl.close_connection(); This structure makes the example a **minimal instruction console**: you can manually trigger each step of the control pipeline and see the result immediately. Callback Registration ~~~~~~~~~~~~~~~~~~~~~~~~~~~ When the user presses ``1``, the example registers several monitoring and event callbacks. .. code-block:: cpp case '1': { // Register callbacks Drfl.set_on_monitoring_data_ex(OnMonitoringDataExCB); Drfl.set_on_monitoring_ctrl_io_ex(OnMonitoringCtrlIOExCB); Drfl.set_on_log_alarm(OnLogAlarm); // Drfl.set_on_disconnected(OnDisConnected); Drfl.set_on_monitoring_state(OnMonitoringStateCB); // Drfl.set_on_tp_initializing_completed(OnTpInitializingCompleted); // Drfl.set_on_tp_popup(OnTpPopup); // Drfl.set_on_tp_log(OnTpLog); // Drfl.set_on_tp_progress(OnTpProgress); // Drfl.set_on_tp_get_user_input(OnTpGetuserInput); // Drfl.set_on_rt_monitoring_data(OnRTMonitoringData); std::cout << "Register Callbacks !! " << std::endl; } break; These callbacks receive extended monitoring data and control-box I/O data, print alarm information (``OnLogAlarm``) and print each robot state change (``OnMonitoringStateCB``). The registrations of ``OnDisConnected``, the TP callbacks and the RT monitoring callback are commented out. ``OnMonitoringDataExCB`` and ``OnMonitoringCtrlIOExCB`` return before printing anything. Example console output: .. code-block:: text input key : 1 Register Callbacks !! Connection to Controller ~~~~~~~~~~~~~~~~~~~~~~~~~~ Pressing ``2`` opens a TCP/IP connection to the controller. .. code-block:: cpp case '2': { // Establish connection if (!Drfl.open_connection("127.0.0.1")) { std::cout << "open_connection failed" << std::endl; break; } std::cout << "Open Connection !! " << std::endl; } break; - The example uses ``127.0.0.1``, assuming a **local emulator** (DART Platform / virtual controller). - For a **real robot controller**, replace this with the actual controller IP. - If ``open_connection()`` returns false, the sample prints ``open_connection failed`` and returns to the menu. Example console output: .. code-block:: text input key : 2 Open Connection !! Control Authority Handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Key ``3`` registers a simple access-control callback and then requests control authority. .. code-block:: cpp case '3': { //Acquire control authority - register callback BEFORE sending request Drfl.set_on_monitoring_access_control([](const MONITORING_ACCESS_CONTROL eTrasnsitControl) { if(MONITORING_ACCESS_CONTROL_GRANT == eTrasnsitControl) { g_bHasControlAuthority = TRUE; std::cout << "\nSuccessfully got Control Authority !! " << std::endl; }else { std::cout << "Unintended autority.. " << std::endl; } } ); // FORCE_REQUEST goes through server's async message queue (SendMessageToRC), // which can be delayed if the server is processing previous disconnect events. // Retry once if the first attempt doesn't get GRANT within 5 seconds. g_bHasControlAuthority = FALSE; Drfl.manage_access_control(MANAGE_ACCESS_CONTROL_FORCE_REQUEST); std::cout << "Try to get Control Authority !!" << std::endl; for (int i = 0; i < 50; i++) { if (g_bHasControlAuthority) break; this_thread::sleep_for(std::chrono::milliseconds(100)); } if (!g_bHasControlAuthority) { // Retry once - server may have been busy processing previous disconnect Drfl.manage_access_control(MANAGE_ACCESS_CONTROL_FORCE_REQUEST); std::cout << "Retrying..." << std::endl; for (int i = 0; i < 50; i++) { if (g_bHasControlAuthority) break; this_thread::sleep_for(std::chrono::milliseconds(100)); } } if (!g_bHasControlAuthority) { std::cout << "Failed to get Control Authority." << std::endl; } } break; - The callback is registered before the request is sent. - ``MANAGE_ACCESS_CONTROL_FORCE_REQUEST`` forces an access request to the controller. - When granted, ``g_bHasControlAuthority`` is set to ``TRUE`` and a message is printed. - The sample waits up to 5 seconds for the grant, sends the request once more if it has not arrived, waits up to 5 seconds again, and prints a failure message if control authority is still not granted. Example console output: .. code-block:: text input key : 3 Try to get Control Authority !! Successfully got Control Authority !! Servo On & Monitoring Version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Key ``4`` configures the monitoring version, sets a control-box digital output, prints system information and turns Servo On. .. code-block:: cpp case '4': { if (!g_bHasControlAuthority) { std::cout << "No control authority. Run step 3 first." << std::endl; break; } // Turn servo on and set monitoring version Drfl.setup_monitoring_version(1); Drfl.set_digital_output(GPIO_CTRLBOX_DIGITAL_INDEX_10, TRUE); SYSTEM_VERSION tSysVerion = {'\0',}; Drfl.get_system_version(&tSysVerion); cout << "System version: " << tSysVerion._szController << endl; cout << "Library version: " << Drfl.get_library_version() << endl; Drfl.set_robot_control(CONTROL_SERVO_ON); int nRetryCount = 0; ROBOT_STATE ePrevState = STATE_LAST; int nSameStateCount = 0; const int RETRY_INTERVAL = 10; // retry every 10*500ms = 5 seconds while ((Drfl.get_robot_state() != STATE_STANDBY) || !g_bHasControlAuthority) { ROBOT_STATE eState = Drfl.get_robot_state(); bool bSendCommand = false; if (eState != ePrevState) { ePrevState = eState; nSameStateCount = 0; bSendCommand = true; } else if (++nSameStateCount >= RETRY_INTERVAL) { nSameStateCount = 0; bSendCommand = true; } if (bSendCommand) { switch ((unsigned char)eState) { case STATE_SAFE_STOP: cout << ">> Recovery: SAFE_STOP -> resetting..." << endl; Drfl.set_safe_stop_reset_type(SAFE_STOP_RESET_TYPE_DEFAULT); Drfl.set_robot_control(CONTROL_RESET_SAFET_STOP); break; case STATE_SAFE_OFF: cout << ">> Recovery: SAFE_OFF -> servo on..." << endl; Drfl.set_robot_control(CONTROL_SERVO_ON); break; case STATE_SAFE_STOP2: cout << ">> Recovery: SAFE_STOP2 -> recovering..." << endl; Drfl.set_robot_control(CONTROL_RECOVERY_SAFE_STOP); break; case STATE_SAFE_OFF2: cout << ">> Recovery: SAFE_OFF2 -> recovering..." << endl; Drfl.set_robot_control(CONTROL_RECOVERY_SAFE_OFF); break; case STATE_NOT_READY: cout << ">> Recovery: NOT_READY -> servo on..." << endl; Drfl.set_robot_control(CONTROL_SERVO_ON); break; default: cout << ">> Unknown state: " << (int)eState << endl; break; } } this_thread::sleep_for(std::chrono::milliseconds(500)); if (++nRetryCount > 20) { // 10 second timeout cout << ">> Timeout waiting for STANDBY (state=" << (int)eState << ")" << endl; break; } } if (Drfl.get_robot_state() == STATE_STANDBY && g_bHasControlAuthority) std::cout << "Servo On !! " << std::endl; else std::cout << "Failed to reach STANDBY state." << std::endl; } break; - Without control authority, the step prints a message and does nothing else. - ``setup_monitoring_version(1)`` selects the newer monitoring context. - ``set_digital_output(GPIO_CTRLBOX_DIGITAL_INDEX_10, TRUE)`` turns on control-box digital output 10. - ``set_robot_control(CONTROL_SERVO_ON)`` turns Servo On. - The loop checks the robot state every 500 ms. When the state changes, and every 5 seconds while it stays the same, it sends the recovery command for ``STATE_SAFE_STOP``, ``STATE_SAFE_OFF``, ``STATE_SAFE_STOP2``, ``STATE_SAFE_OFF2`` or ``STATE_NOT_READY``. - The loop ends when both **robot state = STANDBY** and **control authority** are satisfied, or after about 10 seconds. Example console output: .. code-block:: text input key : 4 System version: GF03020000 Library version: GL013300 Servo On !! Robot Mode & System Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Key ``5`` switches to **AUTONOMOUS** mode and **REAL** system. .. code-block:: cpp case '5': { //Set robot mode and system if (!Drfl.set_robot_mode(ROBOT_MODE_AUTONOMOUS) || !Drfl.set_robot_system(ROBOT_SYSTEM_REAL)) { std::cout << "Set Auto Mode failed" << std::endl; break; } std::cout << "Set Auto Mode !!" << std::endl; } break; - ``ROBOT_MODE_AUTONOMOUS`` enables programmatic motion execution. - ``ROBOT_SYSTEM_REAL`` indicates that the motion is executed on a real robot. To execute in virtual mode, change it to ``ROBOT_SYSTEM_VIRTUAL``. - If either call returns false, the sample prints ``Set Auto Mode failed`` and returns to the menu. Example console output: .. code-block:: text input key : 5 Set Auto Mode !! Joint Motion Execution ~~~~~~~~~~~~~~~~~~~~~~~~~~ Key ``6`` executes a simple **joint-space motion** using ``movej``. .. code-block:: cpp case '6': { //Execute motion std::cout << "Move Start !! " << std::endl; float p1[6] = {0, 0, -90, 0, 90, 0}; Drfl.movej(p1, 60, 30); // Move to posj(0,0,10,0,10,0) with v=60, a=30 // float p2[6] = {0, 0, 9, 0, 90, 0}; // Drfl.movej(p2, 30, 30); // Move back to posj(0,0,0,0,0,0) with v=30, a=30 //You cannot specify only selected parameters; even when using time, you must provide velocity and acceleration first. std::cout << "Move End !! " << std::endl; LPROBOT_POSE pCur = Drfl.get_current_pose(); std::cout << " Current Pose : " << pCur->_fPosition[0] << ", " << pCur->_fPosition[1] << ", " << pCur->_fPosition[2] << ", " << pCur->_fPosition[3] << ", " << pCur->_fPosition[4] << ", " << pCur->_fPosition[5] << std::endl; } break; - The move goes to the joint position (0, 0, -90, 0, 90, 0) deg at velocity 60, acceleration 30. - The second ``movej`` is commented out. - After the move, the sample prints the current joint position returned by ``get_current_pose()``. Example console output: .. code-block:: text input key : 6 Move Start !! Move End !! Kinematics Test ~~~~~~~~~~~~~~~~~~~ Key ``7`` demonstrates the **inverse kinematics** functions ``ikin`` and ``ikin_norm``. .. code-block:: cpp case '7': { // Motion using kinematics functions float x1[6] = {370.9, 719.7, 651.5, 90, -180, 0}; //Task-space pose for ikin conversion float fIterThreshold[2] = {0.005, 0.01}; LPROBOT_POSE res = Drfl.ikin(x1, 255); //ROBOT_POSE holds float[6] elements. //Store the ikin result (shallow copy) float q1[6] = {0,}; for(int i=0; i<6; i++){ q1[i] = res->_fPosition[i]; //Read elements from LPROBOT_POSE (return value) } std::cout << " Get ikin Success (Task Space -> Joint Space) !! " << std::endl; std::cout << " Joint Values [q1]: " << q1[0] << ", " << q1[1] << ", " << q1[2] << ", " << q1[3] << ", " << q1[4] << ", " << q1[5] << std::endl; this_thread::sleep_for(std::chrono::milliseconds(100)); LPINVERSE_KINEMATIC_RESPONSE res2 = Drfl.ikin_norm(x1, 255, COORDINATE_SYSTEM_BASE, 0); //ROBOT_POSE holds float[6] elements. //Store the ikin_norm result (shallow copy) float q2[6] = {0,}; for(int i=0; i<6; i++){ q2[i] = res2->_fTargetPos[i]; //Read elements from LPROBOT_POSE (return value) } std::cout << " Get ikin_norm Success (Task Space -> Joint Space) !! " << std::endl; std::cout << " Joint Values [q2]: " << q2[0] << ", " << q2[1] << ", " << q2[2] << ", " << q2[3] << ", " << q2[4] << ", " << q2[5] << std::endl; // Drfl.movej(q1, 60, 30); } break; - ``ikin`` and ``ikin_norm`` compute a joint-space solution from the task-space pose ``x1``; the sample passes 255 as the solution space. - The sample prints both results. The ``movej`` to the ``ikin`` result is commented out. Example console output: .. code-block:: text input key : 7 Get ikin Success (Task Space -> Joint Space) !! DRL Script Execution ~~~~~~~~~~~~~~~~~~~~~~~ Key ``8`` sends and executes a **DRL script** that performs two ``movej`` motions. .. code-block:: cpp case '8': { // DRL string strDrl = "movej([0, 0, 10, 0, 10, 0], 60, 30)\nmovej([10, 20, 30, 40, 50, 60], 60, 30)\n"; //DRL script to move between two positions using movej Drfl.drl_start(ROBOT_SYSTEM_REAL, strDrl); //Set ROBOT_SYSTEM to VIRTUAL for a virtual robot, and pass the DRL script Drfl.set_on_program_stopped(OnProgramStopped); std::cout << "Sent DRL Script !!" << std::endl; } break; The registered ``OnProgramStopped`` callback handles program termination. .. code-block:: cpp void OnProgramStopped(const PROGRAM_STOP_CAUSE) { // The program has already stopped; drl_stop() here would wait on this callback thread. // Do only work under 50 msec. // assert(Drfl.SetRobotMode(ROBOT_MODE_MANUAL)); cout << "program stopped" << endl; } ``OnProgramStopped`` only prints ``program stopped``. The program has already stopped, so the callback does not call ``drl_stop()``. See :ref:`Rules for callback functions ` for the functions that must not be called inside a callback. During execution, alarm logs may appear when the DRL program stops. .. code-block:: text input key : 8 Sent DRL Script !! input key : Alarm Info: group(1), index(2007), param(), param(), param() program stopped This confirms that the DRL script was sent, the program executed, and the stop event and alarm were handled correctly. .. note:: A DRL script string and helper flags are also defined at the top of the sample. |br| Key ``8`` uses its own local ``strDrl``; the global ``strDrl`` below is not used by any menu key. .. code-block:: cpp string strDrl = "\r\n\ loop = 0\r\n\ while loop < 1003:\r\n\ movej(posj(10,10,10,10,10,10), vel=60, acc=60)\r\n\ movej(posj(0,0,0,0,0,0), vel=60, acc=60)\r\n\ loop+=1\r\n"; Xenomai / Real-time Environment Notes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When the sample is compiled with the ``__XENO__`` macro, the following code paths are enabled. .. code-block:: cpp #ifdef __XENO__ #include #include #include #else //#include "stdafx.h" //#include //#include //#include #endif // __XENO__ In the main loop, the real-time task is scheduled with a fixed period. .. code-block:: cpp #ifdef __XENO__ unsigned long overrun = 0; const double tick = 1000000; // 1ms rt_task_set_periodic(nullptr, TM_NOW, tick); if (rt_task_wait_period(&overrun) == -ETIMEDOUT) { std::cout << __func__ << ": \x1B[37m\x1B[41mover-runs: " << overrun << "\x1B[0m\x1B[0K" << std::endl; } #else std::this_thread::sleep_for(std::chrono::microseconds(1000)); #endif // __XENO__ - In **non-RT environments**, the loop simply sleeps using ``std::this_thread::sleep_for``. - In **Xenomai RT environments**, the loop is driven by ``rt_task_set_periodic`` and ``rt_task_wait_period`` to achieve deterministic timing and to detect possible **overruns**. These blocks are independent of the DRFLEx API logic itself and are only required when deploying the sample in a strict real-time control environment.