Skip to content

4.14 C99 Modbus

개요

C99 Modbus 는 두 계층으로 나뉩니다. ArmHandle* 는 SDK 세션을 담당하고, ArmModbusSlaveHandle* 는 특정 슬레이브 컨텍스트를 담당합니다.

권장 호출 순서

단계인터페이스설명
1Arm_Connect로봇 세션을 생성합니다.
2Arm_Modbus_GetParam필요에 따라 대상 채널의 시리얼 포트 파라미터를 읽습니다.
3Arm_Modbus_GetSlave슬레이브 핸들을 생성합니다. 원격 슬레이브 상태는 이후 읽기/쓰기 결과로 확인합니다.
4Arm_ModbusSlave_Read* / Arm_ModbusSlave_Write*코일, holding register, discrete input, input register에 대해 읽기/쓰기를 수행합니다.
5Arm_ModbusSlave_SerialSend / Arm_ModbusSlave_SerialReceive필요에 따라 시리얼 포트 패스스루를 사용합니다.
6Arm_ModbusSlave_Destroy슬레이브 핸들을 폐기합니다.

인터페이스 시그니처

Arm_Modbus_GetSlave

c
int Arm_Modbus_GetSlave(ArmHandle* h, int channel, int32_t slaveId, int32_t masterId, ArmModbusSlaveHandle** outSlave);
항목설명
설명Modbus 슬레이브 핸들을 생성합니다.
요청 파라미터h : ArmHandle* , C99 세션 핸들이며 일반적으로 Arm_Create() 에서 생성합니다. 비즈니스 인터페이스는 먼저 연결에 성공해야 합니다.
channel : int , Modbus 채널 열거값입니다.
slaveId : int32_t , Modbus 슬레이브 ID입니다.
masterId : int32_t , 마스터 파라미터 ID 또는 채널 컨텍스트 ID입니다.
outSlave : ArmModbusSlaveHandle** , 출력 Modbus 슬레이브 핸들이며 성공 후 호출자가 폐기해야 합니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.

Arm_Modbus_GetParam

c
int Arm_Modbus_GetParam(ArmHandle* h, int channel, int32_t masterId, ArmSerialParams* outParams);
항목설명
설명Modbus 채널의 시리얼 포트 파라미터를 읽습니다.
요청 파라미터h : ArmHandle* , C99 세션 핸들이며 일반적으로 Arm_Create() 에서 생성합니다. 비즈니스 인터페이스는 먼저 연결에 성공해야 합니다.
channel : int , Modbus 채널 열거값입니다.
masterId : int32_t , 마스터 파라미터 ID 또는 채널 컨텍스트 ID입니다.
outParams : ArmSerialParams* , 시리얼 포트 파라미터 출력 구조체 포인터입니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.

Arm_Modbus_SetParam

c
int Arm_Modbus_SetParam(ArmHandle* h, const ArmSerialParams* params, int32_t* outId);
항목설명
설명Modbus 채널의 시리얼 포트 파라미터를 설정합니다.
요청 파라미터h : ArmHandle* , C99 세션 핸들이며 일반적으로 Arm_Create() 에서 생성합니다. 비즈니스 인터페이스는 먼저 연결에 성공해야 합니다.
params : const ArmSerialParams* , 시리얼 포트 파라미터 구조체 포인터입니다.
outId : int32_t* , 파라미터 기록 ID 출력 포인터입니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.

Arm_ModbusSlave_Destroy

c
void Arm_ModbusSlave_Destroy(ArmModbusSlaveHandle* slave);
항목설명
설명Modbus 슬레이브 핸들을 폐기합니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
반환값반환값 없음

Arm_ModbusSlave_IsValid

c
int Arm_ModbusSlave_IsValid(ArmModbusSlaveHandle* slave);
항목설명
설명Modbus 슬레이브 핸들이 유효한지 확인합니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
반환값1 은 핸들이 유효함을, 0 은 핸들이 이미 무효화되었음을 의미합니다. slave == NULL 이거나 예외 상황이면 오류 코드를 반환합니다.

