Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
sanny32 committed Oct 24, 2024
2 parents a5dda45 + d3b43be commit 28c370e
Show file tree
Hide file tree
Showing 35 changed files with 863 additions and 281 deletions.
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
# Open ModSim
Open ModSim is a free implimentation of modbus slave (server) utility for modbus-tcp and modbus-rtu protocols.

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



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


## Features
Expand Down Expand Up @@ -40,23 +38,24 @@ Registers
Increment - simulate register from Low Limit to High Limit with a given Step
Decrement - simulate register from High Limit to Low Limit with a given Step

Modbus Logging
## Modbus Logging

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

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


## Extended Featues

Modbus Message Parser
- Modbus Message Parser

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


## Scripting
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.

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


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
```javascript
Expand All @@ -79,6 +78,9 @@ function clear()
/* init function */
function init()
{
/* Set the server address base starts from one (1-based) */
Server.addressBase = AddressBase.Base1;

clear();

/* Runs when Hodling register value at address 1 was changed */
Expand Down
76 changes: 76 additions & 0 deletions omodsim/controls/addressbasecombobox.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include <QEvent>
#include "addressbasecombobox.h"

///
/// \brief AddressBaseComboBox::AddressBaseComboBox
/// \param parent
///
AddressBaseComboBox::AddressBaseComboBox(QWidget* parent)
: QComboBox(parent)
{
addItem(tr("0-based"), QVariant::fromValue(AddressBase::Base0));
addItem(tr("1-based"), QVariant::fromValue(AddressBase::Base1));

connect(this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &AddressBaseComboBox::on_currentIndexChanged);
}

///
/// \brief AddressBaseComboBox::changeEvent
/// \param event
///
void AddressBaseComboBox::changeEvent(QEvent* event)
{
if (event->type() == QEvent::LanguageChange)
{
for(int i = 0; i < count(); i++)
{
switch(itemData(i).value<AddressBase>())
{
case AddressBase::Base0:
setItemText(i, tr("0-based"));
break;

case AddressBase::Base1:
setItemText(i, tr("1-based"));
break;
}
}
}

QComboBox::changeEvent(event);
}

///
/// \brief AddressBaseComboBox::currentAddressBase
/// \return
///
AddressBase AddressBaseComboBox::currentAddressBase() const
{
return currentData().value<AddressBase>();
}

///
/// \brief AddressBaseComboBox::setCurrentAddressBase
/// \param pointType
///
void AddressBaseComboBox::setCurrentAddressBase(AddressBase base)
{
const auto idx = findData(QVariant::fromValue(base));
if(idx == currentIndex())
{
emit currentIndexChanged(idx);
}
else if(idx != -1)
{
setCurrentIndex(idx);
}
}

///
/// \brief AddressBaseComboBox::on_currentIndexChanged
/// \param index
///
void AddressBaseComboBox::on_currentIndexChanged(int index)
{
emit addressBaseChanged(itemData(index).value<AddressBase>());
}
29 changes: 29 additions & 0 deletions omodsim/controls/addressbasecombobox.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef ADDRESSBASECOMBOBOX_H
#define ADDRESSBASECOMBOBOX_H

#include <QComboBox>
#include "enums.h"

///
/// \brief The AddressBaseComboBox class
///
class AddressBaseComboBox : public QComboBox
{
Q_OBJECT
public:
explicit AddressBaseComboBox(QWidget *parent = nullptr);

AddressBase currentAddressBase() const;
void setCurrentAddressBase(AddressBase base);

signals:
void addressBaseChanged(AddressBase base);

protected:
void changeEvent(QEvent* event) override;

private slots:
void on_currentIndexChanged(int);
};

#endif // ADDRESSBASECOMBOBOX_H
35 changes: 18 additions & 17 deletions omodsim/controls/modbusmessagewidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ void ModbusMessageWidget::update()
QString("%1 (%2)").arg(formatUInt8Value(_dataDisplayMode, func), func) :
formatUInt8Value(_dataDisplayMode, func);
addItem(tr("<b>Function Code:</b> %1").arg(function));
const auto addrBase = tr("(0-based)");

switch(_mm->function())
{
Expand All @@ -202,7 +203,7 @@ void ModbusMessageWidget::update()
auto req = reinterpret_cast<const ReadCoilsRequest*>(_mm);
const auto startAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->startAddress()) : "??";
const auto length = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->length()): "??";
addItem(tr("<b>Start Address:</b> %1").arg(startAddress));
addItem(tr("<b>Start Address:</b> %1 %2").arg(startAddress, addrBase));
addItem(tr("<b>Length:</b> %1").arg(length));
}
else
Expand All @@ -221,7 +222,7 @@ void ModbusMessageWidget::update()
auto req = reinterpret_cast<const ReadDiscreteInputsRequest*>(_mm);
const auto startAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->startAddress()) : "??";
const auto length = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->length()): "??";
addItem(tr("<b>Start Address:</b> %1").arg(startAddress));
addItem(tr("<b>Start Address:</b> %1 %2").arg(startAddress, addrBase));
addItem(tr("<b>Length:</b> %1").arg(length));
}
else
Expand All @@ -240,7 +241,7 @@ void ModbusMessageWidget::update()
auto req = reinterpret_cast<const ReadHoldingRegistersRequest*>(_mm);
const auto startAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->startAddress()) : "??";
const auto length = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->length()): "??";
addItem(tr("<b>Start Address:</b> %1").arg(startAddress));
addItem(tr("<b>Start Address:</b> %1 %2").arg(startAddress, addrBase));
addItem(tr("<b>Length:</b> %1").arg(length));
}
else
Expand All @@ -259,7 +260,7 @@ void ModbusMessageWidget::update()
auto req = reinterpret_cast<const ReadInputRegistersRequest*>(_mm);
const auto startAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->startAddress()) : "??";
const auto length = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->length()): "??";
addItem(tr("<b>Start Address:</b> %1").arg(startAddress));
addItem(tr("<b>Start Address:</b> %1 %2").arg(startAddress, addrBase));
addItem(tr("<b>Length:</b> %1").arg(length));
}
else
Expand All @@ -278,15 +279,15 @@ void ModbusMessageWidget::update()
auto req = reinterpret_cast<const WriteSingleCoilRequest*>(_mm);
const auto outputAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->address()) : "??";
const auto outputValue = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->value()) : "??";
addItem(tr("<b>Output Address:</b> %1").arg(outputAddress));
addItem(tr("<b>Output Address:</b> %1 %2").arg(outputAddress, addrBase));
addItem(tr("<b>Output Value:</b> %1").arg(outputValue));
}
else
{
auto resp = reinterpret_cast<const WriteSingleCoilResponse*>(_mm);
const auto outputAddress = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->address()) : "??";
const auto outputValue = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->value()) : "??";
addItem(tr("<b>Output Address:</b> %1").arg(outputAddress));
addItem(tr("<b>Output Address:</b> %1 %2").arg(outputAddress, addrBase));
addItem(tr("<b>Output Value:</b> %1").arg(outputValue));
}
break;
Expand All @@ -297,15 +298,15 @@ void ModbusMessageWidget::update()
auto req = reinterpret_cast<const WriteSingleRegisterRequest*>(_mm);
const auto registerAddress = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->address()) : "??";
const auto registerValue = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->value()) : "??";
addItem(tr("<b>Register Address:</b> %1").arg(registerAddress));
addItem(tr("<b>Register Address:</b> %1 %2").arg(registerAddress, addrBase));
addItem(tr("<b>Register Value:</b> %1").arg(registerValue));
}
else
{
auto resp = reinterpret_cast<const WriteSingleRegisterResponse*>(_mm);
const auto registerAddress = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->address()) : "??";
const auto registerValue = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->value()) : "??";
addItem(tr("<b>Register Address:</b> %1").arg(registerAddress));
addItem(tr("<b>Register Address:</b> %1 %2").arg(registerAddress, addrBase));
addItem(tr("<b>Register Value:</b> %1").arg(registerValue));
}
break;
Expand Down Expand Up @@ -374,7 +375,7 @@ void ModbusMessageWidget::update()
const auto quantity = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->quantity()) : "??";
const auto byteCount = req->isValid() ? formatUInt8Value(_dataDisplayMode, req->byteCount()) : "?";
const auto values = req->isValid() ? formatUInt8Array(_dataDisplayMode, req->values()) : "???";
addItem(tr("<b>Starting Address:</b> %1").arg(startAddr));
addItem(tr("<b>Starting Address:</b> %1 %2").arg(startAddr, addrBase));
addItem(tr("<b>Quantity of Outputs:</b> %1").arg(quantity));
addItem(tr("<b>Byte Count:</b> %1").arg(byteCount));
addItem(tr("<b>Output Value:</b> %1").arg(values));
Expand All @@ -384,7 +385,7 @@ void ModbusMessageWidget::update()
auto resp = reinterpret_cast<const WriteMultipleCoilsResponse*>(_mm);
const auto startAddr = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->startAddress()) : "??";
const auto quantity = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->quantity()) : "??";
addItem(tr("<b>Starting Address:</b> %1").arg(startAddr));
addItem(tr("<b>Starting Address:</b> %1 %2").arg(startAddr, addrBase));
addItem(tr("<b>Quantity of Outputs:</b> %1").arg(quantity));
}
break;
Expand All @@ -397,7 +398,7 @@ void ModbusMessageWidget::update()
const auto quantity = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->quantity()) : "??";
const auto byteCount = req->isValid() ? formatUInt8Value(_dataDisplayMode, req->byteCount()) : "?";
const auto values = req->isValid() ? formatUInt16Array(_dataDisplayMode, req->values(), _byteOrder) : "???";
addItem(tr("<b>Starting Address:</b> %1").arg(startAddr));
addItem(tr("<b>Starting Address:</b> %1 %2").arg(startAddr, addrBase));
addItem(tr("<b>Quantity of Registers:</b> %1").arg(quantity));
addItem(tr("<b>Byte Count:</b> %1").arg(byteCount));
addItem(tr("<b>Registers Value:</b> %1").arg(values));
Expand All @@ -407,7 +408,7 @@ void ModbusMessageWidget::update()
auto resp = reinterpret_cast<const WriteMultipleRegistersResponse*>(_mm);
const auto startAddr = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->startAddress()) : "??";
const auto quantity = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->quantity()) : "??";
addItem(tr("<b>Starting Address:</b> %1").arg(startAddr));
addItem(tr("<b>Starting Address:</b> %1 %2").arg(startAddr, addrBase));
addItem(tr("<b>Quantity of Registers:</b> %1").arg(quantity));
}
break;
Expand Down Expand Up @@ -468,7 +469,7 @@ void ModbusMessageWidget::update()
const auto address = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->address()) : "??";
const auto andMask = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->andMask()) : "??";
const auto orMask = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->orMask()) : "??";
addItem(tr("<b>Address:</b> %1").arg(address));
addItem(tr("<b>Address:</b> %1 %2").arg(address, addrBase));
addItem(tr("<b>And Mask:</b> %1").arg(andMask));
addItem(tr("<b>Or Mask:</b> %1").arg(orMask));
}
Expand All @@ -478,7 +479,7 @@ void ModbusMessageWidget::update()
const auto address = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->address()) : "??";
const auto andMask = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->andMask()) : "??";
const auto orMask = resp->isValid() ? formatUInt16Value(_dataDisplayMode, resp->orMask()) : "??";
addItem(tr("<b>Address:</b> %1").arg(address));
addItem(tr("<b>Address:</b> %1 %2").arg(address, addrBase));
addItem(tr("<b>And Mask:</b> %1").arg(andMask));
addItem(tr("<b>Or Mask:</b> %1").arg(orMask));
}
Expand All @@ -494,9 +495,9 @@ void ModbusMessageWidget::update()
const auto writeLength = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->writeLength()) : "??";
const auto writeByteCount = req->isValid() ? formatUInt8Value(_dataDisplayMode, req->writeByteCount()) : "?";
const auto writeValues = req->isValid() ? formatUInt16Array(_dataDisplayMode, req->writeValues(), _byteOrder) : "???";
addItem(tr("<b>Read Starting Address:</b> %1").arg(readStartAddr));
addItem(tr("<b>Read Starting Address:</b> %1 %2").arg(readStartAddr, addrBase));
addItem(tr("<b>Quantity to Read:</b> %1").arg(readLength));
addItem(tr("<b>Write Starting Address:</b> %1").arg(writeStartAddr));
addItem(tr("<b>Write Starting Address:</b> %1 %2").arg(writeStartAddr, addrBase));
addItem(tr("<b>Quantity to Write:</b> %1").arg(writeLength));
addItem(tr("<b>Write Byte Count:</b> %1").arg(writeByteCount));
addItem(tr("<b>Write Registers Value:</b> %1").arg(writeValues));
Expand All @@ -516,7 +517,7 @@ void ModbusMessageWidget::update()
{
auto req = reinterpret_cast<const ReadFifoQueueRequest*>(_mm);
const auto fifoAddr = req->isValid() ? formatUInt16Value(_dataDisplayMode, req->fifoAddress()) : "??";
addItem(tr("<b>FIFO Point Address:</b> %1").arg(fifoAddr));
addItem(tr("<b>FIFO Point Address:</b> %1 %2").arg(fifoAddr, addrBase));
}
else
{
Expand Down
7 changes: 4 additions & 3 deletions omodsim/controls/outputwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,8 @@ QModelIndex OutputListModel::find(QModbusDataUnit::RegisterType type, quint16 ad
if(_parentWidget->_displayDefinition.PointType != type)
return QModelIndex();

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

Expand Down Expand Up @@ -644,8 +645,8 @@ AddressDescriptionMap OutputWidget::descriptionMap() const
for(int i = 0; i < _listModel->rowCount(); i++)
{
const auto desc = _listModel->data(_listModel->index(i), DescriptionRole).toString();
const quint16 addr = _listModel->data(_listModel->index(i), AddressRole).toUInt();
descriptionMap[{_displayDefinition.PointType, addr}] = desc;
const quint16 addr = _listModel->data(_listModel->index(i), AddressRole).toUInt() - (_displayDefinition.ZeroBasedAddress ? 0 : 1);
descriptionMap[{_displayDefinition.PointType, addr }] = desc;
}
return descriptionMap;
}
Expand Down
13 changes: 12 additions & 1 deletion omodsim/controls/scriptcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ void ScriptControl::setByteOrder(const ByteOrder* order)
_byteOrder = const_cast<ByteOrder*>(order);
}

///
/// \brief ScriptControl::setAddressBase
/// \param base
///
void ScriptControl::setAddressBase(AddressBase base)
{
_addressBase = base;
}

///
/// \brief ScriptControl::isAutoCompleteEnabled
/// \return
Expand Down Expand Up @@ -210,7 +219,7 @@ void ScriptControl::runScript(RunMode mode, int interval)
_scriptCode = script();

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

_console->clear();
Expand Down Expand Up @@ -255,6 +265,7 @@ void ScriptControl::stopScript()
_jsEngine.globalObject().deleteProperty("Server");
_jsEngine.globalObject().deleteProperty("console");
_jsEngine.globalObject().deleteProperty("Register");
_jsEngine.globalObject().deleteProperty("AddressBase");

_storage = nullptr;
_server = nullptr;
Expand Down
2 changes: 2 additions & 0 deletions omodsim/controls/scriptcontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class ScriptControl : public QWidget

void setModbusMultiServer(ModbusMultiServer* server);
void setByteOrder(const ByteOrder* order);
void setAddressBase(AddressBase base);

bool isAutoCompleteEnabled() const;
void enableAutoComplete(bool enable);
Expand Down Expand Up @@ -77,6 +78,7 @@ private slots:
QSharedPointer<console> _console;

ByteOrder* _byteOrder = nullptr;
AddressBase _addressBase = AddressBase::Base1;
ModbusMultiServer* _mbMultiServer = nullptr;
};

Expand Down
Loading

0 comments on commit 28c370e

Please sign in to comment.