Skip to content

Commit 28c370e

Browse files
committed
Merge remote-tracking branch 'origin/dev'
2 parents a5dda45 + d3b43be commit 28c370e

35 files changed

Lines changed: 863 additions & 281 deletions

README.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
# Open ModSim
22
Open ModSim is a free implimentation of modbus slave (server) utility for modbus-tcp and modbus-rtu protocols.
33

4-
![image](https://github.com/sanny32/OpenModSim/assets/13627951/3788824d-cf3f-4e98-9f5f-856e99106f6c)
4+
![image](https://github.com/user-attachments/assets/b57d329f-02c9-42c2-957c-295a67efcf67)
55

6-
7-
8-
![image](https://github.com/sanny32/OpenModSim/assets/13627951/f5dd90b6-2301-495b-ae86-409b2afd4eaf)
6+
![image](https://github.com/user-attachments/assets/730900fb-c8b4-4675-ba8b-cee5d7e9dd9e)
97

108

119
## Features
@@ -40,23 +38,24 @@ Registers
4038
Increment - simulate register from Low Limit to High Limit with a given Step
4139
Decrement - simulate register from High Limit to Low Limit with a given Step
4240

43-
Modbus Logging
41+
## Modbus Logging
4442

45-
![image](https://github.com/sanny32/OpenModSim/assets/13627951/72cb3860-b672-41fd-8ec6-3399170a28df)
4643

44+
![image](https://github.com/user-attachments/assets/d8dc67fc-efce-4d40-81df-5ed54a958952)
4745

4846

4947
## Extended Featues
5048

51-
Modbus Message Parser
49+
- Modbus Message Parser
5250

5351
![image](https://github.com/sanny32/OpenModSim/assets/13627951/7e9744b8-f4b3-439a-a312-79cbdc426dc2)
5452

5553

5654
## Scripting
5755
From version 1.2.0 Open ModSim supports scripting. Qt runtime implements the [ECMAScript Language Specification](http://www.ecma-international.org/publications/standards/Ecma-262.htm) standard, so Javascript is used to write code.
5856

59-
![image](https://github.com/sanny32/OpenModSim/assets/13627951/ab115064-877f-4f6f-a1b9-4ac6c2feb042)
57+
![image](https://github.com/user-attachments/assets/e8d31089-345a-4c26-bb0c-fe2ea92f85d4)
58+
6059

6160
Scripts can be launched in two modes: Once or Periodically. If you run script in Once mode the script will stop after it finishes executing. In Periodically mode, the script will start after a certain period of time until the user stops it or the method is called
6261
```javascript
@@ -79,6 +78,9 @@ function clear()
7978
/* init function */
8079
function init()
8180
{
81+
/* Set the server address base starts from one (1-based) */
82+
Server.addressBase = AddressBase.Base1;
83+
8284
clear();
8385

8486
/* Runs when Hodling register value at address 1 was changed */
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include <QEvent>
2+
#include "addressbasecombobox.h"
3+
4+
///
5+
/// \brief AddressBaseComboBox::AddressBaseComboBox
6+
/// \param parent
7+
///
8+
AddressBaseComboBox::AddressBaseComboBox(QWidget* parent)
9+
: QComboBox(parent)
10+
{
11+
addItem(tr("0-based"), QVariant::fromValue(AddressBase::Base0));
12+
addItem(tr("1-based"), QVariant::fromValue(AddressBase::Base1));
13+
14+
connect(this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &AddressBaseComboBox::on_currentIndexChanged);
15+
}
16+
17+
///
18+
/// \brief AddressBaseComboBox::changeEvent
19+
/// \param event
20+
///
21+
void AddressBaseComboBox::changeEvent(QEvent* event)
22+
{
23+
if (event->type() == QEvent::LanguageChange)
24+
{
25+
for(int i = 0; i < count(); i++)
26+
{
27+
switch(itemData(i).value<AddressBase>())
28+
{
29+
case AddressBase::Base0:
30+
setItemText(i, tr("0-based"));
31+
break;
32+
33+
case AddressBase::Base1:
34+
setItemText(i, tr("1-based"));
35+
break;
36+
}
37+
}
38+
}
39+
40+
QComboBox::changeEvent(event);
41+
}
42+
43+
///
44+
/// \brief AddressBaseComboBox::currentAddressBase
45+
/// \return
46+
///
47+
AddressBase AddressBaseComboBox::currentAddressBase() const
48+
{
49+
return currentData().value<AddressBase>();
50+
}
51+
52+
///
53+
/// \brief AddressBaseComboBox::setCurrentAddressBase
54+
/// \param pointType
55+
///
56+
void AddressBaseComboBox::setCurrentAddressBase(AddressBase base)
57+
{
58+
const auto idx = findData(QVariant::fromValue(base));
59+
if(idx == currentIndex())
60+
{
61+
emit currentIndexChanged(idx);
62+
}
63+
else if(idx != -1)
64+
{
65+
setCurrentIndex(idx);
66+
}
67+
}
68+
69+
///
70+
/// \brief AddressBaseComboBox::on_currentIndexChanged
71+
/// \param index
72+
///
73+
void AddressBaseComboBox::on_currentIndexChanged(int index)
74+
{
75+
emit addressBaseChanged(itemData(index).value<AddressBase>());
76+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#ifndef ADDRESSBASECOMBOBOX_H
2+
#define ADDRESSBASECOMBOBOX_H
3+
4+
#include <QComboBox>
5+
#include "enums.h"
6+
7+
///
8+
/// \brief The AddressBaseComboBox class
9+
///
10+
class AddressBaseComboBox : public QComboBox
11+
{
12+
Q_OBJECT
13+
public:
14+
explicit AddressBaseComboBox(QWidget *parent = nullptr);
15+
16+
AddressBase currentAddressBase() const;
17+
void setCurrentAddressBase(AddressBase base);
18+
19+
signals:
20+
void addressBaseChanged(AddressBase base);
21+
22+
protected:
23+
void changeEvent(QEvent* event) override;
24+
25+
private slots:
26+
void on_currentIndexChanged(int);
27+
};
28+
29+
#endif // ADDRESSBASECOMBOBOX_H

omodsim/controls/modbusmessagewidget.cpp

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ void ModbusMessageWidget::update()
193193
QString("%1 (%2)").arg(formatUInt8Value(_dataDisplayMode, func), func) :
194194
formatUInt8Value(_dataDisplayMode, func);
195195
addItem(tr("<b>Function Code:</b> %1").arg(function));
196+
const auto addrBase = tr("(0-based)");
196197

197198
switch(_mm->function())
198199
{
@@ -202,7 +203,7 @@ void ModbusMessageWidget::update()
202203
auto req = reinterpret_cast<const ReadCoilsRequest*>(_mm);
203204
const auto startAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->startAddress()) : "??";
204205
const auto length = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->length()): "??";
205-
addItem(tr("<b>Start Address:</b> %1").arg(startAddress));
206+
addItem(tr("<b>Start Address:</b> %1 %2").arg(startAddress, addrBase));
206207
addItem(tr("<b>Length:</b> %1").arg(length));
207208
}
208209
else
@@ -221,7 +222,7 @@ void ModbusMessageWidget::update()
221222
auto req = reinterpret_cast<const ReadDiscreteInputsRequest*>(_mm);
222223
const auto startAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->startAddress()) : "??";
223224
const auto length = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->length()): "??";
224-
addItem(tr("<b>Start Address:</b> %1").arg(startAddress));
225+
addItem(tr("<b>Start Address:</b> %1 %2").arg(startAddress, addrBase));
225226
addItem(tr("<b>Length:</b> %1").arg(length));
226227
}
227228
else
@@ -240,7 +241,7 @@ void ModbusMessageWidget::update()
240241
auto req = reinterpret_cast<const ReadHoldingRegistersRequest*>(_mm);
241242
const auto startAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->startAddress()) : "??";
242243
const auto length = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->length()): "??";
243-
addItem(tr("<b>Start Address:</b> %1").arg(startAddress));
244+
addItem(tr("<b>Start Address:</b> %1 %2").arg(startAddress, addrBase));
244245
addItem(tr("<b>Length:</b> %1").arg(length));
245246
}
246247
else
@@ -259,7 +260,7 @@ void ModbusMessageWidget::update()
259260
auto req = reinterpret_cast<const ReadInputRegistersRequest*>(_mm);
260261
const auto startAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->startAddress()) : "??";
261262
const auto length = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->length()): "??";
262-
addItem(tr("<b>Start Address:</b> %1").arg(startAddress));
263+
addItem(tr("<b>Start Address:</b> %1 %2").arg(startAddress, addrBase));
263264
addItem(tr("<b>Length:</b> %1").arg(length));
264265
}
265266
else
@@ -278,15 +279,15 @@ void ModbusMessageWidget::update()
278279
auto req = reinterpret_cast<const WriteSingleCoilRequest*>(_mm);
279280
const auto outputAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->address()) : "??";
280281
const auto outputValue = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->value()) : "??";
281-
addItem(tr("<b>Output Address:</b> %1").arg(outputAddress));
282+
addItem(tr("<b>Output Address:</b> %1 %2").arg(outputAddress, addrBase));
282283
addItem(tr("<b>Output Value:</b> %1").arg(outputValue));
283284
}
284285
else
285286
{
286287
auto resp = reinterpret_cast<const WriteSingleCoilResponse*>(_mm);
287288
const auto outputAddress = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->address()) : "??";
288289
const auto outputValue = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->value()) : "??";
289-
addItem(tr("<b>Output Address:</b> %1").arg(outputAddress));
290+
addItem(tr("<b>Output Address:</b> %1 %2").arg(outputAddress, addrBase));
290291
addItem(tr("<b>Output Value:</b> %1").arg(outputValue));
291292
}
292293
break;
@@ -297,15 +298,15 @@ void ModbusMessageWidget::update()
297298
auto req = reinterpret_cast<const WriteSingleRegisterRequest*>(_mm);
298299
const auto registerAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->address()) : "??";
299300
const auto registerValue = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->value()) : "??";
300-
addItem(tr("<b>Register Address:</b> %1").arg(registerAddress));
301+
addItem(tr("<b>Register Address:</b> %1 %2").arg(registerAddress, addrBase));
301302
addItem(tr("<b>Register Value:</b> %1").arg(registerValue));
302303
}
303304
else
304305
{
305306
auto resp = reinterpret_cast<const WriteSingleRegisterResponse*>(_mm);
306307
const auto registerAddress = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->address()) : "??";
307308
const auto registerValue = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->value()) : "??";
308-
addItem(tr("<b>Register Address:</b> %1").arg(registerAddress));
309+
addItem(tr("<b>Register Address:</b> %1 %2").arg(registerAddress, addrBase));
309310
addItem(tr("<b>Register Value:</b> %1").arg(registerValue));
310311
}
311312
break;
@@ -374,7 +375,7 @@ void ModbusMessageWidget::update()
374375
const auto quantity = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->quantity()) : "??";
375376
const auto byteCount = req->isValid() ? formatUInt8Value(_dataDisplayMode, req->byteCount()) : "?";
376377
const auto values = req->isValid() ? formatUInt8Array(_dataDisplayMode, req->values()) : "???";
377-
addItem(tr("<b>Starting Address:</b> %1").arg(startAddr));
378+
addItem(tr("<b>Starting Address:</b> %1 %2").arg(startAddr, addrBase));
378379
addItem(tr("<b>Quantity of Outputs:</b> %1").arg(quantity));
379380
addItem(tr("<b>Byte Count:</b> %1").arg(byteCount));
380381
addItem(tr("<b>Output Value:</b> %1").arg(values));
@@ -384,7 +385,7 @@ void ModbusMessageWidget::update()
384385
auto resp = reinterpret_cast<const WriteMultipleCoilsResponse*>(_mm);
385386
const auto startAddr = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->startAddress()) : "??";
386387
const auto quantity = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->quantity()) : "??";
387-
addItem(tr("<b>Starting Address:</b> %1").arg(startAddr));
388+
addItem(tr("<b>Starting Address:</b> %1 %2").arg(startAddr, addrBase));
388389
addItem(tr("<b>Quantity of Outputs:</b> %1").arg(quantity));
389390
}
390391
break;
@@ -397,7 +398,7 @@ void ModbusMessageWidget::update()
397398
const auto quantity = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->quantity()) : "??";
398399
const auto byteCount = req->isValid() ? formatUInt8Value(_dataDisplayMode, req->byteCount()) : "?";
399400
const auto values = req->isValid() ? formatUInt16Array(_dataDisplayMode, req->values(), _byteOrder) : "???";
400-
addItem(tr("<b>Starting Address:</b> %1").arg(startAddr));
401+
addItem(tr("<b>Starting Address:</b> %1 %2").arg(startAddr, addrBase));
401402
addItem(tr("<b>Quantity of Registers:</b> %1").arg(quantity));
402403
addItem(tr("<b>Byte Count:</b> %1").arg(byteCount));
403404
addItem(tr("<b>Registers Value:</b> %1").arg(values));
@@ -407,7 +408,7 @@ void ModbusMessageWidget::update()
407408
auto resp = reinterpret_cast<const WriteMultipleRegistersResponse*>(_mm);
408409
const auto startAddr = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->startAddress()) : "??";
409410
const auto quantity = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->quantity()) : "??";
410-
addItem(tr("<b>Starting Address:</b> %1").arg(startAddr));
411+
addItem(tr("<b>Starting Address:</b> %1 %2").arg(startAddr, addrBase));
411412
addItem(tr("<b>Quantity of Registers:</b> %1").arg(quantity));
412413
}
413414
break;
@@ -468,7 +469,7 @@ void ModbusMessageWidget::update()
468469
const auto address = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->address()) : "??";
469470
const auto andMask = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->andMask()) : "??";
470471
const auto orMask = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->orMask()) : "??";
471-
addItem(tr("<b>Address:</b> %1").arg(address));
472+
addItem(tr("<b>Address:</b> %1 %2").arg(address, addrBase));
472473
addItem(tr("<b>And Mask:</b> %1").arg(andMask));
473474
addItem(tr("<b>Or Mask:</b> %1").arg(orMask));
474475
}
@@ -478,7 +479,7 @@ void ModbusMessageWidget::update()
478479
const auto address = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->address()) : "??";
479480
const auto andMask = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->andMask()) : "??";
480481
const auto orMask = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->orMask()) : "??";
481-
addItem(tr("<b>Address:</b> %1").arg(address));
482+
addItem(tr("<b>Address:</b> %1 %2").arg(address, addrBase));
482483
addItem(tr("<b>And Mask:</b> %1").arg(andMask));
483484
addItem(tr("<b>Or Mask:</b> %1").arg(orMask));
484485
}
@@ -494,9 +495,9 @@ void ModbusMessageWidget::update()
494495
const auto writeLength = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->writeLength()) : "??";
495496
const auto writeByteCount = req->isValid() ? formatUInt8Value(_dataDisplayMode, req->writeByteCount()) : "?";
496497
const auto writeValues = req->isValid() ? formatUInt16Array(_dataDisplayMode, req->writeValues(), _byteOrder) : "???";
497-
addItem(tr("<b>Read Starting Address:</b> %1").arg(readStartAddr));
498+
addItem(tr("<b>Read Starting Address:</b> %1 %2").arg(readStartAddr, addrBase));
498499
addItem(tr("<b>Quantity to Read:</b> %1").arg(readLength));
499-
addItem(tr("<b>Write Starting Address:</b> %1").arg(writeStartAddr));
500+
addItem(tr("<b>Write Starting Address:</b> %1 %2").arg(writeStartAddr, addrBase));
500501
addItem(tr("<b>Quantity to Write:</b> %1").arg(writeLength));
501502
addItem(tr("<b>Write Byte Count:</b> %1").arg(writeByteCount));
502503
addItem(tr("<b>Write Registers Value:</b> %1").arg(writeValues));
@@ -516,7 +517,7 @@ void ModbusMessageWidget::update()
516517
{
517518
auto req = reinterpret_cast<const ReadFifoQueueRequest*>(_mm);
518519
const auto fifoAddr = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->fifoAddress()) : "??";
519-
addItem(tr("<b>FIFO Point Address:</b> %1").arg(fifoAddr));
520+
addItem(tr("<b>FIFO Point Address:</b> %1 %2").arg(fifoAddr, addrBase));
520521
}
521522
else
522523
{

omodsim/controls/outputwidget.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,8 @@ QModelIndex OutputListModel::find(QModbusDataUnit::RegisterType type, quint16 ad
294294
if(_parentWidget->_displayDefinition.PointType != type)
295295
return QModelIndex();
296296

297-
const int row = addr - _parentWidget->_displayDefinition.PointAddress;
297+
const auto dd = _parentWidget->_displayDefinition;
298+
const int row = addr - (dd.PointAddress - (dd.ZeroBasedAddress ? 0 : 1));
298299
if(row >= 0 && row < rowCount())
299300
return index(row);
300301

@@ -644,8 +645,8 @@ AddressDescriptionMap OutputWidget::descriptionMap() const
644645
for(int i = 0; i < _listModel->rowCount(); i++)
645646
{
646647
const auto desc = _listModel->data(_listModel->index(i), DescriptionRole).toString();
647-
const quint16 addr = _listModel->data(_listModel->index(i), AddressRole).toUInt();
648-
descriptionMap[{_displayDefinition.PointType, addr}] = desc;
648+
const quint16 addr = _listModel->data(_listModel->index(i), AddressRole).toUInt() - (_displayDefinition.ZeroBasedAddress ? 0 : 1);
649+
descriptionMap[{_displayDefinition.PointType, addr }] = desc;
649650
}
650651
return descriptionMap;
651652
}

omodsim/controls/scriptcontrol.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ void ScriptControl::setByteOrder(const ByteOrder* order)
5454
_byteOrder = const_cast<ByteOrder*>(order);
5555
}
5656

57+
///
58+
/// \brief ScriptControl::setAddressBase
59+
/// \param base
60+
///
61+
void ScriptControl::setAddressBase(AddressBase base)
62+
{
63+
_addressBase = base;
64+
}
65+
5766
///
5867
/// \brief ScriptControl::isAutoCompleteEnabled
5968
/// \return
@@ -210,7 +219,7 @@ void ScriptControl::runScript(RunMode mode, int interval)
210219
_scriptCode = script();
211220

212221
_storage = QSharedPointer<Storage>(new Storage);
213-
_server = QSharedPointer<Server>(new Server(_mbMultiServer, _byteOrder));
222+
_server = QSharedPointer<Server>(new Server(_mbMultiServer, _byteOrder, _addressBase));
214223
_script = QSharedPointer<Script>(new Script(interval));
215224
_console = QSharedPointer<console>(new console(ui->console));
216225
connect(_script.get(), &Script::stopped, this, &ScriptControl::stopScript, Qt::QueuedConnection);
@@ -220,6 +229,7 @@ void ScriptControl::runScript(RunMode mode, int interval)
220229
_jsEngine.globalObject().setProperty("Server", _jsEngine.newQObject(_server.get()));
221230
_jsEngine.globalObject().setProperty("console", _jsEngine.newQObject(_console.get()));
222231
_jsEngine.globalObject().setProperty("Register", _jsEngine.newQMetaObject(&Register::staticMetaObject));
232+
_jsEngine.globalObject().setProperty("AddressBase", _jsEngine.newQMetaObject(&Address::staticMetaObject));
223233
_jsEngine.setInterrupted(false);
224234

225235
_console->clear();
@@ -255,6 +265,7 @@ void ScriptControl::stopScript()
255265
_jsEngine.globalObject().deleteProperty("Server");
256266
_jsEngine.globalObject().deleteProperty("console");
257267
_jsEngine.globalObject().deleteProperty("Register");
268+
_jsEngine.globalObject().deleteProperty("AddressBase");
258269

259270
_storage = nullptr;
260271
_server = nullptr;

omodsim/controls/scriptcontrol.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class ScriptControl : public QWidget
3131

3232
void setModbusMultiServer(ModbusMultiServer* server);
3333
void setByteOrder(const ByteOrder* order);
34+
void setAddressBase(AddressBase base);
3435

3536
bool isAutoCompleteEnabled() const;
3637
void enableAutoComplete(bool enable);
@@ -77,6 +78,7 @@ private slots:
7778
QSharedPointer<console> _console;
7879

7980
ByteOrder* _byteOrder = nullptr;
81+
AddressBase _addressBase = AddressBase::Base1;
8082
ModbusMultiServer* _mbMultiServer = nullptr;
8183
};
8284

0 commit comments

Comments
 (0)