Browse Source

New configuration widgets for Modbus RTU hardware

Neal Wilson 7 years ago
parent
commit
19a82f3104
3 changed files with 340 additions and 1 deletions
  1. 2
    0
      src/daterangeselector.w
  2. 330
    0
      src/modbus.w
  3. 8
    1
      src/typica.w

+ 2
- 0
src/daterangeselector.w View File

@@ -82,8 +82,10 @@ class DateRangeSelector : public QWidget
82 82
 @<DateRangeSelector implementation@>
83 83
 
84 84
 #ifdef __unix__
85
+#ifndef __linux__
85 86
 #include "moc_daterangeselector.cpp"
86 87
 #endif
88
+#endif
87 89
 
88 90
 @ The custom range pop up is represented as a separate class which is not to be
89 91
 instantiated except by |DateRangeSelector|.

+ 330
- 0
src/modbus.w View File

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

+ 8
- 1
src/typica.w View File

@@ -609,6 +609,7 @@ generated file empty.
609 609
 @<SerialScaleConfWidget implementation@>@/
610 610
 @<ValueAnnotation implementation@>@/
611 611
 @<ValueAnnotationConfWidget implementation@>@/
612
+@<ModbusNG implementation@>@/
612 613
 
613 614
 @ A few headers are required for various parts of \pn{}. These allow the use of
614 615
 various Qt modules.
@@ -17098,7 +17099,11 @@ StopSelector::StopSelector(QWidget *parent) : QComboBox(parent)
17098 17099
 
17099 17100
 @** Configuration of Serial Devices Using Modbus RTU.
17100 17101
 
17101
-\noindent One protocol that is used across a broad class of devices is called
17102
+\noindent The following sections contain the details of older code related to
17103
+Modbus RTU support. While the various selector widgets are shared with the new
17104
+code, the configuration widgets and device interfaces are being replaced.
17105
+
17106
+One protocol that is used across a broad class of devices is called
17102 17107
 Modbus RTU. This protocol allows multiple devices to be chained together on a
17103 17108
 two wire bus which can be connected to a single serial port. The communication
17104 17109
 protocol involves a single message which is sent from a master device (in this
@@ -19330,6 +19335,8 @@ app.registerDeviceConfigurationWidget("modbusrtu", ModbusConfigurator::staticMet
19330 19335
 inserter = new NodeInserter(tr("Modbus RTU Device"), tr("Modbus RTU Device"), "modbusrtu", NULL);
19331 19336
 topLevelNodeInserters.append(inserter);
19332 19337
 
19338
+@i modbus.w
19339
+
19333 19340
 @i unsupportedserial.w
19334 19341
 
19335 19342
 @i phidgets.w

Loading…
Cancel
Save