modbusrtuinterface.cpp
1 #include "modbusrtuinterface.h"
2 
3 std::deque<weak_ptr<XModbusRTUInterface::PortWrapper>> XModbusRTUInterface::s_openedPorts;
4 XMutex XModbusRTUInterface::s_globalMutex;
5 
6 XModbusRTUInterface::XModbusRTUInterface(const char *name, bool runtime, const shared_ptr<XDriver> &driver) :
7  XCharInterface(name, runtime, driver) {
8  setEOS("");
9  setSerialEOS("");
10  setSerialBaudRate(9600);
11  setSerialStopBits(1);
12 }
13 
14 XModbusRTUInterface::~XModbusRTUInterface() {
15 }
16 void
17 XModbusRTUInterface::open() throw (XInterfaceError &) {
19  {
20  Snapshot shot( *this);
21  XScopedLock<XMutex> glock( s_globalMutex);
22  for(auto it = s_openedPorts.begin(); it != s_openedPorts.end();) {
23  if(auto pt = it->lock()) {
24  if(pt->port->portString() == shot[ *port()].to_str()) {
25 // if(pt->m_ != serialBaudRate())
26 // throw XInterface::XOpenInterfaceError(__FILE__, __LINE__);
27 // if(pt->serialStopBits() != serialStopBits())
28 // throw XInterface::XOpenInterfaceError(__FILE__, __LINE__);
29 // if(pt->serialParity() != serialParity())
30 // throw XInterface::XOpenInterfaceError(__FILE__, __LINE__);
31  m_openedPort = pt;
32  //The COMM port has been already opened by m_openedPort.
33  return;
34  }
35  ++it;
36  }
37  else
38  it = s_openedPorts.erase(it); //cleans garbage.
39  }
40  //Opens new COMM device.
41  XCharInterface::open();
42  m_openedPort = std::make_shared<PortWrapper>();
43  m_openedPort->port = openedPort();
44  m_openedPort->lastTimeStamp = XTime::now();
45  s_openedPorts.push_back(m_openedPort);
46  }
47 }
48 void
49 XModbusRTUInterface::close() throw (XInterfaceError &) {
51  m_openedPort.reset(); //release shared_ptr to the port if any.
52  XCharInterface::close(); //release shared_ptr to the port if any.
53 }
54 
55 uint16_t
56 XModbusRTUInterface::crc16(const unsigned char *bytes, uint32_t count) {
57  uint16_t z = 0xffffu;
58  for(uint32_t i = 0; i < count; ++i) {
59  uint16_t x = bytes[i];
60  z ^= x;
61  for(int shifts = 0; shifts < 8; ++shifts) {
62  uint16_t lsb = z % 2;
63  z = z >> 1;
64  if(lsb)
65  z ^= 0xa001u;
66  }
67  }
68  return (z % 0x100u) * 0x100u + z / 0x100u;
69 }
70 void
71 XModbusRTUInterface::query_unicast(unsigned int func_code,
72  const std::vector<unsigned char> &bytes, std::vector<unsigned char> &ret_buf) {
73  auto port = m_openedPort;
74 
75  double msec_per_char = 1e3 / serialBaudRate() * 12;
76  double silent = 1e-3 * std::max(3.0, 3.5 * msec_per_char);
77  for(;; msecsleep(silent * 1e3 / 2 + 1)) {
78  {
79  XScopedLock<XMutex> portlock(port->mutex);
80 
81  if(XTime::now() - port->lastTimeStamp < silent)
82  continue; //puts silent interval. Waits outside the lock.
83 
84  port->lastTimeStamp = XTime::now();
85  port->lastTimeStamp += 0.1; //for possible throwing.
86 
87  unsigned int slave_addr = ***address();
88  std::vector<unsigned char> buf(bytes.size() + 4);
89  buf[0] = static_cast<unsigned char>(slave_addr);
90  buf[1] = static_cast<unsigned char>(func_code);
91  std::copy(bytes.begin(), bytes.end(), &buf[2]);
92  uint16_t crc = crc16( &buf[0], buf.size() - 2);
93  set_word( &buf[buf.size() - 2], crc);
94 
95  port->port->write( reinterpret_cast<char*>(&buf[0]), buf.size());
96  // msecsleep(buf.size() * msec_per_char + std::max(3.0, 3.5 * msec_per_char) + 0.5); //For O_NONBLOCK.
97 
98  buf.resize(ret_buf.size() + 4);
99  port->port->receive(2); //addr + func_code.
100  std::copy(port->port->buffer().begin(), port->port->buffer().end(), buf.begin());
101 
102  if(buf[0] != slave_addr) {
103  if(buf[1] == slave_addr) {
104  buf[0] = buf[1];
105  port->port->receive(1); //func_code.
106  buf[1] = port->port->buffer()[0];
107  }
108  else if((buf[1] & 0x7fu) == func_code) {
109  buf[0] = slave_addr;
110  }
111  else
112  throw XInterfaceError(formatString("Modbus RTU Address Error, got %u instead of %u, and func. = %u.", buf[0], slave_addr, buf[1]), __FILE__, __LINE__);
113  //met spurious start bit.
114  gWarnPrint(formatString("Modbus RTU, ignores spurious start bit before a response for slave %u.", slave_addr));
115  }
116  if((buf[1] & 0x7fu) != func_code)
117  throw XInterfaceError("Modbus RTU Format Error.", __FILE__, __LINE__);
118  if(buf[1] != func_code) {
119  port->port->receive(3);
120  switch(port->port->buffer()[0]) {
121  case 0x01:
122  throw XInterfaceError("Modbus RTU Ill Function.", __FILE__, __LINE__);
123  case 0x02:
124  throw XInterfaceError("Modbus RTU Wrong Data Address.", __FILE__, __LINE__);
125  case 0x03:
126  throw XInterfaceError("Modbus RTU Wrong Data.", __FILE__, __LINE__);
127  case 0x04:
128  throw XInterfaceError("Modbus RTU Slave Error.", __FILE__, __LINE__);
129  default:
130  throw XInterfaceError("Modbus RTU Format Error.", __FILE__, __LINE__);
131  }
132  }
133 
134  port->port->receive( ret_buf.size() + 2); //Rest of message.
135  std::copy(port->port->buffer().begin(), port->port->buffer().end(), buf.begin() + 2);
136  crc = crc16( &buf[0], buf.size() - 2);
137  if(crc != get_word( &buf[buf.size() - 2]))
138  throw XInterfaceError("Modbus RTU CRC Error.", __FILE__, __LINE__);
139  std::copy(port->port->buffer().begin(), port->port->buffer().end() - 2, ret_buf.begin());
140 
141  port->lastTimeStamp = XTime::now();
142  }
143  break;
144  }
145 }
146 void
147 XModbusRTUInterface::readHoldingResistors(uint16_t res_addr, int count, std::vector<uint16_t> &data) {
148  std::vector<unsigned char> wrbuf(4);
149  set_word( &wrbuf[0], res_addr);
150  set_word( &wrbuf[2], count);
151  std::vector<unsigned char> rdbuf(2 * count + 1);
152  query_unicast(0x03, wrbuf, rdbuf);
153  data.resize(count);
154  if(rdbuf[0] != 2 * count)
155  throw XInterfaceError("Modbus RTU Format Error.", __FILE__, __LINE__);
156  for(unsigned int i = 0; i < count; ++i) {
157  data[i] = get_word( &rdbuf[2 * i + 1]);
158  }
159 }
160 void
161 XModbusRTUInterface::presetSingleResistor(uint16_t res_addr, uint16_t data) {
162  std::vector<unsigned char> wrbuf(4);
163  set_word( &wrbuf[0], res_addr);
164  set_word( &wrbuf[2], data);
165  std::vector<unsigned char> rdbuf(4);
166  query_unicast(0x06, wrbuf, rdbuf);
167  if(rdbuf.back() != wrbuf.back())
168  throw XInterfaceError("Modbus Format Error.", __FILE__, __LINE__);
169 }
170 void
171 XModbusRTUInterface::presetMultipleResistors(uint16_t res_no, int count, const std::vector<uint16_t> &data) {
172  std::vector<unsigned char> wrbuf(5 + 2 * count);
173  set_word( &wrbuf[0], res_no);
174  set_word( &wrbuf[2], count);
175  wrbuf[4] = count * 2;
176  int idx = 5;
177  for(auto it = data.begin(); it != data.end(); ++it) {
178  set_word( &wrbuf[idx], *it);
179  idx += 2;
180  }
181  std::vector<unsigned char> rdbuf(4);
182  query_unicast(0x10, wrbuf, rdbuf);
183 }
184 void
185 XModbusRTUInterface::diagnostics() {
186  std::vector<unsigned char> wrbuf(4);
187  set_word( &wrbuf[0], 0);
188  set_word( &wrbuf[2], 0x1234);
189  std::vector<unsigned char> rdbuf(4);
190  query_unicast(0x08, wrbuf, rdbuf);
191 }

Generated for KAME4 by  doxygen 1.8.3