Typica is a free program for professional coffee roasters. https://typica.us
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

modbus.w 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. @** Another Approach for Modbus RTU Support.
  2. \noindent The original code for dealing with Modbus RTU devices had a number of
  3. limitations. It was awkward to configure, limited to using a single device per
  4. bus, supported a small number of channels, and only worked with fixed point
  5. numeric representations. The following sections are an initial draft of a more
  6. flexible reworking of Modbus RTU support which should be easier to extend to
  7. cover Modbus TCP, allows a wider range of numeric representations, and allows
  8. for the use of any number of devices on the bus and any number of channels.
  9. Initial support is focused on function 3 and 4 registers with a known
  10. configuration. Outputs and the ability to configure inputs based on the values
  11. at other addresses will be added later. Modbus TCP support is also planned.
  12. Once feature parity with the old Modbus code is reached, the older code will be
  13. removed.
  14. Rather than use a single configuration widget for the entire bus, this is now
  15. split to use one configuration widget for the bus and another widget for an
  16. input channel.
  17. @<Class declarations@>=
  18. class ModbusNGConfWidget : public BasicDeviceConfigurationWidget
  19. {
  20. Q_OBJECT
  21. public:
  22. Q_INVOKABLE ModbusNGConfWidget(DeviceTreeModel *model, const QModelIndex &index);
  23. private slots:
  24. void updatePort(const QString &value);
  25. void updateBaudRate(const QString &value);
  26. void updateParity(int value);
  27. void updateFlowControl(int value);
  28. void updateStopBits(int value);
  29. void addInput();
  30. private:
  31. ParitySelector *m_parity;
  32. FlowSelector *m_flow;
  33. StopSelector *m_stop;
  34. };
  35. @ The configuration widget for the bus only handles the details of the serial
  36. port and allows adding child nodes representing input channels.
  37. @<ModbusNG implementation@>=
  38. ModbusNGConfWidget::ModbusNGConfWidget(DeviceTreeModel *model, const QModelIndex &index) :
  39. BasicDeviceConfigurationWidget(model, index), m_parity(new ParitySelector),
  40. m_flow(new FlowSelector), m_stop(new StopSelector)
  41. {
  42. QFormLayout *layout = new QFormLayout;
  43. PortSelector *port = new PortSelector;
  44. BaudSelector *baud = new BaudSelector;
  45. QPushButton *newInput = new QPushButton(tr("Add Input Channel"));
  46. layout->addRow(QString(tr("Port:")), port);
  47. layout->addRow(QString(tr("Baud rate:")), baud);
  48. layout->addRow(QString(tr("Parity:")), m_parity);
  49. layout->addRow(QString(tr("Flow control:")), m_flow);
  50. layout->addRow(QString(tr("Stop bits:")), m_stop);
  51. layout->addRow(newInput);
  52. @<Get device configuration data for current node@>@;
  53. for(int i = 0; i < configData.size(); i++)
  54. {
  55. node = configData.at(i).toElement();
  56. if(node.attribute("name") == "port")
  57. {
  58. int idx = port->findText(node.attribute("value"));
  59. if(idx >= 0)
  60. {
  61. port->setCurrentIndex(idx);
  62. }
  63. else
  64. {
  65. port->addItem(node.attribute("value"));
  66. }
  67. }
  68. else if(node.attribute("name") == "baud")
  69. {
  70. baud->setCurrentIndex(baud->findText(node.attribute("value")));
  71. }
  72. else if(node.attribute("name") == "parity")
  73. {
  74. m_parity->setCurrentIndex(m_parity->findData(node.attribute("value")));
  75. }
  76. else if(node.attribute("name") == "flow")
  77. {
  78. m_flow->setCurrentIndex(m_flow->findData(node.attribute("value")));
  79. }
  80. else if(node.attribute("name") == "stop")
  81. {
  82. m_stop->setCurrentIndex(m_stop->findData(node.attribute("value")));
  83. }
  84. }
  85. updatePort(port->currentText());
  86. updateBaudRate(baud->currentText());
  87. updateParity(m_parity->currentIndex());
  88. updateFlowControl(m_flow->currentIndex());
  89. updateStopBits(m_stop->currentIndex());
  90. connect(port, SIGNAL(currentIndexChanged(QString)), this, SLOT(updatePort(QString)));
  91. connect(port, SIGNAL(editTextChanged(QString)), this, SLOT(updatePort(QString)));
  92. connect(baud, SIGNAL(currentIndexChanged(QString)), this, SLOT(updateBaudRate(QString)));
  93. connect(m_parity, SIGNAL(currentIndexChanged(int)), this, SLOT(updateParity(int)));
  94. connect(m_flow, SIGNAL(currentIndexChanged(int)),
  95. this, SLOT(updateFlowControl(int)));
  96. connect(m_stop, SIGNAL(currentIndexChanged(int)), this, SLOT(updateStopBits(int)));
  97. connect(newInput, SIGNAL(clicked()), this, SLOT(addInput()));
  98. setLayout(layout);
  99. }
  100. void ModbusNGConfWidget::updatePort(const QString &value)
  101. {
  102. updateAttribute("port", value);
  103. }
  104. void ModbusNGConfWidget::updateBaudRate(const QString &value)
  105. {
  106. updateAttribute("baud", value);
  107. }
  108. void ModbusNGConfWidget::updateParity(int value)
  109. {
  110. updateAttribute("parity", m_parity->itemData(value).toString());
  111. }
  112. void ModbusNGConfWidget::updateFlowControl(int value)
  113. {
  114. updateAttribute("flow", m_flow->itemData(value).toString());
  115. }
  116. void ModbusNGConfWidget::updateStopBits(int value)
  117. {
  118. updateAttribute("stop", m_stop->itemData(value).toString());
  119. }
  120. void ModbusNGConfWidget::addInput()
  121. {
  122. insertChildNode(tr("Input"), "modbusnginput");
  123. }
  124. @ Next, there is a configuration widget for input channels.
  125. @<Class declarations@>=
  126. class ModbusNGInputConfWidget : public BasicDeviceConfigurationWidget
  127. {
  128. Q_OBJECT
  129. public:
  130. Q_INVOKABLE ModbusNGInputConfWidget(DeviceTreeModel *model, const QModelIndex &index);
  131. private slots:
  132. void updateStation(int value);
  133. void updateAddress(int value);
  134. void updateFunction(int value);
  135. void updateFormat(int value);
  136. void updateDecimals(int value);
  137. void updateUnit(int value);
  138. void updateColumnName(const QString &value);
  139. void updateHidden(bool value);
  140. };
  141. @ This is where the function, address, and additional required details about
  142. an input channel are defind.
  143. @<ModbusNG implementation@>=
  144. ModbusNGInputConfWidget::ModbusNGInputConfWidget(DeviceTreeModel *model, const QModelIndex &index) : BasicDeviceConfigurationWidget(model, index)
  145. {
  146. QFormLayout *layout = new QFormLayout;
  147. QSpinBox *station = new QSpinBox;
  148. station->setMinimum(1);
  149. station->setMaximum(247);
  150. layout->addRow(tr("Station ID"), station);
  151. QComboBox *function = new QComboBox;
  152. function->addItem("3", "3");
  153. function->addItem("4", "4");
  154. function->setCurrentIndex(1);
  155. layout->addRow(tr("Function"), function);
  156. ShortHexSpinBox *address = new ShortHexSpinBox;
  157. layout->addRow(tr("Address"), address);
  158. QComboBox *format = new QComboBox;
  159. format->addItem(tr("16 bits fixed point"), "16fixedint");
  160. format->addItem(tr("32 bits floating point (High Low)"), "32floathl");
  161. format->addItem(tr("32 bits floating point (Low High)"), "32floatlh");
  162. layout->addRow(tr("Data format"), format);
  163. QSpinBox *decimals = new QSpinBox;
  164. decimals->setMinimum(0);
  165. decimals->setMaximum(9);
  166. layout->addRow(tr("Decimal places"), decimals);
  167. QComboBox *unit = new QComboBox;
  168. unit->addItem("Celsius", "C");
  169. unit->addItem("Fahrenheit", "F");
  170. unit->setCurrentIndex(1);
  171. layout->addRow(tr("Unit"), unit);
  172. QLineEdit *column = new QLineEdit;
  173. layout->addRow(tr("Column name"), column);
  174. QCheckBox *hidden = new QCheckBox(tr("Hide this channel"));
  175. layout->addRow(hidden);
  176. @<Get device configuration data for current node@>@;
  177. for(int i = 0; i < configData.size(); i++)
  178. {
  179. node = configData.at(i).toElement();
  180. if(node.attribute("name") == "station")
  181. {
  182. station->setValue(node.attribute("value").toInt());
  183. }
  184. else if(node.attribute("name") == "function")
  185. {
  186. function->setCurrentIndex(function->findText(node.attribute("value")));
  187. }
  188. else if(node.attribute("name") == "address")
  189. {
  190. address->setValue(node.attribute("value").toInt());
  191. }
  192. else if(node.attribute("name") == "format")
  193. {
  194. format->setCurrentIndex(format->findData(node.attribute("value")));
  195. }
  196. else if(node.attribute("name") == "decimals")
  197. {
  198. decimals->setValue(node.attribute("value").toInt());
  199. }
  200. else if(node.attribute("name") == "unit")
  201. {
  202. unit->setCurrentIndex(unit->findData(node.attribute("value")));
  203. }
  204. else if(node.attribute("name") == "column")
  205. {
  206. column->setText(node.attribute("value"));
  207. }
  208. else if(node.attribute("name") == "hidden")
  209. {
  210. hidden->setChecked(node.attribute("value") == "true" ? true : false);
  211. }
  212. }
  213. updateStation(station->value());
  214. updateFunction(function->currentIndex());
  215. updateAddress(address->value());
  216. updateFormat(format->currentIndex());
  217. updateDecimals(decimals->value());
  218. updateUnit(unit->currentIndex());
  219. updateColumnName(column->text());
  220. updateHidden(hidden->isChecked());
  221. connect(station, SIGNAL(valueChanged(int)), this, SLOT(updateStation(int)));
  222. connect(function, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFunction(int)));
  223. connect(address, SIGNAL(valueChanged(int)), this, SLOT(updateAddress(int)));
  224. connect(format, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFormat(int)));
  225. connect(decimals, SIGNAL(valueChanged(int)), this, SLOT(updateDecimals(int)));
  226. connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(updateUnit(int)));
  227. connect(column, SIGNAL(textEdited(QString)), this, SLOT(updateColumnName(QString)));
  228. connect(hidden, SIGNAL(toggled(bool)), this, SLOT(updateHidden(bool)));
  229. setLayout(layout);
  230. }
  231. void ModbusNGInputConfWidget::updateStation(int value)
  232. {
  233. updateAttribute("station", QString("%1").arg(value));
  234. }
  235. void ModbusNGInputConfWidget::updateFunction(int value)
  236. {
  237. updateAttribute("function", QString("%1").arg(value == 0 ? "3" : "4"));
  238. }
  239. void ModbusNGInputConfWidget::updateAddress(int value)
  240. {
  241. updateAttribute("address", QString("%1").arg(value));
  242. }
  243. void ModbusNGInputConfWidget::updateFormat(int value)
  244. {
  245. switch(value)
  246. {
  247. case 0:
  248. updateAttribute("format", "16fixedint");
  249. break;
  250. case 1:
  251. updateAttribute("format", "32floathl");
  252. break;
  253. case 2:
  254. updateAttribute("format", "32floatlh");
  255. break;
  256. }
  257. }
  258. void ModbusNGInputConfWidget::updateDecimals(int value)
  259. {
  260. updateAttribute("decimals", QString("%1").arg(value));
  261. }
  262. void ModbusNGInputConfWidget::updateUnit(int value)
  263. {
  264. switch(value)
  265. {
  266. case 0:
  267. updateAttribute("unit", "C");
  268. break;
  269. case 1:
  270. updateAttribute("unit", "F");
  271. break;
  272. }
  273. }
  274. void ModbusNGInputConfWidget::updateColumnName(const QString &value)
  275. {
  276. updateAttribute("column", value);
  277. }
  278. void ModbusNGInputConfWidget::updateHidden(bool value)
  279. {
  280. updateAttribute("hidden", value ? "true" : "false");
  281. }
  282. @ The configuration widgets need to be registered.
  283. @<Register device configuration widgets@>=
  284. app.registerDeviceConfigurationWidget("modbusngport", ModbusNGConfWidget::staticMetaObject);
  285. app.registerDeviceConfigurationWidget("modbusnginput",
  286. ModbusNGInputConfWidget::staticMetaObject);
  287. @ A |NodeInserter| is also needed.
  288. @<Register top level device configuration nodes@>=
  289. inserter = new NodeInserter(tr("ModbusNG Port"), tr("Modbus RTU Port"), "modbusngport", NULL);
  290. topLevelNodeInserters.append(inserter);
  291. @ Another class is used to handle the communication with the bus and serve as
  292. an integration point for \pn{}.