Arm_ModbusSlave_GetParam

c
int Arm_ModbusSlave_GetParam(ArmModbusSlaveHandle* slave, ArmSerialParams* outParams);
항목설명
설명슬레이브 컨텍스트 기준으로 시리얼 포트 파라미터를 읽습니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
outParams : ArmSerialParams* , 시리얼 포트 파라미터 출력 구조체 포인터입니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.

Arm_ModbusSlave_SetParam

c
int Arm_ModbusSlave_SetParam(ArmModbusSlaveHandle* slave, const ArmSerialParams* params, int32_t* outId);
항목설명
설명슬레이브 컨텍스트 기준으로 시리얼 포트 파라미터를 설정합니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
params : const ArmSerialParams* , 시리얼 포트 파라미터 구조체 포인터입니다.
outId : int32_t* , 파라미터 기록 ID 출력 포인터입니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.

Arm_ModbusSlave_ReadCoils

c
int Arm_ModbusSlave_ReadCoils(ArmModbusSlaveHandle* slave, int32_t addr, int32_t num, int32_t* outArray, size_t maxCount, size_t* outCount);
항목설명
설명코일을 읽습니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
addr : int32_t , 시작 주소입니다.
num : int32_t , 읽을 개수입니다.
outArray : int32_t* , 호출자가 할당하는 출력 배열입니다.
maxCount : size_t , 출력 배열 용량이며 호출자가 최대 몇 개의 요소를 받을 수 있는지를 나타냅니다.
outCount : size_t* , 출력 개수 포인터이며 성공 시 실제 개수를 기록합니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.
비고배열 출력은 호출자가 할당합니다. maxCount 는 용량을 나타내고, outCount 는 실제 개수를 반환합니다.

Arm_ModbusSlave_WriteCoils

c
int Arm_ModbusSlave_WriteCoils(ArmModbusSlaveHandle* slave, int32_t addr, const int32_t* values, size_t count);
항목설명
설명코일에 씁니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
addr : int32_t , 시작 주소입니다.
values : const int32_t* , 쓸 값 배열입니다.
count : size_t , 배열 요소 개수입니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.

Arm_ModbusSlave_ReadHoldingRegs

c
int Arm_ModbusSlave_ReadHoldingRegs(ArmModbusSlaveHandle* slave, int32_t addr, int32_t num, int32_t* outArray, size_t maxCount, size_t* outCount);
항목설명
설명holding register를 읽습니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
addr : int32_t , 시작 주소입니다.
num : int32_t , 읽을 개수입니다.
outArray : int32_t* , 호출자가 할당하는 출력 배열입니다.
maxCount : size_t , 출력 배열 용량이며 호출자가 최대 몇 개의 요소를 받을 수 있는지를 나타냅니다.
outCount : size_t* , 출력 개수 포인터이며 성공 시 실제 개수를 기록합니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.
비고배열 출력은 호출자가 할당합니다. maxCount 는 용량을 나타내고, outCount 는 실제 개수를 반환합니다.

Arm_ModbusSlave_WriteHoldingRegs

c
int Arm_ModbusSlave_WriteHoldingRegs(ArmModbusSlaveHandle* slave, int32_t addr, const int32_t* values, size_t count);
항목설명
설명holding register에 씁니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
addr : int32_t , 시작 주소입니다.
values : const int32_t* , 쓸 값 배열입니다.
count : size_t , 배열 요소 개수입니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.

Arm_ModbusSlave_ReadDiscreteInputs

c
int Arm_ModbusSlave_ReadDiscreteInputs(ArmModbusSlaveHandle* slave, int32_t addr, int32_t num, int32_t* outArray, size_t maxCount, size_t* outCount);
항목설명
설명discrete input을 읽습니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
addr : int32_t , 시작 주소입니다.
num : int32_t , 읽을 개수입니다.
outArray : int32_t* , 호출자가 할당하는 출력 배열입니다.
maxCount : size_t , 출력 배열 용량이며 호출자가 최대 몇 개의 요소를 받을 수 있는지를 나타냅니다.
outCount : size_t* , 출력 개수 포인터이며 성공 시 실제 개수를 기록합니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.
비고배열 출력은 호출자가 할당합니다. maxCount 는 용량을 나타내고, outCount 는 실제 개수를 반환합니다.

