6.6.4 **minimal_welding_sample.cpp** ------------------------------------------- This example (``4_minimal_welding_sample.cpp``) demonstrates how to configure and execute a digital welding process over EtherNet/IP using the Doosan Robotics API: - register monitoring / alarm / access control callbacks - open a TCP/IP connection and request control authority - turn Servo On and print system/library versions - switch to AUTONOMOUS mode and the virtual robot system - execute a full DRL-based welding sequence - configure the welding interface and motion purely via the C++ API and run a weave weld This example is located in the following path: |br| ``API-DRFL/example/Linux_64/4_minimal_welding_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
.. note:: The ``app_weld_*`` functions that this sample calls are declared only when ``DRCF_VERSION`` is 2 (DRCF v2 controllers). The sample does not define ``DRCF_VERSION``, and ``DRFLEx.h`` defaults it to 3. See :ref:`DRCF Version Compatibility `. Setup & Global Variables ~~~~~~~~~~~~~~~~~~~~~~~~ The sample defines a global controller object, several state flags, the controller IP address and a helper function that returns a DRL welding script as a string. .. code-block:: cpp #include #include #include #include #include #include #include "../../include/DRFLEx.h" using namespace DRAFramework; using namespace std::chrono_literals; #undef NDEBUG #include CDRFLEx Drfl; bool g_bHasControlAuthority = FALSE; bool g_TpInitailizingComplted = FALSE; bool moving = FALSE; bool bAlterFlag = FALSE; std::string IP_ADDR = "127.0.0.1"; std::string getWeldingSampleDrl(); A set of callback handlers reacts to TP initialization, homing completion, program stop and monitoring data. The monitoring data handlers and ``OnProgramStopped`` have empty bodies. .. code-block:: cpp void OnTpInitializingCompleted() { // Tp 초기화 이후 제어권 요청. g_TpInitailizingComplted = TRUE; Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_FORCE_REQUEST); } void OnHommingCompleted() { // 50msec 이내 작업만 수행할 것. cout << "homming completed" << endl; } void OnProgramStopped(const PROGRAM_STOP_CAUSE v) { } void OnMonitoringDataCB(const LPMONITORING_DATA pData) { } void OnMonitoringDataExCB(const LPMONITORING_DATA_EX pData) { } void OnMonitoringCtrlIOCB(const LPMONITORING_CTRLIO pData) { } void OnMonitoringCtrlIOExCB(const LPMONITORING_CTRLIO_EX2 pData) { } These globals and callbacks track control authority, request it again when the TP finishes initializing or the authority is lost, reconnect when the connection drops, and print alarm, popup, TP log, progress and user-input messages. Main Loop ~~~~~~~~~~~~~ The main loop runs after initialization and waits for a single-character command. .. code-block:: cpp while (true) { cout << "\ninput key : "; char ch; cin >> ch; switch (ch) { case '0': { // drl sample. Drfl.drl_start(ROBOT_SYSTEM_VIRTUAL, getWeldingSampleDrl()); } break; // ... case '2':{ } break; default: break; } } Drfl.CloseConnection(); return 0; - ``0`` : run the **DRL-based welding script** returned by ``getWeldingSampleDrl()``. - ``1`` : configure the welding interface directly via **C++ structures** and execute a **weaving welding motion** using ``amovel`` with a welding app type. - ``2`` : an empty case. Callback Registration ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unlike the minimal instruction example (where callbacks are registered on a key press), this welding sample registers all callbacks **at startup** before opening the connection. .. code-block:: cpp int main(int argc, char** argv) { Drfl.set_on_homming_completed(OnHommingCompleted); Drfl.set_on_monitoring_data(OnMonitoringDataCB); Drfl.set_on_monitoring_data_ex(OnMonitoringDataExCB); Drfl.set_on_monitoring_ctrl_io(OnMonitoringCtrlIOCB); Drfl.set_on_monitoring_ctrl_io_ex(OnMonitoringCtrlIOExCB); Drfl.set_on_monitoring_state(OnMonitoringStateCB); Drfl.set_on_monitoring_access_control(OnMonitroingAccessControlCB); Drfl.set_on_tp_initializing_completed(OnTpInitializingCompleted); Drfl.set_on_log_alarm(OnLogAlarm); 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); Drfl.set_on_program_stopped(OnProgramStopped); Drfl.set_on_disconnected(OnDisConnected); assert(Drfl.open_connection(IP_ADDR)); - ``OnMonitoringStateCB`` has an empty body; its state handling is commented out. - ``OnMonitroingAccessControlCB`` answers a **REQUEST** with ``MANAGE_ACCESS_CONTROL_RESPONSE_YES``, sets ``g_bHasControlAuthority`` on **GRANT**, and clears it on **DENY** or **LOSS**. - ``OnTpInitializingCompleted`` sets ``g_TpInitailizingComplted`` and immediately issues a **force request** for control authority. - ``OnLogAlarm``, ``OnTpPopup``, ``OnTpLog``, ``OnTpProgress`` and ``OnTpGetuserInput`` print diagnostic information to the console. - ``OnDisConnected`` retries ``open_connection()`` every second until it succeeds. - The sample undefines ``NDEBUG`` before including ``assert.h``, so the ``open_connection()`` call inside ``assert()`` runs in every build and the program aborts if it returns false. The access control callback updates ``g_bHasControlAuthority`` whenever the controller changes the access state. On **DENY** or **LOSS**, once the TP has finished initializing, it requests control authority again with a force request. .. code-block:: cpp void OnMonitroingAccessControlCB( const MONITORING_ACCESS_CONTROL eTrasnsitControl) { // 50msec 이내 작업만 수행할 것. std::cout << "OnMonitroingAccessControlCB !! " << int(eTrasnsitControl) << std::endl; switch (eTrasnsitControl) { case MONITORING_ACCESS_CONTROL_REQUEST: assert(Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_RESPONSE_YES)); // Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_RESPONSE_YES); break; case MONITORING_ACCESS_CONTROL_GRANT: g_bHasControlAuthority = TRUE; break; case MONITORING_ACCESS_CONTROL_DENY: case MONITORING_ACCESS_CONTROL_LOSS: g_bHasControlAuthority = FALSE; if (g_TpInitailizingComplted) { Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_FORCE_REQUEST); } break; default: break; } } When the access-control state changes, the callback prints the numeric value of ``MONITORING_ACCESS_CONTROL``, for example 2 for **GRANT**. .. code-block:: text OnMonitroingAccessControlCB !! 2 Different integer values correspond to the enum constants defined in ``MONITORING_ACCESS_CONTROL`` (REQUEST, GRANT, DENY, LOSS, ...). The robot-state callback is registered, but every statement in its body is commented out. .. code-block:: cpp void OnMonitoringStateCB(const ROBOT_STATE eState) { // std::cout << "monitoring state cb " << std::endl; // // 50msec 이내 작업만 수행할 것. // switch ((unsigned char)eState) { // ... // default: // break; // } // return; } Alarm information from the controller is reported through the log-alarm callback. .. code-block:: cpp void OnLogAlarm(LPLOG_ALARM tLog) { cout << "Alarm Info: " << "group(" << (unsigned int)tLog->_iGroup << "), index(" << tLog->_iIndex << "), param(" << tLog->_szParam[0] << "), param(" << tLog->_szParam[1] << "), param(" << tLog->_szParam[2] << ")" << endl; } Example output: .. code-block:: text Alarm Info: group(1), index(1037), param(), param(), param() Each alarm is logged with a **group** and **index** code plus up to three string parameters. These values can be cross-referenced with the alarm table in the controller manual to identify the exact cause (e.g., Servo On failure, limit violation, welding interface error, etc.). Connection to Controller ~~~~~~~~~~~~~~~~~~~~~~~~~~~ After registering callbacks, the controller connection is opened via TCP/IP, the monitoring version is set to the latest and the system version is read. .. code-block:: cpp assert(Drfl.open_connection(IP_ADDR)); Drfl.setup_monitoring_version(1); // (0 -> older version, 1 -> latest) SYSTEM_VERSION tSysVerion = {'\0',}; Drfl.get_system_version(&tSysVerion); Control Authority & Servo On Handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The sample sends one force request for control authority and one Servo On command, prints the system and library versions, and then polls ``get_robot_state()`` every second. .. code-block:: cpp if(true!=Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_FORCE_REQUEST)) { std::cout << "ManageAccessControl failure " << std::endl; } // std::cout << "ManageAccessControl done " << std::endl; Drfl.set_robot_control(CONTROL_SERVO_ON); cout << "System version: " << tSysVerion._szController << endl; cout << "Library version: " << Drfl.get_library_version() << endl; // Drfl.set_digital_output(GPIO_CTRLBOX_DIGITAL_INDEX_10, TRUE); while ((Drfl.get_robot_state() != STATE_STANDBY) || !g_bHasControlAuthority) { std::cout << "Drfl.get_robot_state() : " <_fTargetPos[i]; } target_wel_posx[0] += 200; // Refer to drl Sample Code. Drfl.set_singularity_handling(SINGULARITY_AVOIDANCE_AVOID); unsigned char dry_run = 1; // (flag_dry_run: 실용접(0) / 모의용접(1)) unsigned char weld_con_adj = 1; // 용접 조건 조정 여부 설정(app_weld_set_weld_cond_analog에서 설정한 조건 적용(1) / 조정값 적용(0)) float velx[2] = { 250.0, 80.625 }; float accx[2] = { 1000., 322.5 }; float g_op_speed = 100; Drfl.set_robot_mode(ROBOT_MODE_AUTONOMOUS); // 사전 TCP 설정 : EWM 용접기 기준 Welding_Torch(0, 200, 300, 90, 67.5, 0) if (Drfl.get_tcp() != "Welding Torch") Drfl.set_tcp("Welding Torch"); this_thread::sleep_for(200ms); // Drfl.change_operation_speed(g_op_speed); CONFIG_DIGITAL_WELDING_INTERFACE_PROCESS r2m_process_data = { // _bEnable / _nDataType / _nPositionalNumber / _fMinData / _fMaxData / _nByteOffset / _nBitOffset / _nComnDataType / _nMaxDigitSize {1, 0, 0, 0, 0, 0, 4, 0, 1}, // _tWeldingStart { 1, 0, 0, 0, 0, 0, 5, 0, 1}, // _tRobotReady {1, 0, 0, 0, 0, 1, 4, 0, 1} // _tErrorReset }; Drfl.app_weld_set_interface_eip_r2m_process(r2m_process_data); // ... Drfl.app_weld_set_interface_eip_r2m_mode(r2m_mode_data); // ... Drfl.app_weld_set_interface_eip_r2m_test(r2m_test_data); // ... Drfl.app_weld_set_interface_eip_r2m_condition(r2m_condition_data); // ... Drfl.app_weld_set_interface_eip_r2m_option(r2m_option_data); // ... Drfl.app_weld_set_interface_eip_m2r_process2(m2r_process2_data); // ... Drfl.app_weld_set_interface_eip_m2r_monitoring(m2r_monitoring_data); // ... Drfl.app_weld_set_interface_eip_m2r_other(m2r_other_data); this_thread::sleep_for(500ms); Drfl.app_weld_enable_digital(1); this_thread::sleep_for(500ms); CONFIG_DIGITAL_WELDING_CONDITION weld_condition = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; weld_condition._cVirtualWelding = dry_run; weld_condition._fTargetVel = 3.f; weld_condition._fMaxVelLimit = 100.f; weld_condition._nWeldingMode = 1; weld_condition._nPulseMode = 0; weld_condition._nJobNumber = 4; weld_condition._nSynergicID = 1; Drfl.app_weld_set_weld_cond_digital(weld_condition); // ... this_thread::sleep_for(1000ms); // ... CONFIG_TRAPEZOID_WEAVING_SETTING weaving_trap = { //Weaving_trap 설정 0.f, 0.f, 0.f, { 0.f, 3.f }, { 0.f, -3.f }, 0.3f, 0.1f, 0.2f, 0.3f, 0.1f, 0.2f }; // ... Drfl.app_weld_weave_cond_trapezoidal(weaving_trap); // ... float welding_velx[2] = { 3,3 }; float welding_accx[2] = { 70,70 }; Drfl.amovel(target_wel_posx, welding_velx, welding_accx, 0, MOVE_MODE_ABSOLUTE, MOVE_REFERENCE_BASE, BLENDING_SPEED_TYPE_DUPLICATE, DR_MV_APP_WELD); Drfl.mwait(); break; } - **Task pose offset**: the welding target is the task pose read after the move to the ready joint position, plus 200 on X. - **Digital welding interface mapping** is done with a series of ``CONFIG_DIGITAL_WELDING_INTERFACE_*`` structures; the elided lines define each structure in the same way as ``r2m_process_data``. - **Weaving parameters** (offsets, amplitude, timing) are configured through ``CONFIG_TRAPEZOID_WEAVING_SETTING`` and applied via ``app_weld_weave_cond_trapezoidal``. - The final motion uses ``amovel(..., DR_MV_APP_WELD)``, which executes a **linear weld with weaving** along the specified target pose.