commit 383c49a6e11521d887f32b32ef1e2c6dfe295bdd Author: Steffen Pohle Date: Mon Sep 6 11:31:00 2021 +0200 adding files diff --git a/Changelog b/Changelog new file mode 100644 index 0000000..6d56a79 --- /dev/null +++ b/Changelog @@ -0,0 +1,3 @@ + +Initial Application: + diff --git a/HowTo-Cross-Compile.txt b/HowTo-Cross-Compile.txt new file mode 100644 index 0000000..e4f4aff --- /dev/null +++ b/HowTo-Cross-Compile.txt @@ -0,0 +1,206 @@ +Cross Compile +=============================================================================== + +needed: + wine + wine64 + mingw-w64 + mingw-w64-tools + + += += lib-glib +=============================================================================== + +cross-file.ini + + [host_machine] + system = 'windows' + cpu_family = 'x86_64' + cpu = 'x86_64' + endian = 'little' + + [properties] + c_args = [] + c_link_args = [] + + [binaries] + c = '/usr/bin/x86_64-w64-mingw32-gcc' + cpp = '/usr/bin/x86_64-w64-mingw32-g++' + ar = '/usr/bin/x86_64-w64-mingw32-ar' + strip = '/usr/bin/x86_64-w64-mingw32-strip' + 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' + +mkdir build +cd build +meson --buildtype=release --prefix=/opt/W64-cross-compile/ --cross-file ../cross-file.ini +mseon compile +meson install +cp `find -name "*.dll"` /opt/W64-cross-compile/lib/ + + += += atk +=============================================================================== + +cross-file.ini + + [host_machine] + system = 'windows' + cpu_family = 'x86_64' + cpu = 'x86_64' + endian = 'little' + + [properties] + c_args = [] + c_link_args = [] + + [binaries] + c = '/usr/bin/x86_64-w64-mingw32-gcc' + cpp = '/usr/bin/x86_64-w64-mingw32-g++' + ar = '/usr/bin/x86_64-w64-mingw32-ar' + strip = '/usr/bin/x86_64-w64-mingw32-strip' + 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' + +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 +mseon compile +meson install +cp `find -name "*.dll"` /opt/W64-cross-compile/lib/ + += += pango +=============================================================================== + +cross-file.ini + + [host_machine] + system = 'windows' + cpu_family = 'x86_64' + cpu = 'x86_64' + endian = 'little' + + [properties] + c_args = [] + c_link_args = [] + + [binaries] + c = '/usr/bin/x86_64-w64-mingw32-gcc' + cpp = '/usr/bin/x86_64-w64-mingw32-g++' + ar = '/usr/bin/x86_64-w64-mingw32-ar' + strip = '/usr/bin/x86_64-w64-mingw32-strip' + 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' + +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 +mseon compile +meson install +cp `find -name "*.dll"` /opt/W64-cross-compile/lib/ + + += += gdk-pixbuf +=============================================================================== + +cross-file.ini + + [host_machine] + system = 'windows' + cpu_family = 'x86_64' + cpu = 'x86_64' + endian = 'little' + + [properties] + c_args = [] + c_link_args = [] + + [binaries] + c = '/usr/bin/x86_64-w64-mingw32-gcc' + cpp = '/usr/bin/x86_64-w64-mingw32-g++' + ar = '/usr/bin/x86_64-w64-mingw32-ar' + strip = '/usr/bin/x86_64-w64-mingw32-strip' + 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' + +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 +mseon compile +meson install +cp `find -name "*.dll"` /opt/W64-cross-compile/lib/ + + += += libepoxy +=============================================================================== + +cross-file.ini + + [host_machine] + system = 'windows' + cpu_family = 'x86_64' + cpu = 'x86_64' + endian = 'little' + + [properties] + c_args = [] + c_link_args = [] + + [binaries] + c = '/usr/bin/x86_64-w64-mingw32-gcc' + cpp = '/usr/bin/x86_64-w64-mingw32-g++' + ar = '/usr/bin/x86_64-w64-mingw32-ar' + strip = '/usr/bin/x86_64-w64-mingw32-strip' + 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' + +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 +mseon compile +meson install +cp `find -name "*.dll"` /opt/W64-cross-compile/lib/ + + + += += GTK +=============================================================================== +cp /opt/W64-cross-compile/bin/*.exe ~/.wine/drive_c/windows/system32/ +cp /opt/W64-cross-compile/lib/*.dll ~/.wine/drive_c/windows/system32/ + +vim gdk/win32/gdkprivate-win32.h .... +search around line 300.. and add the extern expression +300 /* The singleton selection object pointer */ +301 extern GdkWin32Selection *_win32_selection; + +PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig ./configure --prefix=/opt/W64-cross-compile/ --host=x86_64-w64-mingw32 +PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig make +make install + +will fail with +/bin/bash: Zeile 1: ../../gtk/gtk-update-icon-cache.exe: Kann die Binärdatei nicht ausführen: Fehler im Format der Programmdatei +make[3]: *** [Makefile:1673: install-update-icon-cache] Fehler 126 +edit the Makefile in demos/gtk-demos and add in line the wine prefix. +the same we do for +/bin/bash: Zeile 1: ../../gtk/gtk-update-icon-cache.exe: Kann die Binärdatei nicht ausführen: Fehler im Format der Programmdatei +make[5]: *** [Makefile:1184: install-update-icon-cache] Fehler 126 +make[5]: Verzeichnis „/home/steffen/gtkcrosscompile/gtk+-3.24.0/demos/widget-factory“ wird verlassen +make install +cp `find -name "*.dll"` /opt/W64-cross-compile/lib/ + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c10bda2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,6 @@ + +The license model for this software (TestModbus-Server) is not yet choosen. +At the moment you can use this software free of charge at your own risk. + +06.Sep.2021 Steffen Pohle + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d861fef --- /dev/null +++ b/Makefile @@ -0,0 +1,128 @@ +.SILENT: help +VERSION = 1.0.0 +APP = testmodbus-server + +-include Makefile.rules + +OBJECTS = gui.oo main.oo mbsconfig.oo modbus.oo guimodbusdata.oo guivalues.oo json.oo tcp.oo +DISTNAME=testmodbus-server-$(VERSION) + +ifeq ($(TARGET),) +noconfig: help +endif + +all: Makefile.rules $(TARGET) + +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 buildwindows to generate the build for windows (uses cross compiler)" + +configlinux: clean + cp -f Makefile.rules.linux Makefile.rules + make config + +configwindows: clean + cp -f Makefile.rules.windows Makefile.rules + make config + +configcross: clean + cp -f Makefile.rules.crosswindows Makefile.rules + make config + +config: Makefile.rules + echo "#ifndef _CONFIG_H_" > config.h + echo "#define _CONFIG_H_" >> config.h + echo "" >> config.h + echo "#define VERSION \"$(VERSION)\"" >> config.h + echo "" >> config.h + echo "#endif" >> config.h + + +$(TARGET): $(OBJECTS) + $(CPP) -o $(TARGET) $(OBJECTS) $(LDFLAGS) $(LIBS) + +.SUFFIXES: +.SUFFIXES: .c .cc .C .cpp .oo + +.cc.oo : $(INCLUDES) + $(CPP) -o $@ -c $(CPPFLAGS) $< + +clean: + rm -f *.o *.oo *.c~ *.h~ *.cc~ *.ui~ $(APP) Makefile~ + rm -rf *.dll + rm -rf *.exe + rm -rf Makefile.rules + rm -rf test-fc16 + rm -rf test-fc15 + +dist: clean + rm -rf $(DISTNAME) + mkdir $(DISTNAME) + cp Makefile* $(DISTNAME) + cp Readme $(DISTNAME) + cp COPYING $(DISTNAME) + cp Changelog $(DISTNAME) + cp *.ui $(DISTNAME) + mkdir $(DISTNAME)/othersources + cp -r othersources/gdk-pixbuf-2.42.6.tar.xz $(DISTNAME)/othersources + cp -r othersources/glib-main.tar.bz2 $(DISTNAME)/othersources + cp -r othersources/gtk+-3.24.0.tar.xz $(DISTNAME)/othersources + cp -r othersources/libepoxy-master.zip $(DISTNAME)/othersources + cp -r othersources/*.txt $(DISTNAME)/othersources + cp -r othersources/pango-1.48.7.tar.xz $(DISTNAME)/othersources + cp -rf *.h $(DISTNAME) + cp -rf *.cc $(DISTNAME) + tar cvzf $(DISTNAME).tgz $(DISTNAME) + rm -rf $(DISTNAME) + +dep: + $(CXX) -MM `ls *.cc` $(CXXFLAGS) > $(DEPENDFILE) + +test-fc16: test-fc16.cc + $(CPP) -o test-fc16 test-fc16.cc $(LDFLAGS) -I/usr/include -I/usr/local/include -L/usr/local/lib -L/usr/lib + +test-fc15: test-fc15.cc + $(CPP) -o test-fc15 test-fc15.cc $(LDFLAGS) -I/usr/include -I/usr/local/include -L/usr/local/lib -L/usr/lib + +copydll: $(TARGET) + echo "delete all librarys" + rm -rf *.dll + echo "copy linked dll first level" + cp -vf `ldd testmodbus-server.exe | grep .dll | grep /mingw64/bin | cut -d' ' -f3 ` ./ + echo "copy network dll" + cp /usr/lib/*UDPTCPNetwork.dll ./ + echo "copy linked dll second level" + cp -vf `ldd testmodbus-server.exe | grep .dll | grep /mingw64/bin | cut -d' ' -f3 ` ./ + + +buildwindows: + rm -rf TestModbus-Server-$(VERSION) + mkdir TestModbus-Server-$(VERSION) + make clean + make configcross + make $(TARGET) + cp testmodbus-server.exe TestModbus-Server-$(VERSION)/ + cp testmodbus-server.ui TestModbus-Server-$(VERSION)/ + cp testmodbus-server.png TestModbus-Server-$(VERSION)/ + cp Readme TestModbus-Server-$(VERSION)/ + cp Changelog TestModbus-Server-$(VERSION)/ + cp COPYING TestModbus-Server-$(VERSION)/ + mkdir TestModbus-Server-$(VERSION)/othersources + cp -r othersources/gdk-pixbuf-2.42.6.tar.xz TestModbus-Server-$(VERSION)/othersources + cp -r othersources/glib-main.tar.bz2 TestModbus-Server-$(VERSION)/othersources + cp -r othersources/gtk+-3.24.0.tar.xz TestModbus-Server-$(VERSION)/othersources + cp -r othersources/libepoxy-master.zip TestModbus-Server-$(VERSION)/othersources + cp -r othersources/*.txt TestModbus-Server-$(VERSION)/othersources + cp -r othersources/pango-1.48.7.tar.xz TestModbus-Server-$(VERSION)/othersources + tar xvzf othersources/winbuild-dll.tgz -C TestModbus-Server-$(VERSION)/ + tar xvzf othersources/winbuild-share.tgz -C TestModbus-Server-$(VERSION)/ + + +-include $(DEPENDFILE) + +.PHONY: all +.PHONY: count +.PHONY: clean diff --git a/Makefile.rules.crosswindows b/Makefile.rules.crosswindows new file mode 100644 index 0000000..605f71c --- /dev/null +++ b/Makefile.rules.crosswindows @@ -0,0 +1,8 @@ + +TARGET = $(APP).exe + +CPP = /usr/bin/x86_64-w64-mingw32-g++ +CPPFLAGS = -ggdb -Wall -O0 `PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig pkg-config --cflags gtk+-3.0 gmodule-export-2.0` -Wl,--export-dynamic -DBUILD_WINDOWS=1 -Wdeprecated +INCLUDES = +LDFLAGS = -lws2_32 +LIBS = `PKG_CONFIG_PATH=/opt/W64-cross-compile/lib/pkgconfig pkg-config --libs gtk+-3.0 gmodule-export-2.0` -L/usr/lib -mwindows diff --git a/Makefile.rules.linux b/Makefile.rules.linux new file mode 100644 index 0000000..081b8a9 --- /dev/null +++ b/Makefile.rules.linux @@ -0,0 +1,8 @@ + +TARGET = $(APP) + +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 = +LDFLAGS = +LIBS = `pkg-config --libs gtk+-3.0 gmodule-export-2.0` -L/usr/lib diff --git a/Makefile.rules.windows b/Makefile.rules.windows new file mode 100644 index 0000000..883e91d --- /dev/null +++ b/Makefile.rules.windows @@ -0,0 +1,8 @@ + +TARGET = $(APP).exe + +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 = +LDFLAGS = -lWs2_32 +LIBS = `pkg-config --libs gtk+-3.0 gmodule-export-2.0` -L/usr/lib diff --git a/README.md b/README.md new file mode 100644 index 0000000..3cf749b --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# testmodbus-server + +This programm is used for testing modbus-tcpip configuations. +If you do not know what modbus-tcpip mean stop using this programm. + +This software is written by Steffen Pohle (steffen@gulpe.de), check for a +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 + make configlinux + make + + +# Installation +not supported. + + +# Cross Compilation for Windows (build on Debian, target Windows) +Configue the Makefiles.rules.crosswindows file to your needs. +A little manual on my cross compilation setup can be found +in HowTo-Cross-Compile.txt. Make sure you copy all needed dll +files to dll in this directory. + + make buildwindows + +All needed files will be placed in a separate subfolder together with all +needed librarys (needs to prepared, see Makefile for some hints). + +For precompiled windows binarys look at this [link](https://steffen.gulpe.de/modbus-tcpip/). + diff --git a/gui.cc b/gui.cc new file mode 100644 index 0000000..dd098cf --- /dev/null +++ b/gui.cc @@ -0,0 +1,773 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// gui.cc is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) +#else + #include /* close() */ +#endif +#include +#include +#include "gui.h" +#include "modbus.h" +#include "guivalues.h" +#include "json.h" +#include "mbsconfig.h" +#include "config.h" + +extern GtkBuilder *_builder_; // work around for threads +extern void addvar_displaywithvalues (gpointer data, GuiValue *v); + +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// call back functions +// + +gboolean cb_window_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) { +// GtkBuilder *builder = (GtkBuilder *) data; + + cb_menu_save(widget, data); + + return FALSE; +} + +void cb_window_show (GtkWidget *widget, gpointer data) { +// GtkBuilder *builder = (GtkBuilder *) data; + mbdata_show(widget, data); + valdata_show(widget, data); + + g_timeout_add(100, Value_Loop, NULL); +}; + + +void cb_menu_new (GtkWidget *widget, gpointer data) { +// GtkBuilder *builder = (GtkBuilder *) data; + modbus.EnableAll(0); + MBData_EnableAll(0); + modbus.RequestsClear(); + MBData_ReqReset (); + Value_DelAll(); +}; + + +void cb_menu_open (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 File", + window, + action, + "_Cancel", + GTK_RESPONSE_CANCEL, + "_Open", + GTK_RESPONSE_ACCEPT, + NULL); + + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*.modbus"); + gtk_file_filter_set_name(filter, "Test Modbus Config"); + 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); + config.SetFilename(filename); + g_free (filename); + load_file(config.GetFilename().c_str()); + } + gtk_widget_destroy (dialog); +}; + + +void cb_menu_save (GtkWidget *widget, gpointer data) { +// GtkBuilder *builder = (GtkBuilder *) data; + + if (config.GetFilename().length() == 0) + cb_menu_saveas (widget, data); + else { + save_file(config.GetFilename().c_str()); + } +}; + + +void cb_menu_saveas (GtkWidget *widget, gpointer data) { + GtkBuilder *builder = (GtkBuilder *) data; + GtkWindow *window = GTK_WINDOW (gtk_builder_get_object (builder, "testmodbus-server")); + GtkWidget *dialog; + GtkFileChooser *chooser; + GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE; + GtkFileFilter *filter; + gint res; + + dialog = gtk_file_chooser_dialog_new ("Save File", + window, + action, + "_Cancel", + GTK_RESPONSE_CANCEL, + "_Save", + GTK_RESPONSE_ACCEPT, + NULL); + chooser = GTK_FILE_CHOOSER (dialog); + + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*.modbus"); + gtk_file_filter_set_name(filter, "Test Modbus Config"); + 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); + + gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE); + if (config.GetFilename().length () == 0) + gtk_file_chooser_set_current_name (chooser, "noname.modbus"); + else + gtk_file_chooser_set_filename (chooser, config.GetFilename().c_str()); + + res = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (res == GTK_RESPONSE_ACCEPT) { + char *filename; + + filename = gtk_file_chooser_get_filename (chooser); + + config.SetFilename(filename); + cb_menu_save (widget, data); + g_free (filename); + } + + gtk_widget_destroy (dialog); +}; + + +void cb_menu_quit (GtkWidget *widget, gpointer data) { + GtkBuilder *builder = (GtkBuilder *) data; + GtkWindow *window = GTK_WINDOW (gtk_builder_get_object (builder, "testmodbus-server")); + + cb_menu_save(widget, data); + + gtk_window_close(window); +}; + + +// +// try to set new port +void cb_port_enter (GtkWidget *widget, gpointer data) { + config.SetPort(atoi(gtk_entry_get_text (GTK_ENTRY(widget)))); + gtk_entry_set_text(GTK_ENTRY(widget), std::to_string(config.GetPort()).c_str()); +}; + + +void cb_btn_start (GtkWidget *widget, gpointer data) { + GtkBuilder *builder = (GtkBuilder *) data; + GtkWidget *portentry = GTK_WIDGET (gtk_builder_get_object (builder, "port_entry")); + + int port = atoi(gtk_entry_get_text(GTK_ENTRY(portentry))); + + if (modbus.isRunning()) { + printf ("Stop Server\n"); + modbus.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)) { + gtk_button_set_label(GTK_BUTTON(widget),_("Stop")); + gtk_widget_set_sensitive(portentry, FALSE); + } + usleep (250000); + if (modbus.isRunning() == 0) { + modbus.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")); + } + } +}; + + + +void addvar_displaywithvalues (gpointer data, GuiValue *v) { + GtkBuilder *builder = (GtkBuilder *) data; + GtkWidget *dlg = GTK_WIDGET (gtk_builder_get_object (builder, "addvar")); + GtkWidget *reg = GTK_WIDGET (gtk_builder_get_object (builder, "adddlg_regnum")); + GtkWidget *typeCB = GTK_WIDGET (gtk_builder_get_object (builder, "adddlg_type")); + GtkWidget *simCB = GTK_WIDGET (gtk_builder_get_object (builder, "adddlg_simmulation")); + GtkWidget *name = GTK_WIDGET (gtk_builder_get_object (builder, "adddlg_name")); + GtkWidget *value = GTK_WIDGET (gtk_builder_get_object (builder, "adddlg_value")); + GtkWidget *change = GTK_WIDGET (gtk_builder_get_object (builder, "adddlg_change")); + GtkWidget *fc[4]; + int i, fcnum; + + GtkWidget *type = gtk_bin_get_child(GTK_BIN(typeCB)); + GtkWidget *sim = gtk_bin_get_child(GTK_BIN(simCB)); + + gtk_entry_set_text(GTK_ENTRY(name), "noname"); + + if (v == NULL) { + fcnum = 1; + } + else if (v->fc < 1 || v-> fc > 4) fcnum = 1; + else fcnum = v->fc; + + for (i = 0; i < 4; i++) { + fc[i] = GTK_WIDGET (gtk_builder_get_object (builder, ((string)"adddlg_FC"+std::to_string(i+1)).c_str())); + } + + if (v != NULL) { + gtk_entry_set_text(GTK_ENTRY(name), v->name.c_str()); + gtk_entry_set_text(GTK_ENTRY(value), v->value.c_str()); + gtk_entry_set_text(GTK_ENTRY(type), v->type.c_str()); + gtk_entry_set_text(GTK_ENTRY(sim), v->sim.c_str()); + gtk_entry_set_text(GTK_ENTRY(reg), std::to_string(v->reg).c_str()); + } + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fc[fcnum-1]), true); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(change), false); + + gint result = gtk_dialog_run(GTK_DIALOG(dlg)); + if (result == GTK_RESPONSE_ACCEPT || result == GTK_RESPONSE_APPLY) { + GuiValue vn; + vn.value = gtk_entry_get_text(GTK_ENTRY(value)); + vn.name = gtk_entry_get_text(GTK_ENTRY(name)); + vn.type = gtk_entry_get_text(GTK_ENTRY(type)); + vn.sim = gtk_entry_get_text(GTK_ENTRY(sim)); + vn.reg = atoi (gtk_entry_get_text(GTK_ENTRY(reg))); + for (i = 0; i < 4; i++) { + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fc[i]))) vn.fc = i+1; + } + Value_Set(&vn); // this will reset the vn.value with the reigsters.. + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(change)) == true) { + uint16_t regvals[4]; + int regstowrite = 1; + + vn.value = gtk_entry_get_text(GTK_ENTRY(value)); // load the from the entry + if (Value_SetValue(vn.value, vn.type, vn.fc, ®stowrite, regvals)) { + modbus.SetRegValue(vn.fc, vn.reg, regstowrite, (uint16_t*)regvals); + } + + } + } + gtk_widget_hide(GTK_WIDGET(dlg)); +}; + +void cb_btn_addvar (GtkWidget *widget, gpointer data) { + addvar_displaywithvalues(data, NULL); +}; + + +void cb_btn_editvar (GtkWidget *widget, gpointer data) { + 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(data, &v); + } +}; + +void cb_btn_delvar (GtkWidget *widget, gpointer data) { + GtkTreeIter iter; + GtkTreeModel *model; + GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv")); + gchar *v_name; + 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, + -1); + Value_Del(v_name); + g_free (v_name); + } + +}; + +////////////////////////////////////////////////////// +// +// update gui/add netdata side +// +gboolean cb_thread_network_data_add (gpointer data) { + 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; + + if (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", + "foreground", "blue", "style", PANGO_WEIGHT_BOLD, "family", "Monospace", NULL); + tag_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", + "foreground", "green", "style", PANGO_WEIGHT_THIN, "family", "Sans", NULL); + tag_modbus = gtk_text_buffer_create_tag (textbuffer, "Modbus", + "foreground", "black", "style", PANGO_WEIGHT_NORMAL, "family", "Sans", NULL); + } + strftime (timetext, 255, "%H:%M:%S", curtime); + + // + // build hex dump + // + text = ""; + for (int i = 0; i < mbdata->bufferlen; i++) { + unsigned char c = mbdata->buffer[i]; + char hexnum[] = "0123456789ABCDEF"; + + if (i == 0) text += " "; + 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]; + } + gtk_text_buffer_get_start_iter(textbuffer, &start); + gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Data", NULL); + + // + // first line + // + text = ""; + if (mbdata->fc != 0) { + text = text + "\n TransactionID: " + std::to_string(mbdata->transactionid); + text = text + " ProtocolID: " + std::to_string(mbdata->protoolid); + text = text + " Length: " + std::to_string(mbdata->length); + text = text + "\n UnitID: " + std::to_string(mbdata->unitid); + if (mbdata->fc < 10) text = text + " FC0" + std::to_string(mbdata->fc); + else text = text + " FC" + std::to_string(mbdata->fc); + if (mbdata->fc >= 5) { + text = text + " WRITE "; + if (mbdata->fc == 5) + text = text + " Flag/Coil: " + std::to_string(mbdata->regstart); + else if (mbdata->fc == 6) + text = text + " Register: " + std::to_string(mbdata->regstart); + else if (mbdata->fc == 15) + text = text + " Flag/Coil: " + std::to_string(mbdata->regstart) + " Count: " + std::to_string (mbdata->regcnt); + else if (mbdata->fc == 16) + text = text + " Register: " + std::to_string(mbdata->regstart) + " Count: " + std::to_string (mbdata->regcnt); + } + else { + text = text + " Registers: " + std::to_string (mbdata->regstart); + text = text + " Bytes: " + std::to_string (mbdata->regcnt); + } + } + text = text + "\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); + + // + // + text =""; + text += mbdata->hostname; + if (mbdata->direction == 0) text = text + " RECV "; + else text = text + " SEND "; + text = 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); + + + // + // add date + // + text = "\n"; + text += timetext; + text += " "; + gtk_text_buffer_get_start_iter(textbuffer, &start); + gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Date_Time", NULL); + + free (mbdata); + } + + gtk_text_buffer_get_iter_at_line(textbuffer, &start, 2000); + gtk_text_buffer_get_iter_at_line(textbuffer, &end, 2050); + gtk_text_buffer_delete(textbuffer, &start, &end); + + return FALSE; +}; + + +////////////////////////////////////////////////////// +// +// add a text line +// +gboolean cb_thread_network_text_add (gpointer data) { + if (data) { + std::string text; + 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; + char *textprm = (char*) data; + 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", + "foreground", "blue", "style", PANGO_WEIGHT_BOLD, "family", "Monospace", NULL); + tag_info = 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 =""; + text += textprm; + gtk_text_buffer_get_start_iter(textbuffer, &start); + gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Connection", NULL); + + // + // add date + // + text = "\n"; + text += timetext; + text += " "; + gtk_text_buffer_get_start_iter(textbuffer, &start); + gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Date_Time", NULL); + + free (textprm); + } + + return FALSE; +}; + + +gboolean cb_thread_status (gpointer data) { + string *s = (string *)data; + GtkWidget *status = GTK_WIDGET(gtk_builder_get_object (_builder_, "status_lb")); + gtk_label_set_text(GTK_LABEL(status), s->c_str()); + return FALSE; +} + +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"); + +}; + +void cb_addvar_close (GtkWidget *widget, gpointer data) { + GtkBuilder *builder = (GtkBuilder *) data; + GtkWidget *dlg = GTK_WIDGET (gtk_builder_get_object (builder, "addvar")); + printf ("%s\n", __FUNCTION__); + + gtk_dialog_response(GTK_DIALOG(dlg), GTK_RESPONSE_CANCEL); +}; + + +void displayerror (string error) { + GtkWidget *dialog; + GtkWidget *window = GTK_WIDGET (gtk_builder_get_object (_builder_, "testmodbus-server")); + dialog = gtk_message_dialog_new(GTK_WINDOW(window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + error.c_str()); + gtk_window_set_title(GTK_WINDOW(dialog), "Error"); + gtk_dialog_run(GTK_DIALOG(dialog)); + 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")); + GtkWidget *name = GTK_WIDGET (gtk_builder_get_object (builder, "adddlg_name")); + + GtkWidget *dialog; + int result; + + if (Value_Exist((string)gtk_entry_get_text(GTK_ENTRY(name))) == true) { + dialog = gtk_message_dialog_new(GTK_WINDOW(dlg), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + "Are you sure you want to save?"); + gtk_window_set_title(GTK_WINDOW(dialog), "Question"); + result = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + if (result == GTK_RESPONSE_YES) gtk_dialog_response(GTK_DIALOG(dlg), GTK_RESPONSE_APPLY); + } + else { + gtk_dialog_response(GTK_DIALOG(dlg), GTK_RESPONSE_APPLY); + } + + return; +}; + + +void cb_menu_about(GtkWidget *widget, gpointer data) { + GtkBuilder *builder = (GtkBuilder *) 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_widget_hide(GTK_WIDGET(dlg)); + + return; +} + + +void cb_about_btnclose(GtkWidget *widget, gpointer data) { + GtkBuilder *builder = (GtkBuilder *) data; + GtkWidget *dlg = GTK_WIDGET (gtk_builder_get_object (builder, "dlgabout")); + gtk_dialog_response(GTK_DIALOG(dlg), GTK_RESPONSE_CLOSE); +} + +void cb_btn_enableall (GtkWidget *widget, gpointer data) { + modbus.EnableAll(1); + MBData_EnableAll(1); +}; + + +void cb_btn_disableall (GtkWidget *widget, gpointer data) { + modbus.EnableAll(0); + MBData_EnableAll(0); +}; + + +void cb_btn_clearreq (GtkWidget *widget, gpointer data) { + modbus.RequestsClear(); + MBData_ReqReset (); +}; + + +void cb_btn_enablevalues (GtkWidget *widget, gpointer data) { + GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv")); + GtkTreeIter iter; + GtkTreeModel *model; + gboolean result; + gchar *v_name = NULL; + gchar *v_reg = NULL; + gchar *v_fc = NULL; + gchar *v_type = NULL; + int cnt = 0; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars)); + for (cnt = 0, result = gtk_tree_model_get_iter_first(model, &iter); result == TRUE; + result = gtk_tree_model_iter_next(model, &iter), cnt++) { + 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, + -1); + + if (v_name == NULL) { + if (v_fc != NULL) free (v_fc); + if (v_reg != NULL) free (v_reg); + if (v_type != NULL) free (v_type); + break; + } + + modbus.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); + if (v_fc != NULL) free (v_fc); + if (v_reg != NULL) free (v_reg); + if (v_type != NULL) free (v_type); + } +}; + + + +void save_file(std::string fn) { + GtkWidget *guiautovalue = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "conf_autoaddvalues")); + GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv")); + GtkTreeIter iter; + GtkTreeModel *model; + gboolean result; + gchar *v_name = NULL; + gchar *v_reg = NULL; + gchar *v_fc = NULL; + gchar *v_type = NULL; + gchar *v_value = NULL; + gchar *v_sim = NULL; + int cnt = 0; + JSONParse jp; + JSONParse jv; + JSONElement je; + std::list values; + FILE *f; + + jp.Clear(); + values.clear(); + je.Clear(); + je.type = JSON_T_ARRAY; + je.name = "values"; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars)); + for (cnt = 0, result = gtk_tree_model_get_iter_first(model, &iter); result == TRUE; + result = gtk_tree_model_iter_next(model, &iter), cnt++) { + 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); + + if (v_name == NULL) { + if (v_fc != NULL) free (v_fc); + if (v_reg != NULL) free (v_reg); + if (v_type != NULL) free (v_type); + if (v_sim != NULL) free (v_sim); + if (v_value != NULL) free (v_value); + break; + } + + jv.Clear(); + jv.AddObject("name", v_name); + jv.AddObject("type", v_type); + jv.AddObject("fc", v_fc); + jv.AddObject("reg", v_reg); + jv.AddObject("sim", v_sim); + jv.AddObject("value", v_value); + + if(cnt != 0) + je.value += ','; + je.value += jv.ToString(); + + if (v_name != NULL) free (v_name); + if (v_fc != NULL) free (v_fc); + if (v_reg != NULL) free (v_reg); + if (v_type != NULL) free (v_type); + if (v_sim != NULL) free (v_sim); + if (v_value != NULL) free (v_value); + } + + jp.AddObject(je); + jp.AddObject("port", config.GetPort()); + + cnt = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(guiautovalue)); + jp.AddObject("auto_valueadd", cnt); + + f = fopen(fn.c_str(),"w"); + if (f != NULL) { + int err = fprintf (f, "%s\n",jp.ToString().c_str()); + if (err < 0) printf ("error on writing: %s\n", strerror(errno)); + fclose (f); + } + else printf ("error on open (write): %s\n", strerror(errno)); +}; + +#define FILEBUFFER 0x10000 +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); + MBData_EnableAll(0); + modbus.RequestsClear(); + MBData_ReqReset (); + Value_DelAll(); + std::string confdata; + FILE *f; + char buffer[FILEBUFFER]; + int i; + JSONParse json; + JSONParse value; + std::string temp; + + confdata = ""; + + f = fopen(fn.c_str(),"r"); + if (f != NULL) { + while (!feof(f)) { + fgets (buffer, FILEBUFFER, f); + confdata += buffer; + } + fclose (f); + } + else printf ("error on open (read): %s\n", strerror(errno)); + + json.Set(confdata); + + if (json.GetValueInt("port", &i)) { + config.SetPort(i); + gtk_entry_set_text(GTK_ENTRY(guiport), std::to_string(i).c_str()); + } + + if (json.GetValueInt("auto_valueadd", &i)) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(guiautovalue), i); + } + + + for (i = 0; json.GetObjectIdx("values", i, &value); i++) { + 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_Add(&g); + } +}; + + diff --git a/gui.h b/gui.h new file mode 100644 index 0000000..cc6188e --- /dev/null +++ b/gui.h @@ -0,0 +1,88 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// gui.h is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#ifndef _GUI_H_ +#define _GUI_H_ + +#include +#include +#include + +#include +#include +#include + +#define BUILDER_FILE "testmodbus-server.ui" + +#include "guivalues.h" +#include "guimodbusdata.h" +std::string to_hex16 (int v); +extern uint16_t modbusdata[4][0x10000]; + +void load_file(std::string fn); +void save_file(std::string fn); +void displayerror (string error); + +#ifdef __cplusplus +extern "C" { +#endif + + +// *********************************************************************** +// +// 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 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_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); +G_MODULE_EXPORT void cb_menu_about (GtkWidget *widget, gpointer data); + +// +// About Dialog +G_MODULE_EXPORT void cb_about_btnclose(GtkWidget *widget, gpointer data); + +// +// Variable Buttons +G_MODULE_EXPORT void cb_btn_addvar (GtkWidget *widget, gpointer data); +G_MODULE_EXPORT void cb_btn_editvar (GtkWidget *widget, gpointer data); +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); + +// +// Register Buttons +G_MODULE_EXPORT void cb_btn_enableall (GtkWidget *widget, gpointer data); +G_MODULE_EXPORT void cb_btn_disableall (GtkWidget *widget, gpointer data); +G_MODULE_EXPORT void cb_btn_enablevalues (GtkWidget *widget, gpointer data); +G_MODULE_EXPORT void cb_btn_clearreq (GtkWidget *widget, gpointer data); + + +// +// Port Entry Field and Connect Buttons +// +G_MODULE_EXPORT void cb_port_enter (GtkWidget *widget, gpointer data); +G_MODULE_EXPORT void cb_btn_start (GtkWidget *widget, gpointer data); +G_MODULE_EXPORT gboolean cb_thread_network_data_add (gpointer data); +G_MODULE_EXPORT gboolean cb_thread_network_text_add (gpointer data); +G_MODULE_EXPORT gboolean cb_thread_status (gpointer data); + +G_MODULE_EXPORT gboolean modbus_callback (gpointer data); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/guimodbusdata.cc b/guimodbusdata.cc new file mode 100644 index 0000000..6130c96 --- /dev/null +++ b/guimodbusdata.cc @@ -0,0 +1,281 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// guimodbusdata.cc is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +// +// +// show all modbus registers and the value +// +// + +#include "gui.h" +#include "guimodbusdata.h" +#include "modbus.h" + +extern Modbus modbus; + +extern GtkBuilder *_builder_; // work around for threads +void mbdata_enabletoggled_cb(GtkCellRendererToggle *cellrenderer, char *path, gpointer data); + +#define MAXREG 0x10000 +#define STEPREG 1000 +GtkTreeModel *MBData_create_with_data() { + GtkTreeStore *store; + GtkTreeIter iter, toplevel, secondlevel; + ModbusRegister r; + string txt = ""; + string regname, regname1, regname2; + int regnum, fc; + + store = gtk_tree_store_new (MBDATA_COLCOUNT, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING); + // fill in FC1..FC4 + for (fc = 1; fc <= 4; fc++) { + regname = "FC" + std::to_string(fc); + gtk_tree_store_append (store, &toplevel, NULL); + gtk_tree_store_set (store, &toplevel, + MBDATA_COL_FCREG, regname.c_str(), + -1); + + for (regnum = 0; regnum < MAXREG; regnum++) { + if (regnum % STEPREG == 0) { + regname1 = "FC" + std::to_string(fc) + " " + std::to_string(regnum) + ".." + std::to_string(regnum+STEPREG); + gtk_tree_store_append (store, &secondlevel, &toplevel); + gtk_tree_store_set (store, &secondlevel, MBDATA_COL_FCREG, regname1.c_str(),-1); + } + + modbus.GetRegister(fc, regnum, &r); + regname2 = "FC" + std::to_string(fc) + " " + std::to_string(regnum); + if (fc < 3) { + if (r.value == 0) txt = "false"; + else txt = "true"; + } + else + txt = to_hex16(r.value) + " (" +std::to_string(r.value)+")"; + gtk_tree_store_append (store, &iter, &secondlevel); + gtk_tree_store_set (store, &iter, + MBDATA_COL_FCREG, regname2.c_str(), + MBDATA_COL_REQREAD, r.requested & 1, + MBDATA_COL_REQWRITE, r.requested & 2, + MBDATA_COL_ENABLED, (r.enabled), + MBDATA_COL_VALUE, txt.c_str(), + -1); + } + } + + return GTK_TREE_MODEL (store); +}; + + +void mbdata_show(GtkWidget *widget, gpointer data) { + GtkWidget *view = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(data), "regs_tv")); + GtkCellRenderer *renderer; + GtkTreeModel *model; + + // + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), + -1, + "FC", + renderer, + "text", MBDATA_COL_FCREG, + NULL); + renderer = gtk_cell_renderer_toggle_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), + -1, + "Req. Read", + renderer, + "active", MBDATA_COL_REQREAD, + NULL); + renderer = gtk_cell_renderer_toggle_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), + -1, + "Req. Write", + renderer, + "active", MBDATA_COL_REQWRITE, + NULL); + renderer = gtk_cell_renderer_toggle_new (); + gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE(renderer), true); + g_signal_connect (G_OBJECT (renderer), "toggled", + G_CALLBACK (mbdata_enabletoggled_cb), + NULL); + + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), + -1, + "Enabled", + renderer, + "active", MBDATA_COL_ENABLED, + NULL); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), + -1, + "Value", + renderer, + "text", MBDATA_COL_VALUE, + NULL); + + model = MBData_create_with_data(); + gtk_tree_view_set_model (GTK_TREE_VIEW (view), model); + + g_object_unref(model); +}; + + +void mbdata_enabletoggled_cb(GtkCellRendererToggle *cellrenderer, char *path, gpointer data) { + GtkWidget *mbdata = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "regs_tv")); + GtkTreeIter iter; + GtkTreeModel *model; + int enabled; + char *fcreg = NULL; + int pathint[3]; + int reg; + int fc; + + // retrieve fc and register from path, return if invalid + for (fc = 0; fc < 3; fc++) pathint[fc] = -1; + sscanf (path, "%d:%d:%d", pathint, pathint+1, pathint+2); + fc = pathint[0]+1; + reg = pathint[1]*STEPREG+pathint[2]; + if (pathint[2] == -1) return; + + // read current enable setting, inverse, set to modbus object and set in treeview + model = gtk_tree_view_get_model(GTK_TREE_VIEW(mbdata)); + if (gtk_tree_model_get_iter_from_string(model, &iter, path) == true) { + gtk_tree_model_get(model, &iter, + MBDATA_COL_FCREG, &fcreg, + MBDATA_COL_ENABLED, &enabled, + -1); + if (fcreg != NULL) { + if (enabled == 0) enabled = 1; + else enabled = 0; + gtk_tree_store_set(GTK_TREE_STORE(model), &iter, + MBDATA_COL_ENABLED, enabled, + -1); + modbus.Enable(fc, reg, 1, enabled); + g_free (fcreg); + fcreg = NULL; + } + } +}; + + + +// walk throught the registers and draw them.. if unknown//not yet set check for auto add +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; + gchar *v_fcreg = NULL; + string path; + char value[16] = "0000"; + char hex[] = "0123456789ABCDEF"; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(mbdata)); + + for (i = 0; regstart < MAXREG && i < count; i++, regstart++) { + for (j = 0; j < 4; j++) value[3-j] = hex[(r[i].value >> j)%16]; + + path = std::to_string(fc-1) + ":" + std::to_string(regstart / STEPREG) + ":" + std::to_string(regstart % STEPREG); + // printf ("fc:%d reg:%d val:%d enabled:%d requested:%d path:%s\n", fc, regstart, r[i].value, r[i].enabled, r[i].requested, path.c_str()); + if (gtk_tree_model_get_iter_from_string(model, &iter, path.c_str()) == true) { + gtk_tree_model_get (model, &iter, + MBDATA_COL_FCREG, &v_fcreg, + -1); + if (v_fcreg == NULL) break; + + if (r[i].enabled) { + if (fc == 1) + memcpy (&modbusdata[0][regstart], &r[i].value, 2); + if (fc == 2) + memcpy (&modbusdata[1][regstart], &r[i].value, 2); + if (fc == 4) + memcpy (&modbusdata[3][regstart], &r[i].value, 2); + if (fc == 3 || fc == 6 || fc == 16) + memcpy (&modbusdata[2][regstart], &r[i].value, 2); + } + + if (fc == 1 || fc == 2) { + gtk_tree_store_set(GTK_TREE_STORE(model), &iter, + MBDATA_COL_FCREG, v_fcreg, + MBDATA_COL_REQREAD, (r[i].requested & 1), + MBDATA_COL_REQWRITE, (r[i].requested & 2), + MBDATA_COL_ENABLED, r[i].enabled, + MBDATA_COL_VALUE, r[i].value ? " true" : "false", + -1); + } + if (fc == 3 || fc == 4) { + gtk_tree_store_set(GTK_TREE_STORE(model), &iter, + MBDATA_COL_FCREG, v_fcreg, + MBDATA_COL_REQREAD, (r[i].requested & 1), + MBDATA_COL_REQWRITE, (r[i].requested & 2), + MBDATA_COL_ENABLED, r[i].enabled, + MBDATA_COL_VALUE, ((string)value + " (" + std::to_string(r[i].value) + ")").c_str(), + -1); + } + g_free(v_fcreg); + } + } +}; + + +void MBData_Enable (int fc, int regstart, int count, int onoff) { + GtkWidget *mbdata = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "regs_tv")); + GtkTreeIter iter; + GtkTreeModel *model; + int i; + string path; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(mbdata)); + + for (i = 0; regstart < MAXREG && i < count; i++, regstart++) { + path = std::to_string(fc-1) + ":" + std::to_string(regstart / STEPREG) + ":" + std::to_string(regstart % STEPREG); + if (gtk_tree_model_get_iter_from_string(model, &iter, path.c_str()) == true) { + gtk_tree_store_set(GTK_TREE_STORE(model), &iter, + MBDATA_COL_ENABLED, onoff, + -1); + } + } +}; + + + +void MBData_EnableAll (int onoff) { + GtkWidget *mbdata = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "regs_tv")); + GtkTreeIter iter; + GtkTreeModel *model; + int reg, fc; + string path; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(mbdata)); + for (fc = 1; fc < 5; fc++) for (reg = 0; reg < 0x10000; reg++) { + path = std::to_string(fc-1) + ":" + std::to_string(reg / STEPREG) + ":" + std::to_string(reg % STEPREG); + if (gtk_tree_model_get_iter_from_string(model, &iter, path.c_str()) == true) { + gtk_tree_store_set(GTK_TREE_STORE(model), &iter, + MBDATA_COL_ENABLED, onoff, + -1); + } + } +}; + + +void MBData_ReqReset () { + GtkWidget *mbdata = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "regs_tv")); + GtkTreeIter iter; + GtkTreeModel *model; + int reg, fc; + string path; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(mbdata)); + for (fc = 1; fc < 5; fc++) for (reg = 0; reg < 0x10000; reg++) { + path = std::to_string(fc-1) + ":" + std::to_string(reg / STEPREG) + ":" + std::to_string(reg % STEPREG); + if (gtk_tree_model_get_iter_from_string(model, &iter, path.c_str()) == true) { + gtk_tree_store_set(GTK_TREE_STORE(model), &iter, + MBDATA_COL_REQREAD, 0, + MBDATA_COL_REQWRITE, 0, + -1); + } + } +}; + diff --git a/guimodbusdata.h b/guimodbusdata.h new file mode 100644 index 0000000..e2bc8f4 --- /dev/null +++ b/guimodbusdata.h @@ -0,0 +1,39 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// guimodbusdata.h is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#ifndef _GUIMODBUSDATA_H_ +#define _GUIMODBUSDATA_H_ + +#include "gui.h" +#include "modbus.h" +#include "guivalues.h" + +enum { + MBDATA_COL_FCREG = 0, + MBDATA_COL_REQREAD, + MBDATA_COL_REQWRITE, + MBDATA_COL_ENABLED, + MBDATA_COL_VALUE, + MBDATA_COLCOUNT +}; + +GtkTreeModel *MBData_create_with_data(); +void MBData_ChangeRegs (int fc, int regstart, int count, ModbusRegister *r); +void MBData_Enable (int fc, int regstart, int count, int onoff); +void MBData_EnableAll (int onoff); +void MBData_ReqReset (); + +#ifdef __cplusplus +extern "C" { +#endif + +G_MODULE_EXPORT void mbdata_show(GtkWidget *widget, gpointer data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/guivalues.cc b/guivalues.cc new file mode 100644 index 0000000..0f7fa27 --- /dev/null +++ b/guivalues.cc @@ -0,0 +1,554 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// guivalues.cc is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +// +// +// show all variables and the value +// +// + +#include +#include + +#include "gui.h" +#include "config.h" +#include "mbsconfig.h" +#include "modbus.h" +#include "guivalues.h" + + +extern GtkBuilder *_builder_; // work around for threads + +GuiValue::GuiValue() { + sim = ""; + name = ""; + type = ""; + fc = 0; + reg = 0; +}; + + +GuiValue::~GuiValue() { + +}; + + +// walk throught the registers and draw them.. if unknown//not yet set check for auto add +void Values_ChangeRegs (int fc, int regstart, int count, ModbusRegister *r) { + int autoadd = 0; + int reg, idx; + GtkWidget *cb_autovalue = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "conf_autoaddvalues")); + GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv")); + GtkTreeIter iter; + GtkTreeModel *model; + gboolean result; + gchar *v_name = NULL; + gchar *v_fc = NULL; + gchar *v_reg = NULL; + gchar *v_type = NULL; + gchar *v_sim = NULL; + int found = 0; + int regsize = 1; + + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars)); + autoadd = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_autovalue)); + + for (idx = 0, reg = regstart; idx < count; reg++, idx++) { + for (found = 0, result = gtk_tree_model_get_iter_first(model, &iter); found == 0 && result == TRUE; + result = gtk_tree_model_iter_next(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, + -1); + + if (v_name == NULL || v_reg == NULL || v_fc == NULL || v_type == NULL) { + if (v_name != NULL) g_free(v_name); + if (v_reg != NULL) g_free(v_reg); + if (v_fc != NULL) g_free(v_fc); + if (v_type != NULL) g_free(v_type); + if (v_sim != NULL) g_free(v_sim); + break; + } + + regsize = Value_GetSize(v_type); + if (atoi(v_fc) == fc && reg >= atoi(v_reg) && reg < (atoi(v_reg) + regsize)) { + found = 1; + GuiValue v; + v.name = v_name; + v.fc = atoi(v_fc); + v.reg = atoi(v_reg); + v.type = v_type; + v.sim = v_sim; + Value_Set (&v); + } + + g_free(v_name); + g_free(v_fc); + g_free(v_reg); + g_free(v_sim); + g_free(v_type); + } + + if (autoadd && found == 0) { + GuiValue v; + v.name = "_auto_FC" + std::to_string(fc) + "_REG_" + std::to_string (reg); + v.sim = "NONE"; + v.fc = fc; + v.reg = reg; + if (v.fc == 1 || v.fc == 2) v.type = "BOOL"; + else v.type = "WORD"; + Value_Set (&v); + } + } +}; + + +std::string findparm(std::string t, std::string parm) { + std::string res = ""; + string::size_type i = t.find(parm+"="); + string::size_type j,k; + + if (i != string::npos) { + res = t.substr (i, string::npos); + j = res.find("="); + k = res.find(";"); + if (k != string::npos) k = k -j -1; + if (j != string::npos) res = res.substr(j+1, k); + } + + return res; +} + + +int Value_Simulation(GuiValue *v) { + struct timeval tv; + string s; + + gettimeofday (&tv, NULL); + string sim = v->sim.substr(0, v->sim.find(";")); + + if (sim.compare("PULSE") == 0) { + int ton = 10; + s = findparm(v->sim, "ton"); + if(s.length()>0) ton = atoi(s.c_str()); + + int toff = 50; + s = findparm(v->sim, "toff"); + if(s.length()>0) toff = atoi(s.c_str()); + + int td = ton +toff; + td = tv.tv_sec % td; + if (td <= ton) v->value = "true"; + else v->value = "false"; + + return 1; + } + else if (sim.compare("SIN") == 0) { + int td = 60; + s = findparm(v->sim, "t"); + if(s.length()>0) td = atoi(s.c_str()); + + float min = -100; + s = findparm(v->sim, "min"); + if(s.length()>0) min = atof(s.c_str()); + + float max = 100; + s = findparm(v->sim, "max"); + if(s.length()>0) max = atof(s.c_str()); + + float t = ((float)(tv.tv_sec % td)) + (((float)tv.tv_usec) / 1000000); + t = 4.0 * t * M_PI / td; + v->value = std::to_string((0.5*(max-min)*sin(t))+(min+max)*0.5); + + return 1; + } + else if (sim.compare("SAW") == 0) { + int td = 60; + s = findparm(v->sim, "t"); + if(s.length()>0) td = atoi(s.c_str()); + + float min = -100; + s = findparm(v->sim, "min"); + if(s.length()>0) min = atof(s.c_str()); + + float max = 100; + s = findparm(v->sim, "max"); + if(s.length()>0) max = atof(s.c_str()); + + float t = ((float)(tv.tv_sec % td)) + (((float)tv.tv_usec) / 1000000); + t = t / td; + v->value = std::to_string((max-min)*t+min); + + return 1; + } + + return 0; +}; + + +gboolean Value_Loop(gpointer data) { + GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv")); + GtkTreeIter iter; + GtkTreeModel *model; + gboolean result; + gchar *v_name = NULL; + gchar *v_type = NULL; + gchar *v_value = NULL; + gchar *v_sim = NULL; + gchar *v_fc = NULL; + gchar *v_reg = NULL; + GuiValue v; + int changed = 0; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars)); + for (result = gtk_tree_model_get_iter_first(model, &iter); result == TRUE; + result = gtk_tree_model_iter_next(model, &iter)) { + gtk_tree_model_get (model, &iter, + VALDATA_COL_NAME, &v_name, + VALDATA_COL_TYPE, &v_type, + VALDATA_COL_SIM, &v_sim, + VALDATA_COL_VALUE, &v_value, + VALDATA_COL_FC, &v_fc, + VALDATA_COL_REGSTART, &v_reg, + -1); + if (v_name == NULL) break; + + GuiValue v; + v.name = v_name; + v.fc = atoi(v_fc); + 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, ®stowrite, regvals)) { + modbus.SetRegValue(v.fc, v.reg, regstowrite, (uint16_t*)regvals); + } + } + + g_free(v_name); + g_free(v_type); + g_free(v_sim); + g_free(v_value); + g_free(v_reg); + g_free(v_fc); + } + + return TRUE; // continue timer +} + +void Value_Set(GuiValue *g) { + GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv")); + GtkTreeIter iter; + GtkTreeModel *model; + gboolean result; + gchar *v_name = NULL; + int changed = 0; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars)); + for (result = gtk_tree_model_get_iter_first(model, &iter); result == TRUE && changed == 0; + result = gtk_tree_model_iter_next(model, &iter)) { + gtk_tree_model_get (model, &iter, + VALDATA_COL_NAME, &v_name, + -1); + if (v_name == NULL) break; + if (strcmp(v_name, g->name.c_str()) == 0) { + Value_ModStore(model, &iter, g); + changed = 1; + } + g_free(v_name); + } + + if (changed == 0) + Value_Add(g); +} + + +int Value_GetSize(std::string type) { + if (type.compare("WORD") == 0) return 1; + if (type.compare("DWORD") == 0) return 2; + if (type.compare("FLOAT") == 0) return 2; + if (type.compare("DWORD_SWAP") == 0) return 2; + if (type.compare("FLOAT_SWAP") == 0) return 2; + + return 1; +} + + +void Value_Add(GuiValue *g) { + GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv")); + GtkTreeIter iter; + GtkTreeModel *model; + int count; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars)); + gtk_list_store_append (GTK_LIST_STORE(model), &iter); + Value_ModStore(model, &iter, g); + + count = Value_GetSize(g->type); + modbus.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(), + VALDATA_COL_REGSTART, std::to_string(g->reg).c_str(), + VALDATA_COL_TYPE, g->type.c_str(), + 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 +std::string Value_GetValue(int fc, int reg, string type) { + std::string result = ""; + + if (fc == 1 || fc == 2) { + if (modbusdata[fc-1][reg]) result = "true"; + else result = "false"; + } + else if (fc == 3 || fc == 4) { + if (type.compare ("WORD") == 0) result = std::to_string(modbusdata[fc-1][reg]); + else if (type.compare ("FLOAT") == 0) { + uint8_t data[4]; + float *f = (float*)&data; + + memcpy (data, &modbusdata[fc-1][reg], 2); + memcpy (data+2, &modbusdata[fc-1][reg+1], 2); + + result = std::to_string(*f); + } + else if (type.compare ("FLOAT_SWAP") == 0) { + uint8_t data[4]; + float *f = (float*)&data; + + memcpy (data, &modbusdata[fc-1][reg+1], 2); + memcpy (data+2, &modbusdata[fc-1][reg], 2); + + result = std::to_string((double)*f); + } + else if (type.compare ("DWORD") == 0) { + uint8_t data[4]; + uint32_t *f = (uint32_t*)&data; + + memcpy (data, &modbusdata[fc-1][reg], 2); + memcpy (data+2, &modbusdata[fc-1][reg+1], 2); + + result = std::to_string(*f); + } + else if (type.compare ("DWORD_SWAP") == 0) { + uint8_t data[4]; + uint32_t *f = (uint32_t*)&data; + + memcpy (data, &modbusdata[fc-1][reg+1], 2); + memcpy (data+2, &modbusdata[fc-1][reg], 2); + + result = std::to_string(*f); + } + else if (type.compare ("BOOL") == 0) { + int c; + + for (result = "", c = 0; c < 16; c++) { + if (modbusdata[fc-1][reg] & (1< + +enum { + VALDATA_COL_NAME = 0, + VALDATA_COL_FC, + VALDATA_COL_REGSTART, + VALDATA_COL_TYPE, + VALDATA_COL_SIM, + VALDATA_COL_VALUE, + VALDATA_COLCOUNT +}; + + +class GuiValue { +public: + GuiValue (); + ~GuiValue (); + std::string name; + std::string type; + std::string sim; + std::string value; + int reg; + int fc; +}; + + +void Values_ChangeRegs (int fc, int regstart, int count, ModbusRegister *r); +void Value_Set(GuiValue *v); +void Value_Add(GuiValue *v); +void Value_Del(std::string name); +void Value_DelAll(); +void Value_ModStore(GtkTreeModel *model, GtkTreeIter *iter, GuiValue *g); +int Value_GetSize(std::string type); +std::string Value_GetValue(int fc, int reg, string type); +int Value_SetValue(string value, string type, int fc, int *registers, uint16_t *regs); +gboolean Value_Exist (std::string name); +gboolean Value_Loop(gpointer data); + +#ifdef __cplusplus +extern "C" { +#endif + +G_MODULE_EXPORT void valdata_show(GtkWidget *widget, gpointer data); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/json.cc b/json.cc new file mode 100644 index 0000000..31c6043 --- /dev/null +++ b/json.cc @@ -0,0 +1,494 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// json.cc is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "json.h" + +/*********************************************************************** + *********************************************************************** + * + * JSONParse + * + *********************************************************************** + *********************************************************************** + */ + +enum { + STEP_NONE = 0, + STEP_STARTNAME, + STEP_NAME, + STEP_STARTVALUE, + STEP_VALUE, + STEP_END +}; + + +///////////////////////////////////////////////////////// +// +// clear out all data +// +void JSONParse::Clear() { + jsondata = ""; + names.clear(); +} + +///////////////////////////////////////////////////////// +// +// read every element and keep only this in memory. +// +int JSONParse::Set(string json) { + int i; + int step; + int level; + bool ignorenext; + + JSONElement jelement; + + Clear(); + + // find start and read until end + for (step = STEP_NONE, i = 0, ignorenext = false; i < json.length(); i++) { + // need to copy next character +// debug (0, "JSONParse: step:%d i:%d name:'%s' value:'%s'", step, i, jelement.name.c_str(), jelement.value.c_str()); + if (ignorenext) { + ignorenext = false; + if (step == STEP_NAME) jelement.name += json[i]; + if (step == STEP_VALUE) jelement.value += json[i]; + } + + // searching for startname + else if (step == STEP_NONE) { + if (json[i] == '{') { + step = STEP_STARTNAME; + continue; + } + } + + // searching for startname + else if (step == STEP_STARTNAME) { + if (json[i] == '"') { + step = STEP_NAME; + continue; + } + } + + // copy name + else if (step == STEP_NAME) { + if (json[i] == '"') { + step = STEP_STARTVALUE; + continue; + } + else { + jelement.name += json[i]; + continue; + } + } + + // searching for startvalue + else if (step == STEP_STARTVALUE) { + if (json[i] == '"') { + step = STEP_VALUE; + jelement.type = JSON_T_STRING; + continue; + } + if (json[i] == '{') { + step = STEP_VALUE; + level = 0; + jelement.type = JSON_T_OBJECT; + jelement.value = "{"; + continue; + } + if (json[i] == '[') { + step = STEP_VALUE; + level = 0; + jelement.type = JSON_T_ARRAY; + jelement.value = "["; + continue; + } + if ((json[i] >= '0' && json[i] <= '9') || + (json[i] == '+' || json[i] == '-')) { + step = STEP_VALUE; + level = 0; + jelement.type = JSON_T_NUMBER; + jelement.value = json[i]; + continue; + } + } + + // copy value + else if (step == STEP_VALUE) { + if (jelement.type == JSON_T_STRING) { + if (json[i] == '"') step = STEP_END; + else jelement.value += json[i]; + continue; + } + else if (jelement.type == JSON_T_OBJECT) { + if (json[i] == '}' && level == 0) { + jelement.value += json[i]; + step = STEP_END; + } + else { + if (json[i] == '{') level++; // increase level + if (json[i] == '}') level--; // decrease level + jelement.value += json[i]; + } + continue; + } + else if (jelement.type == JSON_T_ARRAY) { + if (json[i] == ']' && level == 0) { + jelement.value += json[i]; + step = STEP_END; + } + else { + if (json[i] == '[') level++; // increase level + if (json[i] == ']') level--; // decrease level + jelement.value += json[i]; + } + continue; + } + else if (jelement.type == JSON_T_NUMBER) { + if ((json[i] < '0' || json[i] > '9') && json[i] != '.' && + json[i] != '+' && json[i] != 'e' && json[i] != 'E') step = STEP_END; + else { + jelement.value += json[i]; + continue; + } + } + } + + // another element? + if (step == STEP_END) { + if (json[i] == ',') { +// debug (0, "* JSON.Set Add name:%s", jelement.name.c_str()); + if (jelement.type != JSON_T_NONE) { +// debug (0, "%s:%d json add element type:%d", __FILE__, __LINE__, jelement.type); + names.push_back (jelement); + } + jelement.Clear(); + step = STEP_STARTNAME; + } + continue; + } + } +// debug (0, "* JSON.Set Add name:%s", jelement.name.c_str()); + if (jelement.type != JSON_T_NONE) { +// debug (0, "%s:%d json add element type:%d", __FILE__, __LINE__, jelement.type); + names.push_back (jelement); + } + + return 0; +}; + + +int JSONParse::GetValue(string varname, string *dest) { + list::iterator iter; + + if (dest == NULL) return 0; + *dest = ""; + + for (iter = names.begin(); iter != names.end(); iter++) { + if (varname.compare(iter->name) == 0) { + *dest = iter->value; + return 1; + } + } + + return 0; +}; + + +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; +}; + + +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; +}; + + +int JSONParse::GetObject(string varname, JSONParse *dest) { + list::iterator iter; + + if (dest == NULL) return 0; + + for (iter = names.begin(); iter != names.end(); iter++) { + if (varname.compare(iter->name) == 0) { + dest->Set(iter->value); + return 1; + } + } + + return 0; +}; + + +#define MAXRECURSIVE 255 +int JSONParse::GetIdx(string src, int idx, string *dest) { + char recursive[MAXRECURSIVE]; + int i = 0, rcnt = 0, cnt = 0; + + (*dest) = ""; +// printf("\n***************************************idx:%d\n", idx); + + for (i = 0; i < MAXRECURSIVE; i++) recursive[i] = 0; + for (i = 0; i < src.length() && rcnt < MAXRECURSIVE && cnt <= idx; i++) { +// printf ("i:%d rcnt:%d['%c'] cnt:%d char:'%c' ous:'%s'\n", +// i, rcnt, recursive[rcnt], cnt, src[i], dest->c_str()); + if (src[i] == '[') { + recursive[rcnt++] = src[i]; + continue; + } + else if (src[i] == '{' && recursive[rcnt] != '"') recursive[++rcnt] = src[i]; + else if (src[i] == '}' && recursive[rcnt] == '{') rcnt--; + else if (src[i] == '"' && recursive[rcnt] == '"') rcnt--; + else if (src[i] == '"') recursive[++rcnt] = src[i]; + else if (src[i] == ',' && rcnt == 1) { + cnt++; + continue; + } + else if (src[i] == ']' && rcnt == 1) { + continue; + } + + if (rcnt > 0 && cnt == idx) { + (*dest) += src[i]; + if (src[i] == '\\') (*dest) += src[i]; + } + else { + if (src[i] == '\\')i++; + } + } + +// printf("\n***************************************idx:%d cnt:%d\n", idx, cnt); +// printf("in:'%s'\n***\nout:'%s'\n\n*****\n", src.c_str(), dest->c_str()); + + // + // final checks + if (cnt == 0 && idx == 0 && // empty source/array? + dest->size() == 0) return 0; // + if (cnt >= idx) return 1; // found the right element + return 0; // element not found +} +#undef MAXRECURSIVE + +int JSONParse::GetValueIdx(string varname, int idx, string *dest) { + list::iterator iter; + + if (dest == NULL) return 0; + + for (iter = names.begin(); iter != names.end(); iter++) { + if (varname.compare(iter->name) == 0) { + return GetIdx(iter->value, idx, dest); + } + } + + return 0; +}; + +int JSONParse::GetObjectIdx(string varname, int idx, JSONParse *dest) { + list::iterator iter; + string deststr; + int ret = 0; + + if (dest == NULL) return 0; + + for (iter = names.begin(); iter != names.end(); iter++) { + if (varname.compare(iter->name) == 0) { + ret = GetIdx(iter->value, idx, &deststr); + if (ret == 1) dest->Set(deststr); + return ret; + } + } + + return 0; +}; + +list JSONParse::GetElements() { + list l; + list::iterator iter; + + l.clear(); + for (iter = names.begin(); iter != names.end(); iter++) { + l.push_back(*iter); + } + + return l; +}; + + +void JSONParse::AddObject (JSONElement element) { + names.push_back (element); +}; + + +void JSONParse::AddObject (string name, JSONParse jp) { + JSONElement je; + je.SetObject(name, jp.ToString()); + names.push_back(je); +}; + + +void JSONParse::AddObject (string name, int val) { + JSONElement je; + je.Set(name, val); + names.push_back(je); +}; + + +void JSONParse::AddObject (string name, int64_t val) { + JSONElement je; + je.Set(name, val); + names.push_back(je); +}; + + +void JSONParse::AddObject (string name, string val) { + JSONElement je; + je.Set(name, val); + names.push_back(je); +}; + + +void JSONParse::AddObject (string name, double val) { + JSONElement je; + je.Set(name, val); + names.push_back(je); +}; + + +string JSONParse::ToString() { + list::iterator iter; + string output; + int level, i; + + output = "{"; + + for (level = 1, iter = names.begin(); iter != names.end(); iter++) { + if (iter != names.begin()) output += ","; + output += "\n"; + for (i = 0; i < 4*level; i++) output += " "; + output += iter->GetString(); + } + + output += "\n}\n"; + + return output; +}; + + +/*********************************************************************** + *********************************************************************** + * + * JSONElement + * + *********************************************************************** + *********************************************************************** + */ + + +void JSONElement::Set (string n, double v) { + name = n; + value = to_string(v); + type = JSON_T_NUMBER; +}; + + +void JSONElement::Set (string n, int v) { + name = n; + value = to_string(v); + type = JSON_T_NUMBER; +}; + + +void JSONElement::Set (string n, int64_t v) { + name = n; + value = to_string(v); + type = JSON_T_NUMBER; +}; + + +void JSONElement::Set (string n, string v) { + name = n; + value = v; + type = JSON_T_STRING; +}; + + +void JSONElement::SetArray (string n, list *l) { + list::iterator iter; + + name = n; + value = "["; + type = JSON_T_STRING; + + for (iter = l->begin(); iter != l->end(); iter++) { + if (iter != l->begin()) value += ","; + value += iter->GetString(); + } + value += "]"; +}; + + +void JSONElement::SetObject (string n, string s) { + name = n; + value = s; + type = JSON_T_OBJECT; +}; + + +string JSONElement::GetString () { + string output = ""; + string filename = __FILE__; + + switch (type) { + case(JSON_T_NUMBER): + output += "\"" + name + "\" : " + value; + break; + case(JSON_T_STRING): + if (value.length()==0) { + output += "\"" + name + "\" : \"\""; + } + else if (value[0] != '"') { + output += "\"" + name + "\" : \"" + value + "\""; + } + else output += "\"" + name + "\" : " + value; + break; + case(JSON_T_OBJECT): + output += "\"" + name + "\" : " + value; + break; + case(JSON_T_ARRAY): + if (value.length()==0) { + output += "\"" + name + "\" : []"; + } + else if (value[0] != '[') { + output += "\"" + name + "\" : [" + value + "]"; + } + else output += "\"" + name + "\" : " + value; + break; + default: + output += "\"error\" : \""+ filename + ":" + to_string(__LINE__) + " JSONElement unknown type error\"("+to_string(type)+")"; + break; + } + + return output; +}; + diff --git a/json.h b/json.h new file mode 100644 index 0000000..338f862 --- /dev/null +++ b/json.h @@ -0,0 +1,77 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// json.h is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#ifndef _JSON_H_ +#define _JSON_H_ + +#include +#include +#include + +using namespace std; + +enum { + JSON_T_NONE, + JSON_T_STRING, + JSON_T_NUMBER, + JSON_T_OBJECT, + JSON_T_ARRAY +}; + +class JSONElement { +public: + int type; + string name; + string value; + + JSONElement() { Clear(); }; + ~JSONElement() {}; + + void Clear() { type = JSON_T_NONE; name = ""; value = ""; }; + void Set (string n, double v); + void Set (string n, int v); + void Set (string n, int64_t v); + void Set (string n, string v); + void SetArray (string n, list *l); + void SetObject (string n, string s); + string GetString(); +}; + +class JSONParse { +private: + string jsondata; + list names; + +public: + JSONParse() { Set("{}"); }; + JSONParse(string json) { Set(json); }; + ~JSONParse() {}; + + void Clear(); + int Set(string json); + + int GetValue(string varname, string *dest); + int GetValueInt(string varname, int *dest); + int GetValueInt64(string varname, int64_t *dest); + int GetObject(string varname, JSONParse *dest); + + int GetIdx(string src, int idx, string *dest); + int GetValueIdx(string varname, int idx, string *dest); + int GetObjectIdx(string varname, int idx, JSONParse *dest); + + list GetElements(); + + void AddObject (JSONElement element); + void AddObject (string name, int val); + void AddObject (string name, int64_t val); + void AddObject (string name, string val); + void AddObject (string name, double val); + void AddObject (string name, JSONParse jp); + + string ToString(); +}; + +#endif // _JSON_H_ diff --git a/main.cc b/main.cc new file mode 100644 index 0000000..6b8b40d --- /dev/null +++ b/main.cc @@ -0,0 +1,107 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// main.cc is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#include + +#include "config.h" +#include "mbsconfig.h" +#include "gui.h" +#include "modbus.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// global variables +// + +Config config; +Modbus modbus; +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 + +gboolean modbus_callback(gpointer data); + +int main (int argc, char **argv) { + GtkBuilder *builder; + GObject *window; + +#ifdef BUILD_WINDOWS + char buffer[16]; + setvbuf (stdout, buffer, _IONBF, 16); +#endif + + printf ("TestModbus-Server - %s\n", VERSION); + printf (" https://steffen.gulpe.de/modbus-tcpip\n"); + printf (" written by Steffen Pohle \n"); + + gtk_init (&argc, &argv); + _builder_ = builder = gtk_builder_new (); + gtk_builder_add_from_file (builder, BUILDER_FILE, NULL); + gtk_builder_connect_signals(builder, builder); + + // + // #if defined _WIN32 || defined _WIN64 || defined __CYGWIN__ + // #else + // #endif + // + + modbus.SetCallback(&modbus_callback); + window = gtk_builder_get_object (builder, "testmodbus-server"); + gtk_widget_show_all (GTK_WIDGET(window)); + + if (argc == 2) { + printf ("Load File:%s\n", argv[1]); + config.SetFilename(argv[1]); + load_file(config.GetFilename()); + } + else { + printf ("Load File:default.modbus\n"); + config.SetFilename("default.modbus"); + load_file(config.GetFilename()); + } + + gtk_main (); + + 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; + + MBData_ChangeRegs(mdata->fc, mdata->regstart, mdata->count, mdata->r); + Values_ChangeRegs(mdata->fc, mdata->regstart, mdata->count, mdata->r); + + if (mdata->r) free (mdata->r); + free (mdata); + + 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; +} diff --git a/mbsconfig.cc b/mbsconfig.cc new file mode 100644 index 0000000..ae4a4ab --- /dev/null +++ b/mbsconfig.cc @@ -0,0 +1,32 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// mbsconfig.cc is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "mbsconfig.h" + +Config::Config () { + port = 502; + filename = ""; +}; + + +Config::~Config () { +}; + + +void Config::SetPort (int p) { + if (p < 0) port = 502; + else if (p > 0xFFFF) port = 502; + else port = p; + + printf ("Config::%s (%d)\n", __FUNCTION__, port); +}; + + +void Config::SetFilename (std::string f) { + filename = f; + + printf ("Config::%s (%s)\n", __FUNCTION__, f.c_str()); +}; diff --git a/mbsconfig.h b/mbsconfig.h new file mode 100644 index 0000000..b73d3f7 --- /dev/null +++ b/mbsconfig.h @@ -0,0 +1,47 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// mbsconfig.h is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +/* + modbusserver configuration + */ + +#ifndef _MBSCONFIG_H_ +#define _MBDCONFIG_H_ + +#include "config.h" +#include "modbus.h" +#include + +// maybe soon internationalisation +#define _(__s) __s + +class Config { + private: + std::string filename; + int port; + public: + Config (); + ~Config(); + + void SetPort (int p); + int GetPort () { return port; }; + + void SetFilename(std::string f); + std::string GetFilename() { return filename; }; + + int Save(); // RETURN 0 on error + int Read(); // first set filename RETURN 0 on errro +}; + + + +// +// declared in main.cc +// +extern Config config; +extern Modbus modbus; + +#endif diff --git a/modbus.cc b/modbus.cc new file mode 100644 index 0000000..78860e2 --- /dev/null +++ b/modbus.cc @@ -0,0 +1,891 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// modbus.cc is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#include +#include +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) +#else + #include /* close() */ +#endif + +#include "gui.h" +#include "modbus.h" +#include "mbsconfig.h" +#include "config.h" + +// +// C / C++ Wrapper +gpointer _ServerThread (gpointer data) { + modbus.ServerThread (); + return NULL; +}; + +// +// + +Modbus::Modbus () { + onchangecallback = NULL; + ModbusRegister r = {0, false, 0, false}; + port = 502; + g_mutex_init (&servermutex); + serverthread = NULL; + for (int i = 0; i < MODBUS_MAXCLIENTS; i++) { + clients[i] = NULL; + } + + for (int i = 0; i < MODBUS_IOBUFFER; i++) { + mbarray[FC1][i] = r; + mbarray[FC2][i] = r; + mbarray[FC3][i] = r; + mbarray[FC4][i] = r; + } +}; + +Modbus::~Modbus () { + for (int i = 0; i < MODBUS_MAXCLIENTS; i++) { + if (clients[i] != NULL) { + delete clients[i]; + clients[i] = NULL; + } + } +}; + + +int Modbus::isRunning() { + return tcpserver.IsListen(); +}; + + +int Modbus::Start(int serverport) { + + port = serverport; + serverthread = g_thread_new("network thread", _ServerThread, NULL); + + return 1; +}; + +void Modbus::Stop() { + g_mutex_lock(&servermutex); + for (int i = 0; i < MODBUS_MAXCLIENTS; i++) { + if (clients[i] != NULL) { + delete clients[i]; + clients[i] = NULL; + } + } + tcpserver.Close(); + g_mutex_unlock(&servermutex); +}; + + +void Modbus::CloseConnection(int slot) { + if (slot < 0 || slot >= MODBUS_MAXCLIENTS) return; // slot out of bound + if (clients[slot] != NULL) + delete clients[slot]; + clients[slot] = NULL; +}; + + +void Modbus::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() { + int keep_running = 1; + int ret; + struct timeval cycletimestamp = { 0 }; + float cycletime = 0.0; + string s; + + g_mutex_lock(&servermutex); + ret = tcpserver.Listen(port); + if (ret != 1) { + printf ("error:%s\n", strerror(errno)); + keep_running = 0; + } + g_mutex_unlock(&servermutex); + + // + // primary network loop + // + while (keep_running) { + TCP *tcp = NULL; + int slot; + int len; + + cycletime = get_cycletime (&cycletimestamp); + + // + // accept new connection? + g_mutex_lock(&servermutex); + if ((tcp = tcpserver.Accept()) != NULL) { + for (slot = 0; slot < MODBUS_MAXCLIENTS; slot++) { + if (clients[slot] == NULL) break; + } + + if (slot < MODBUS_MAXCLIENTS && tcp) { + char *msg; + clients[slot] = tcp; + msg = (char*) malloc (255); + snprintf (msg, 255, "new connection from %s\n", clients[slot]->GetRemoteAddr().c_str()); + gdk_threads_add_idle(cb_thread_network_text_add, msg); + } + else { + // no free slot, accept and close right away. + if (tcp) delete tcp; + } + } + + // + // + // loop through all clients + for (slot = 0; slot < MODBUS_MAXCLIENTS; slot++) { + if (clients[slot] != NULL) { + len = clients[slot]->ReadTimeout(inbuffer, MODBUS_IOBUFFER, 0); + if (len < 0) { + delete clients[slot]; + clients[slot] = NULL; + } + else if (len > 0) { + // + // incomming data + // + struct modbus_data *mbindata = (struct modbus_data*) malloc(sizeof (struct modbus_data)); + + if (len > 0x10000) { + printf ("%s:%d out of bound inbuffer? len:%d max %d\n", __FILE__, __LINE__, len, 0x10000); + exit (1); + } + + // + // reset and copy data to default values + strncpy (mbindata->hostname, clients[slot]->GetRemoteAddr().c_str(), TEXT_LEN); + memcpy (mbindata->buffer, inbuffer, len); + mbindata->bufferlen = len; + mbindata->fc = 0; + mbindata->regcnt = -1; + mbindata->regstart = -1; + mbindata->unitid = 0; + mbindata->length = 0; + mbindata->transactionid = 0; + mbindata->protoolid = 0; + + Decode(mbindata); + gdk_threads_add_idle(cb_thread_network_data_add, mbindata); + + // + // fill in outdata + // + + // + // reply data - outbuffer + // + struct modbus_data *mboutdata = (struct modbus_data*) malloc(sizeof (struct modbus_data)); + strncpy (mboutdata->hostname, clients[slot]->GetRemoteAddr().c_str(), TEXT_LEN); + memset (mboutdata->buffer, 0x0, 0x10000); + mboutdata->bufferlen = 0; + mboutdata->fc = 0; + mboutdata->regcnt = -1; + mboutdata->regstart = -1; + mboutdata->unitid = 0; + mboutdata->length = 0; + mboutdata->transactionid = 0; + mboutdata->protoolid = 0; + if (mbindata->fc >= 1 && mbindata->fc <= 4) { + if (WorkerAndEncodeRead(mbindata, mboutdata)) { + clients[slot]->Write(mboutdata->buffer, mboutdata->bufferlen); + } + else { + char *txt = (char*)malloc(255); + snprintf (txt, 255, "error on processing message\n"); + gdk_threads_add_idle(cb_thread_network_text_add, txt); + } + } + else if (mbindata->fc == 5 || mbindata->fc == 6) { + if (WorkerAndEncodeWriteSingle(mbindata, mboutdata)) { + clients[slot]->Write(mboutdata->buffer, mboutdata->bufferlen); + } + else { + char *txt = (char*)malloc(255); + snprintf (txt, 255, "error on processing message\n"); + gdk_threads_add_idle(cb_thread_network_text_add, txt); + } + } + else if (mbindata->fc == 15 || mbindata->fc == 16) { + if (WorkerAndEncodeWriteMulti(mbindata, mboutdata)) { + clients[slot]->Write(mboutdata->buffer, mboutdata->bufferlen); + } + else { + char *txt = (char*)malloc(255); + snprintf (txt, 255, "error on processing message\n"); + gdk_threads_add_idle(cb_thread_network_text_add, txt); + } + } + gdk_threads_add_idle(cb_thread_network_data_add, mboutdata); + } + } + } + // unlock servermutex + cycletime = get_cycletime (&cycletimestamp); + s = "modbus cycletime: " + std::to_string(1000.0 * cycletime) + "ms"; + gdk_threads_add_idle(cb_thread_status, &s); + + g_mutex_unlock(&servermutex); + + // wait some time (10ms) + usleep (10000); + + g_mutex_lock(&servermutex); + keep_running = tcpserver.IsListen(); + g_mutex_unlock(&servermutex); + } + s = "modbus server stoped"; + gdk_threads_add_idle(cb_thread_status, &s); // not thread save we wait 100ms to be sure + usleep (10000); +} + + +// return 0 on error +int Modbus::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 + uint16_t i16; + uint8_t i8; + int pos = 0; + int i; + int c; + 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; + if (mbin->fc == 1 || mbin->fc == 2) { + mbout->length += mbin->regcnt / 8; + if ((mbin->regcnt % 8) > 0) mbout->length += 1; + } + else if (mbin->fc == 4 || mbin->fc == 3) { + mbout->length += mbin->regcnt * 2; // 16bit = 2Bytes + } + if (mbout->length > 0x1000-6) { + printf ("outbuffer to small? mbout-length:%d\n", mbout->length); + return 0; + } + i16 = htons((uint16_t)mbout->length); + memcpy (mbout->buffer+pos, &i16, sizeof(i16)); + pos += sizeof (i16); + + // device id + 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); + + // length of data in bytes?????? + i8 = 0; + if (mbin->fc == 1 || mbin->fc == 2) { + i8 += mbin->regcnt / 8; + if ((mbin->regcnt % 8) > 0) i8 += 1; + } + else if (mbin->fc == 4 || mbin->fc == 3) { + i8 += mbin->regcnt * 2; // 16bit = 2Bytes + } + mbout->regcnt = i8; + memcpy (mbout->buffer+pos, &i8, sizeof(uint8_t)); + pos += sizeof (uint8_t); + + mbout->direction = 1; + + // + // fill in the buffer + if (mbin->fc == 1 || mbin->fc == 2) { + uint8_t u8 = 0; + for (c= 0, i = mbin->regstart; i < mbin->regstart + mbin->regcnt; i++, c++) { + if (c != 0 && c%8 == 0) { + memcpy (&mbout->buffer[pos], &u8, 1); + u8 = 0; + pos++; + } + + if (mbin->fc == 1) { + if (!mbarray[FC1][i].enabled) error = 1; // Error + mbarray[FC1][i].requested |= 1; + if (mbarray[FC1][i].value) u8 |= (1<<(c%8)); + } + else if (mbin->fc == 2) { + if (!mbarray[FC2][i].enabled) error = 1; // Error + mbarray[FC2][i].requested |= 1; + if (mbarray[FC2][i].value) u8 |= (1<<(c%8)); + } + else { + printf ("unknown fc code? fc:%d\n", mbin->fc); + error = 1; + } + } + memcpy (&mbout->buffer[pos], &u8, 1); + pos++; + } + else if (mbin->fc == 4 || mbin->fc == 3) { + for (i = mbin->regstart; i < mbin->regstart + mbin->regcnt; i++) { + if (mbin->fc == 3) { + if (!mbarray[FC3][i].enabled) error = 1; // return nothing + mbarray[FC3][i].requested |= 1; + i16 = htons(mbarray[FC3][i].value); + } + if (mbin->fc == 4) { + if (!mbarray[FC4][i].enabled) error = 1; // return nothing + mbarray[FC4][i].requested |= 1; + i16 = htons(mbarray[FC4][i].value); + } + memcpy (mbout->buffer+pos, &i16, sizeof(i16)); + pos += sizeof (i16); + } + } + + // + // inform the application about the modbus values change + if (onchangecallback != NULL && mbin->regcnt > 0) { + struct modbus_callback_data *mdata = (struct modbus_callback_data*) malloc(sizeof(struct modbus_callback_data)); + + mdata->r = (ModbusRegister *) malloc (sizeof (ModbusRegister) * mbin->regcnt); + int fc = mbin->fc; + if (fc == 5) fc = 1; + if (fc == 6) fc = 3; + if (fc == 15) fc = 1; + if (fc == 16) fc = 3; + + if (fc == 1) + memcpy (mdata->r, &mbarray[FC1][mbin->regstart], sizeof (ModbusRegister) * mbin->regcnt); + else if (fc == 2) + memcpy (mdata->r, &mbarray[FC2][mbin->regstart], sizeof (ModbusRegister) * mbin->regcnt); + else if (fc == 3) + memcpy (mdata->r, &mbarray[FC3][mbin->regstart], sizeof (ModbusRegister) * mbin->regcnt); + else if (fc == 4) + memcpy (mdata->r, &mbarray[FC4][mbin->regstart], sizeof (ModbusRegister) * mbin->regcnt); + + g_mutex_unlock(&servermutex); + mdata->fc = fc; + mdata->regstart = mbin->regstart; + mdata->count = mbin->regcnt; + gdk_threads_add_idle(onchangecallback, mdata); // not thread save we wait 100ms to be sure; + g_mutex_lock(&servermutex); + } + + mbout->bufferlen = pos; + + if (error) return 0; + else return 1; +} + + +int Modbus::WorkerAndEncodeWriteSingle(struct modbus_data *mbin, struct modbus_data *mbout) { + uint16_t i16; + int pos = 0; + 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->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) { + mbarray[FC1][mbin->regstart].requested |= 2; + if (mbarray[FC1][mbin->regstart].enabled) { + if (mbin->regcnt == 0xFF00) mbarray[FC1][mbin->regstart].value = 1; + 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; + + // + // inform the application about the modbus values change + if (onchangecallback != NULL) { + struct modbus_callback_data *mdata = (struct modbus_callback_data*) malloc(sizeof(struct modbus_callback_data)); + mdata->r = (ModbusRegister*) malloc(sizeof(ModbusRegister)); + + int fc = mbin->fc; + if (fc == 5) fc = 1; + if (fc == 6) fc = 3; + if (fc == 15) fc = 1; + if (fc == 16) fc = 3; + *mdata->r = mbarray[fc-1][mbin->regstart]; + mdata->fc = fc; + mdata->regstart = mbin->regstart; + mdata->count = 1; + g_mutex_unlock(&servermutex); + gdk_threads_add_idle(onchangecallback, mdata); // not thread save we wait 100ms to be sure; + g_mutex_lock(&servermutex); + } + + if (error) return 0; + else return 1; +} + + +int Modbus::WorkerAndEncodeWriteMulti(struct modbus_data *mbin, struct modbus_data *mbout) { + uint16_t i16; + int pos = 0; + int i; + int c; + 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->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 == 15) { + for (i = 0; mbin->regstart+i < 0x10000 && i < mbin->regcnt; i++) { + mbarray[FC1][mbin->regstart+i].requested |= 2; + if (mbarray[FC1][mbin->regstart+i].enabled) { + if (mbin->buffer[13+(i/8)] & (1<<(i%8))) mbarray[FC1][mbin->regstart+i].value = 1; + else mbarray[FC1][mbin->regstart+i].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 == 16) { + for (c = 13, i = 0; i < mbin->regcnt && mbin->regstart+i < 0x10000; i++, c += sizeof(uint16_t)) { + memcpy (&i16, mbin->buffer+c, sizeof(i16)); + mbarray[FC3][mbin->regstart+i].requested |= 2; + if (mbarray[FC3][mbin->regstart+i].enabled) + mbarray[FC3][mbin->regstart+i].value = ntohs(i16); + 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; + + // + // inform the application about the modbus values change + if (onchangecallback != NULL && mbin->regcnt > 0 && mbin->regstart > 0 && mbin->regstart < 0x10000) { + struct modbus_callback_data *mdata = (struct modbus_callback_data*) malloc(sizeof(struct modbus_callback_data)); + mdata->r = (ModbusRegister*) malloc (sizeof (ModbusRegister) * mbin->regcnt);; + + int fc = mbin->fc; + if (fc == 5) fc = 1; + if (fc == 6) fc = 3; + if (fc == 15) fc = 1; + if (fc == 16) fc = 3; + + if (mbin->regstart+mbin->regcnt > 0xFFFF) { + printf ("%s:%d ERROR: mbin->regstart+mbin->regcnt > 0xFFFF limit:%x \n", __FILE__, __LINE__, mbin->regstart+mbin->regcnt); + exit (1); + } + + if (fc == 1) + memcpy (mdata->r, &mbarray[FC1][mbin->regstart], sizeof (ModbusRegister) * mbin->regcnt); + else if (fc == 2) + memcpy (mdata->r, &mbarray[FC2][mbin->regstart], sizeof (ModbusRegister) * mbin->regcnt); + else if (fc == 3) + memcpy (mdata->r, &mbarray[FC3][mbin->regstart], sizeof (ModbusRegister) * mbin->regcnt); + else if (fc == 4) + memcpy (mdata->r, &mbarray[FC4][mbin->regstart], sizeof (ModbusRegister) * mbin->regcnt); + + g_mutex_unlock(&servermutex); + mdata->fc = fc; + mdata->regstart = mbin->regstart; + mdata->count = mbin->regcnt; + gdk_threads_add_idle(onchangecallback, mdata); // not thread save we wait 100ms to be sure; + g_mutex_lock(&servermutex); + } + + if (error) return 0; + else return 1; +} + + + +void Modbus::Decode(struct modbus_data *mbdata) { + // + uint16_t i16; + uint8_t i8; + int pos = 0; + + mbdata->fc = 0; + mbdata->regcnt = -1; + mbdata->regstart = -1; + mbdata->unitid = 0; + mbdata->length = 0; + mbdata->direction = 0; + mbdata->transactionid = 0; + mbdata->protoolid = 0; + mbdata->bytecnt = 0; + + if (mbdata->bufferlen < 8) return; + + // Transaction + memcpy (&i16, mbdata->buffer+pos, sizeof(i16)); + pos += sizeof(i16); + mbdata->transactionid = ntohs(i16); + + // protocol + memcpy (&i16, mbdata->buffer+pos, sizeof(i16)); + pos += sizeof(i16); + mbdata->protoolid = ntohs(i16); + + // length + memcpy (&i16, mbdata->buffer+pos, sizeof(i16)); + pos += sizeof(i16); + mbdata->length = ntohs(i16); + + // unitid + memcpy (&i8, mbdata->buffer+pos, sizeof(i8)); + pos += sizeof(i8); + mbdata->unitid = i8; + + // function code + memcpy (&i8, mbdata->buffer+pos, sizeof(i8)); + pos += sizeof(i8); + mbdata->fc = i8; + + // register + memcpy (&i16, mbdata->buffer+pos, sizeof(i16)); + pos += sizeof(i16); + mbdata->regstart = ntohs(i16); + + // number of registers + memcpy (&i16, mbdata->buffer+pos, sizeof(i16)); + pos += sizeof(i16); + mbdata->regcnt = ntohs(i16); + + // unitid + memcpy (&i8, mbdata->buffer+pos, sizeof(i8)); + pos += sizeof(i8); + mbdata->bytecnt = i8; +}; + +int Modbus::GetRegister(int fc, int regnum, ModbusRegister *r) { + r->enabled = false; + r->requested = 0; + r->updated = false; + r->value = 0; + + if (regnum < 0 || regnum >= 0x10000) return -1; + + g_mutex_lock(&servermutex); + + if (fc == 1) *r = mbarray[FC1][regnum]; + else if (fc == 2) *r = mbarray[FC2][regnum]; + else if (fc == 3) *r = mbarray[FC3][regnum]; + else if (fc == 4) *r = mbarray[FC4][regnum]; + + g_mutex_unlock(&servermutex); + + return 0; +}; + + +int Modbus::GetRegisters(int fc, int regnum, int count, ModbusRegister *r) { + r->enabled = false; + r->requested = 0; + r->updated = false; + r->value = 0; + + if (regnum < 0 || regnum >= 0x10000) return -1; + + g_mutex_lock(&servermutex); + + if (fc == 1) *r = mbarray[FC1][regnum]; + else if (fc == 2) *r = mbarray[FC2][regnum]; + else if (fc == 3) *r = mbarray[FC3][regnum]; + else if (fc == 4) *r = mbarray[FC4][regnum]; + + g_mutex_unlock(&servermutex); + + return 0; +}; + + +int Modbus::SetRegister(int fc, int regnum, ModbusRegister *r) { + if (regnum < 0 || regnum >= 0x10000) return -1; + + g_mutex_lock(&servermutex); + + if (fc == 1) mbarray[FC1][regnum] = *r; + else if (fc == 2) mbarray[FC2][regnum] = *r; + else if (fc == 3) mbarray[FC3][regnum] = *r; + else if (fc == 4) mbarray[FC4][regnum] = *r; + + g_mutex_unlock(&servermutex); + + return 0; +} + + +int Modbus::SetRegisters(int fc, int regnum, int count, ModbusRegister *r) { + int reg = regnum; + int regend = regnum+count; + + g_mutex_lock(&servermutex); + for (reg = regnum; reg < regend; regnum++) { + if (reg < 0 && reg >= 0x10000) { + g_mutex_unlock(&servermutex); + return -1; + } + + if (fc == 1) mbarray[FC1][reg] = *r; + else if (fc == 2) mbarray[FC2][reg] = *r; + else if (fc == 3) mbarray[FC3][reg] = *r; + else if (fc == 4) mbarray[FC4][reg] = *r; + r++; + } + g_mutex_unlock(&servermutex); + + return 0; +} + + +void Modbus::Enable(int fc, int regstart, int count, int onoff) { + int i; + if (fc < 1 || fc > 4) return; + if (regstart < 0) return; + fc--; + + g_mutex_lock(&servermutex); + for (i = 0; i < count && regstart < MODBUS_IOBUFFER; i++, regstart++) { + mbarray[fc][regstart].enabled = onoff; + } + g_mutex_unlock(&servermutex); +}; + + +void Modbus::RequestsClear() { + int fc, reg; + + g_mutex_lock(&servermutex); + for (fc = 0; fc < 4; fc++) for (reg = 0; reg < MODBUS_IOBUFFER; reg++) { + mbarray[fc][reg].requested = 0; + } + g_mutex_unlock(&servermutex); +}; + + +void Modbus::EnableAll(int onoff) { + int fc, reg; + + g_mutex_lock(&servermutex); + for (fc = 0; fc < 4; fc++) for (reg = 0; reg < MODBUS_IOBUFFER; reg++) { + mbarray[fc][reg].enabled = onoff; + } + g_mutex_unlock(&servermutex); +}; + + +void Modbus::SetRegValue(int fc, int regstart, int count, uint16_t *values) { + int reg; + + if (fc <= 0 || fc > 4) { + printf ("%s:%d fc(%d) is set out of range\n", __FILE__, __LINE__, fc); + return; + } + + if (regstart < 0) { + printf ("%s:%d regstart(%d) is set out of range\n", __FILE__, __LINE__, regstart); + return; + } + + if (regstart + count >= MODBUS_IOBUFFER || regstart < 0) { + printf ("%s:%d regstart(%d) or count(%d) are set out of range\n", __FILE__, __LINE__, regstart, count); + return; + } + + g_mutex_lock(&servermutex); + for (int i = 0, reg = regstart; i < count && reg < 0x10000; i++, reg++) { + mbarray[fc-1][reg].value = values[i]; + } + + // + // inform the application about the modbus values change + if (onchangecallback != NULL && count > 0 && regstart >= 0 && regstart < 0x10000) { + struct modbus_callback_data *mdata = (struct modbus_callback_data*) malloc(sizeof(struct modbus_callback_data)); + mdata->r = (ModbusRegister*) malloc (sizeof (ModbusRegister) * count); + + if (fc == 5) fc = 1; + if (fc == 6) fc = 3; + if (fc == 15) fc = 1; + if (fc == 16) fc = 3; + + if (regstart+count > 0xFFFF) { + printf ("%s:%d ERROR: regstart+count > 0xFFFF limit:%x \n", __FILE__, __LINE__, regstart+count); + exit (1); + } + + if (fc == 1) + memcpy (mdata->r, &mbarray[FC1][regstart], sizeof (ModbusRegister) * count); + else if (fc == 2) + memcpy (mdata->r, &mbarray[FC2][regstart], sizeof (ModbusRegister) * count); + else if (fc == 3) + memcpy (mdata->r, &mbarray[FC3][regstart], sizeof (ModbusRegister) * count); + else if (fc == 4) + memcpy (mdata->r, &mbarray[FC4][regstart], sizeof (ModbusRegister) * count); + + g_mutex_unlock(&servermutex); + mdata->fc = fc; + mdata->regstart = regstart; + mdata->count = count; + gdk_threads_add_idle(onchangecallback, mdata); // not thread save we wait 100ms to be sure; + 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); +}; + + diff --git a/modbus.h b/modbus.h new file mode 100644 index 0000000..23071ff --- /dev/null +++ b/modbus.h @@ -0,0 +1,113 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// modbus.h is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#ifndef _MODBUS_H_ +#define _MODBUS_H_ + + +#include +#include +#include + +#include "tcp.h" +#include + + +enum { + FC1 = 0, + FC2, + FC3, + FC4, + FCCOUNT +}; + +#define TEXT_LEN 512 +#define MODBUS_MAXCLIENTS 5 +#define MODBUS_IOBUFFER 0x10000 + +struct modbus_data { + char hostname[TEXT_LEN]; + char buffer[0x10000]; + size_t bufferlen; + int transactionid; + int protoolid; + int length; + int unitid; + int direction; + int fc; + int regstart; + int regcnt; + int bytecnt; +}; + + +// return the cycletime in us +float get_cycletime(struct timeval *t); + +struct { + uint16_t value; + bool enabled; + int requested; // 1 .. Read 2 .. Write + bool updated; +} typedef ModbusRegister; + + +struct modbus_callback_data { + int fc; + int regstart; + int count; + ModbusRegister *r; +}; + + +class Modbus { +private: + TCP tcpserver; + int port; + TCP *clients[MODBUS_MAXCLIENTS]; + GThread *serverthread; + GMutex servermutex; + gboolean (*onchangecallback)(gpointer data); + ModbusRegister mbarray[FCCOUNT][MODBUS_IOBUFFER]; // for all 4 Registertypes .. + + char inbuffer[MODBUS_IOBUFFER]; + char outbuffer[MODBUS_IOBUFFER]; + + void Decode(struct modbus_data *mbdata); + // return 0 on error, 1 on no error + 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(); + + void ServerThread(); + void SetCallback (gboolean (*callback_func)(gpointer data)); + int Start(int serverport); + void Stop(); + + int GetRegister(int fc, int regnum, ModbusRegister *r); + int GetRegisters(int fc, int regnum, int count, ModbusRegister *r); + + 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 Enable(int fc, int regstart, int count, int onoff); + void EnableAll(int onoff); + void RequestsClear(); + void SetRegValue(int fc, int regstart, int count, uint16_t *values); + + int isRunning(); +}; + +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) +#define usleep(_us_) Sleep(_us_/1000) +#endif + +#endif diff --git a/tcp.cc b/tcp.cc new file mode 100644 index 0000000..f72a4e0 --- /dev/null +++ b/tcp.cc @@ -0,0 +1,521 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// tcp.cc is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "tcp.h" +#include +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) +#else + #include /* close() */ +#endif +#include +#include /* memset() */ +#include + +using namespace std; + +int tcpinit; +// +// convert host and port to sockaddr_in6 +// +int dns_filladdr (string host, string port, int ai_family, struct sockaddr_storage *sAddr) { + struct addrinfo hints, *res; + int err; + + bzero (&hints, sizeof (struct addrinfo)); + hints.ai_family = ai_family; + hints.ai_socktype = SOCK_DGRAM; + + if ((err = getaddrinfo (host.c_str(), port.c_str(), &hints, &res)) < 0) { + fprintf (stdout, "dns_filladdr (getaddrinfo):%s\n", gai_strerror (err)); + return -1; + } + + memcpy (sAddr, res->ai_addr, res->ai_addrlen); + freeaddrinfo (res); + + return 1; +}; + + +// +// convert int to char* +// +char* itoa(char* buffer, int number, int size) { + snprintf (buffer, size, "%d", number); + return buffer; +} + + +TCP::~TCP() { + Close(); +} + +TCP::TCP() { + if (tcpinit == 0) { +#ifdef BUILD_WINDOWS + + WORD wVersionRequested; + WSADATA wsaData; + int err; + + /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + /* Tell the user that we could not find a usable */ + /* Winsock DLL. */ + printf("WSAStartup failed with error: %d\n", err); + return; + } +#endif + tcpinit = 1; + } + sock = 0; + writecnt = 0; + readcnt = 0; + islisten = 0; +}; + +TCP::TCP(int s) { + TCP(); + sock = s; +// memset (&localaddr, 0x0, sizeof(localaddr)); +// memset (&remoteaddr, 0x0, sizeof(remoteaddr)); + writecnt = 0; + readcnt = 0; + islisten = 0; +}; + +TCP::TCP(string h, string p) { + TCP(); + Connect (h,p); +}; + + +int TCP::Listen(int port) { + char buffer[NET_BUFFERSIZE]; + int err, i; + struct addrinfo hints, *res, *rp; + + if (sock > 0) Close(); +// FIXME: solution to get both (IPV4 and IPV6) to work at the same time? + bzero (&hints, sizeof (struct addrinfo)); + hints.ai_flags = AI_PASSIVE; +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) + hints.ai_family = AF_INET; +#else + hints.ai_family = AF_INET6; +#endif + hints.ai_socktype = SOCK_STREAM; + if ((err = getaddrinfo (NULL, itoa(buffer, port, 32), &hints, &res)) != 0) { + return 0; + } + + // + // walk through all results until we could connect + // + for (rp = res; rp != NULL; rp = rp->ai_next) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sock == -1) continue; + + i = 1; + if ((err = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char*)&i, sizeof (i))) != 0) { + printf ("%s:%d setsockopt error\n", __FILE__, __LINE__); + } + + if ((err = bind (sock, rp->ai_addr, rp->ai_addrlen)) < 0) { + close (sock); + sock = -1; + continue; + } + if (listen (sock, 8) < 0) { // maximum of 8 connections at the time + close (sock); + sock = -1; + continue; + } + break; + } + + freeaddrinfo (res); + + if (rp == NULL) { + sock = -1; + return 0; + } + + islisten = 1; + + return 1; +}; + + +TCP* TCP::Accept() { + fd_set rfds; + struct timeval tv; + int retval, err, i; + 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; + + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + tv.tv_sec = 0; + tv.tv_usec = 1000; + + retval = select (sock+1, &rfds, NULL, NULL, &tv); + if (retval == -1 && errno == EINTR) { + retval = 0; + } + else if (retval == -1) { + return NULL; + } + else if (retval) { +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) + newsock = accept (sock, (struct sockaddr *) &cliaddr, (int*) &cliaddr_len); +#else + newsock = accept (sock, (struct sockaddr *) &cliaddr, &cliaddr_len); +#endif + if (newsock < 0) return NULL; + tcp = new TCP(); + tcp->SetSocket(newsock, &cliaddr, cliaddr_len); + + return tcp; + } + return NULL; +} + + +int TCP::Connect(string h, string p) { + int i = 0; + + remote_host = h; + remote_port = p; + + return Connect(); +}; + + +int TCP::Connect(string hostport, int defaultport) { + char buffer[32]; + int pos = hostport.rfind(':', hostport.length()); + if (pos == -1) { + remote_port = itoa (buffer, defaultport, 32); + remote_host = hostport; + } + else { + remote_port = hostport.substr (pos+1, hostport.length() - pos); + remote_host = hostport.substr (0, pos); + } + + return Connect(); +}; + + +int TCP::Connect() { + int err, s; + struct addrinfo hints, *res, *rp; + struct timeval timeout; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6 + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + + err = getaddrinfo(remote_host.c_str(), remote_port.c_str(), &hints, &res); + if (err != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); + return 0; + } + + // + // walk through all results until we could connect + // + for (rp = res; rp != NULL; rp = rp->ai_next) { + s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + // setup timeout to max 2 secs + timeout.tv_sec = 2; + timeout.tv_usec = 0; + setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); + + if (s == -1) continue; + if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) { + sock = s; + break; + } + close(s); + } + + freeaddrinfo(res); + + // + // connect not successfull + // + if (rp == NULL) return 0; + + writecnt = 0; + readcnt = 0; + + return 1; +}; + + +long int TCP::ReadPop (char *buffer, long int pktlen, long int bufferlen) { + memmove (buffer, buffer+pktlen, bufferlen-pktlen); + return bufferlen-pktlen; +} + + +long int TCP::Read(char *buffer, long int len) { + long int len_ = len; + + if (sock <= 0) return -2; + len_ = recv (sock, buffer, len, 0); + + if (len_ < 0 && errno == EAGAIN) len_ = 0; + else if (len_ < 0 && errno != EAGAIN) { +#ifdef BUILD_WINDOWS + printf ("%s ERROR:%s\n", __FUNCTION__, strerror (errno)); +#else + fprintf (stderr, "%s ERROR:%s\n", __FUNCTION__, strerror (errno)); +#endif + len_ = -2; + } + else if (len_ == 0) len_ = -1; + if (len_ < 0) Close(); + + readcnt += len_; + + return len_; +}; + + +long int TCP::ReadTimeout(char *buffer, long int len, int timeout) { + int data = IsData (timeout); + + if (data > 0) return Read (buffer, len); + else if (data < 0) return -1; + else return 0; +}; + + +////////////////////////////////////////////////////////// +// +// write data, generate no signal if some error occures +long int TCP::Write(char *buffer, long int len) { + int i; + int to = NET_MAX_RETRY; + + if (sock <= 0) return -1; + + do { + i = send (sock, buffer, len, MSG_NOSIGNAL); + } while (i == -1 && (to--) > 0 && errno == EINTR); + + if (i < 0) Close (); + writecnt += i; + + return i; +}; + + +void TCP::Close() { +#ifdef BUILD_WINDOWS + closesocket(sock); +#else + if (sock > 0) close (sock); +#endif + sock = -1; + islisten = false; +}; + + +void TCP::SetSocket(SOCKET s, struct sockaddr_storage *saddr, socklen_t saddrlen) { + char host[NET_HOSTLEN]; + char port[NET_PORTLEN]; + int err; + + if (sock > 0) Close(); + if (s > 0) { + sock = s; + + if (saddr != NULL) { + memcpy (&peeraddr, saddr, sizeof(peeraddr)); + if ((err = getnameinfo ((struct sockaddr*) saddr, saddrlen, host, NET_HOSTLEN, + port, NET_PORTLEN, NI_NUMERICHOST | NI_NUMERICSERV)) == 0) { + remote_host = host; + remote_port = port; + } else { + printf ("error: getnameinfo"); +/* if (err == EAI_AGAIN) printf ("EAI_AGAIN\n"); + if (err == EAI_BADFLAGS) printf ("EAI_BADFLAGS\n"); + if (err == EAI_FAIL) printf ("EAI_FAIL\n"); + if (err == EAI_FAMILY) printf ("EAI_FAMILY\n"); + if (err == EAI_MEMORY) printf ("EAI_MEMORY\n"); + if (err == EAI_NONAME) printf ("EAI_NONAME\n"); + if (err == EAI_OVERFLOW) printf ("EAI_OVERFLOW\n"); + if (err == EAI_SYSTEM) printf ("EAI_SYSTEM\n"); */ // windows seem to have different error codes + } + } + } + else sock = -1; +}; + + +int TCP::IsConnected() { + return (sock > 0); +}; + + +int TCP::IsData(int timeout) { + fd_set sockset; + struct timeval tval; + + if (sock <= 0) return -1; + + FD_ZERO (&sockset); + FD_SET (sock, &sockset); + tval.tv_sec = timeout / 1000; + tval.tv_usec = (timeout % 1000); + if ((select (sock + 1, &sockset, NULL, NULL, &tval)) != -1) { + if (FD_ISSET (sock, &sockset)) return 1; + return 0; + } + else { + if (errno == EBADF) sock = -1; + } + return 0; +}; + + +int TCP::WebGetFile (string url, char *buffer, int maxsize) { + char outdata[NET_BUFFERSIZE]; + char indata[NET_BUFFERSIZE]; + char host[NET_HOSTLEN]; + char port[NET_PORTLEN]; + int breakup, len, head, i, buffpos = 0; + + if (!IsConnected()) { + strncpy (host, WebGetURLHost (url).c_str(), NET_HOSTLEN); + strncpy (port, WebGetURLPort (url).c_str(), NET_PORTLEN); + Connect (host, port); + } + + if (!IsConnected()) return -1; + + // send http request + snprintf (outdata, NET_BUFFERSIZE, "GET %s HTTP/1.0\nUser-Agent: unknown \nHost: %s\n\n", WebGetURLFile (url).c_str(), host); + Write (outdata, strlen (outdata)); + + // wait for data start + i = breakup = 0; + head = 1; + while (!breakup && head && (len = ReadTimeout (indata, NET_BUFFERSIZE-1, 2000)) >= 0) { + indata[len] = 0; + while (i < len -4 && head && !breakup) { + if (indata[i] == 0x0d && indata [i+1] == 0x0a && indata [i+2] == 0x0d && indata [i+3] == 0x0a) { + head = 0; + i = i + 4; + buffpos = len-i; + if (buffpos > maxsize) buffpos = maxsize; + memcpy (buffer, indata+i, buffpos); + } + + else if (strncmp (indata+i, "Content-Length:", 15) == 0) { + i = i + 16; + } + + else if (strncmp (indata+i, "403 ", 4) == 0) breakup = 1; + else i++; + } + } + + // read data + while (!breakup && (len = ReadTimeout (indata, NET_BUFFERSIZE-1, 2000)) >= 0) { + i = len; + if (i > maxsize-buffpos) i = maxsize-buffpos; + if (i > 0) { + memcpy (buffer+buffpos, indata, i); + buffpos += i; + } + } + + return buffpos; +}; + +const string TCP::WebGetURLHost (string url) { + string result; + int posSServPort = 7; // begin server:port + int posEServPort = 0; // end Server:Port + int posPort = -1; // port Position + + posEServPort = url.find("/", 7); + result = url.substr (posSServPort, posEServPort-posSServPort); + posPort = result.find(":"); + if (posPort > 0) + result = url.substr (posSServPort, posPort); + + return result; +}; + +const string TCP::WebGetURLPort (string url) { + string result; + int posSServPort = 7; // begin server:port + int posEServPort = 0; // end Server:Port + int posPort = -1; // port Position + + posEServPort = url.find("/", 7); + result = url.substr (posSServPort, posEServPort-posSServPort); + posPort = result.find(":"); + if (posPort > 0) + result = result.substr (posPort+1); + else + result = "80"; + + return result; +}; + +const string TCP::WebGetURLFile (string url) { + string result; + int posEServPort = 0; // end Server:Port + + posEServPort = url.find("/", 7); + result = url.substr (posEServPort); + + return result; +}; + + +const string TCP::GetRemoteAddr() { + string ret = ""; + socklen_t addrlen = sizeof (peeraddr); + char host[256] = ""; + char port[256] = ""; + + if (getnameinfo ((struct sockaddr*)&peeraddr, addrlen, host, 255, port, 255, NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + printf ("getnameinfo error: %s\n", strerror(errno)); + } + + ret = (string)host + ":" + (string)port; + + return ret; +}; + + +const string TCP::GetLocalAddr() { + string ret; + + return ret; +}; + + diff --git a/tcp.h b/tcp.h new file mode 100644 index 0000000..23ca6b9 --- /dev/null +++ b/tcp.h @@ -0,0 +1,123 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// tcp.h is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#ifndef _TCP_H_ +#define _TCP_H_ + +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) + +#define WIN32_LEAN_AND_MEAN + +#define _NTDDI_VERSION_FROM_WIN32_WINNT2(ver) ver##0000 +#define _NTDDI_VERSION_FROM_WIN32_WINNT(ver) _NTDDI_VERSION_FROM_WIN32_WINNT2(ver) + +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x501 +#endif +#ifndef NTDDI_VERSION +# define NTDDI_VERSION _NTDDI_VERSION_FROM_WIN32_WINNT(_WIN32_WINNT) +#endif + +// #include +#include +#include +#include +#include + +#define socklen_t size_t + +#ifndef bzero +#define bzero(__z__, __x__) memset (__z__, 0x0, __x__) +#endif + +#ifndef MSG_NOSIGNAL +# define MSG_NOSIGNAL 0 +#endif + +#else + +#include +#include +#include +#include +#include +#include + +#endif + +#include + +using namespace std; + +#define SOCKET int + +#define NET_HOSTLEN 256 +#define NET_PORTLEN 6 +#define NET_BUFFERSIZE 1024 +#define NET_MAX_RETRY 5 // retry to send data EINTR +#define NET_MAX_TIMEOUT 30000 // timeout in ms + + +/************************************************************************ + * + * global functions needed for networking + * + */ +int dns_filladdr (string host, string port, int ai_family, + struct sockaddr_storage *sAddr); +char *itoa(char* buffer, int number, int size); +void UDPTCPNetwork_Startup(); +extern int UDPTCPNetwork_init; + + +/************************************************************************ + * tcp related functions + */ +class TCP { +private: + SOCKET sock; + struct sockaddr_storage peeraddr; + string remote_host; + string remote_port; + int readcnt; + int writecnt; + int islisten; +public: + TCP(); + TCP(SOCKET s); + TCP(string h, string p); + TCP(string hostport, int defaultport); + ~TCP(); + + int Connect(); + int Connect(string h, string p); + int Connect(string hostport, int defaultport); + long int ReadPop (char *buffer, long int pktlen, long int bufferlen); + long int ReadTimeout(char *buffer, long int len, int timeout); + long int Read(char *buffer, long int len); + long int Write(char *buffer, long int len); + void Close(); + int IsConnected(); + int IsData(int timeout); // timeout in ms; + int IsListen() { return islisten; }; + + int Listen(int port); + TCP* Accept(); + + SOCKET GetSocket() { return sock; }; + void SetSocket(SOCKET s, struct sockaddr_storage *saddr, socklen_t saddrlen); + + const string GetRemoteAddr(); + const string GetLocalAddr(); + + const string WebGetURLHost (string url); + const string WebGetURLPort (string url); + const string WebGetURLFile (string url); + int WebGetFile (string url, char *buffer, int maxsize); +}; + +#endif + diff --git a/test-fc15.cc b/test-fc15.cc new file mode 100644 index 0000000..89ad133 --- /dev/null +++ b/test-fc15.cc @@ -0,0 +1,110 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// test-fc15.cc is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#define SIZE_BUFFER 4096 +int main (int argc, char **argv) { + unsigned char data[] = { 0x00,0x70,0x00,0x00 , 0x00,0x0B,0x01,0x10 , + 0x00,0x04,0x00,0x02 , 0x04,0xC0,0x02,0x7B , + 0xA5 }; + + char buffer[SIZE_BUFFER]; + int pos; + TCP tcp; + int port; + int i,c; + int regstart; + int regcnt; + uint8_t ui8; + uint16_t ui16; + float f; + + memset (buffer, 0x00, SIZE_BUFFER); + + if (argc < 3) { + printf("test-fc15 server port register values.[01010101]..\n"); + printf("test-fc15 localhost 502 16 1111100000001010000\n"); + return 0; + } + + port = atoi (argv[2]); + printf ("%d connecting to: %s:%d\n", argc, argv[1], port); + if (tcp.Connect(argv[1], port) != 1) { + printf ("could not connect.\n"); + return -1; + } + + if (argc == 3) { + tcp.Write((char*)data, sizeof(data)); + } + else if (argc > 4) { + regstart = atoi (argv[3]); + regcnt = strlen(argv[4]); + + printf ("writing %d floats, offset %d\n", regcnt, regstart); + + // construct modbus protocol + pos = 0; + + // transact id + ui16 = 0; + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + // proitocol id + ui16 = 0; + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + // lenght + ui16 = htons(4 + regcnt/8); + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + // device id + ui8 = 1; + memcpy (buffer+pos, &ui8, sizeof(ui8)); + pos += sizeof(ui8); + + // function code + ui8 = 15; + memcpy (buffer+pos, &ui8, sizeof(ui8)); + pos += sizeof(ui8); + + // starting address + ui16 = htons((uint16_t)regstart); + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + // number of registers + ui16 = htons((uint16_t)regcnt); + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + // number of bytes + ui8 = regcnt*4; + memcpy (buffer+pos, &ui8, sizeof(ui8)); + pos += sizeof(ui8); + + buffer[pos] = 0; + for (i = 0; i < regcnt; i++) { + if (i > 0 && i % 8 == 0) { + pos++; + buffer[pos] = 0; + } + if (argv[4][i] == '1') buffer[pos] |= (1 << i%8); + } + pos++; + + tcp.Write((char*)buffer, pos); + } + tcp.Close(); + + return 0; +}; diff --git a/test-fc16.cc b/test-fc16.cc new file mode 100644 index 0000000..7f93ef2 --- /dev/null +++ b/test-fc16.cc @@ -0,0 +1,115 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// test-fc16.cc is part of TestModbus-Server. +// +///////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#define SIZE_BUFFER 4096 +int main (int argc, char **argv) { + unsigned char data[] = { 0x00,0x70,0x00,0x00 , 0x00,0x0B,0x01,0x10 , + 0x00,0x04,0x00,0x02 , 0x04,0xC0,0x02,0x7B , + 0xA5 }; + + char buffer[SIZE_BUFFER]; + int pos; + TCP tcp; + int port; + int i,c; + int regstart; + int regcnt; + uint8_t ui8; + uint16_t ui16; + float f; + + memset (buffer, 0x00, SIZE_BUFFER); + + if (argc < 3) { + printf("test-fc16 server port register values...\n"); + printf("test-fc16 localhost 502 16 25.0 0.54 1000.5\n"); + return 0; + } + + port = atoi (argv[2]); + printf ("%d connecting to: %s:%d\n", argc, argv[1], port); + if (tcp.Connect(argv[1], port) != 1) { + printf ("could not connect.\n"); + return -1; + } + + if (argc == 3) { + tcp.Write((char*)data, sizeof(data)); + } + else if (argc > 4) { + regstart = atoi (argv[3]); + regcnt = argc-4; + + printf ("writing %d floats, offset %d\n", regcnt, regstart); + + // construct modbus protocol + pos = 0; + + // transact id + ui16 = 0; + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + // proitocol id + ui16 = 0; + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + // lenght + ui16 = htons(3 + regcnt*4); + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + // device id + ui8 = 1; + memcpy (buffer+pos, &ui8, sizeof(ui8)); + pos += sizeof(ui8); + + // function code + ui8 = 16; + memcpy (buffer+pos, &ui8, sizeof(ui8)); + pos += sizeof(ui8); + + // starting address + ui16 = htons((uint16_t)regstart); + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + // number of registers + ui16 = htons((uint16_t)regcnt*2); + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + // number of bytes + ui8 = regcnt*4; + memcpy (buffer+pos, &ui8, sizeof(ui8)); + pos += sizeof(ui8); + + for (i = 0; i < regcnt; i++) { + f = (float) atof(argv[i+4]); + printf ("i: %d string:%s f:%f\n", i, argv[i+4], f); + + memcpy (&ui16, &f, sizeof(ui16)); + ui16 = htons(ui16); + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + + memcpy (&ui16, ((char*)&f +2), sizeof(ui16)); + ui16 = htons(ui16); + memcpy (buffer+pos, &ui16, sizeof(ui16)); + pos += sizeof(ui16); + } + + tcp.Write((char*)buffer, pos); + } + tcp.Close(); + + return 0; +}; diff --git a/testmodbus-server.png b/testmodbus-server.png new file mode 100644 index 0000000..7d1bb00 Binary files /dev/null and b/testmodbus-server.png differ diff --git a/testmodbus-server.ui b/testmodbus-server.ui new file mode 100644 index 0000000..8010628 --- /dev/null +++ b/testmodbus-server.ui @@ -0,0 +1,1118 @@ + + + + + + + + + + + + + + + + WORD + + + DWORD + + + DWORD_SWAP + + + FLOAT + + + FLOAT_SWAP + + + BOOL + + + + + False + Add Variable or Flag + dialog + + + False + vertical + 2 + + + False + end + + + gtk-add + True + True + True + True + True + + + + True + True + 0 + + + + + gtk-close + True + True + True + True + True + + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + vertical + 10 + + + True + False + 9 + True + + + Coils +[Read/Write] + True + True + False + True + True + adddlg_FC2 + + + False + True + 0 + + + + + Digital In +[Read] + True + True + False + True + True + adddlg_FC1 + + + False + True + 1 + + + + + Holding Reg. +[Read/Write] + True + True + False + True + True + adddlg_FC1 + + + False + True + 2 + + + + + Input Reg. +[Read] + True + True + False + True + True + adddlg_FC1 + + + False + True + 3 + + + + + False + True + 0 + + + + + + True + False + 4 + 4 + 4 + 4 + 4 + 4 + + + True + False + end + Name : + + + 0 + 0 + + + + + True + True + + + 1 + 0 + + + + + True + False + end + Type : + + + 0 + 2 + + + + + True + False + end + Flag / Register : + + + 0 + 1 + + + + + True + False + end + Simmulation : + + + 0 + 3 + + + + + True + True + + + 1 + 1 + + + + + True + False + liststore1 + 0 + on + True + 0 + + + False + + + + + 1 + 2 + + + + + True + False + end + Value : + + + 0 + 4 + + + + + True + False + + + True + True + + + False + True + 0 + + + + + Change + True + True + False + True + + + False + True + 1 + + + + + 1 + 4 + + + + + True + False + 0 + True + + NONE + SAW;t=60;min=0;max=1000 + SIN;t=60;min=-100;max=100; + PULSE;ton=10;toff=60 + + + + True + + + + + 1 + 3 + + + + + False + False + 4 + 1 + + + + + True + True + 1 + + + + + + + False + 600 + 400 + testmodbus-server.png + True + + + + + + True + False + vertical + + + True + False + + + True + False + _Datei + True + + + True + False + + + gtk-new + True + False + True + True + + + + + + gtk-open + True + False + True + True + + + + + + gtk-save + True + False + True + True + + + + + + gtk-save-as + True + False + True + True + + + + + + True + False + + + + + gtk-quit + True + False + True + True + + + + + + + + + + True + False + _Hilfe + True + + + True + False + + + gtk-about + True + False + True + True + + + + + + + + + + False + True + 0 + + + + + True + True + True + + + 200 + True + False + vertical + + + True + False + Netzwerk + + + False + True + 0 + + + + + 200 + True + True + in + 200 + + + True + True + False + + + + + True + True + 1 + + + + + False + False + + + + + True + True + + + True + False + vertical + + + True + True + in + + + True + True + + + + + + + + True + True + 0 + + + + + True + False + 3 + + + Req. Clear + True + True + True + + + + False + False + 0 + + + + + Enable All + True + True + True + + + + False + False + 1 + + + + + Enable Values + True + True + True + + + + False + False + 2 + + + + + Disable All + True + True + True + + + + False + True + 4 + + + + + False + True + 4 + 1 + + + + + 1 + + + + + True + False + Registers + + + False + + + + + True + False + vertical + + + True + True + in + + + True + True + + + + + + + + True + True + 1 + + + + + True + False + 5 + + + Add + True + True + True + + + + False + False + 0 + + + + + Edit + True + True + True + + + + False + False + 1 + + + + + Delete + True + True + True + + + + False + False + 2 + + + + + False + True + 5 + end + 2 + + + + + 1 + + + + + True + False + Values + + + 1 + False + + + + + + True + False + True + True + + + Auto Add Values + True + True + False + True + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + 2 + + + + + True + False + Config + + + 2 + False + + + + + True + True + + + + + True + True + 3 + 1 + + + + + True + False + 12 + + + True + False + Port: + + + False + True + end + -1 + + + + + True + False + 4 + .. + + + False + True + 0 + + + + + Start + True + True + True + + + + False + True + 4 + end + 1 + + + + + 0 + True + True + 92 + 6 + 6 + 502 + + + + + False + True + end + 2 + + + + + True + False + .. + + + False + True + 4 + + + + + False + True + 3 + 2 + + + + + + + + TestModbus-Server program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TestModbus-Server is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + + + 500 + 400 + False + dialog + + + False + vertical + 2 + + + False + end + + + + + + gtk-close + True + True + True + True + True + + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + vertical + + + True + False + True + + + True + False + + + + + + True + False + testmodbus-server.png + + + False + False + 1 + + + + + + + + False + True + 0 + + + + + True + False + vertical + + + True + False + 28 + TestModbus-Server + + + + + + + + False + True + 0 + + + + + True + False + version + + + + + + False + True + 14 + 1 + + + + + True + False + https://steffen.gulpe.de/modbus-tcpip + + + False + True + 2 + + + + + True + True + 7 + 1 + + + + + False + True + 0 + + + + + True + True + in + + + True + True + textbuffer1 + + + + + True + True + 2 + + + + + True + False + + + False + True + 3 + + + + + True + True + 1 + + + + + +