adding files

master
Steffen Pohle 4 years ago
commit 383c49a6e1

@ -0,0 +1,3 @@
Initial Application:

@ -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/

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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/).

773
gui.cc

@ -0,0 +1,773 @@
/////////////////////////////////////////////////////////////////////////////////
//
// gui.cc is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
#else
#include <unistd.h> /* close() */
#endif
#include <stdio.h>
#include <list>
#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, &regstowrite, 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<JSONElement> 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);
}
};

88
gui.h

@ -0,0 +1,88 @@
/////////////////////////////////////////////////////////////////////////////////
//
// gui.h is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#ifndef _GUI_H_
#define _GUI_H_
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib.h>
#include <string>
#include <iostream>
#include <list>
#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

@ -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);
}
}
};

@ -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

@ -0,0 +1,554 @@
/////////////////////////////////////////////////////////////////////////////////
//
// guivalues.cc is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
//
//
// show all variables and the value
//
//
#include <sys/time.h>
#include <math.h>
#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, &regstowrite, 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<<c)) result = "1" + result;
else result = "0" + result;
}
}
}
else {
printf ("%s:%d Some Error fc:%d\n", __FILE__, __LINE__, fc);
}
return result;
}
int Value_SetValue(string value, string type, int fc, int *regcnt, uint16_t *regvalues) {
uint8_t data[4];
if (fc == 1 || fc == 2) {
*regcnt = 1;
if (value.compare("false") == 0 || value.compare("0") == 0)
*regvalues = 0;
else *regvalues = 1;
}
else {
if (type.compare("FLOAT") == 0) {
float *f = (float*)data;
*f = (float) atof (value.c_str());
memcpy (regvalues, data, 2);
memcpy (regvalues+1, data+2, 2);
*regcnt = 2;
}
else if (type.compare("FLOAT_SWAP") == 0) {
float *f = (float*)data;
*f = (float) atof (value.c_str());
memcpy (regvalues+1, data, 2);
memcpy (regvalues, data+2, 2);
*regcnt = 2;
}
else if (type.compare("WORD") == 0) {
uint16_t i16;
i16 = (uint16_t) atoi (value.c_str());
memcpy (regvalues, &i16, 2);
*regcnt = 1;
}
}
return 1;
}
//////////////////////////////////////////////////
// delete Value from list
void Value_Del(string name) {
gboolean res = false;
GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv"));
GtkTreeIter iter;
GtkTreeModel *model;
gboolean result;
gchar *v_name = NULL;
printf ("%s:%d Delete: %s\n", __FILE__, __LINE__, name.c_str());
model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars));
for (result = gtk_tree_model_get_iter_first(model, &iter); result == true && res == false;
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, name.c_str()) == 0) {
printf ("Found\n");
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
res = true;
}
g_free(v_name);
}
return;
}
//////////////////////////////////////////////////
// delete all values
void Value_DelAll() {
GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv"));
GtkTreeModel *model;
printf ("%s:%d Delete All\n", __FILE__, __LINE__);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars));
gtk_list_store_clear(GTK_LIST_STORE(model));
return;
}
//////////////////////////////////////////////////
// check if Value is already set
gboolean Value_Exist(std::string name) {
gboolean res = false;
GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv"));
GtkTreeIter iter;
GtkTreeModel *model;
gboolean result;
gchar *v_name = NULL;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars));
for (result = gtk_tree_model_get_iter_first(model, &iter); result == true && res == true;
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, name.c_str()) == 0) {
res = true;
}
g_free(v_name);
}
return res;
}
void valdata_show(GtkWidget *widget, gpointer data) {
GtkWidget *view = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(data), "vars_tv"));
GtkCellRenderer *renderer;
GtkTreeModel *model;
//
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Name",
renderer,
"text", VALDATA_COL_NAME,
NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"FC",
renderer,
"text", VALDATA_COL_FC,
NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Register",
renderer,
"text", VALDATA_COL_REGSTART,
NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Type",
renderer,
"text", VALDATA_COL_TYPE,
NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Simulation",
renderer,
"text", VALDATA_COL_SIM,
NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Value",
renderer,
"text", VALDATA_COL_VALUE,
NULL);
model = GTK_TREE_MODEL(gtk_list_store_new (VALDATA_COLCOUNT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING));
gtk_tree_view_set_model (GTK_TREE_VIEW (view), model);
g_object_unref(model);
};

@ -0,0 +1,61 @@
/////////////////////////////////////////////////////////////////////////////////
//
// guivalues.h is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#ifndef _GUIVALUE_H_
#define _GUIVALUE_H_
#include "gui.h"
#include "modbus.h"
#include <string>
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

@ -0,0 +1,494 @@
/////////////////////////////////////////////////////////////////////////////////
//
// json.cc is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#include <list>
#include <string>
#include <string.h>
#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<JSONElement>::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<JSONElement>::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<JSONElement>::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<JSONElement>::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<JSONElement> JSONParse::GetElements() {
list<JSONElement> l;
list<JSONElement>::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<JSONElement>::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<JSONElement> *l) {
list<JSONElement>::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;
};

@ -0,0 +1,77 @@
/////////////////////////////////////////////////////////////////////////////////
//
// json.h is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#ifndef _JSON_H_
#define _JSON_H_
#include <list>
#include <string>
#include <string.h>
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<JSONElement> *l);
void SetObject (string n, string s);
string GetString();
};
class JSONParse {
private:
string jsondata;
list<JSONElement> 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<JSONElement> 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_

@ -0,0 +1,107 @@
/////////////////////////////////////////////////////////////////////////////////
//
// main.cc is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#include <sys/time.h>
#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 <steffen@gulpe.de>\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;
}

@ -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());
};

@ -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 <string>
// 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

@ -0,0 +1,891 @@
/////////////////////////////////////////////////////////////////////////////////
//
// modbus.cc is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#include <errno.h>
#include <glib.h>
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
#else
#include <unistd.h> /* 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);
};

@ -0,0 +1,113 @@
/////////////////////////////////////////////////////////////////////////////////
//
// modbus.h is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#ifndef _MODBUS_H_
#define _MODBUS_H_
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib.h>
#include "tcp.h"
#include <list>
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

521
tcp.cc

@ -0,0 +1,521 @@
/////////////////////////////////////////////////////////////////////////////////
//
// tcp.cc is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#include "tcp.h"
#include <stdio.h>
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
#else
#include <unistd.h> /* close() */
#endif
#include <stdlib.h>
#include <string.h> /* memset() */
#include <errno.h>
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;
};

123
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 <winsock.h>
#include <winsock2.h>
#include <io.h>
#include <ws2tcpip.h>
#include <string.h>
#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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/un.h>
#endif
#include <string>
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

@ -0,0 +1,110 @@
/////////////////////////////////////////////////////////////////////////////////
//
// test-fc15.cc is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#include <UDPTCPNetwork.h>
#include <stdio.h>
#include <stdlib.h>
#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;
};

@ -0,0 +1,115 @@
/////////////////////////////////////////////////////////////////////////////////
//
// test-fc16.cc is part of TestModbus-Server.
//
/////////////////////////////////////////////////////////////////////////////////
#include <UDPTCPNetwork.h>
#include <stdio.h>
#include <stdlib.h>
#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;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

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