Compare commits

..

25 Commits

Author SHA1 Message Date
Steffen Pohle c6f263e3d2 some code cleanup and fixing all the warnings
10 months ago
Steffen Pohle 1c6fb1c3b9 some code cleanup and fixing all the warnings
10 months ago
Steffen Pohle d7faa21676 Update 'README.md'
10 months ago
Steffen Pohle b021f134c1 windows alwas windows.
10 months ago
Steffen Pohle 5cc7e6832c adding bulk load, some little gui improvements.
10 months ago
Steffen Pohle d1d48749df cross build manual fixed.
1 year ago
Steffen Pohle c15d89180d changed build version
1 year ago
Steffen Pohle cd08819ea3 fixed single word/coil write fc5/fc6
2 years ago
Steffen Pohle f0c160a9d6 fixed windows build, somehow not all DLLs are being copied
2 years ago
Steffen Pohle bcbb0514bc fixed windows build part two. missing include of stdint.h
2 years ago
Steffen Pohle 34e4995764 fixed windows build. missing include of stdint.h
2 years ago
Steffen Pohle de29f19930 Makefile target buildwindows will create the zip file
3 years ago
Steffen Pohle e416a16bc8 fixed: ignore faulty requests
3 years ago
Steffen Pohle 49a1b404ee fixed: ignore faulty requests
3 years ago
Steffen Pohle 1d3f575df5 gui issues fixed.
3 years ago
Steffen Pohle 59614436a2 crosscompiling works again
3 years ago
Steffen Pohle c122133021 better debug message
3 years ago
Steffen Pohle 32fec3f9a5 fixing little bugs. closing connection on error.
3 years ago
Steffen Pohle 32f2c40c06 added information message
3 years ago
Steffen Pohle e4574423f4 change readme file
3 years ago
Steffen Pohle 4c8d70d1b0 code cleanup
3 years ago
Steffen Pohle 8cb56a1fed removed testmodbus-client files to new repository.
3 years ago
Steffen Pohle 018d96efb6 preparation for testmodbus-client
3 years ago
Steffen Pohle b1577fbdab prepare server code for an maybe client
3 years ago
Steffen Pohle f6d0ece8a6 prepare server code for an maybe client
3 years ago

@ -1,3 +1,30 @@
Version 1.0.5
2025-11-29:
- double click on the variable list works.
- added bulk load of CSV files.
- set values within the saved file and thier content
Version 1.0.4
2024-11-08:
- fixed single coil / word write. Response was not correct.
2024-08-07:
- fixed compile on windows. stdint.h was not included
2024-02-22:
- fixed: ignore incorrect requests
2023-09-19:
- performance issues fixed.
- gui issues if more than 200 values where added fixed.
- closing connection if an error occured.
2023-09-16:
- added message if a connection is closed.
2022-10-31:
- fixed use of uninitialized and wrong value.

