Skip to content

Commit d8eadde

Browse files
committed
Merge remote-tracking branch 'origin/dev'
2 parents 9c2272a + 157e2d6 commit d8eadde

24 files changed

+534
-81
lines changed

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ OpenModScan is a free and open-source Modbus Master (Client) utility supporting
44
![image](https://github.com/user-attachments/assets/aaa2b5c8-9f47-4e17-803d-2af7d24fb0c7)
55

66

7-
![image](https://github.com/user-attachments/assets/2bfe852f-1cc2-433d-8917-70815228a1c3)
8-
7+
![image](https://github.com/user-attachments/assets/19451264-2b0f-4821-897e-0cbee5831b14)
98

109
## Features
1110

@@ -50,9 +49,20 @@ Registers
5049

5150
## Building
5251
Building is available via cmake (with installed Qt version 5.15 and above) or Qt Creator. Supports both OS Microsoft Windows and Linux.
52+
53+
## About supported operating systems
54+
55+
The following operating systems are supported for OpenModScan.
56+
- Microsoft Windows 7 x86 and amd64 or later.
57+
- Ubuntu Linux 22.04 amd64 or later.
58+
- Fedora Linux 41 amd64 or later.
59+
- Alt Linux 11 amd64 or later.
60+
- Astra Linux 1.7 amd64 or later.
61+
- RedOS 8 amd64 or later.
62+
5363

5464
## MIT License
55-
Copyright 2024 Alexandr Ananev [mail@ananev.org]
65+
Copyright 2025 Alexandr Ananev [mail@ananev.org]
5666

5767
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5868

omodscan/CMakeLists.txt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.25)
22

33
project(omodscan
4-
VERSION 1.9.1
4+
VERSION 1.9.2
55
DESCRIPTION "An Open Source Modbus Master (Client) Utility"
66
LANGUAGES CXX)
77

@@ -10,6 +10,7 @@ set(CMAKE_AUTOMOC ON)
1010
set(CMAKE_AUTORCC ON)
1111
set(CMAKE_CXX_STANDARD 17)
1212
set(CMAKE_CXX_STANDARD_REQUIRED ON)
13+
set(PRODUCT_NAME "Open ModScan")
1314

1415
# Find Qt6 or fallback to Qt5
1516
find_package(Qt6 COMPONENTS Core Gui Widgets Network PrintSupport SerialBus SerialPort Core5Compat LinguistTools QUIET)
@@ -18,10 +19,12 @@ if (NOT Qt6_FOUND)
1819
endif()
1920

2021
# Define target
21-
add_executable(omodscan)
22+
add_executable(omodscan
23+
controls/historycombobox.h controls/historycombobox.cpp
24+
controls/hostscombobox.h controls/hostscombobox.cpp)
2225

2326
# Define compile definitions
24-
add_compile_definitions(APP_NAME="Open ModScan")
27+
add_compile_definitions(APP_NAME="${PRODUCT_NAME}")
2528
add_compile_definitions(APP_DESCRIPTION="${PROJECT_DESCRIPTION}")
2629
add_compile_definitions(APP_VERSION="${PROJECT_VERSION}")
2730

@@ -236,9 +239,9 @@ if(Qt6_FOUND)
236239
qt_add_lupdate(omodscan
237240
TS_FILES ${TS_FILES}
238241
SOURCES ${SOURCES} ${HEADERS} ${UI_FILES}
239-
OUTPUT_DIR translations
240242
)
241243
target_link_libraries(omodscan PRIVATE Qt::Core5Compat)
244+
target_link_libraries(omodscan PRIVATE Qt6::Core)
242245
else()
243246
add_custom_command(
244247
OUTPUT ${TS_FILES}
@@ -250,11 +253,23 @@ else()
250253
COMMENT "Updating translation files..."
251254
)
252255
add_custom_target(update_translations ALL DEPENDS ${TS_FILES})
253-
add_dependencies(omodscan update_translations)
254256
endif()
255257

256258
if(WIN32)
257-
target_sources(omodscan PRIVATE res/omodscan.ico)
259+
string(REPLACE "." ";" VERSION_LIST ${PROJECT_VERSION})
260+
list(GET VERSION_LIST 0 VERSION_MAJOR)
261+
list(GET VERSION_LIST 1 VERSION_MINOR)
262+
list(GET VERSION_LIST 2 VERSION_PATCH)
263+
264+
set(EXECUTABLE_NAME "omodscan")
265+
set(ICON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/omodscan.ico")
266+
set(RC_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/omodscan.rc.in")
267+
set(GENERATED_RC "${CMAKE_BINARY_DIR}/omodscan.rc")
268+
269+
configure_file(${RC_TEMPLATE} ${GENERATED_RC} @ONLY)
270+
271+
target_sources(omodscan PRIVATE ${ICON_PATH} ${GENERATED_RC})
258272
set_target_properties(omodscan PROPERTIES WIN32_EXECUTABLE ON)
259273
endif()
260274

275+
add_dependencies(omodscan update_translations)

omodscan/connectiondetails.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,11 @@ struct ConnectionDetails
311311
TcpConnectionParams TcpParams;
312312
SerialConnectionParams SerialParams;
313313
ModbusProtocolSelections ModbusParams;
314+
bool ExcludeVirtualPorts = false;
314315

315316
bool operator==(const ConnectionDetails& cd) const{
316317
return Type == cd.Type &&
318+
ExcludeVirtualPorts == cd.ExcludeVirtualPorts &&
317319
ModbusParams == cd.ModbusParams &&
318320
((Type == ConnectionType::Tcp) ? TcpParams == cd.TcpParams: SerialParams == cd.SerialParams);
319321
}
@@ -361,6 +363,7 @@ inline QDataStream& operator >>(QDataStream& in, ConnectionDetails& params)
361363
inline QSettings& operator <<(QSettings& out, const ConnectionDetails& params)
362364
{
363365
out.setValue("ConnectionParams/Type", (uint)params.Type);
366+
out.setValue("ConnectionParams/ExcludeVirtualPorts", params.ExcludeVirtualPorts);
364367
out << params.TcpParams;
365368
out << params.SerialParams;
366369
out << params.ModbusParams;
@@ -377,6 +380,7 @@ inline QSettings& operator <<(QSettings& out, const ConnectionDetails& params)
377380
inline QSettings& operator >>(QSettings& in, ConnectionDetails& params)
378381
{
379382
params.Type = (ConnectionType)in.value("ConnectionParams/Type", 0).toUInt();
383+
params.ExcludeVirtualPorts = in.value("ConnectionParams/ExcludeVirtualPorts", false).toBool();
380384
in >> params.TcpParams;
381385
in >> params.SerialParams;
382386
in >> params.ModbusParams;

omodscan/controls/connectioncombobox.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,30 @@ Q_DECLARE_METATYPE(ConnectionPort)
1919
ConnectionComboBox::ConnectionComboBox(QWidget* parent)
2020
:QComboBox(parent)
2121
{
22-
addItem(tr("Remote TCP/IP Server"), ConnectionType::Tcp, QString());
22+
setExcludeVirtuals(false);
23+
}
24+
25+
///
26+
/// \brief ConnectionComboBox::excludeVirtuals
27+
/// \return
28+
///
29+
bool ConnectionComboBox::excludeVirtuals() const
30+
{
31+
return _excludeVirtuals;
32+
}
2333

24-
for(auto&& port: getAvailableSerialPorts())
34+
///
35+
/// \brief ConnectionComboBox::setExcludeVirtuals
36+
/// \param exclude
37+
///
38+
void ConnectionComboBox::setExcludeVirtuals(bool exclude)
39+
{
40+
_excludeVirtuals = exclude;
41+
42+
clear();
43+
44+
addItem(tr("Remote TCP/IP Server"), ConnectionType::Tcp, QString());
45+
for(auto&& port: getAvailableSerialPorts(_excludeVirtuals))
2546
{
2647
const auto text = QString(tr("Direct Connection to %1")).arg(port);
2748
addItem(text, ConnectionType::Serial, port);

omodscan/controls/connectioncombobox.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@ class ConnectionComboBox : public QComboBox
1313
public:
1414
ConnectionComboBox(QWidget* parent = nullptr);
1515

16+
bool excludeVirtuals() const;
17+
void setExcludeVirtuals(bool exclude);
18+
1619
ConnectionType currentConnectionType() const;
1720
void setCurrentConnectionType(ConnectionType type, const QString& portName);
1821

1922
QString currentPortName() const;
2023

2124
private:
2225
void addItem(const QString& text, ConnectionType type, const QString& portName);
26+
27+
private:
28+
bool _excludeVirtuals = false;
2329
};
2430

2531
#endif // CONNECTIONCOMBOBOX_H
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#include "historycombobox.h"
2+
3+
///
4+
/// \brief HistoryComboBox::HistoryComboBox
5+
/// \param parent
6+
/// \param maxItems
7+
/// \param settingsKey
8+
///
9+
HistoryComboBox::HistoryComboBox(QWidget *parent, int maxItems, const QString &settingsKey)
10+
: QComboBox(parent), _maxItems(maxItems), _settingsKey(settingsKey)
11+
{
12+
setEditable(true);
13+
setInsertPolicy(QComboBox::NoInsert);
14+
loadHistory();
15+
}
16+
17+
///
18+
/// \brief HistoryComboBox::~HistoryComboBox
19+
///
20+
HistoryComboBox::~HistoryComboBox()
21+
{
22+
}
23+
24+
///
25+
/// \brief HistoryComboBox::addToHistory
26+
/// \param text
27+
///
28+
void HistoryComboBox::addToHistory(const QString &text)
29+
{
30+
if (text.isEmpty())
31+
return;
32+
33+
// Remove duplicates
34+
int index = findText(text);
35+
if (index >= 0)
36+
removeItem(index);
37+
38+
// Add to the beginning
39+
insertItem(0, text);
40+
41+
// Limit the number of items
42+
while (count() > _maxItems)
43+
removeItem(_maxItems);
44+
}
45+
46+
///
47+
/// \brief HistoryComboBox::saveHistory
48+
///
49+
void HistoryComboBox::saveHistory()
50+
{
51+
if (_settingsKey.isEmpty())
52+
return;
53+
54+
QSettings m(QSettings::NativeFormat, QSettings::UserScope, APP_NAME, APP_VERSION);
55+
m.beginGroup("HistoryComboBox");
56+
57+
QStringList history;
58+
for (int i = 0; i < count(); ++i)
59+
history << itemText(i);
60+
61+
m.setValue(_settingsKey, history);
62+
m.endGroup();
63+
}
64+
65+
///
66+
/// \brief HistoryComboBox::loadHistory
67+
///
68+
void HistoryComboBox::loadHistory()
69+
{
70+
if (_settingsKey.isEmpty())
71+
return;
72+
73+
QSettings m(QSettings::NativeFormat, QSettings::UserScope, APP_NAME, APP_VERSION);
74+
m.beginGroup("HistoryComboBox");
75+
QStringList history = m.value(_settingsKey).toStringList();
76+
77+
if (!history.isEmpty())
78+
addItems(history);
79+
80+
m.endGroup();
81+
}
82+
83+
///
84+
/// \brief HistoryComboBox::clearHistory
85+
///
86+
void HistoryComboBox::clearHistory()
87+
{
88+
clear();
89+
if (!_settingsKey.isEmpty())
90+
{
91+
QSettings m(QSettings::NativeFormat, QSettings::UserScope, APP_NAME, APP_VERSION);
92+
m.beginGroup("HistoryComboBox");
93+
m.remove(_settingsKey);
94+
m.endGroup();
95+
}
96+
}
97+
98+
///
99+
/// \brief HistoryComboBox::setMaxItems
100+
/// \param max
101+
///
102+
void HistoryComboBox::setMaxItems(int max)
103+
{
104+
_maxItems = max;
105+
while (count() > _maxItems)
106+
removeItem(_maxItems);
107+
}
108+
109+
///
110+
/// \brief HistoryComboBox::maxItems
111+
/// \return
112+
///
113+
int HistoryComboBox::maxItems() const
114+
{
115+
return _maxItems;
116+
}
117+
118+
///
119+
/// \brief HistoryComboBox::focusOutEvent
120+
/// \param event
121+
///
122+
void HistoryComboBox::focusOutEvent(QFocusEvent *event)
123+
{
124+
QString currentText = this->currentText();
125+
if (!currentText.isEmpty() && findText(currentText) == -1)
126+
addToHistory(currentText);
127+
128+
QComboBox::focusOutEvent(event);
129+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#ifndef HISTORYCOMBOBOX_H
2+
#define HISTORYCOMBOBOX_H
3+
4+
#include <QComboBox>
5+
#include <QSettings>
6+
#include <QStringList>
7+
8+
///
9+
/// \brief The HistoryComboBox class
10+
///
11+
class HistoryComboBox : public QComboBox
12+
{
13+
Q_OBJECT
14+
15+
public:
16+
explicit HistoryComboBox(QWidget *parent = nullptr, int maxItems = 10, const QString &settingsKey = QString());
17+
~HistoryComboBox();
18+
19+
void addToHistory(const QString &text);
20+
void saveHistory();
21+
void loadHistory();
22+
void clearHistory();
23+
24+
void setMaxItems(int max);
25+
int maxItems() const;
26+
27+
protected:
28+
void focusOutEvent(QFocusEvent *event) override;
29+
30+
private:
31+
int _maxItems;
32+
QString _settingsKey;
33+
};
34+
35+
#endif // HISTORYCOMBOBOX_H

0 commit comments

Comments
 (0)