Arm_ModbusSlave_ReadInputRegs

c
int Arm_ModbusSlave_ReadInputRegs(ArmModbusSlaveHandle* slave, int32_t addr, int32_t num, int32_t* outArray, size_t maxCount, size_t* outCount);
항목설명
설명input register를 읽습니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
addr : int32_t , 시작 주소입니다.
num : int32_t , 읽을 개수입니다.
outArray : int32_t* , 호출자가 할당하는 출력 배열입니다.
maxCount : size_t , 출력 배열 용량이며 호출자가 최대 몇 개의 요소를 받을 수 있는지를 나타냅니다.
outCount : size_t* , 출력 개수 포인터이며 성공 시 실제 개수를 기록합니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.
비고배열 출력은 호출자가 할당합니다. maxCount 는 용량을 나타내고, outCount 는 실제 개수를 반환합니다.

Arm_ModbusSlave_WriteInputRegs

c
int Arm_ModbusSlave_WriteInputRegs(ArmModbusSlaveHandle* slave, int32_t addr, const int32_t* values, size_t count);
항목설명
설명input register에 씁니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
addr : int32_t , 시작 주소입니다.
values : const int32_t* , 쓸 값 배열입니다.
count : size_t , 배열 요소 개수입니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.

Arm_ModbusSlave_SerialSend

c
int Arm_ModbusSlave_SerialSend(ArmModbusSlaveHandle* slave, const char* msg);
항목설명
설명시리얼 포트 패스스루 데이터를 전송합니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
msg : const char* , 시리얼 포트 패스스루 전송 텍스트입니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.

Arm_ModbusSlave_SerialReceive

c
int Arm_ModbusSlave_SerialReceive(ArmModbusSlaveHandle* slave, char* outBuf, size_t bufSize);
항목설명
설명시리얼 포트 패스스루 데이터를 수신합니다.
요청 파라미터slave : ArmModbusSlaveHandle* , Arm_Modbus_GetSlave() 에서 생성한 Modbus 슬레이브 핸들입니다.
outBuf : char* , 호출자가 할당하는 출력 문자열 버퍼입니다.
bufSize : size_t , 출력 버퍼 크기이며 끝의 \0 공간을 포함합니다.
반환값STATUS_CODE 정수값입니다. 0 은 성공을 의미하며, 그 외 값은 상태 코드에 따라 처리합니다.
비고문자열 출력은 호출자가 제공한 버퍼를 사용하며, 버퍼가 부족하면 상태 코드에 따라 반환합니다.

자주 사용하는 타입

ArmModbusChannel

열거값설명
ARM_MODBUS_CHANNEL_CONTROLLER_TCP_TO_4852컨트롤러 TCP-to-485 채널
ARM_MODBUS_CHANNEL_WRIST_485_03손목 485_0 채널
ARM_MODBUS_CHANNEL_WRIST_485_14손목 485_1 채널
ARM_MODBUS_CHANNEL_CONTROLLER_4855컨트롤러 485 채널

ArmModbusParity

열거값설명
ARM_MODBUS_PARITY_NONE78패리티 없음, ASCII N
ARM_MODBUS_PARITY_ODD79홀수 패리티, ASCII O
ARM_MODBUS_PARITY_EVEN69짝수 패리티, ASCII E

ArmSerialParams

필드타입설명
idint32_t파라미터 기록 ID
channelint32_tArmModbusChannel 열거값
ipchar[128]대상 IP
portint32_t포트
baudint32_t보드레이트
dataBitint32_t데이터 비트
stopBitint32_t정지 비트
parityint32_tArmModbusParity 열거값
timeoutint32_ttimeout, 밀리초

포인터 및 버퍼 규칙

시나리오반환
h == NULL 또는 slave == NULL오류 코드를 반환합니다.
필수 out 포인터가 NULLINVALID_PARAMETER 를 반환합니다.
배열 읽기 시 outArray == NULL 이고 outCount != NULL개수만 되돌려 씁니다.
배열 읽기 버퍼 부족BUFFER_TOO_SMALL 을 반환하고 outCount0 으로 설정합니다.
Arm_ModbusSlave_SerialReceivebufSize == 0BUFFER_TOO_SMALL 을 반환합니다.
SerialReceive 실패 및 outBuf 유효빈 문자열을 씁니다.

파라미터 및 수명 주기

항목규칙
연결 전제먼저 Arm_Connect() 가 성공해야 합니다.
addr시작 주소는 addr >= 0 이어야 합니다.
읽기 개수num1..120 이어야 합니다.
쓰기 개수count1..1024 여야 합니다.
주소 상한addr + 개수 - 165535 를 초과할 수 없습니다.
valuescount > 0 이면 비어 있으면 안 됩니다.
슬레이브 핸들이를 생성한 ArmHandle* 에 의존하므로 Arm_Disconnect() 를 넘어 재사용할 수 없습니다.
outIdSetParam 성공 시 컨트롤러가 확인한 파라미터 기록 ID를 씁니다.

동작 규칙:

  • Arm_Modbus_GetSlave() 는 로컬 핸들을 생성합니다. 원격 슬레이브 상태는 이후 읽기/쓰기 결과로 확인합니다.
  • Arm_ModbusSlave_IsValid() 는 로컬 핸들이 여전히 유효한 세션에 바인딩되어 있는지 확인합니다. 원격 온라인 상태는 읽기/쓰기 결과로 확인합니다.
  • Arm_Modbus_GetParam()ARM_MODBUS_CHANNEL_CONTROLLER_TCP_TO_485 채널에서 masterId 를 컨트롤러로 보냅니다. 다른 채널은 일반적으로 masterId 를 무시합니다.
  • 시리얼 포트 패스스루는 채널 기준으로 동작합니다. slaveId / masterId 는 일반 Modbus 세션 컨텍스트에 사용됩니다.
  • 읽기 인터페이스는 컨트롤러가 반환한 정수 배열을 반환합니다. 코일 값도 컨트롤러 결과대로 표시합니다.
  • 쓰기 인터페이스는 주소 범위와 개수를 검증합니다. 각 values 요소의 비즈니스 의미는 컨트롤러가 처리합니다.

실패 기준

시나리오반환
슬레이브 핸들이 유효하지 않음INVALID_SESSION
채널 열거값 또는 패리티 열거값이 올바르지 않음INVALID_PARAMETER
addr < 0INVALID_PARAMETER
num < 1 또는 num > 120INVALID_PARAMETER
count < 1 또는 count > 1024INVALID_PARAMETER
addr + 개수 - 1 > 65535INVALID_PARAMETER
컨트롤러가 예상과 다른 데이터 구조를 반환OTHER_ERR

최소 호출 예제

c
#include <stdio.h>  // Modbus 조회 결과 출력을 위해 printf를 포함합니다.
#include "c_arm_api.h"  // C99 SDK 최상위 헤더를 포함합니다.
int main(void)  // 예제 프로그램 진입점
{  // 예제 main 함수 시작
    ArmHandle* h = Arm_Create();  // C99 세션 핸들을 생성합니다.
    ArmModbusSlaveHandle* slave = NULL;  // Modbus 슬레이브 핸들 수신을 준비합니다.
    int32_t coils[8] = {0};  // 코일 값 수신 버퍼를 준비합니다.
    size_t outCount = 0U;  // 실제 읽은 개수 수신 변수를 준비합니다.
    if (h == NULL) {  // 세션 핸들 생성 실패 여부를 확인합니다.
        return 1;  // 생성 실패 시 종료합니다.
    }  // 핸들 생성 검사 종료
    if (Arm_Connect(h, "10.27.1.254", NULL) != 0) {  // 컨트롤러 또는 라우팅 주소에 연결합니다.
        Arm_Destroy(h);  // 연결 실패 시 세션 핸들을 폐기합니다.
        return 1;  // 오류 코드를 반환합니다.
    }  // 연결 검사 종료
    if (Arm_Modbus_GetSlave(h, ARM_MODBUS_CHANNEL_CONTROLLER_485, 1, 0, &slave) != 0) {  // 컨트롤러 485 채널의 슬레이브 핸들을 생성합니다.
        Arm_Disconnect(h);  // 생성 실패 시 로봇 연결을 해제합니다.
        Arm_Destroy(h);  // 생성 실패 시 세션 핸들을 폐기합니다.
        return 1;  // 오류 코드를 반환합니다.
    }  // 슬레이브 핸들 생성 검사 종료
    int ret = Arm_ModbusSlave_ReadCoils(slave, 0, 2, coils, 8, &outCount);  // 주소 0부터 코일 값 2개를 읽습니다.
    if (ret == 0) {  // 코일 읽기 성공 여부를 확인합니다.
        printf("coil_count=%zu\n", outCount);  // 실제 읽은 개수를 출력합니다.
    }  // 코일 읽기 검사 종료
    Arm_ModbusSlave_Destroy(slave);  // 슬레이브 핸들을 폐기합니다.
    Arm_Disconnect(h);  // 로봇 연결을 해제합니다.
    Arm_Destroy(h);  // 세션 핸들을 폐기합니다.
    return ret == 0 ? 0 : 1;  // 읽기 결과에 따라 예제 상태를 반환합니다.
}  // 예제 main 함수 종료

시나리오 예제

아래 조각은 연결에 성공한 ArmHandle* h 가 이미 있다고 가정합니다. 파라미터 설정, 레지스터 쓰기, 시리얼 포트 패스스루는 현장 장비에 영향을 주므로 확인 후 실행하세요.

시리얼 포트 파라미터 조회 및 설정

c
ArmSerialParams params = {0};  // 시리얼 포트 파라미터 출력 구조체를 준비합니다.
int32_t savedId = 0;  // 컨트롤러가 확인한 파라미터 기록 ID 수신을 준비합니다.
int getRet = Arm_Modbus_GetParam(h, ARM_MODBUS_CHANNEL_CONTROLLER_485, 0, &params);  // 컨트롤러 485 채널 파라미터를 조회합니다.
params.channel = ARM_MODBUS_CHANNEL_CONTROLLER_485;  // 설정할 채널을 지정합니다.
/* int setRet = Arm_Modbus_SetParam(h, &params, &savedId); */  // 시리얼 포트 파라미터 설정은 컨트롤러 설정을 변경하므로 확인 후 실행합니다.
(void)getRet;  // 예제에서 조회 상태 코드를 유지합니다.
(void)savedId;  // 예제에서 설정 출력 변수를 유지합니다.

슬레이브 읽기/쓰기

c
int32_t values[4] = {0};  // 읽기 출력 버퍼를 준비합니다.
int32_t writeValues[2] = {1, 0};  // 코일에 쓸 값을 준비합니다.
size_t outCount = 0U;  // 읽기 개수 수신을 준비합니다.
int coilsRet = Arm_ModbusSlave_ReadCoils(slave, 0, 2, values, 4, &outCount);  // 코일을 읽습니다.
int holdingRet = Arm_ModbusSlave_ReadHoldingRegs(slave, 0, 2, values, 4, &outCount);  // holding register를 읽습니다.
int discreteRet = Arm_ModbusSlave_ReadDiscreteInputs(slave, 0, 2, values, 4, &outCount);  // discrete input을 읽습니다.
int inputRet = Arm_ModbusSlave_ReadInputRegs(slave, 0, 2, values, 4, &outCount);  // input register를 읽습니다.
/* int writeCoilsRet = Arm_ModbusSlave_WriteCoils(slave, 0, writeValues, 2); */  // 코일 쓰기는 슬레이브 상태를 변경하므로 확인 후 실행합니다.
(void)coilsRet;  // 예제에서 코일 읽기 상태 코드를 유지합니다.
(void)holdingRet;  // 예제에서 holding register 읽기 상태 코드를 유지합니다.
(void)discreteRet;  // 예제에서 discrete input 읽기 상태 코드를 유지합니다.
(void)inputRet;  // 예제에서 input register 읽기 상태 코드를 유지합니다.

슬레이브 컨텍스트 기준 파라미터 조회 및 시리얼 포트 패스스루

c
ArmSerialParams params = {0};  // 시리얼 포트 파라미터 출력 구조체를 준비합니다.
char recvBuf[1024] = {0};  // 시리얼 포트 패스스루 수신 버퍼를 준비합니다.
int valid = Arm_ModbusSlave_IsValid(slave);  // 슬레이브 핸들이 여전히 유효한지 확인합니다.
int getRet = Arm_ModbusSlave_GetParam(slave, &params);  // 슬레이브 컨텍스트 기준으로 시리얼 포트 파라미터를 조회합니다.
/* int sendRet = Arm_ModbusSlave_SerialSend(slave, "010300000002"); */  // 시리얼 포트 패스스루 전송은 현장 버스에 접근하므로 확인 후 실행합니다.
/* int recvRet = Arm_ModbusSlave_SerialReceive(slave, recvBuf, sizeof(recvBuf)); */  // 시리얼 포트 패스스루 수신은 현장 데이터를 기다리므로 확인 후 실행합니다.
(void)valid;  // 예제에서 유효성 검사 결과를 유지합니다.
(void)getRet;  // 예제에서 조회 상태 코드를 유지합니다.

예제 코드

c99/modbus_basic/src/main.cpp
cpp
#include <stdio.h>
#include <string.h>

extern "C" {
#include "c_arm_api.h"
}

int main(void)
{
    // [ZH] 本示例直接在源码中写死连接地址,不解析命令行参数。
    // [EN] This example hard-codes the connection addresses in the source code and does not parse command-line arguments.
    // [ZH] 创建并连接 SDK 句柄。
    // [EN] Create the SDK handle and connect to the robot.
    ArmHandle* handle = Arm_Create();
    if (handle == NULL) {
        printf("[c99_modbus] 创建句柄失败 / Failed to create the handle\n");
        return 1;
    }
    int ret = Arm_Connect(handle, "10.27.1.2", "10.27.1.102");
    if (ret != 0) {
        printf("[c99_modbus] 连接失败 / Connect failed, 状态码 / Status code: %d\n", ret);
        Arm_Destroy(handle);
        return 1;
    }
    printf("[c99_modbus] 机器人连接成功 / Robot connected successfully\n");

    // [ZH] 准备主站串口参数并读取当前值。
    // [EN] Prepare master serial params and read the current values.
    ArmSerialParams masterParams = {0};
    int32_t paramId = 0;
    snprintf(masterParams.ip, sizeof(masterParams.ip), "127.0.0.1");
    masterParams.channel = ARM_MODBUS_CHANNEL_CONTROLLER_485;
    masterParams.id = 1;
    masterParams.port = 502;
    masterParams.baud = 9600;
    masterParams.dataBit = 8;
    masterParams.stopBit = 1;
    masterParams.parity = ARM_MODBUS_PARITY_NONE;
    masterParams.timeout = 1000;
    ret = Arm_Modbus_GetParam(handle, ARM_MODBUS_CHANNEL_CONTROLLER_485, 0, &masterParams);
    printf("[c99_modbus] GetParam 状态码 / GetParam status code: %d, channel=%d, baud=%d\n", ret, masterParams.channel, masterParams.baud);
    ret = Arm_Modbus_SetParam(handle, &masterParams, &paramId);
    printf("[c99_modbus] SetParam 状态码 / SetParam status code: %d, id=%d\n", ret, paramId);

    // [ZH] 获取从站句柄并顺序执行全部从站接口。
    // [EN] Acquire the slave handle and execute all slave APIs in sequence.
    ArmModbusSlaveHandle* slave = NULL;
    ret = Arm_Modbus_GetSlave(handle, ARM_MODBUS_CHANNEL_CONTROLLER_485, 1, 0, &slave);
    printf("[c99_modbus] GetSlave 状态码 / GetSlave status code: %d\n", ret);
    if (slave != NULL) {
        int valid = Arm_ModbusSlave_IsValid(slave);
        printf("[c99_modbus] Slave_IsValid / Slave_IsValid: %d\n", valid);
        int32_t values[8] = {0};
        size_t valueCount = 0U;
        ret = Arm_ModbusSlave_ReadCoils(slave, 0, 2, values, 8U, &valueCount);
        printf("[c99_modbus] ReadCoils 状态码 / ReadCoils status code: %d, count=%zu\n", ret, valueCount);
        int32_t writeCoils[2] = {1, 0};
        ret = Arm_ModbusSlave_WriteCoils(slave, 0, writeCoils, 2U);
        printf("[c99_modbus] WriteCoils 状态码 / WriteCoils status code: %d\n", ret);
        ret = Arm_ModbusSlave_ReadHoldingRegs(slave, 0, 2, values, 8U, &valueCount);
        printf("[c99_modbus] ReadHoldingRegs 状态码 / ReadHoldingRegs status code: %d, count=%zu\n", ret, valueCount);
        int32_t holdingRegs[2] = {1, 2};
        ret = Arm_ModbusSlave_WriteHoldingRegs(slave, 0, holdingRegs, 2U);
        printf("[c99_modbus] WriteHoldingRegs 状态码 / WriteHoldingRegs status code: %d\n", ret);
        ret = Arm_ModbusSlave_ReadDiscreteInputs(slave, 0, 2, values, 8U, &valueCount);
        printf("[c99_modbus] ReadDiscreteInputs 状态码 / ReadDiscreteInputs status code: %d, count=%zu\n", ret, valueCount);
        ret = Arm_ModbusSlave_ReadInputRegs(slave, 0, 2, values, 8U, &valueCount);
        printf("[c99_modbus] ReadInputRegs 状态码 / ReadInputRegs status code: %d, count=%zu\n", ret, valueCount);
        int32_t inputRegs[2] = {3, 4};
        ret = Arm_ModbusSlave_WriteInputRegs(slave, 0, inputRegs, 2U);
        printf("[c99_modbus] WriteInputRegs 状态码 / WriteInputRegs status code: %d\n", ret);
        ret = Arm_ModbusSlave_SetParam(slave, &masterParams, &paramId);
        printf("[c99_modbus] Slave_SetParam 状态码 / Slave_SetParam status code: %d, id=%d\n", ret, paramId);
        ret = Arm_ModbusSlave_GetParam(slave, &masterParams);
        printf("[c99_modbus] Slave_GetParam 状态码 / Slave_GetParam status code: %d, baud=%d\n", ret, masterParams.baud);
        ret = Arm_ModbusSlave_SerialSend(slave, "hello from c99 example");
        printf("[c99_modbus] SerialSend 状态码 / SerialSend status code: %d\n", ret);
        char recvBuf[256] = {0};
        ret = Arm_ModbusSlave_SerialReceive(slave, recvBuf, sizeof(recvBuf));
        printf("[c99_modbus] SerialReceive 状态码 / SerialReceive status code: %d, 内容 / Text: %s\n", ret, recvBuf);
        Arm_ModbusSlave_Destroy(slave);
        printf("[c99_modbus] 从站句柄已释放 / Slave handle destroyed\n");
    }

    // [ZH] 断开连接并销毁句柄。
    // [EN] Disconnect and destroy the handle.
    Arm_Disconnect(handle);
    Arm_Destroy(handle);
    printf("[c99_modbus] 示例结束 / Example finished\n");
    return 0;
}