Dawn Framework 1.0
Universal data acquisition framework for embedded systems
tcp.cxx
1// dawn/src/proto/modbus/tcp.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/proto/modbus/tcp.hxx"
7
8#include "dawn/io/common.hxx"
9
10using namespace dawn;
11
12int CProtoModbusTcp::configureDesc(const CDescObject &desc)
13{
14 SObjectCfg::SObjectCfgItem *item = nullptr;
15 size_t offset = 0;
16
17 for (size_t i = 0; i < desc.getSize(); i++)
18 {
19 item = desc.objectCfgItemNext(offset);
20
22 {
23 DAWNERR("Unsupported Modbus TCP config 0x%08" PRIx32 "\n", item->cfgid.v);
24 return -EINVAL;
25 }
26
27 switch (item->cfgid.s.id)
28 {
29 case PROTO_MODBUS_TCP_CFG_IOBIND:
30 {
31 for (size_t j = 0; j < item->cfgid.s.size;)
32 {
33 SProtoModbusIOBind *tmp = reinterpret_cast<SProtoModbusIOBind *>(item->data + j);
34
35 allocObject(tmp);
36 j += sizeof(SProtoModbusIOBind) / 4 + tmp->size;
37 }
38
39 break;
40 }
41
42 case PROTO_MODBUS_TCP_CFG_PORT:
43 {
44 port = static_cast<uint16_t>(item->data[0]);
45 break;
46 }
47
48 default:
49 {
50 DAWNERR("Unsupported Modbus TCP config 0x%08" PRIx32 "\n", item->cfgid.v);
51 return -EINVAL;
52 }
53 }
54 }
55
56 return OK;
57}
58
59void CProtoModbusTcp::allocObject(SProtoModbusIOBind *alloc)
60{
61 for (size_t i = 0; i < alloc->size; i++)
62 {
63 DAWNINFO("allocate object 0x%" PRIx32 "\n", alloc->objid[i]);
64
65 setObjectMapItem(alloc->objid[i], nullptr);
66 }
67
68 valloc_push_back(alloc);
69};
70
71void CProtoModbusTcp::thread()
72{
73 DAWNINFO("start modbus tcp thread\n");
74
75 do
76 {
77 int ret;
78
79 ret = nxmb_poll(mbhandle);
80 if (ret < 0 && ret != -ETIMEDOUT && ret != -EAGAIN && ret != -ECONNRESET)
81 {
82 DAWNERR("nxmb_poll error: %d\n", ret);
83 break;
84 }
85 }
86 while (!workerThread().shouldQuit());
87
88 workerThread().markThreadFinished();
89}
90
91int CProtoModbusTcp::modbusInitialize()
92{
93 struct nxmb_config_s config;
94 int ret;
95
96 memset(&config, 0, sizeof(config));
97 config.unit_id = saddr;
98 config.is_client = false;
99 config.mode = NXMB_MODE_TCP;
100 config.transport.tcp.port = port;
101 config.transport.tcp.host = NULL;
102 config.transport.tcp.bindaddr = NULL;
103
104 ret = nxmb_create(&mbhandle, &config);
105 if (ret < 0)
106 {
107 DAWNERR("ERROR: nxmb_create failed: %d\n", ret);
108 return ret;
109 }
110
111 memset(&callbacks, 0, sizeof(callbacks));
112#ifdef CONFIG_DAWN_PROTO_MODBUS_COIL
113 callbacks.coil_cb = [](FAR uint8_t *buf,
114 uint16_t addr,
115 uint16_t ncoils,
116 enum nxmb_regmode_e mode,
117 FAR void *priv) -> int
118 { return static_cast<CProtoModbusRegs *>(priv)->coilsCb(buf, addr, ncoils, mode, priv); };
119#endif
120#ifdef CONFIG_DAWN_PROTO_MODBUS_DISCRETE
121 callbacks.discrete_cb =
122 [](FAR uint8_t *buf, uint16_t addr, uint16_t ndiscrete, FAR void *priv) -> int
123 { return static_cast<CProtoModbusRegs *>(priv)->discreteCb(buf, addr, ndiscrete, priv); };
124#endif
125#ifdef CONFIG_DAWN_PROTO_MODBUS_INPUT
126 callbacks.input_cb = [](FAR uint8_t *buf, uint16_t addr, uint16_t nregs, FAR void *priv) -> int
127 { return static_cast<CProtoModbusRegs *>(priv)->inputCb(buf, addr, nregs, priv); };
128#endif
129#ifdef CONFIG_DAWN_PROTO_MODBUS_HOLDING
130 callbacks.holding_cb = [](FAR uint8_t *buf,
131 uint16_t addr,
132 uint16_t nregs,
133 enum nxmb_regmode_e mode,
134 FAR void *priv) -> int
135 { return static_cast<CProtoModbusRegs *>(priv)->holdingCb(buf, addr, nregs, mode, priv); };
136#endif
137 callbacks.priv = static_cast<CProtoModbusRegs *>(this);
138
139 ret = nxmb_set_callbacks(mbhandle, &callbacks);
140 if (ret < 0)
141 {
142 DAWNERR("ERROR: nxmb_set_callbacks failed: %d\n", ret);
143 nxmb_destroy(mbhandle);
144 return ret;
145 }
146
147 ret = nxmb_enable(mbhandle);
148 if (ret < 0)
149 {
150 DAWNERR("ERROR: nxmb_enable failed: %d\n", ret);
151 nxmb_destroy(mbhandle);
152 mbhandle = NULL;
153 return ret;
154 }
155
156 return OK;
157}
158
159CProtoModbusTcp::~CProtoModbusTcp()
160{
161 deinit();
162}
163
165{
166 int ret;
167
168 ret = configureDesc(getDesc());
169 if (ret != OK)
170 {
171 DAWNERR("Modbus TCP configure failed (error %d)\n", ret);
172 return ret;
173 }
174
175 return OK;
176}
177
179{
180 int ret;
181
182 ret = createRegs();
183 if (ret < 0)
184 {
185 DAWNERR("failed to create registers %d\n", ret);
186 return ret;
187 }
188
189 return OK;
190}
191
193{
194 destroyRegs();
195
196 if (mbhandle != NULL)
197 {
198 nxmb_disable(mbhandle);
199 nxmb_destroy(mbhandle);
200 mbhandle = NULL;
201 }
202
203 return OK;
204}
205
207{
208 int ret;
209
210 /* Do not open the TCP listen socket during init(). If the socket is
211 * already listening before the worker thread starts polling NxModbus,
212 * a host can complete TCP connect() and then hit a first-request timeout
213 * while Dawn is still finishing startup. Open/enable the transport here so
214 * "protocol started" and "socket accepts requests" become the same event.
215 */
216
217 ret = modbusInitialize();
218 if (ret < 0)
219 {
220 return ret;
221 }
222
223 ret = startWorkerThread([this]() { thread(); });
224 if (ret < 0)
225 {
226 DAWNERR("failed to start thread %d\n", ret);
227 nxmb_disable(mbhandle);
228 nxmb_destroy(mbhandle);
229 mbhandle = NULL;
230 return ret;
231 }
232
233 return OK;
234};
235
237{
239
240 return OK;
241};
242
244{
245 return workerThreadRunning();
246}
void setObjectMapItem(SObjectId::ObjectId id, CObject *obj)
Set an item in the object map.
Definition bindable.cxx:23
Descriptor wrapper for individual object configuration.
size_t getSize() const
Get number of configuration items for this object.
SObjectCfg::SObjectCfgItem * objectCfgItemNext(size_t &offset) const
Get config item at current offset and advance past it.
CDescObject & getDesc()
Get descriptor object for this object.
Definition object.cxx:190
@ PROTO_CLASS_MODBUS_TCP
Modbus TCP (network implementation).
Definition common.hxx:71
Modbus register management base class (shared RTU/TCP logic).
Definition regs.hxx:30
int init()
One-time initialize object after bindings are resolved.
Definition tcp.cxx:178
int doStart()
Start implementation hook.
Definition tcp.cxx:206
int doStop()
Stop implementation hook.
Definition tcp.cxx:236
bool hasThread() const
Check if a background thread is active.
Definition tcp.cxx:243
int configure()
Configure object from descriptor data.
Definition tcp.cxx:164
int deinit()
De-initialize object.
Definition tcp.cxx:192
bool workerThreadRunning() const
Check if the worker thread is running.
Definition thread.hxx:269
int stopWorkerThread()
Stop the worker thread.
Definition thread.hxx:258
int startWorkerThread(Func &&func)
Start the worker thread with a given function.
Definition thread.hxx:246
CThreadedObject & workerThread()
Get a reference to this thread controller.
Definition thread.hxx:280
Out-of-tree user-extension hooks for Dawn.
Definition bindable.hxx:13
Single configuration item within object.
ObjectCfgData_t data[]
Configuration data array (flexible, size from cfgid.s.size).
UObjectCfgId cfgid
Configuration ID header (type, class, id, size, rw, dtype).
ObjectCfgId v
Raw 32-bit ConfigID value (for storage, comparison).
Definition objectcfg.hxx:82
uint32_t cls
Object class (bits 21-29, max 511).
uint32_t id
Configuration identifier (bits 0-4, max 31).
Definition objectcfg.hxx:94
uint32_t size
Configuration data size in 32-bit words (bits 5-14, max 1023).
struct dawn::SObjectCfg::UObjectCfgId::@10 s
Bit-field structure for named member access.