@ -27,7 +27,7 @@ This configuration file will be needed on all meson based builds.
cpu = 'x86_64'
endian = 'little'
[properties]
[built-in options]
c_args = []
c_link_args = []
@ -39,7 +39,7 @@ This configuration file will be needed on all meson based builds.
pkgconfig = '/usr/bin/x86_64-w64-mingw32-pkg-config'
windres = '/usr/bin/x86_64-w64-mingw32-windres'
ld = '/usr/bin/x86_64-w64-mingw32-ld'
exe_wrapper = 'wine64'
exe_wrapper = 'wine'
## lib-glib
@ -48,8 +48,8 @@ Compile the library using the following steps.
mkdir build
cd build
meson --buildtype=release --prefix=/opt/W64-cross-compile/ --cross-file ../../cross-file.ini
mseon compile
meson setup --buildtype=release --prefix=/opt/W64-cross-compile/ --cross-file ../../cross-file.ini
meson compile
if an error like `glib/glibconfig.h:65:51: error: missing binary operator before token "("` occures edit the line and delete the **G_GNUC_EXTENSION** expression.
@ -65,7 +65,7 @@ The flags -Dintrospection=false will tell meson to disable some building options
mkdir build
cd build
PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig meson --buildtype=release -Dintrospection=false -Ddocs=false --prefix=/opt/W64-cross-compile/ --cross-file ../../cross-file.ini
PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig meson setup --buildtype=release -Dintrospection=false -Ddocs=false --prefix=/opt/W64-cross-compile/ --cross-file ../../cross-file.ini
meson compile
meson install
@ -73,14 +73,22 @@ Copy manualy all compiled `*.dll` files to the destination
cp `find -name "*.dll"` /opt/W64-cross-compile/lib/
## pango
Compile the library using the following steps. This will also download and compile some more librarys like `libpng` and `cairo` librarys.
mkdir build
cd build
PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig meson --buildtype=release --prefix=/opt/W64-cross-compile/ --cross-file ../../cross-file.ini
meson compile
PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig meson setup --buildtype=release --prefix=/opt/W64-cross-compile/ --cross-file ../../cross-file.ini
For some reason the **harfbuzz** library wants to compile with the **HAVE_FT_GET_TRANSFORM** definition set. While this function is not defined in the sources at all. The workaround is to comment out the following line in `build/subprojects/harfbuzz/config.h` file.
// #define HAVE_FT_GET_TRANSFORM 1
Add `#include <cairo-dwrite.h> in file `../pango/pangocairo-dwrite-font.cpp` and start the compilation.
PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig meson compile
In case of an error with unresolved symbols like undefined reference to '__strcat_chk'. You need to edit the `cross-file.ini` and add the linker flags/option `-lssp` to the `c_link_args` parameters. Remove the build directory and restart configuring and compiling this libraray again.
@ -91,7 +99,7 @@ In case of an error with unresolved symbols like undefined reference to '__strca
mkdir build
cd build
PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig meson --buildtype=release --prefix=/opt/W64-cross-compile/ --cross-file ../../cross-file.ini
PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig meson setup --buildtype=release --prefix=/opt/W64-cross-compile/ --cross-file ../../cross-file.ini
meson compile
meson install
cp `find -name "*.dll"` /opt/W64-cross-compile/lib/

@ -1,23 +1,22 @@
.SILENT: help
VERSION = 1.0.1
APP = testmodbus-server
VERSION = 1.0.5
-include Makefile.rules
OBJECTS = gui.oo main.oo mbsconfig.oo modbus.oo guimodbusdata.oo guivalues.oo json.oo tcp.oo
OBJECTSSRV = gui.oo server.oo mbsconfig.oo modbussrv.oo guimodbusdata.oo guivalues.oo json.oo tcp.oo
DISTNAME=testmodbus-server-$(VERSION)
ifeq ($(TARGET),)
ifeq ($(CONFIGSET),)
noconfig: help
endif
all: Makefile.rules $(TARGET)
all: Makefile.rules testmodbus-server$(TARGETEXT)
help:
echo "set up configuration"
echo " make configwindows to generate the windows build"
echo " make configcross to generate the windows cross build"
echo " make configlinux to generate the linix build"
echo " make configlinux to generate the linux build"
echo " make buildwindows to generate the build for windows (uses cross compiler)"
configlinux: clean
@ -41,8 +40,8 @@ config: Makefile.rules
echo "#endif" >> config.h
$(TARGET): $(OBJECTS)
$(CPP) -o $(TARGET) $(OBJECTS) $(LDFLAGS) $(LIBS)
testmodbus-server$(TARGETEXT): $(OBJECTSSRV)
$(CPP) -o testmodbus-server$(TARGETEXT) $(OBJECTSSRV) $(LDFLAGS) $(LIBS)
.SUFFIXES:
.SUFFIXES: .c .cc .C .cpp .oo
@ -52,12 +51,13 @@ $(TARGET): $(OBJECTS)
clean:
rm -rf TestModbus-Server-$(VERSION)/
rm -f *.o *.oo *.c~ *.h~ *.cc~ *.ui~ $(APP) Makefile~
rm -f *.o *.oo *.c~ *.h~ *.cc~ *.ui~ testmodbus-server$(TARGETEXT) testmodbus-client$(TARGETEXT) Makefile~
rm -rf *.dll
rm -rf *.exe
rm -rf Makefile.rules
rm -rf test-fc16
rm -rf test-fc15
rm -rf config.h
dist: clean
rm -rf $(DISTNAME)
@ -94,11 +94,15 @@ buildwindows: clean
cp Changelog TestModbus-Server-$(VERSION)/
cp LICENSE TestModbus-Server-$(VERSION)/
./copydlls.sh
./copydlls.sh
./copydlls.sh
./copydlls.sh
./copyshare.sh
cp /usr/lib/gcc/x86_64-w64-mingw32/14-win32/*.dll TestModbus-Server-$(VERSION)/
for i in `ldd TestModbus-Server.exe |grep -vi "/c/windows" | cut -d" " -f3`; do cp $$i TestModbus-Server-$(VERSION)/; done
mv *.dll TestModbus-Server-$(VERSION)/
mv share TestModbus-Server-$(VERSION)/
#tar xvzf $(CROSSENV)/files/winbuild-dll.tgz -C TestModbus-Server-$(VERSION)/
#tar xvzf $(CROSSENV)/files/winbuild-share.tgz -C TestModbus-Server-$(VERSION)/
zip -r TestModbus-Server-$(VERSION)-win.zip TestModbus-Server-$(VERSION)/
-include $(DEPENDFILE)

@ -1,5 +1,6 @@
TARGET = $(APP).exe
TARGETEXT = .exe
CONFIGSET = YES
CROSSENV = /opt/W64-cross-compile/
CPP = /usr/bin/x86_64-w64-mingw32-g++
CPPFLAGS = -ggdb -Wall -O0 `PKG_CONFIG_PATH=$(CROSSENV)/lib/pkgconfig pkg-config --cflags gtk+-3.0 gmodule-export-2.0` -Wl,--export-dynamic -DBUILD_WINDOWS=1 -Wdeprecated

@ -1,6 +1,6 @@
TARGET = $(APP)
TARGETEXT =
CONFIGSET = YES
CPP = c++
CPPFLAGS = -ggdb -Wall -O0 `pkg-config --cflags gtk+-3.0 gmodule-export-2.0` -Wl,--export-dynamic -I/usr/include -DBUILD_LINUX=1
INCLUDES =

@ -1,6 +1,6 @@
TARGET = $(APP).exe
TARGETEXT = .exe
CONFIGSET = YES
CPP = g++
CPPFLAGS = -ggdb -Wall -O0 `pkg-config --cflags gtk+-3.0 gmodule-export-2.0` -Wl,--export-dynamic -I/usr/include -DBUILD_WINDOWS=1
INCLUDES =

@ -9,14 +9,16 @@ newer version of this software at https://steffen.gulpe.de/modbus-tcpip/
![Image_01](https://steffen.gulpe.de/modbus-tcpip/screenshot-winver1-0-0-regs.PNG)
![Image_02](https://steffen.gulpe.de/modbus-tcpip/screenshot-winver1-0-0-values.PNG)
# Compilation on Linux
As requirement to compile this application you need to install the gnome3-devel packages. If this is done you can compile and run the application with the following commands:
# Compilation
make configlinux
make
./testmodbus-server
# Installation
not supported.
not needed.
# Cross Compilation for Windows (build on Debian, target Windows)
@ -32,3 +34,16 @@ needed librarys (needs to prepared, see Makefile for some hints).
For precompiled windows binarys look at this [link](https://steffen.gulpe.de/modbus-tcpip/).
# some usefull functions
Bulk load of csv files works.
NAME,TYPE,FC,REGISTER,VALUE
TEST_FC3_0,FLOAT,3,0,1.5
TEST_FC3_2,FLOAT,3,2,1.6
TEST_FC4_4,FLOAT,4,4,2.6
TEST_FC4_6,FLOAT,4,6,15.6

@ -2,10 +2,16 @@
CROSS_DEST_DIR=/opt/W64-cross-compile/lib
CROSS_COMPILER_DIR=/usr/x86_64-w64-mingw32/lib
CROSS_GCC_DIR=/usr/lib/gcc/x86_64-w64-mingw32/10-win32
#
# in case of issues, check the path
# ls -la /usr/lib/gcc/x86_64-w64-mingw32/
#
CROSS_GCC_DIR=/usr/lib/gcc/x86_64-w64-mingw32/12-win32
cp -v $CROSS_COMPILER_DIR/zlib1.dll ./
# copy dll dependencys
copy_dependency() {
local I

275
gui.cc

@ -10,8 +10,9 @@
#endif
#include <stdio.h>
#include <list>
#include <sys/time.h>
#include "gui.h"
#include "modbus.h"
#include "modbussrv.h"
#include "guivalues.h"
#include "json.h"
#include "mbsconfig.h"
@ -19,17 +20,28 @@
extern GtkBuilder *_builder_; // work around for threads
extern void addvar_displaywithvalues (gpointer data, GuiValue *v);
extern int gnome_dblclick_timeout;
int gui_double_click = 0;
struct timeval doubleclick_time;
/// @brief function is too slow i need <400ms .... :( but we will resit the value jzst in case...
/// @param data
/// @return
gboolean cb_timeout_doubleclick(gpointer data) {
gui_double_click = 0;
return FALSE;
};
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// call back functions
//
gboolean cb_window_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) {
// GtkBuilder *builder = (GtkBuilder *) data;
gboolean cb_window_delete_event (GtkWidget *widget, gpointer data) {
cb_menu_save(widget, data);
return FALSE;
}
@ -44,9 +56,9 @@ void cb_window_show (GtkWidget *widget, gpointer data) {
void cb_menu_new (GtkWidget *widget, gpointer data) {
// GtkBuilder *builder = (GtkBuilder *) data;
modbus.EnableAll(0);
modbussrv.EnableAll(0);
MBData_EnableAll(0);
modbus.RequestsClear();
modbussrv.RequestsClear();
MBData_ReqReset ();
Value_DelAll();
};
@ -92,6 +104,91 @@ void cb_menu_open (GtkWidget *widget, gpointer data) {
};
void cb_menu_openbulk (GtkWidget *widget, gpointer data) {
GtkBuilder *builder = (GtkBuilder *) data;
GtkWindow *window = GTK_WINDOW (gtk_builder_get_object (builder, "testmodbus-server"));
GtkWidget *dialog;
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
GtkFileFilter *filter;
gint res;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
dialog = gtk_file_chooser_dialog_new ("Open Bulk File",
window,
action,
"_Cancel",
GTK_RESPONSE_CANCEL,
"_Open",
GTK_RESPONSE_ACCEPT,
NULL);
filter = gtk_file_filter_new();
gtk_file_filter_add_pattern(filter, "*.csv");
gtk_file_filter_set_name(filter, "CSV File");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
filter = gtk_file_filter_new();
gtk_file_filter_add_pattern(filter, "*.*");
gtk_file_filter_set_name(filter, "All Files");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_ACCEPT) {
char *filename;
GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
filename = gtk_file_chooser_get_filename (chooser);
load_bulkfile(filename);
g_free(filename);
}
gtk_widget_destroy (dialog);
};
gboolean cb_vars_btnrelease (GtkWidget *widget, gpointer user_data) {
//
// selfhandling double click :) seem to work
if (gui_double_click == 1 && get_cycletime(&doubleclick_time) < gnome_dblclick_timeout) {
GuiValue v;
GtkTreeIter iter;
GtkTreeModel *model;
GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv"));
gchar *v_name;
gchar *v_fc;
gchar *v_reg;
gchar *v_type;
gchar *v_sim;
gchar *v_value;
GtkTreeSelection *sel;
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(vars));
if (sel && gtk_tree_selection_get_selected(GTK_TREE_SELECTION(sel), &model, &iter)) {
gtk_tree_model_get (model, &iter,
VALDATA_COL_NAME, &v_name,
VALDATA_COL_FC, &v_fc,
VALDATA_COL_REGSTART, &v_reg,
VALDATA_COL_TYPE, &v_type,
VALDATA_COL_SIM, &v_sim,
VALDATA_COL_VALUE, &v_value,
-1);
v.name = v_name;
v.reg = atoi (v_reg);
v.fc = atoi (v_fc);
v.type = v_type;
v.sim = v_sim;
v.value = v_value;
addvar_displaywithvalues(_builder_, &v);
}
}
else {
gui_double_click = 1;
gettimeofday(&doubleclick_time, NULL);
g_timeout_add(gnome_dblclick_timeout, cb_timeout_doubleclick, _builder_);
}
return FALSE;
};
void cb_menu_save (GtkWidget *widget, gpointer data) {
// GtkBuilder *builder = (GtkBuilder *) data;
@ -177,21 +274,21 @@ void cb_btn_start (GtkWidget *widget, gpointer data) {
int port = atoi(gtk_entry_get_text(GTK_ENTRY(portentry)));
if (modbus.isRunning()) {
if (modbussrv.isRunning()) {
printf ("Stop Server\n");
modbus.Stop();
modbussrv.Stop();
gtk_widget_set_sensitive(portentry, TRUE);
gtk_button_set_label(GTK_BUTTON(widget), _("Start"));
}
else {
printf ("Start Server (port: %d)\n", port);
if (modbus.Start(port)) {
if (modbussrv.Start(port)) {
gtk_button_set_label(GTK_BUTTON(widget),_("Stop"));
gtk_widget_set_sensitive(portentry, FALSE);
}
usleep (250000);
if (modbus.isRunning() == 0) {
modbus.Stop();
if (modbussrv.isRunning() == 0) {
modbussrv.Stop();
displayerror("modbus server could not been started.\nSee console output for errors.");
gtk_widget_set_sensitive(portentry, TRUE);
gtk_button_set_label(GTK_BUTTON(widget), _("Start"));
@ -256,9 +353,8 @@ void addvar_displaywithvalues (gpointer data, GuiValue *v) {
vn.value = gtk_entry_get_text(GTK_ENTRY(value)); // load the from the entry
if (Value_SetValue(vn.value, vn.type, vn.fc, &regstowrite, regvals)) {
modbus.SetRegValue(vn.fc, vn.reg, regstowrite, (uint16_t*)regvals);
modbussrv.SetRegValue(vn.fc, vn.reg, regstowrite, (uint16_t*)regvals);
}
}
}
gtk_widget_hide(GTK_WIDGET(dlg));
@ -335,23 +431,21 @@ gboolean cb_thread_network_data_add (gpointer data) {
struct modbus_data *mbdata = (struct modbus_data *)data;
std::string text;
char timetext[255];
GtkTextTag *tag_datetime;
GtkTextTag *tag_data;
GtkTextTag *tag_info;
GtkTextTag *tag_modbus;
time_t _tm =time(NULL);
struct tm * curtime = localtime (&_tm);
static int _once = 0;
if (_once == 0) {
_once = 1;
tag_datetime = gtk_text_buffer_create_tag (textbuffer, "Date_Time",
gtk_text_buffer_create_tag (textbuffer, "Date_Time",
"foreground", "blue", "style", PANGO_WEIGHT_BOLD, "family", "Monospace", NULL);
tag_data = gtk_text_buffer_create_tag (textbuffer, "Data",
gtk_text_buffer_create_tag (textbuffer, "Data",
"foreground", "black", "style", PANGO_WEIGHT_NORMAL, "family", "Monospace", NULL);
tag_info = gtk_text_buffer_create_tag (textbuffer, "Info",
gtk_text_buffer_create_tag (textbuffer, "Info",
"foreground", "green", "style", PANGO_WEIGHT_THIN, "family", "Sans", NULL);
tag_modbus = gtk_text_buffer_create_tag (textbuffer, "Modbus",
gtk_text_buffer_create_tag (textbuffer, "Error",
"foreground", "red", "style", PANGO_WEIGHT_BOLD, "family", "Sans", NULL);
gtk_text_buffer_create_tag (textbuffer, "Modbus",
"foreground", "black", "style", PANGO_WEIGHT_NORMAL, "family", "Sans", NULL);
}
strftime (timetext, 255, "%H:%M:%S", curtime);
@ -360,7 +454,7 @@ gboolean cb_thread_network_data_add (gpointer data) {
// build hex dump
//
text = "";
for (int i = 0; i < mbdata->bufferlen; i++) {
for (size_t i = 0; i < mbdata->bufferlen; i++) {
unsigned char c = mbdata->buffer[i];
char hexnum[] = "0123456789ABCDEF";
@ -368,11 +462,12 @@ gboolean cb_thread_network_data_add (gpointer data) {
else if (i % 32 == 0) text += "\n ";
else if (i % 4 == 0 && i > 0) text += " : ";
else if (i % 2 == 0 && i > 0) text += ":";
// else text += " ";
text += hexnum[c/16];
text += hexnum[c%16];
}
text += "\n";
gtk_text_buffer_get_start_iter(textbuffer, &start);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Data", NULL);
@ -400,23 +495,33 @@ gboolean cb_thread_network_data_add (gpointer data) {
}
else {
text = text + " Registers: " + std::to_string (mbdata->regstart);
text = text + " Bytes: " + std::to_string (mbdata->regcnt);
text = text + " Cnt: " + std::to_string (mbdata->regcnt);
}
}
text = text + "\n";
text = text + " Bytecnt: "+ std::to_string (mbdata->bytecnt) +"\n";
gtk_text_buffer_get_start_iter(textbuffer, &start);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Modbus", NULL);
//
// number of bytes
text = std::to_string(mbdata->bufferlen) + " Bytes";
gtk_text_buffer_get_start_iter(textbuffer, &start);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Info", NULL);
//
text ="";
text += mbdata->hostname;
if (mbdata->direction == 0) text = text + " RECV ";
else text = text + " SEND ";
text = text + std::to_string(mbdata->bufferlen) + " Bytes";
// Hostname
text = mbdata->hostname;
gtk_text_buffer_get_start_iter(textbuffer, &start);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Info", NULL);
//
// direction or error
if (mbdata->direction == 0)
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, (const char*)" RECV ", -1, "Info", NULL);
else if (mbdata->direction == 1)
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, (const char*)" SEND ", -1, "Info", NULL);
else
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, (const char*)" ** ERROR **", -1, "Error", NULL);
//
// add date
@ -448,9 +553,7 @@ gboolean cb_thread_network_text_add (gpointer data) {
char timetext[255];
GtkWidget *textview = GTK_WIDGET(gtk_builder_get_object (_builder_, "network_text"));
GtkTextBuffer *textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
GtkTextIter start, end;
GtkTextTag *tag_datetime;
GtkTextTag *tag_info;
GtkTextIter start;
char *textprm = (char*) data;
time_t _tm =time(NULL);
struct tm * curtime = localtime (&_tm);
@ -458,17 +561,14 @@ gboolean cb_thread_network_text_add (gpointer data) {
if (_once == 0) {
_once = 1;
tag_datetime = gtk_text_buffer_create_tag (textbuffer, "Date_Time",
gtk_text_buffer_create_tag (textbuffer, "Date_Time",
"foreground", "blue", "style", PANGO_WEIGHT_BOLD, "family", "Monospace", NULL);
tag_info = gtk_text_buffer_create_tag (textbuffer, "Connection",
gtk_text_buffer_create_tag (textbuffer, "Connection",
"foreground", "red", "style", PANGO_WEIGHT_BOLD, "family", "Sans", NULL);
}
strftime (timetext, 255, "%H:%M:%S", curtime);
//
// build hex dump
//
//
//
text ="";
@ -501,10 +601,7 @@ gboolean cb_thread_status (gpointer data) {
void cb_networkdata_show (GtkWidget *widget, gpointer data) {
GtkWidget *textview = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(data), "network_text"));
GtkTextBuffer *textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
printf ("*************************** \n");
gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
};
void cb_addvar_close (GtkWidget *widget, gpointer data) {
@ -529,6 +626,7 @@ void displayerror (string error) {
gtk_widget_destroy(dialog);
}
void cb_addvar_addedit (GtkWidget *widget, gpointer data) {
GtkBuilder *builder = (GtkBuilder *) data;
GtkWidget *dlg = GTK_WIDGET (gtk_builder_get_object (builder, "addvar"));
@ -561,7 +659,7 @@ void cb_menu_about(GtkWidget *widget, gpointer data) {
GtkWidget *dlg = GTK_WIDGET (gtk_builder_get_object (builder, "dlgabout"));
GtkWidget *label = GTK_WIDGET (gtk_builder_get_object (builder, "dlgabout_version"));
gtk_label_set_text(GTK_LABEL(label), VERSION);
gint result = gtk_dialog_run(GTK_DIALOG(dlg));
gtk_dialog_run(GTK_DIALOG(dlg));
gtk_widget_hide(GTK_WIDGET(dlg));
return;
@ -575,19 +673,19 @@ void cb_about_btnclose(GtkWidget *widget, gpointer data) {
}
void cb_btn_enableall (GtkWidget *widget, gpointer data) {
modbus.EnableAll(1);
modbussrv.EnableAll(1);
MBData_EnableAll(1);
};
void cb_btn_disableall (GtkWidget *widget, gpointer data) {
modbus.EnableAll(0);
modbussrv.EnableAll(0);
MBData_EnableAll(0);
};
void cb_btn_clearreq (GtkWidget *widget, gpointer data) {
modbus.RequestsClear();
modbussrv.RequestsClear();
MBData_ReqReset ();
};
@ -620,7 +718,7 @@ void cb_btn_enablevalues (GtkWidget *widget, gpointer data) {
break;
}
modbus.Enable(atoi(v_fc), atoi(v_reg), Value_GetSize(v_type), 1);
modbussrv.Enable(atoi(v_fc), atoi(v_reg), Value_GetSize(v_type), 1);
MBData_Enable(atoi(v_fc), atoi(v_reg), Value_GetSize(v_type), 1);
if (v_name != NULL) free (v_name);
@ -718,12 +816,10 @@ void load_file(std::string fn) {
GtkWidget *guiautovalue = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "conf_autoaddvalues"));
GtkWidget *guiport = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "port_entry"));
GuiValue g;
int autoadd = 0;
modbus.EnableAll(0);
modbussrv.EnableAll(0);
MBData_EnableAll(0);
modbus.RequestsClear();
modbussrv.RequestsClear();
MBData_ReqReset ();
Value_DelAll();
std::string confdata;
@ -759,15 +855,88 @@ void load_file(std::string fn) {
for (i = 0; json.GetObjectIdx("values", i, &value); i++) {
uint16_t regvals[4];
int regstowrite = 1;
value.GetValue("name", &g.name);
value.GetValue("type", &g.type);
value.GetValue("sim", &g.sim);
value.GetValue("value", &g.value);
value.GetValue("fc", &temp); g.fc = atoi(temp.c_str());
value.GetValue("reg", &temp); g.reg = atoi(temp.c_str());
value.GetValueInt("fc", &g.fc);
value.GetValueInt("reg", &g.reg);
Value_Add(&g);
if (Value_SetValue(g.value, g.type, g.fc, &regstowrite, regvals)) {
modbussrv.SetRegValue(g.fc, g.reg, regstowrite, (uint16_t*)regvals);
}
}
};
void load_bulkfile(std::string fn) {
GuiValue g;
FILE *f;
char buffer[FILEBUFFER];
char *pos, *next;
JSONParse json;
JSONParse value;
std::string temp;
uint16_t regvals[4];
int regstowrite = 1;
printf ("%s\n", __FUNCTION__);
f = fopen(fn.c_str(),"r");
if (f != NULL) {
while (!feof(f)) {
// NAME,TYPE,FC,REG,VALUE
fgets (buffer, FILEBUFFER, f);
// name
pos = buffer;
if ((next = strchr(pos, ',')) == NULL) {
printf ("load bulk: could not find ','\n");
continue;
}
*next = 0;
g.name = pos;
// type
pos = next+1;
if ((next = strchr(pos, ',')) == NULL) continue;
*next = 0;
g.type = pos;
// fc
pos = next+1;
if ((next = strchr(pos, ',')) == NULL) continue;
*next = 0;
g.fc = atoi(pos);
// reg
pos = next+1;
if ((next = strchr(pos, ',')) == NULL) continue;
*next = 0;
g.reg = atoi(pos);
// value
pos = next+1;
g.value = pos;
printf ("%s -> %s\n", pos, g.value.c_str());
g.sim = "NONE";
Value_Add(&g);
printf ("%s -> %s\n", pos, g.value.c_str());
regstowrite = 1;
if (Value_SetValue(g.value, g.type, g.fc, &regstowrite, regvals)) {
modbussrv.SetRegValue(g.fc, g.reg, regstowrite, (uint16_t*)regvals);
printf ("%s add %s type:%s with value %s\n", __FUNCTION__, g.name.c_str(), g.type.c_str(), g.value.c_str());
}
else printf ("%s add %s without value\n", __FUNCTION__, g.name.c_str());
}
fclose (f);
}
else printf ("error on open bulk: %s\n", strerror(errno));
};

11
gui.h

@ -15,14 +15,16 @@
#include <iostream>
#include <list>
#define BUILDER_FILE "testmodbus-server.ui"
#include "guivalues.h"
#include "guimodbusdata.h"
#define LEN_FIELD 128
std::string to_hex16 (int v);
extern uint16_t modbusdata[4][0x10000];
void load_file(std::string fn);
void load_bulkfile(std::string fn);
void save_file(std::string fn);
void displayerror (string error);
@ -36,10 +38,10 @@ extern "C" {
// main windows call backs
//
G_MODULE_EXPORT void cb_window_show (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT gboolean cb_window_delete_event (GtkWidget *widget,
GdkEvent *event, gpointer data);
G_MODULE_EXPORT gboolean cb_window_delete_event (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT void cb_menu_new (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT void cb_menu_open (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT void cb_menu_openbulk (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT void cb_menu_save (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT void cb_menu_saveas (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT void cb_menu_quit (GtkWidget *widget, gpointer data);
@ -59,6 +61,7 @@ G_MODULE_EXPORT void cb_btn_delvar (GtkWidget *widget, gpointer data);
// Add Variable Dialog
G_MODULE_EXPORT void cb_addvar_close (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT void cb_addvar_addedit (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT gboolean cb_vars_btnrelease (GtkWidget *widget, gpointer user_data);
//
// Register Buttons

@ -12,9 +12,9 @@
#include "gui.h"
#include "guimodbusdata.h"
#include "modbus.h"
#include "modbussrv.h"
extern Modbus modbus;
extern ModbusSrv modbussrv;
extern GtkBuilder *_builder_; // work around for threads
void mbdata_enabletoggled_cb(GtkCellRendererToggle *cellrenderer, char *path, gpointer data);
@ -45,7 +45,7 @@ GtkTreeModel *MBData_create_with_data() {
gtk_tree_store_set (store, &secondlevel, MBDATA_COL_FCREG, regname1.c_str(),-1);
}
modbus.GetRegister(fc, regnum, &r);
modbussrv.GetRegister(fc, regnum, &r);
regname2 = "FC" + std::to_string(fc) + " " + std::to_string(regnum);
if (fc < 3) {
if (r.value == 0) txt = "false";
@ -152,7 +152,7 @@ void mbdata_enabletoggled_cb(GtkCellRendererToggle *cellrenderer, char *path, gp
gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
MBDATA_COL_ENABLED, enabled,
-1);
modbus.Enable(fc, reg, 1, enabled);
modbussrv.Enable(fc, reg, 1, enabled);
g_free (fcreg);
fcreg = NULL;
}
@ -166,11 +166,10 @@ void MBData_ChangeRegs (int fc, int regstart, int count, ModbusRegister *r) {
GtkWidget *mbdata = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "regs_tv"));
GtkTreeIter iter;
GtkTreeModel *model;
int i,j;
int i;
gchar *v_fcreg = NULL;
string path;
char value[16] = "0000";
char hex[] = "0123456789ABCDEF";
model = gtk_tree_view_get_model(GTK_TREE_VIEW(mbdata));

@ -8,7 +8,7 @@
#define _GUIMODBUSDATA_H_
#include "gui.h"
#include "modbus.h"
#include "modbussrv.h"
#include "guivalues.h"
enum {

@ -16,10 +16,10 @@
#include "gui.h"
#include "config.h"
#include "mbsconfig.h"
#include "modbus.h"
#include "modbussrv.h"
#include "guivalues.h"
void Value_ModStore(GtkTreeModel *model, GtkTreeIter *iter, GuiValue *g);
extern GtkBuilder *_builder_; // work around for threads
GuiValue::GuiValue() {
@ -226,15 +226,21 @@ gboolean Value_Loop(gpointer data) {
v.reg = atoi(v_reg);
v.type = v_type;
v.sim = v_sim;
Value_Set (&v);
changed = Value_Simulation(&v);
if (changed) {
uint16_t regvals[4];
int regstowrite = 1;
if (Value_SetValue(v.value, v.type, v.fc, &regstowrite, regvals)) {
modbus.SetRegValue(v.fc, v.reg, regstowrite, (uint16_t*)regvals);
}
modbussrv.SetRegValue(v.fc, v.reg, regstowrite, (uint16_t*)regvals);
}
gtk_list_store_set(GTK_LIST_STORE(model), &iter,
VALDATA_COL_NAME, v.name.c_str(),
VALDATA_COL_FC, std::to_string(v.fc).c_str(),
VALDATA_COL_REGSTART, std::to_string(v.reg).c_str(),
VALDATA_COL_TYPE, v.type.c_str(),
VALDATA_COL_VALUE, Value_GetValue(v.fc, v.reg, v.type).c_str(),
VALDATA_COL_SIM, v.sim.c_str(),
-1);
}
g_free(v_name);
@ -266,6 +272,7 @@ void Value_Set(GuiValue *g) {
if (strcmp(v_name, g->name.c_str()) == 0) {
Value_ModStore(model, &iter, g);
changed = 1;
break;
}
g_free(v_name);
}
@ -297,13 +304,12 @@ void Value_Add(GuiValue *g) {
Value_ModStore(model, &iter, g);
count = Value_GetSize(g->type);
modbus.Enable(g->fc, g->reg, count, 1);
modbussrv.Enable(g->fc, g->reg, count, 1);
MBData_Enable(g->fc, g->reg, count, 1);
}
void Value_ModStore(GtkTreeModel *model, GtkTreeIter *iter, GuiValue *g) {
g->value = Value_GetValue(g->fc, g->reg, g->type);
gtk_list_store_set(GTK_LIST_STORE(model), iter,
VALDATA_COL_NAME, g->name.c_str(),
VALDATA_COL_FC, std::to_string(g->fc).c_str(),
@ -312,17 +318,11 @@ void Value_ModStore(GtkTreeModel *model, GtkTreeIter *iter, GuiValue *g) {
VALDATA_COL_VALUE, Value_GetValue(g->fc, g->reg, g->type).c_str(),
VALDATA_COL_SIM, g->sim.c_str(),
-1);
// for (int i = 0; i < 10; i++) {
// float *f = (float *)&modbusdata[2][i];
// printf ("%f ", *f);
// }
// printf ("\n");
};
///////////////////////////////////////////////////
// return valuze as string
// return value as string
std::string Value_GetValue(int fc, int reg, string type) {
std::string result = "";
@ -372,6 +372,9 @@ std::string Value_GetValue(int fc, int reg, string type) {
int c;
for (result = "", c = 0; c < 16; c++) {
if (c % 8 == 0 && c != 0) result = ":" + result;
else if (c % 4 == 0 && c != 0) result = "." + result;
if (modbusdata[fc-1][reg] & (1<<c)) result = "1" + result;
else result = "0" + result;
}
@ -385,9 +388,15 @@ std::string Value_GetValue(int fc, int reg, string type) {
}
/// @brief convert the value to the memory representation of the register
/// @param value value to convert
/// @param type type
/// @param fc function code
/// @param regcnt return: contains number of changed registers
/// @param regvalues return: contains registers to change (must be at last 4 bytes wide)
/// @return
int Value_SetValue(string value, string type, int fc, int *regcnt, uint16_t *regvalues) {
uint8_t data[4];
if (fc == 1 || fc == 2) {
*regcnt = 1;
if (value.compare("false") == 0 || value.compare("0") == 0)
@ -416,6 +425,19 @@ int Value_SetValue(string value, string type, int fc, int *regcnt, uint16_t *reg
memcpy (regvalues, &i16, 2);
*regcnt = 1;
}
else if (type.compare("BOOL") == 0) {
uint16_t i16;
int ival;
std::size_t c;
for (ival = 0, c = 0; c < value.length(); c++) {
if (value[c] == '0') ival = ival << 1;
else if (value[c] == '1') ival = (ival << 1) + 1;
}
i16 = (uint16_t) ival;
memcpy (regvalues, &i16, 2);
*regcnt = 1;
}
}
return 1;

@ -8,7 +8,7 @@
#define _GUIVALUE_H_
#include "gui.h"
#include "modbus.h"
#include "modbussrv.h"
#include <string>
enum {

@ -43,7 +43,7 @@ void JSONParse::Clear() {
// read every element and keep only this in memory.
//
int JSONParse::Set(string json) {
int i;
std::size_t i;
int step;
int level;
bool ignorenext;
@ -205,23 +205,21 @@ int JSONParse::GetValue(string varname, string *dest) {
int JSONParse::GetValueInt(string varname, int *dest) {
string s;
int res = GetValue(varname, &s);
if (res) {
*dest = atoi (s.c_str());
return 1;
}
return 0;
if (res) *dest = atoi (s.c_str());
return res;
};
int JSONParse::GetValueInt64(string varname, int64_t *dest) {
string s;
int res = GetValue(varname, &s);
if (res) {
*dest = atol (s.c_str());
return 1;
}
return 0;
if (res) *dest = atol (s.c_str());
return res;
};
@ -244,7 +242,8 @@ int JSONParse::GetObject(string varname, JSONParse *dest) {
#define MAXRECURSIVE 255
int JSONParse::GetIdx(string src, int idx, string *dest) {
char recursive[MAXRECURSIVE];
int i = 0, rcnt = 0, cnt = 0;
int rcnt = 0, cnt = 0;
std::size_t i;
(*dest) = "";
// printf("\n***************************************idx:%d\n", idx);

@ -10,6 +10,7 @@
#include <list>
#include <string>
#include <string.h>
#include <stdint.h>
using namespace std;

@ -9,10 +9,10 @@
*/
#ifndef _MBSCONFIG_H_
#define _MBDCONFIG_H_
#define _MBSCONFIG_H_
#include "config.h"
#include "modbus.h"
#include "modbussrv.h"
#include <string>
// maybe soon internationalisation
@ -42,6 +42,6 @@ class Config {
// declared in main.cc
//
extern Config config;
extern Modbus modbus;
extern ModbusSrv modbussrv;
#endif

@ -6,27 +6,58 @@
#include <errno.h>
#include <glib.h>
#include <sys/time.h>
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
#else
#include <unistd.h> /* close() */
#endif
#include "gui.h"
#include "modbus.h"
#include "modbussrv.h"
#include "mbsconfig.h"
#include "config.h"
std::string to_hex16 (int v) {
char HEX[] = "0123456789ABCDEF";
int i = v;
int n;
std::string txt = "";
for (n = 0; n < 4; n++) {
txt = HEX[i%16]+ txt;
i = i / 16;
}
return txt;
}
float get_cycletime(struct timeval *t) {
struct timeval t1;
float f = 0.0;
t1 = *t;
gettimeofday(t, NULL);
f = (float)(t->tv_sec - t1.tv_sec) + ((t->tv_usec - t1.tv_usec) / 1000000.0);
return f;
}
//
// C / C++ Wrapper
gpointer _ServerThread (gpointer data) {
modbus.ServerThread ();
modbussrv.ServerThread ();
return NULL;
};
//
//
Modbus::Modbus () {
ModbusSrv::ModbusSrv () {
onchangecallback = NULL;
ModbusRegister r = {0, false, 0, false};
port = 502;
@ -44,7 +75,7 @@ Modbus::Modbus () {
}
};
Modbus::~Modbus () {
ModbusSrv::~ModbusSrv () {
for (int i = 0; i < MODBUS_MAXCLIENTS; i++) {
if (clients[i] != NULL) {
delete clients[i];
@ -54,12 +85,12 @@ Modbus::~Modbus () {
};
int Modbus::isRunning() {
int ModbusSrv::isRunning() {
return tcpserver.IsListen();
};
int Modbus::Start(int serverport) {
int ModbusSrv::Start(int serverport) {
port = serverport;
serverthread = g_thread_new("network thread", _ServerThread, NULL);
@ -67,7 +98,7 @@ int Modbus::Start(int serverport) {
return 1;
};
void Modbus::Stop() {
void ModbusSrv::Stop() {
g_mutex_lock(&servermutex);
for (int i = 0; i < MODBUS_MAXCLIENTS; i++) {
if (clients[i] != NULL) {
@ -80,7 +111,7 @@ void Modbus::Stop() {
};
void Modbus::CloseConnection(int slot) {
void ModbusSrv::CloseConnection(int slot) {
if (slot < 0 || slot >= MODBUS_MAXCLIENTS) return; // slot out of bound
if (clients[slot] != NULL)
delete clients[slot];
@ -88,30 +119,12 @@ void Modbus::CloseConnection(int slot) {
};
void Modbus::SetCallback (gboolean (*callback_func)(gpointer data)) {
void ModbusSrv::SetCallback (gboolean (*callback_func)(gpointer data)) {
onchangecallback = callback_func;
}
/***************************************************************************************************
* this should only be called from within the ServerThread function
*/
void Modbus::ReadData(gpointer pdata) {
int slot;
long int len;
for (slot = 0; slot < MODBUS_MAXCLIENTS; slot++) if (&clients[slot] == pdata) break;
if (slot < 0 || slot >= MODBUS_MAXCLIENTS) return; // slot out of bound
if (clients[slot] == NULL) return; // slot not connected?
len = clients[slot]->Read(inbuffer, MODBUS_IOBUFFER);
if (len < 0) {
CloseConnection(slot);
}
};
void Modbus::ServerThread() {
void ModbusSrv::ServerThread() {
int keep_running = 1;
int ret;
struct timeval cycletimestamp = { 0 };
@ -148,12 +161,18 @@ void Modbus::ServerThread() {
char *msg;
clients[slot] = tcp;
msg = (char*) malloc (255);
snprintf (msg, 255, "new connection from %s\n", clients[slot]->GetRemoteAddr().c_str());
snprintf (msg, 255, "new connection from %s, use slot:%d\n", clients[slot]->GetRemoteAddr().c_str(), slot);
gdk_threads_add_idle(cb_thread_network_text_add, msg);
}
else {
// no free slot, accept and close right away.
if (tcp) delete tcp;
if (tcp) {
char *msg;
msg = (char*) malloc (255);
snprintf (msg, 255, "no free slot, close connection.\n");
gdk_threads_add_idle(cb_thread_network_text_add, msg);
delete tcp;
}
}
}
@ -164,6 +183,10 @@ void Modbus::ServerThread() {
if (clients[slot] != NULL) {
len = clients[slot]->ReadTimeout(inbuffer, MODBUS_IOBUFFER, 0);
if (len < 0) {
char *msg;
msg = (char*) malloc (255);
snprintf (msg, 255, "connection on slot %d closed.\n", slot);
gdk_threads_add_idle(cb_thread_network_text_add, msg);
delete clients[slot];
clients[slot] = NULL;
}
@ -191,9 +214,12 @@ void Modbus::ServerThread() {
mbindata->transactionid = 0;
mbindata->protoolid = 0;
Decode(mbindata);
if (Decode(mbindata) == 0) {
mbindata->direction = 2; // mark as error
gdk_threads_add_idle(cb_thread_network_data_add, mbindata);
continue;
}
gdk_threads_add_idle(cb_thread_network_data_add, mbindata);
//
// fill in outdata
//
@ -218,8 +244,11 @@ void Modbus::ServerThread() {
}
else {
char *txt = (char*)malloc(255);
snprintf (txt, 255, "error on processing message\n");
snprintf (txt, 255, "error on processing message, close connection\n");
gdk_threads_add_idle(cb_thread_network_text_add, txt);
delete clients[slot];
clients[slot] = NULL;
}
}
else if (mbindata->fc == 5 || mbindata->fc == 6) {
@ -228,8 +257,10 @@ void Modbus::ServerThread() {
}
else {
char *txt = (char*)malloc(255);
snprintf (txt, 255, "error on processing message\n");
snprintf (txt, 255, "error on processing message, close connection\n");
gdk_threads_add_idle(cb_thread_network_text_add, txt);
delete clients[slot];
clients[slot] = NULL;
}
}
else if (mbindata->fc == 15 || mbindata->fc == 16) {
@ -238,8 +269,10 @@ void Modbus::ServerThread() {
}
else {
char *txt = (char*)malloc(255);
snprintf (txt, 255, "error on processing message\n");
snprintf (txt, 255, "error on processing message, close connection\n");
gdk_threads_add_idle(cb_thread_network_text_add, txt);
delete clients[slot];
clients[slot] = NULL;
}
}
gdk_threads_add_idle(cb_thread_network_data_add, mboutdata);
@ -267,7 +300,7 @@ void Modbus::ServerThread() {
// return 0 on error
int Modbus::WorkerAndEncodeRead(struct modbus_data *mbin, struct modbus_data *mbout) {
int ModbusSrv::WorkerAndEncodeRead(struct modbus_data *mbin, struct modbus_data *mbout) {
//
// to prevent race condition and invalid data: servermutex must be already locked
// buffer on outdata must be of size 0x10000
@ -414,40 +447,15 @@ int Modbus::WorkerAndEncodeRead(struct modbus_data *mbin, struct modbus_data *mb
}
int Modbus::WorkerAndEncodeWriteSingle(struct modbus_data *mbin, struct modbus_data *mbout) {
uint16_t i16;
int pos = 0;
int ModbusSrv::WorkerAndEncodeWriteSingle(struct modbus_data *mbin, struct modbus_data *mbout) {
int error = 0;
// transaction
mbout->transactionid = mbin->transactionid;
i16 = htons((uint16_t)mbin->transactionid);
memcpy (mbout->buffer+pos, &i16, sizeof(i16));
pos += sizeof (i16);
// protocolid
mbout->protoolid = mbin->protoolid;
i16 = htons((uint16_t)mbin->protoolid);
memcpy (mbout->buffer+pos, &i16, sizeof(i16));
pos += sizeof (i16);
// length 3 + number of registers
mbout->length = 3;
mbout->length += mbin->regcnt * 2; // 16bit = 2Bytes
i16 = htons((uint16_t)mbout->length);
memcpy (mbout->buffer+pos, &i16, sizeof(i16));
pos += sizeof (i16);
// device id
mbout->length = 0;
mbout->unitid = mbin->unitid;
memcpy (mbout->buffer+pos, &mbout->unitid, sizeof(uint8_t));
pos += sizeof (uint8_t);
// fc
mbout->fc = mbin->fc;
memcpy (mbout->buffer+pos, &mbout->fc, sizeof(uint8_t));
pos += sizeof (uint8_t);
mbout->direction = 1;
if (mbin->fc == 5) {
@ -457,33 +465,17 @@ int Modbus::WorkerAndEncodeWriteSingle(struct modbus_data *mbin, struct modbus_d
else mbarray[FC1][mbin->regstart].value = 0;
}
else error = 1;
// return register and value
i16 = htons((uint16_t)mbin->regstart);
memcpy (mbout->buffer+pos, &i16, sizeof(i16));
pos += sizeof (i16);
i16 = htons((uint16_t)mbin->regcnt);
memcpy (mbout->buffer+pos, &i16, sizeof(i16));
pos += sizeof (i16);
}
if (mbin->fc == 6) {
mbarray[FC3][mbin->regstart].requested |= 2;
if (mbarray[FC3][mbin->regstart].enabled)
mbarray[FC3][mbin->regstart].value = mbin->regcnt;
else error = 1;
// return register and value
i16 = htons((uint16_t)mbin->regstart);
memcpy (mbout->buffer+pos, &i16, sizeof(i16));
pos += sizeof (i16);
i16 = htons((uint16_t)mbin->regcnt);
memcpy (mbout->buffer+pos, &i16, sizeof(i16));
pos += sizeof (i16);
}
mbout->bufferlen = pos;
// mirror the output
memcpy (mbout->buffer, mbin->buffer, mbin->bufferlen);
mbout->bufferlen = mbin->bufferlen;
//
// inform the application about the modbus values change
@ -510,7 +502,7 @@ int Modbus::WorkerAndEncodeWriteSingle(struct modbus_data *mbin, struct modbus_d
}
int Modbus::WorkerAndEncodeWriteMulti(struct modbus_data *mbin, struct modbus_data *mbout) {
int ModbusSrv::WorkerAndEncodeWriteMulti(struct modbus_data *mbin, struct modbus_data *mbout) {
uint16_t i16;
int pos = 0;
int i;
@ -627,15 +619,17 @@ int Modbus::WorkerAndEncodeWriteMulti(struct modbus_data *mbin, struct modbus_da
}
void Modbus::Decode(struct modbus_data *mbdata) {
//
// return 1 on success, 0 on error
int ModbusSrv::Decode(struct modbus_data *mbdata) {
//
uint16_t i16;
uint8_t i8;
int pos = 0;
int ret = 1;
mbdata->fc = 0;
mbdata->regcnt = -1;
mbdata->regcnt = 0;
mbdata->regstart =-1;
mbdata->unitid = 0;
mbdata->length = 0;
@ -644,7 +638,7 @@ void Modbus::Decode(struct modbus_data *mbdata) {
mbdata->protoolid = 0;
mbdata->bytecnt = 0;
if (mbdata->bufferlen < 8) return;
if (mbdata->bufferlen < 8) return 0;
// Transaction
memcpy (&i16, mbdata->buffer+pos, sizeof(i16));
@ -662,32 +656,45 @@ void Modbus::Decode(struct modbus_data *mbdata) {
mbdata->length = ntohs(i16);
// unitid
if (mbdata->length < pos-6) ret = 0;
memcpy (&i8, mbdata->buffer+pos, sizeof(i8));
pos += sizeof(i8);
mbdata->unitid = i8;
// function code
if (mbdata->length < pos-6) ret = 0;
memcpy (&i8, mbdata->buffer+pos, sizeof(i8));
pos += sizeof(i8);
mbdata->fc = i8;
// register
if (mbdata->length < pos-6) ret = 0;
memcpy (&i16, mbdata->buffer+pos, sizeof(i16));
pos += sizeof(i16);
mbdata->regstart = ntohs(i16);
// not needed for single write
if (mbdata->fc == 5 || mbdata->fc == 6) {
mbdata->regcnt = 0;
mbdata->bytecnt = 0;
}
// number of registers
if (mbdata->length < pos-6) ret = 0;
memcpy (&i16, mbdata->buffer+pos, sizeof(i16));
pos += sizeof(i16);
mbdata->regcnt = ntohs(i16);
// unitid
// bytecnt
// if (mbdata->length < pos-6) ret = 0;
memcpy (&i8, mbdata->buffer+pos, sizeof(i8));
pos += sizeof(i8);
mbdata->bytecnt = i8;
return ret;
};
int Modbus::GetRegister(int fc, int regnum, ModbusRegister *r) {
int ModbusSrv::GetRegister(int fc, int regnum, ModbusRegister *r) {
r->enabled = false;
r->requested = 0;
r->updated = false;
@ -708,7 +715,7 @@ int Modbus::GetRegister(int fc, int regnum, ModbusRegister *r) {
};
int Modbus::GetRegisters(int fc, int regnum, int count, ModbusRegister *r) {
int ModbusSrv::GetRegisters(int fc, int regnum, int count, ModbusRegister *r) {
r->enabled = false;
r->requested = 0;
r->updated = false;
@ -729,7 +736,7 @@ int Modbus::GetRegisters(int fc, int regnum, int count, ModbusRegister *r) {
};
int Modbus::SetRegister(int fc, int regnum, ModbusRegister *r) {
int ModbusSrv::SetRegister(int fc, int regnum, ModbusRegister *r) {
if (regnum < 0 || regnum >= 0x10000) return -1;
g_mutex_lock(&servermutex);
@ -745,7 +752,7 @@ int Modbus::SetRegister(int fc, int regnum, ModbusRegister *r) {
}
int Modbus::SetRegisters(int fc, int regnum, int count, ModbusRegister *r) {
int ModbusSrv::SetRegisters(int fc, int regnum, int count, ModbusRegister *r) {
int reg = regnum;
int regend = regnum+count;
@ -768,7 +775,7 @@ int Modbus::SetRegisters(int fc, int regnum, int count, ModbusRegister *r) {
}
void Modbus::Enable(int fc, int regstart, int count, int onoff) {
void ModbusSrv::Enable(int fc, int regstart, int count, int onoff) {
int i;
if (fc < 1 || fc > 4) return;
if (regstart < 0) return;
@ -782,7 +789,7 @@ void Modbus::Enable(int fc, int regstart, int count, int onoff) {
};
void Modbus::RequestsClear() {
void ModbusSrv::RequestsClear() {
int fc, reg;
g_mutex_lock(&servermutex);
@ -793,7 +800,7 @@ void Modbus::RequestsClear() {
};
void Modbus::EnableAll(int onoff) {
void ModbusSrv::EnableAll(int onoff) {
int fc, reg;
g_mutex_lock(&servermutex);
@ -804,9 +811,7 @@ void Modbus::EnableAll(int onoff) {
};
void Modbus::SetRegValue(int fc, int regstart, int count, uint16_t *values) {
int reg;
void ModbusSrv::SetRegValue(int fc, int regstart, int count, uint16_t *values) {
if (fc <= 0 || fc > 4) {
printf ("%s:%d fc(%d) is set out of range\n", __FILE__, __LINE__, fc);
return;
@ -860,31 +865,6 @@ void Modbus::SetRegValue(int fc, int regstart, int count, uint16_t *values) {
g_mutex_lock(&servermutex);
}
/* // debug del me
printf ("%s:%d %s regs (", __FILE__, __LINE__, __FUNCTION__);
std::string text;
text = "";
if (fc == 5) fc = 1;
if (fc == 6) fc = 3;
if (fc == 15) fc = 1;
if (fc == 16) fc = 3;
for (int r = 0; r < count; r++) {
unsigned char c;
char hexnum[] = "0123456789ABCDEF";
c = *(((unsigned char *)&mbarray[fc-1][regstart+r])+0);
text += hexnum[c/16];
text += hexnum[c%16];
c = *(((unsigned char *)&mbarray[fc-1][regstart+r])+1);
text += hexnum[c/16];
text += hexnum[c%16];
text += ":";
}
printf ("%s)\n", text.c_str());
*/
g_mutex_unlock(&servermutex);
};

@ -36,7 +36,7 @@ struct modbus_data {
int protoolid;
int length;
int unitid;
int direction;
int direction; // 0 - RECV, 1 - SEND, 2 - ERROR
int fc;
int regstart;
int regcnt;
@ -45,6 +45,7 @@ struct modbus_data {
// return the cycletime in us
std::string to_hex16 (int v);
float get_cycletime(struct timeval *t);
struct {
@ -63,7 +64,7 @@ struct modbus_callback_data {
};
class Modbus {
class ModbusSrv {
private:
TCP tcpserver;
int port;
@ -76,14 +77,14 @@ private:
char inbuffer[MODBUS_IOBUFFER];
char outbuffer[MODBUS_IOBUFFER];
void Decode(struct modbus_data *mbdata);
// return 0 on error, 1 on no error
int Decode(struct modbus_data *mbdata);
// return 0 on error, 1 on success
int WorkerAndEncodeRead(struct modbus_data *mbin, struct modbus_data *mbout);
int WorkerAndEncodeWriteSingle(struct modbus_data *mbin, struct modbus_data *mbout);
int WorkerAndEncodeWriteMulti(struct modbus_data *mbin, struct modbus_data *mbout);
public:
Modbus();
~Modbus();
ModbusSrv();
~ModbusSrv();
void ServerThread();
void SetCallback (gboolean (*callback_func)(gpointer data));
@ -96,7 +97,7 @@ public:
int SetRegister(int fc, int regnum, ModbusRegister *r);
int SetRegisters(int fc, int regnum, int count, ModbusRegister *r);
void CloseConnection(int slot);
void ReadData(gpointer pdata);
// void ReadData(gpointer pdata);
void Enable(int fc, int regstart, int count, int onoff);
void EnableAll(int onoff);
@ -106,6 +107,15 @@ public:
int isRunning();
};
class ModbusCli {
private:
public:
ModbusCli() {};
~ModbusCli() {};
};
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
#define usleep(_us_) Sleep(_us_/1000)
#endif

@ -4,23 +4,27 @@
//
/////////////////////////////////////////////////////////////////////////////////
#include <sys/time.h>
#include <gio/gio.h>
#include <stdio.h>
#include <glib.h>
#include "config.h"
#include "mbsconfig.h"
#include "gui.h"
#include "modbus.h"
#include "modbussrv.h"
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// global variables
//
#define BUILDER_FILE "testmodbus-server.ui"
Config config;
Modbus modbus;
ModbusSrv modbussrv;
GtkBuilder *_builder_ = NULL; // work around for the thread situation
uint16_t modbusdata[4][0x10000]; // needed for work with the gui will by synced by the modbus call back functions
int gnome_dblclick_timeout = 500;
gboolean modbus_callback(gpointer data);
int main (int argc, char **argv) {
@ -36,6 +40,26 @@ int main (int argc, char **argv) {
printf (" https://steffen.gulpe.de/modbus-tcpip\n");
printf (" written by Steffen Pohle <steffen@gulpe.de>\n");
//
// read out some gnome defined values
#if defined _WIN32 || defined _WIN64 || defined __CYGWIN__
#else
GSettings *settings;
// from the google KI :)
// Das Schema (Schema) für die Mauseinstellungen erstellen
// In neueren GNOME-Versionen ist das Schema: 'org.gnome.desktop.peripherals.mouse'
// In älteren Versionen könnte es 'org.gnome.settings-daemon.peripherals.mouse' sein
settings = g_settings_new("org.gnome.desktop.peripherals.mouse");
if (settings == NULL) {
fprintf(stderr, "Fehler beim Laden des GSettings-Schemas.\n");
return 1;
}
gnome_dblclick_timeout = g_settings_get_int(settings, "double-click");
printf("Die aktuelle Doppelklickzeit beträgt: %d ms\n", gnome_dblclick_timeout);
g_object_unref(settings);
#endif
gtk_init (&argc, &argv);
_builder_ = builder = gtk_builder_new ();
gtk_builder_add_from_file (builder, BUILDER_FILE, NULL);
@ -47,7 +71,7 @@ int main (int argc, char **argv) {
// #endif
//
modbus.SetCallback(&modbus_callback);
modbussrv.SetCallback(&modbus_callback);
window = gtk_builder_get_object (builder, "testmodbus-server");
gtk_widget_show_all (GTK_WIDGET(window));
@ -67,20 +91,6 @@ int main (int argc, char **argv) {
return 0;
}
string to_hex16 (int v) {
char HEX[] = "0123456789ABCDEF";
int i = v;
int n;
string txt = "";
for (n = 0; n < 4; n++) {
txt = HEX[i%16]+ txt;
i = i / 16;
}
return txt;
}
gboolean modbus_callback(gpointer data) {
struct modbus_callback_data *mdata = (struct modbus_callback_data *) data;
@ -93,15 +103,3 @@ gboolean modbus_callback(gpointer data) {
return FALSE;
};
float get_cycletime(struct timeval *t) {
struct timeval t1;
float f = 0.0;
t1 = *t;
gettimeofday(t, NULL);
f = (float)(t->tv_sec - t1.tv_sec) + ((t->tv_usec - t1.tv_usec) / 1000000.0);
return f;
}

@ -158,12 +158,10 @@ int TCP::Listen(int port) {
TCP* TCP::Accept() {
fd_set rfds;
struct timeval tv;
int retval, err, i;
int retval;
SOCKET newsock;
struct sockaddr_storage cliaddr;
socklen_t cliaddr_len = sizeof(struct sockaddr_storage);
char host[NET_BUFFERSIZE];
char port[NET_BUFFERSIZE];
TCP *tcp = NULL;
if (sock <= 0) return NULL;
@ -197,8 +195,6 @@ TCP* TCP::Accept() {
int TCP::Connect(string h, string p) {
int i = 0;
remote_host = h;
remote_port = p;

@ -7,6 +7,7 @@
#include "tcp.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define SIZE_BUFFER 4096
int main (int argc, char **argv) {

@ -7,6 +7,7 @@
#include "tcp.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define SIZE_BUFFER 4096

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save