commit
383c49a6e1
@ -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/
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
# 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/).
|
||||||
|
|
@ -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, ®stowrite, regvals)) {
|
||||||
|
modbus.SetRegValue(vn.fc, vn.reg, regstowrite, (uint16_t*)regvals);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gtk_widget_hide(GTK_WIDGET(dlg));
|
||||||
|
};
|
||||||
|
|
||||||
|
void cb_btn_addvar (GtkWidget *widget, gpointer data) {
|
||||||
|
addvar_displaywithvalues(data, NULL);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void cb_btn_editvar (GtkWidget *widget, gpointer data) {
|
||||||
|
GuiValue v;
|
||||||
|
GtkTreeIter iter;
|
||||||
|
GtkTreeModel *model;
|
||||||
|
GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv"));
|
||||||
|
gchar *v_name;
|
||||||
|
gchar *v_fc;
|
||||||
|
gchar *v_reg;
|
||||||
|
gchar *v_type;
|
||||||
|
gchar *v_sim;
|
||||||
|
gchar *v_value;
|
||||||
|
GtkTreeSelection *sel;
|
||||||
|
|
||||||
|
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(vars));
|
||||||
|
if (sel && gtk_tree_selection_get_selected(GTK_TREE_SELECTION(sel), &model, &iter)) {
|
||||||
|
gtk_tree_model_get (model, &iter,
|
||||||
|
VALDATA_COL_NAME, &v_name,
|
||||||
|
VALDATA_COL_FC, &v_fc,
|
||||||
|
VALDATA_COL_REGSTART, &v_reg,
|
||||||
|
VALDATA_COL_TYPE, &v_type,
|
||||||
|
VALDATA_COL_SIM, &v_sim,
|
||||||
|
VALDATA_COL_VALUE, &v_value,
|
||||||
|
-1);
|
||||||
|
|
||||||
|
v.name = v_name;
|
||||||
|
v.reg = atoi (v_reg);
|
||||||
|
v.fc = atoi (v_fc);
|
||||||
|
v.type = v_type;
|
||||||
|
v.sim = v_sim;
|
||||||
|
v.value = v_value;
|
||||||
|
|
||||||
|
addvar_displaywithvalues(data, &v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void cb_btn_delvar (GtkWidget *widget, gpointer data) {
|
||||||
|
GtkTreeIter iter;
|
||||||
|
GtkTreeModel *model;
|
||||||
|
GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv"));
|
||||||
|
gchar *v_name;
|
||||||
|
GtkTreeSelection *sel;
|
||||||
|
|
||||||
|
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(vars));
|
||||||
|
if (sel && gtk_tree_selection_get_selected(GTK_TREE_SELECTION(sel), &model, &iter)) {
|
||||||
|
gtk_tree_model_get (model, &iter,
|
||||||
|
VALDATA_COL_NAME, &v_name,
|
||||||
|
-1);
|
||||||
|
Value_Del(v_name);
|
||||||
|
g_free (v_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// update gui/add netdata side
|
||||||
|
//
|
||||||
|
gboolean cb_thread_network_data_add (gpointer data) {
|
||||||
|
GtkWidget *textview = GTK_WIDGET(gtk_builder_get_object (_builder_, "network_text"));
|
||||||
|
GtkTextBuffer *textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
|
||||||
|
GtkTextIter start, end;
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
struct modbus_data *mbdata = (struct modbus_data *)data;
|
||||||
|
std::string text;
|
||||||
|
char timetext[255];
|
||||||
|
GtkTextTag *tag_datetime;
|
||||||
|
GtkTextTag *tag_data;
|
||||||
|
GtkTextTag *tag_info;
|
||||||
|
GtkTextTag *tag_modbus;
|
||||||
|
time_t _tm =time(NULL);
|
||||||
|
struct tm * curtime = localtime (&_tm);
|
||||||
|
static int _once = 0;
|
||||||
|
|
||||||
|
if (_once == 0) {
|
||||||
|
_once = 1;
|
||||||
|
tag_datetime = gtk_text_buffer_create_tag (textbuffer, "Date_Time",
|
||||||
|
"foreground", "blue", "style", PANGO_WEIGHT_BOLD, "family", "Monospace", NULL);
|
||||||
|
tag_data = gtk_text_buffer_create_tag (textbuffer, "Data",
|
||||||
|
"foreground", "black", "style", PANGO_WEIGHT_NORMAL, "family", "Monospace", NULL);
|
||||||
|
tag_info = gtk_text_buffer_create_tag (textbuffer, "Info",
|
||||||
|
"foreground", "green", "style", PANGO_WEIGHT_THIN, "family", "Sans", NULL);
|
||||||
|
tag_modbus = gtk_text_buffer_create_tag (textbuffer, "Modbus",
|
||||||
|
"foreground", "black", "style", PANGO_WEIGHT_NORMAL, "family", "Sans", NULL);
|
||||||
|
}
|
||||||
|
strftime (timetext, 255, "%H:%M:%S", curtime);
|
||||||
|
|
||||||
|
//
|
||||||
|
// build hex dump
|
||||||
|
//
|
||||||
|
text = "";
|
||||||
|
for (int i = 0; i < mbdata->bufferlen; i++) {
|
||||||
|
unsigned char c = mbdata->buffer[i];
|
||||||
|
char hexnum[] = "0123456789ABCDEF";
|
||||||
|
|
||||||
|
if (i == 0) text += " ";
|
||||||
|
else if (i % 32 == 0) text += "\n ";
|
||||||
|
else if (i % 4 == 0 && i > 0) text += " : ";
|
||||||
|
else if (i % 2 == 0 && i > 0) text += ":";
|
||||||
|
// else text += " ";
|
||||||
|
|
||||||
|
text += hexnum[c/16];
|
||||||
|
text += hexnum[c%16];
|
||||||
|
}
|
||||||
|
gtk_text_buffer_get_start_iter(textbuffer, &start);
|
||||||
|
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Data", NULL);
|
||||||
|
|
||||||
|
//
|
||||||
|
// first line
|
||||||
|
//
|
||||||
|
text = "";
|
||||||
|
if (mbdata->fc != 0) {
|
||||||
|
text = text + "\n TransactionID: " + std::to_string(mbdata->transactionid);
|
||||||
|
text = text + " ProtocolID: " + std::to_string(mbdata->protoolid);
|
||||||
|
text = text + " Length: " + std::to_string(mbdata->length);
|
||||||
|
text = text + "\n UnitID: " + std::to_string(mbdata->unitid);
|
||||||
|
if (mbdata->fc < 10) text = text + " FC0" + std::to_string(mbdata->fc);
|
||||||
|
else text = text + " FC" + std::to_string(mbdata->fc);
|
||||||
|
if (mbdata->fc >= 5) {
|
||||||
|
text = text + " WRITE ";
|
||||||
|
if (mbdata->fc == 5)
|
||||||
|
text = text + " Flag/Coil: " + std::to_string(mbdata->regstart);
|
||||||
|
else if (mbdata->fc == 6)
|
||||||
|
text = text + " Register: " + std::to_string(mbdata->regstart);
|
||||||
|
else if (mbdata->fc == 15)
|
||||||
|
text = text + " Flag/Coil: " + std::to_string(mbdata->regstart) + " Count: " + std::to_string (mbdata->regcnt);
|
||||||
|
else if (mbdata->fc == 16)
|
||||||
|
text = text + " Register: " + std::to_string(mbdata->regstart) + " Count: " + std::to_string (mbdata->regcnt);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text = text + " Registers: " + std::to_string (mbdata->regstart);
|
||||||
|
text = text + " Bytes: " + std::to_string (mbdata->regcnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
text = text + "\n";
|
||||||
|
gtk_text_buffer_get_start_iter(textbuffer, &start);
|
||||||
|
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Modbus", NULL);
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
text ="";
|
||||||
|
text += mbdata->hostname;
|
||||||
|
if (mbdata->direction == 0) text = text + " RECV ";
|
||||||
|
else text = text + " SEND ";
|
||||||
|
text = text + std::to_string(mbdata->bufferlen) + " Bytes";
|
||||||
|
gtk_text_buffer_get_start_iter(textbuffer, &start);
|
||||||
|
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Info", NULL);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// add date
|
||||||
|
//
|
||||||
|
text = "\n";
|
||||||
|
text += timetext;
|
||||||
|
text += " ";
|
||||||
|
gtk_text_buffer_get_start_iter(textbuffer, &start);
|
||||||
|
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Date_Time", NULL);
|
||||||
|
|
||||||
|
free (mbdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_text_buffer_get_iter_at_line(textbuffer, &start, 2000);
|
||||||
|
gtk_text_buffer_get_iter_at_line(textbuffer, &end, 2050);
|
||||||
|
gtk_text_buffer_delete(textbuffer, &start, &end);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// add a text line
|
||||||
|
//
|
||||||
|
gboolean cb_thread_network_text_add (gpointer data) {
|
||||||
|
if (data) {
|
||||||
|
std::string text;
|
||||||
|
char timetext[255];
|
||||||
|
GtkWidget *textview = GTK_WIDGET(gtk_builder_get_object (_builder_, "network_text"));
|
||||||
|
GtkTextBuffer *textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
|
||||||
|
GtkTextIter start, end;
|
||||||
|
GtkTextTag *tag_datetime;
|
||||||
|
GtkTextTag *tag_info;
|
||||||
|
char *textprm = (char*) data;
|
||||||
|
time_t _tm =time(NULL);
|
||||||
|
struct tm * curtime = localtime (&_tm);
|
||||||
|
static int _once = 0;
|
||||||
|
|
||||||
|
if (_once == 0) {
|
||||||
|
_once = 1;
|
||||||
|
tag_datetime = gtk_text_buffer_create_tag (textbuffer, "Date_Time",
|
||||||
|
"foreground", "blue", "style", PANGO_WEIGHT_BOLD, "family", "Monospace", NULL);
|
||||||
|
tag_info = gtk_text_buffer_create_tag (textbuffer, "Connection",
|
||||||
|
"foreground", "red", "style", PANGO_WEIGHT_BOLD, "family", "Sans", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
strftime (timetext, 255, "%H:%M:%S", curtime);
|
||||||
|
|
||||||
|
//
|
||||||
|
// build hex dump
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
text ="";
|
||||||
|
text += textprm;
|
||||||
|
gtk_text_buffer_get_start_iter(textbuffer, &start);
|
||||||
|
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Connection", NULL);
|
||||||
|
|
||||||
|
//
|
||||||
|
// add date
|
||||||
|
//
|
||||||
|
text = "\n";
|
||||||
|
text += timetext;
|
||||||
|
text += " ";
|
||||||
|
gtk_text_buffer_get_start_iter(textbuffer, &start);
|
||||||
|
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &start, text.c_str(), -1, "Date_Time", NULL);
|
||||||
|
|
||||||
|
free (textprm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
gboolean cb_thread_status (gpointer data) {
|
||||||
|
string *s = (string *)data;
|
||||||
|
GtkWidget *status = GTK_WIDGET(gtk_builder_get_object (_builder_, "status_lb"));
|
||||||
|
gtk_label_set_text(GTK_LABEL(status), s->c_str());
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cb_networkdata_show (GtkWidget *widget, gpointer data) {
|
||||||
|
GtkWidget *textview = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(data), "network_text"));
|
||||||
|
GtkTextBuffer *textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
|
||||||
|
|
||||||
|
printf ("*************************** \n");
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void cb_addvar_close (GtkWidget *widget, gpointer data) {
|
||||||
|
GtkBuilder *builder = (GtkBuilder *) data;
|
||||||
|
GtkWidget *dlg = GTK_WIDGET (gtk_builder_get_object (builder, "addvar"));
|
||||||
|
printf ("%s\n", __FUNCTION__);
|
||||||
|
|
||||||
|
gtk_dialog_response(GTK_DIALOG(dlg), GTK_RESPONSE_CANCEL);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void displayerror (string error) {
|
||||||
|
GtkWidget *dialog;
|
||||||
|
GtkWidget *window = GTK_WIDGET (gtk_builder_get_object (_builder_, "testmodbus-server"));
|
||||||
|
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
|
||||||
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
GTK_MESSAGE_ERROR,
|
||||||
|
GTK_BUTTONS_OK,
|
||||||
|
error.c_str());
|
||||||
|
gtk_window_set_title(GTK_WINDOW(dialog), "Error");
|
||||||
|
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||||
|
gtk_widget_destroy(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cb_addvar_addedit (GtkWidget *widget, gpointer data) {
|
||||||
|
GtkBuilder *builder = (GtkBuilder *) data;
|
||||||
|
GtkWidget *dlg = GTK_WIDGET (gtk_builder_get_object (builder, "addvar"));
|
||||||
|
GtkWidget *name = GTK_WIDGET (gtk_builder_get_object (builder, "adddlg_name"));
|
||||||
|
|
||||||
|
GtkWidget *dialog;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (Value_Exist((string)gtk_entry_get_text(GTK_ENTRY(name))) == true) {
|
||||||
|
dialog = gtk_message_dialog_new(GTK_WINDOW(dlg),
|
||||||
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
GTK_MESSAGE_QUESTION,
|
||||||
|
GTK_BUTTONS_YES_NO,
|
||||||
|
"Are you sure you want to save?");
|
||||||
|
gtk_window_set_title(GTK_WINDOW(dialog), "Question");
|
||||||
|
result = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||||
|
gtk_widget_destroy(dialog);
|
||||||
|
if (result == GTK_RESPONSE_YES) gtk_dialog_response(GTK_DIALOG(dlg), GTK_RESPONSE_APPLY);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gtk_dialog_response(GTK_DIALOG(dlg), GTK_RESPONSE_APPLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void cb_menu_about(GtkWidget *widget, gpointer data) {
|
||||||
|
GtkBuilder *builder = (GtkBuilder *) data;
|
||||||
|
GtkWidget *dlg = GTK_WIDGET (gtk_builder_get_object (builder, "dlgabout"));
|
||||||
|
GtkWidget *label = GTK_WIDGET (gtk_builder_get_object (builder, "dlgabout_version"));
|
||||||
|
gtk_label_set_text(GTK_LABEL(label), VERSION);
|
||||||
|
gint result = gtk_dialog_run(GTK_DIALOG(dlg));
|
||||||
|
gtk_widget_hide(GTK_WIDGET(dlg));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cb_about_btnclose(GtkWidget *widget, gpointer data) {
|
||||||
|
GtkBuilder *builder = (GtkBuilder *) data;
|
||||||
|
GtkWidget *dlg = GTK_WIDGET (gtk_builder_get_object (builder, "dlgabout"));
|
||||||
|
gtk_dialog_response(GTK_DIALOG(dlg), GTK_RESPONSE_CLOSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cb_btn_enableall (GtkWidget *widget, gpointer data) {
|
||||||
|
modbus.EnableAll(1);
|
||||||
|
MBData_EnableAll(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void cb_btn_disableall (GtkWidget *widget, gpointer data) {
|
||||||
|
modbus.EnableAll(0);
|
||||||
|
MBData_EnableAll(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void cb_btn_clearreq (GtkWidget *widget, gpointer data) {
|
||||||
|
modbus.RequestsClear();
|
||||||
|
MBData_ReqReset ();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void cb_btn_enablevalues (GtkWidget *widget, gpointer data) {
|
||||||
|
GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv"));
|
||||||
|
GtkTreeIter iter;
|
||||||
|
GtkTreeModel *model;
|
||||||
|
gboolean result;
|
||||||
|
gchar *v_name = NULL;
|
||||||
|
gchar *v_reg = NULL;
|
||||||
|
gchar *v_fc = NULL;
|
||||||
|
gchar *v_type = NULL;
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars));
|
||||||
|
for (cnt = 0, result = gtk_tree_model_get_iter_first(model, &iter); result == TRUE;
|
||||||
|
result = gtk_tree_model_iter_next(model, &iter), cnt++) {
|
||||||
|
gtk_tree_model_get (model, &iter,
|
||||||
|
VALDATA_COL_NAME, &v_name,
|
||||||
|
VALDATA_COL_FC, &v_fc,
|
||||||
|
VALDATA_COL_REGSTART, &v_reg,
|
||||||
|
VALDATA_COL_TYPE, &v_type,
|
||||||
|
-1);
|
||||||
|
|
||||||
|
if (v_name == NULL) {
|
||||||
|
if (v_fc != NULL) free (v_fc);
|
||||||
|
if (v_reg != NULL) free (v_reg);
|
||||||
|
if (v_type != NULL) free (v_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
modbus.Enable(atoi(v_fc), atoi(v_reg), Value_GetSize(v_type), 1);
|
||||||
|
MBData_Enable(atoi(v_fc), atoi(v_reg), Value_GetSize(v_type), 1);
|
||||||
|
|
||||||
|
if (v_name != NULL) free (v_name);
|
||||||
|
if (v_fc != NULL) free (v_fc);
|
||||||
|
if (v_reg != NULL) free (v_reg);
|
||||||
|
if (v_type != NULL) free (v_type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void save_file(std::string fn) {
|
||||||
|
GtkWidget *guiautovalue = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "conf_autoaddvalues"));
|
||||||
|
GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv"));
|
||||||
|
GtkTreeIter iter;
|
||||||
|
GtkTreeModel *model;
|
||||||
|
gboolean result;
|
||||||
|
gchar *v_name = NULL;
|
||||||
|
gchar *v_reg = NULL;
|
||||||
|
gchar *v_fc = NULL;
|
||||||
|
gchar *v_type = NULL;
|
||||||
|
gchar *v_value = NULL;
|
||||||
|
gchar *v_sim = NULL;
|
||||||
|
int cnt = 0;
|
||||||
|
JSONParse jp;
|
||||||
|
JSONParse jv;
|
||||||
|
JSONElement je;
|
||||||
|
std::list<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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -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, ®stowrite, regvals)) {
|
||||||
|
modbus.SetRegValue(v.fc, v.reg, regstowrite, (uint16_t*)regvals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(v_name);
|
||||||
|
g_free(v_type);
|
||||||
|
g_free(v_sim);
|
||||||
|
g_free(v_value);
|
||||||
|
g_free(v_reg);
|
||||||
|
g_free(v_fc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE; // continue timer
|
||||||
|
}
|
||||||
|
|
||||||
|
void Value_Set(GuiValue *g) {
|
||||||
|
GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv"));
|
||||||
|
GtkTreeIter iter;
|
||||||
|
GtkTreeModel *model;
|
||||||
|
gboolean result;
|
||||||
|
gchar *v_name = NULL;
|
||||||
|
int changed = 0;
|
||||||
|
|
||||||
|
model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars));
|
||||||
|
for (result = gtk_tree_model_get_iter_first(model, &iter); result == TRUE && changed == 0;
|
||||||
|
result = gtk_tree_model_iter_next(model, &iter)) {
|
||||||
|
gtk_tree_model_get (model, &iter,
|
||||||
|
VALDATA_COL_NAME, &v_name,
|
||||||
|
-1);
|
||||||
|
if (v_name == NULL) break;
|
||||||
|
if (strcmp(v_name, g->name.c_str()) == 0) {
|
||||||
|
Value_ModStore(model, &iter, g);
|
||||||
|
changed = 1;
|
||||||
|
}
|
||||||
|
g_free(v_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed == 0)
|
||||||
|
Value_Add(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Value_GetSize(std::string type) {
|
||||||
|
if (type.compare("WORD") == 0) return 1;
|
||||||
|
if (type.compare("DWORD") == 0) return 2;
|
||||||
|
if (type.compare("FLOAT") == 0) return 2;
|
||||||
|
if (type.compare("DWORD_SWAP") == 0) return 2;
|
||||||
|
if (type.compare("FLOAT_SWAP") == 0) return 2;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Value_Add(GuiValue *g) {
|
||||||
|
GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv"));
|
||||||
|
GtkTreeIter iter;
|
||||||
|
GtkTreeModel *model;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
model = gtk_tree_view_get_model(GTK_TREE_VIEW(vars));
|
||||||
|
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
|
||||||
|
Value_ModStore(model, &iter, g);
|
||||||
|
|
||||||
|
count = Value_GetSize(g->type);
|
||||||
|
modbus.Enable(g->fc, g->reg, count, 1);
|
||||||
|
MBData_Enable(g->fc, g->reg, count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Value_ModStore(GtkTreeModel *model, GtkTreeIter *iter, GuiValue *g) {
|
||||||
|
g->value = Value_GetValue(g->fc, g->reg, g->type);
|
||||||
|
gtk_list_store_set(GTK_LIST_STORE(model), iter,
|
||||||
|
VALDATA_COL_NAME, g->name.c_str(),
|
||||||
|
VALDATA_COL_FC, std::to_string(g->fc).c_str(),
|
||||||
|
VALDATA_COL_REGSTART, std::to_string(g->reg).c_str(),
|
||||||
|
VALDATA_COL_TYPE, g->type.c_str(),
|
||||||
|
VALDATA_COL_VALUE, Value_GetValue(g->fc, g->reg, g->type).c_str(),
|
||||||
|
VALDATA_COL_SIM, g->sim.c_str(),
|
||||||
|
-1);
|
||||||
|
|
||||||
|
// for (int i = 0; i < 10; i++) {
|
||||||
|
// float *f = (float *)&modbusdata[2][i];
|
||||||
|
// printf ("%f ", *f);
|
||||||
|
// }
|
||||||
|
// printf ("\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// return valuze as string
|
||||||
|
std::string Value_GetValue(int fc, int reg, string type) {
|
||||||
|
std::string result = "";
|
||||||
|
|
||||||
|
if (fc == 1 || fc == 2) {
|
||||||
|
if (modbusdata[fc-1][reg]) result = "true";
|
||||||
|
else result = "false";
|
||||||
|
}
|
||||||
|
else if (fc == 3 || fc == 4) {
|
||||||
|
if (type.compare ("WORD") == 0) result = std::to_string(modbusdata[fc-1][reg]);
|
||||||
|
else if (type.compare ("FLOAT") == 0) {
|
||||||
|
uint8_t data[4];
|
||||||
|
float *f = (float*)&data;
|
||||||
|
|
||||||
|
memcpy (data, &modbusdata[fc-1][reg], 2);
|
||||||
|
memcpy (data+2, &modbusdata[fc-1][reg+1], 2);
|
||||||
|
|
||||||
|
result = std::to_string(*f);
|
||||||
|
}
|
||||||
|
else if (type.compare ("FLOAT_SWAP") == 0) {
|
||||||
|
uint8_t data[4];
|
||||||
|
float *f = (float*)&data;
|
||||||
|
|
||||||
|
memcpy (data, &modbusdata[fc-1][reg+1], 2);
|
||||||
|
memcpy (data+2, &modbusdata[fc-1][reg], 2);
|
||||||
|
|
||||||
|
result = std::to_string((double)*f);
|
||||||
|
}
|
||||||
|
else if (type.compare ("DWORD") == 0) {
|
||||||
|
uint8_t data[4];
|
||||||
|
uint32_t *f = (uint32_t*)&data;
|
||||||
|
|
||||||
|
memcpy (data, &modbusdata[fc-1][reg], 2);
|
||||||
|
memcpy (data+2, &modbusdata[fc-1][reg+1], 2);
|
||||||
|
|
||||||
|
result = std::to_string(*f);
|
||||||
|
}
|
||||||
|
else if (type.compare ("DWORD_SWAP") == 0) {
|
||||||
|
uint8_t data[4];
|
||||||
|
uint32_t *f = (uint32_t*)&data;
|
||||||
|
|
||||||
|
memcpy (data, &modbusdata[fc-1][reg+1], 2);
|
||||||
|
memcpy (data+2, &modbusdata[fc-1][reg], 2);
|
||||||
|
|
||||||
|
result = std::to_string(*f);
|
||||||
|
}
|
||||||
|
else if (type.compare ("BOOL") == 0) {
|
||||||
|
int c;
|
||||||
|
|
||||||
|
for (result = "", c = 0; c < 16; c++) {
|
||||||
|
if (modbusdata[fc-1][reg] & (1<<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
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
|
};
|
After Width: | Height: | Size: 4.9 KiB |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue