티스토리 뷰

반응형

안녕하세요 드레이크입니다^^

 

지난글에 이어 이번에는 libmodbus를 활용하여 Slave를 구성해보도록 하겠습니다.

 

못 보신 분들은 아래 링크를 참고해주세요!

 

https://gosuway.tistory.com/374?category=754463

 

modbus tcp Master (libmodbus 라이브러리)

안녕하세요 드레이크입니다^^ 이번에 modbus tcp를 연동하는 프로젝트를 진행하게 되어 modbus를 처음 접하게 되었는데요 처음에는 아래 사이트를 통해 개념을 숙지하였구요 http://comfilewiki.co.kr/ko/dok

gosuway.tistory.com

 

Slave는 TCP 소켓 통신에서 서버 개념과 같다고 볼 수 있습니다.

 

아래는 실제 사용한 소스코드이니 참고하시면 도움이 되실겁니다!

 

// CSlave.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <QThread>
#include "modbus/modbus.h"
 
// modbus tcp 서버(Slave)
class CSlave : public QThread
{
private:
    int32_t m_ServerSocket;
    modbus_t *m_Modbus;    // modbus 핸들
    modbus_mapping_t *m_MbMapping; // 메모리 맵
    uint8_t *m_Query;
    int32_t m_HeaderLen;
 
public:
    CSlave();
 
    void run(); // 주 동작부
    bool InitModbusTcp(); // modbus 초기화
    void CloseModbusTcp(); // modbus 종료 처리
    void SetSaleInfo(int32_t Price, int32_t Kg, int32_t Amount); // 충전 정보 보관
};
 
// 클라이언트(Master) 통신 쓰레드
class CClientThread : public QThread
{
private:
    modbus_t *m_Modbus;    // modbus 핸들
    modbus_mapping_t *m_MbMapping; // 메모리 맵
    uint8_t *m_Query;
    int32_t m_HeaderLen;
    int32_t m_RxCount;
 
public:
    CClientThread();
 
    void run(); // 주 동작부
    void SetClientInfo(modbus_t *Modbus, uint8_t *Query, modbus_mapping_t *MbMapping, int32_t HeaderLen); // 클라이언트 정보 전달
};
cs

 

// CSlave.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <stdlib.h>
#include <unistd.h>
#include "modbus/unit-test.h"
 
#include "CSlave.h"
 
// 쓰레드 동기화 처리
static pthread_mutex_t NI_Mutex = PTHREAD_MUTEX_INITIALIZER;
 
CSlave::CSlave()
{
    m_ServerSocket = -1;
    m_Modbus = nullptr;
    m_MbMapping = nullptr;
    m_Query = nullptr;
}
 
void CSlave::run()
{
    InitModbusTcp();
 
    while (1) {
        // 마스터(클라이언트) 기다림, 연결이 될때까지 대기상태
        if(modbus_tcp_accept(m_Modbus, &m_ServerSocket) == -1)
            g_Log.nameputs("modbus_tcp_accept 실패 @@");
        else    {
            g_Log.nameputs( "modbus_tcp_accept 성공");
 
            // 다른 클라이언트의 접속이 또 들어올 수 있으므로 쓰레드를 생성하여 작업을 이관
            CClientThread *ClientThread = new CClientThread();
 
            ClientThread->SetClientInfo(m_Modbus, m_Query, m_MbMapping, m_HeaderLen);
            ClientThread->start();
        }
 
        msleep(1);
    }
 
    CloseModbusTcp();
}
 
void CSlave::SetSaleInfo(int32_t Value1, int32_t Value2, int32_t Value3)
{
    pthread_mutex_lock(&NI_Mutex);
 
    // 메모리 맵에 보관
    m_MbMapping->tab_registers[0= Value1;
    m_MbMapping->tab_registers[1= Value2;
    m_MbMapping->tab_registers[2= Value3;
 
    pthread_mutex_unlock(&NI_Mutex);
}
 
bool CSlave::InitModbusTcp()
{
    // modbus tcp 메모리 할당
    // 장비 자신인 127.0.0.1 루프백 주소가 modbus_new_tcp 함수에서 동작을 안함ㅠㅠ
    // 그리하여 IP주소가 바뀌거나 넬 충전기로 바뀌면 재부팅 처리!!
    m_Modbus = modbus_new_tcp(g_Option.d.Net.Ip, 502);
 
    if(m_Modbus == nullptr) {
        g_Log.nameputs( "modbus_new_tcp 실패 @@");
        return false;
    }
    else
        g_Log.nameputs( "modbus_new_tcp 성공");
 
    // 메모리 할당
    m_Query = (uint8_t *)malloc(MODBUS_TCP_MAX_ADU_LENGTH);
 
    // 헤더길이 파악
    m_HeaderLen = modbus_get_header_length(m_Modbus);
 
    // 디버그 메시지 표시
    // modbus_set_debug(m_Modbus, TRUE);
 
    // 메모리 주소 맵핑
    // coil / discrete input / holding register / input register
    // 4번지까지 필요하므로 holding register로 10번지까지만 생성
    m_MbMapping = modbus_mapping_new(00100);
 
    if (m_MbMapping == nullptr) {
        g_Log.nameprintf( "modbus_mapping_new 실패 @@ : %s", modbus_strerror(errno));
        modbus_free(m_Modbus);
        return false;
    }
    else
        g_Log.nameputs( "modbus_mapping_new 성공");
 
    m_ServerSocket = modbus_tcp_listen(m_Modbus, 1);
 
    if(m_ServerSocket == -1) {
        g_Log.nameputs( "modbus_tcp_listen 실패 @@");
        return false;
    }
    else
        g_Log.nameputs( "modbus_tcp_listen 성공");
 
    return true;
}
 
void CSlave::CloseModbusTcp()
{
    if(m_ServerSocket)   close(m_ServerSocket);
 
    if(m_MbMapping != nullptr)  modbus_mapping_free(m_MbMapping);
 
    if(m_MbMapping != nullptr)  free(m_Query);
 
    // 이미 할당되어 있다면 메모리 해제
    if(m_Modbus != nullptr)    {
        modbus_close(m_Modbus);
        modbus_free(m_Modbus);
    }
}
 
 
// 클라이언트(Master) 통신 쓰레드
CClientThread::CClientThread()
{
    m_Modbus = nullptr;
    m_Query = nullptr;
    m_MbMapping = nullptr;
    m_HeaderLen = 0;
}
 
void CClientThread::run()
{
    g_Log.nameputs( "클라이언트(Master) 통신 시작!");
 
    while (1) {
        // 데이터 수신
        m_RxCount = modbus_receive(m_Modbus, m_Query);
 
        if(m_RxCount > 0)   {
            // Read holding registers
            if (m_Query[m_HeaderLen] == 0x03) {
                pthread_mutex_lock(&NI_Mutex);
 
                // 데이터 전송
                m_RxCount = modbus_reply(m_Modbus, m_Query, m_RxCount, m_MbMapping);
 
                pthread_mutex_unlock(&NI_Mutex);
            }
        }
 
        // 오류 또는 연결이 끊겼다면 루프를 빠져나간다.
        if ((m_RxCount == -1&& (errno != EMBBADCRC))  {
            g_Log.nameputs( "클라이언트(Master) 연결 끊김");
            break;
        }
 
        msleep(1);
    }
}
 
void CClientThread::SetClientInfo(modbus_t *Modbus, uint8_t *Query, modbus_mapping_t *MbMapping, int32_t HeaderLen)
{
    m_Modbus = Modbus;
    m_Query = Query;
    m_MbMapping = MbMapping;
    m_HeaderLen = HeaderLen;
}
cs
반응형
댓글
반응형
최근에 올라온 글
Total
Today
Yesterday