From 5cc7e6832c49d1fee9c7c9f73cb52798c205c117 Mon Sep 17 00:00:00 2001 From: Steffen Pohle Date: Fri, 28 Nov 2025 22:45:16 +0100 Subject: [PATCH] adding bulk load, some little gui improvements. --- Changelog | 5 + README.md | 12 +- gui.cc | 171 +++++- gui.h | 9 +- guivalues.cc | 13 +- server.cc | 25 +- testmodbus-server.ui | 1346 +++++++++++++++++++++--------------------- 7 files changed, 908 insertions(+), 673 deletions(-) diff --git a/Changelog b/Changelog index cd680b7..03b7cf7 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,9 @@ +Version 1.0.5 + +2025-11-28: +- double click on the variable list works. +- added bulk load of CSV files. Version 1.0.4 diff --git a/README.md b/README.md index 522d9a2..b68369a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ newer version of this software at https://steffen.gulpe.de/modbus-tcpip/ ![Image_01](https://steffen.gulpe.de/modbus-tcpip/screenshot-winver1-0-0-regs.PNG) ![Image_02](https://steffen.gulpe.de/modbus-tcpip/screenshot-winver1-0-0-values.PNG) - # Compilation on Linux As requirement to compile this application you need to install the gnome3-devel packages. If this is done you can compile and run the application with the following commands: @@ -35,3 +34,14 @@ needed librarys (needs to prepared, see Makefile for some hints). For precompiled windows binarys look at this [link](https://steffen.gulpe.de/modbus-tcpip/). + +# some functions - bulk load +- Bulk load of csv files works. + + NAME,TYPE,FC,REGISTER,VALUE + =================================================== + TEST_FC3_0,FLOAT,3,0,1.5 + TEST_FC3_2,FLOAT,3,2,1.6 + TEST_FC4_4,FLOAT,4,4,2.6 + TEST_FC4_6,FLOAT,4,6,15.6 + diff --git a/gui.cc b/gui.cc index f29f917..95472f9 100644 --- a/gui.cc +++ b/gui.cc @@ -10,6 +10,7 @@ #endif #include #include +#include #include "gui.h" #include "modbussrv.h" #include "guivalues.h" @@ -19,9 +20,18 @@ extern GtkBuilder *_builder_; // work around for threads extern void addvar_displaywithvalues (gpointer data, GuiValue *v); +extern int gnome_dblclick_timeout; +int gui_double_click = 0; +struct timeval doubleclick_time; - +/// @brief function is too slow i need <400ms .... :( but we will resit the value jzst in case... +/// @param data +/// @return +gboolean cb_timeout_doubleclick(gpointer data) { + gui_double_click = 0; + return FALSE; +}; ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -29,11 +39,9 @@ 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; +gboolean cb_window_delete_event (GtkWidget *widget, gpointer data) { cb_menu_save(widget, data); - return FALSE; } @@ -96,6 +104,91 @@ void cb_menu_open (GtkWidget *widget, gpointer data) { }; +void cb_menu_openbulk (GtkWidget *widget, gpointer data) { + GtkBuilder *builder = (GtkBuilder *) data; + GtkWindow *window = GTK_WINDOW (gtk_builder_get_object (builder, "testmodbus-server")); + GtkWidget *dialog; + GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; + GtkFileFilter *filter; + gint res; + + printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + dialog = gtk_file_chooser_dialog_new ("Open Bulk File", + window, + action, + "_Cancel", + GTK_RESPONSE_CANCEL, + "_Open", + GTK_RESPONSE_ACCEPT, + NULL); + + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*.csv"); + gtk_file_filter_set_name(filter, "CSV File"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*.*"); + gtk_file_filter_set_name(filter, "All Files"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + + res = gtk_dialog_run (GTK_DIALOG (dialog)); + if (res == GTK_RESPONSE_ACCEPT) { + char *filename; + GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog); + filename = gtk_file_chooser_get_filename (chooser); + load_bulkfile(filename); + g_free(filename); + } + gtk_widget_destroy (dialog); +}; + + +gboolean cb_vars_btnrelease (GtkWidget *widget, gpointer user_data) { + // + // selfhandling double click :) seem to work + if (gui_double_click == 1 && get_cycletime(&doubleclick_time) < gnome_dblclick_timeout) { + GuiValue v; + GtkTreeIter iter; + GtkTreeModel *model; + GtkWidget *vars = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vars_tv")); + gchar *v_name; + gchar *v_fc; + gchar *v_reg; + gchar *v_type; + gchar *v_sim; + gchar *v_value; + GtkTreeSelection *sel; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(vars)); + if (sel && gtk_tree_selection_get_selected(GTK_TREE_SELECTION(sel), &model, &iter)) { + gtk_tree_model_get (model, &iter, + VALDATA_COL_NAME, &v_name, + VALDATA_COL_FC, &v_fc, + VALDATA_COL_REGSTART, &v_reg, + VALDATA_COL_TYPE, &v_type, + VALDATA_COL_SIM, &v_sim, + VALDATA_COL_VALUE, &v_value, + -1); + + v.name = v_name; + v.reg = atoi (v_reg); + v.fc = atoi (v_fc); + v.type = v_type; + v.sim = v_sim; + v.value = v_value; + + addvar_displaywithvalues(_builder_, &v); + } + } + else { + gui_double_click = 1; + gettimeofday(&doubleclick_time, NULL); + g_timeout_add(gnome_dblclick_timeout, cb_timeout_doubleclick, _builder_); + } + + return FALSE; +}; + void cb_menu_save (GtkWidget *widget, gpointer data) { // GtkBuilder *builder = (GtkBuilder *) data; @@ -262,7 +355,6 @@ void addvar_displaywithvalues (gpointer data, GuiValue *v) { if (Value_SetValue(vn.value, vn.type, vn.fc, ®stowrite, regvals)) { modbussrv.SetRegValue(vn.fc, vn.reg, regstowrite, (uint16_t*)regvals); } - } } gtk_widget_hide(GTK_WIDGET(dlg)); @@ -544,6 +636,7 @@ void displayerror (string error) { gtk_widget_destroy(dialog); } + void cb_addvar_addedit (GtkWidget *widget, gpointer data) { GtkBuilder *builder = (GtkBuilder *) data; GtkWidget *dlg = GTK_WIDGET (gtk_builder_get_object (builder, "addvar")); @@ -786,3 +879,71 @@ void load_file(std::string fn) { }; +void load_bulkfile(std::string fn) { + GuiValue g; + + FILE *f; + char buffer[FILEBUFFER]; + char field[LEN_FIELD]; + char *pos, *next; + int i; + JSONParse json; + JSONParse value; + std::string temp; + uint16_t regvals[4]; + int regstowrite = 1; + + printf ("%s\n", __FUNCTION__); + + f = fopen(fn.c_str(),"r"); + if (f != NULL) { + while (!feof(f)) { + // NAME,TYPE,FC,REG,VALUE + fgets (buffer, FILEBUFFER, f); + + // name + pos = buffer; + if ((next = strchr(pos, ',')) == NULL) { + printf ("load bulk: could not find ','\n"); + continue; + } + *next = 0; + g.name = pos; + + // type + pos = next+1; + if ((next = strchr(pos, ',')) == NULL) continue; + *next = 0; + g.type = pos; + + // fc + pos = next+1; + if ((next = strchr(pos, ',')) == NULL) continue; + *next = 0; + g.fc = atoi(pos); + + // reg + pos = next+1; + if ((next = strchr(pos, ',')) == NULL) continue; + *next = 0; + g.reg = atoi(pos); + + // value + pos = next+1; + g.value = pos; + printf ("%s -> %s\n", pos, g.value.c_str()); + g.sim = "NONE"; + + Value_Add(&g); + printf ("%s -> %s\n", pos, g.value.c_str()); + regstowrite = 1; + if (Value_SetValue(g.value, g.type, g.fc, ®stowrite, regvals)) { + modbussrv.SetRegValue(g.fc, g.reg, regstowrite, (uint16_t*)regvals); + printf ("%s add %s type:%s with value %s\n", __FUNCTION__, g.name.c_str(), g.type.c_str(), g.value.c_str()); + } + else printf ("%s add %s without value\n", __FUNCTION__, g.name); + } + fclose (f); + } + else printf ("error on open bulk: %s\n", strerror(errno)); +}; diff --git a/gui.h b/gui.h index 5b0fc3c..e34de30 100644 --- a/gui.h +++ b/gui.h @@ -17,10 +17,14 @@ #include "guivalues.h" #include "guimodbusdata.h" + +#define LEN_FIELD 128 + std::string to_hex16 (int v); extern uint16_t modbusdata[4][0x10000]; void load_file(std::string fn); +void load_bulkfile(std::string fn); void save_file(std::string fn); void displayerror (string error); @@ -34,10 +38,10 @@ extern "C" { // main windows call backs // G_MODULE_EXPORT void cb_window_show (GtkWidget *widget, gpointer data); -G_MODULE_EXPORT gboolean cb_window_delete_event (GtkWidget *widget, - GdkEvent *event, gpointer data); +G_MODULE_EXPORT gboolean cb_window_delete_event (GtkWidget *widget, gpointer data); G_MODULE_EXPORT void cb_menu_new (GtkWidget *widget, gpointer data); G_MODULE_EXPORT void cb_menu_open (GtkWidget *widget, gpointer data); +G_MODULE_EXPORT void cb_menu_openbulk (GtkWidget *widget, gpointer data); G_MODULE_EXPORT void cb_menu_save (GtkWidget *widget, gpointer data); G_MODULE_EXPORT void cb_menu_saveas (GtkWidget *widget, gpointer data); G_MODULE_EXPORT void cb_menu_quit (GtkWidget *widget, gpointer data); @@ -57,6 +61,7 @@ G_MODULE_EXPORT void cb_btn_delvar (GtkWidget *widget, gpointer data); // Add Variable Dialog G_MODULE_EXPORT void cb_addvar_close (GtkWidget *widget, gpointer data); G_MODULE_EXPORT void cb_addvar_addedit (GtkWidget *widget, gpointer data); +G_MODULE_EXPORT gboolean cb_vars_btnrelease (GtkWidget *widget, gpointer user_data); // // Register Buttons diff --git a/guivalues.cc b/guivalues.cc index bf0e2b1..f78e3f3 100644 --- a/guivalues.cc +++ b/guivalues.cc @@ -310,7 +310,6 @@ void Value_Add(GuiValue *g) { 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(), @@ -323,7 +322,7 @@ void Value_ModStore(GtkTreeModel *model, GtkTreeIter *iter, GuiValue *g) { /////////////////////////////////////////////////// -// return valuze as string +// return value as string std::string Value_GetValue(int fc, int reg, string type) { std::string result = ""; @@ -373,6 +372,9 @@ std::string Value_GetValue(int fc, int reg, string type) { int c; for (result = "", c = 0; c < 16; c++) { + if (c % 8 == 0 && c != 0) result = ":" + result; + else if (c % 4 == 0 && c != 0) result = "." + result; + if (modbusdata[fc-1][reg] & (1< +#include +#include + #include "config.h" #include "mbsconfig.h" #include "gui.h" @@ -20,7 +24,7 @@ Config config; ModbusSrv modbussrv; GtkBuilder *_builder_ = NULL; // work around for the thread situation uint16_t modbusdata[4][0x10000]; // needed for work with the gui will by synced by the modbus call back functions - +int gnome_dblclick_timeout = 250; gboolean modbus_callback(gpointer data); int main (int argc, char **argv) { @@ -36,6 +40,25 @@ int main (int argc, char **argv) { printf (" https://steffen.gulpe.de/modbus-tcpip\n"); printf (" written by Steffen Pohle \n"); + // + // read out some gnome defined values + GSettings *settings; + GError *error = NULL; + + // from the google KI :) + // Das Schema (Schema) für die Mauseinstellungen erstellen + // In neueren GNOME-Versionen ist das Schema: 'org.gnome.desktop.peripherals.mouse' + // In älteren Versionen könnte es 'org.gnome.settings-daemon.peripherals.mouse' sein + settings = g_settings_new("org.gnome.desktop.peripherals.mouse"); + if (settings == NULL) { + fprintf(stderr, "Fehler beim Laden des GSettings-Schemas.\n"); + return 1; + } + gnome_dblclick_timeout = g_settings_get_int(settings, "double-click"); + printf("Die aktuelle Doppelklickzeit beträgt: %d ms\n", gnome_dblclick_timeout); + g_object_unref(settings); + + gtk_init (&argc, &argv); _builder_ = builder = gtk_builder_new (); gtk_builder_add_from_file (builder, BUILDER_FILE, NULL); diff --git a/testmodbus-server.ui b/testmodbus-server.ui index 27c251d..ef90b6a 100644 --- a/testmodbus-server.ui +++ b/testmodbus-server.ui @@ -1,5 +1,5 @@ - - - - - - - - - WORD - - - DWORD - - - DWORD_SWAP - - - FLOAT - - - FLOAT_SWAP - - - BOOL - - + + True + False + gtk-missing-image - + False - Add Variable or Flag - dialog - - + 600 + 400 + testmodbus-server.png + True + + + + + + True False vertical - 2 - - + + + True False - end - - gtk-add + True - True - True - True - True - + False + _Datei + True + + + True + False + + + gtk-new + True + False + True + True + + + + + + gtk-open + True + False + True + True + + + + + + gtk-save + True + False + True + True + + + + + + gtk-save-as + True + False + True + True + + + + + + True + False + + + + + Load Bulk + True + False + image1 + False + + + + + + True + False + + + + + gtk-quit + True + False + True + True + + + + + - - True - True - 0 - - - gtk-close + True - True - True - True - True - + False + _Hilfe + True + + + True + False + + + gtk-about + True + False + True + True + + + + + - - True - True - 1 - False - False + True 0 - + True - False - vertical - 10 + True + True - + + 200 True False - 9 - True + vertical - - Coils -[Read/Write] + True - True - False - True - True - adddlg_FC2 + False + Netzwerk False @@ -119,385 +175,499 @@ Author: Steffen Pohle - - Digital In -[Read] + + 200 True True - False - True - True - adddlg_FC1 + in + 200 + + + True + True + False + + - False + True True 1 - - - Holding Reg. -[Read/Write] - True - True - False - True - True - adddlg_FC1 - - - False - True - 2 - - - - - Input Reg. -[Read] - True - True - False - True - True - adddlg_FC1 - - - False - True - 3 - - - False - True - 0 + False + False - - + True - False - 4 - 4 - 4 - 4 - 4 - 4 - - - True - False - end - Name : - - - 0 - 0 - - - - - True - True - - - 1 - 0 - - - - - True - False - end - Type : - - - 0 - 2 - - - - - True - False - end - Flag / Register : - - - 0 - 1 - - - - - True - False - end - Simmulation : - - - 0 - 3 - - - - - True - True - - - 1 - 1 - - + True - + True False - liststore1 - 0 - on - True - 0 - - + vertical + + + True + True + in + + + True + True + + + + + + + + True + True + 0 + + + + + True False + 3 + + + Req. Clear + True + True + True + + + + False + False + 0 + + + + + Enable All + True + True + True + + + + False + False + 1 + + + + + Enable Values + True + True + True + + + + False + False + 2 + + + + + Disable All + True + True + True + + + + False + True + 4 + + + + False + True + 4 + 1 + - 1 - 2 + 1 - + True False - end - Value : + Registers - 0 - 4 + False True False + vertical - + True True + in + + + True + True + + + + + + - False + True True - 0 + 1 - - Change + True - True - False - True + False + 5 + + + Add + True + True + True + + + + False + False + 0 + + + + + Edit + True + True + True + + + + False + False + 1 + + + + + Delete + True + True + True + + + + False + False + 2 + + False True - 1 + 5 + end + 2 - 1 - 4 + 1 + + + + + True + False + Values + + + 1 + False - + + True False - 0 - True - - NONE - SAW;t=60;min=0;max=1000 - SIN;t=60;min=-100;max=100; - PULSE;ton=10;toff=60 - - - + True + True + + + Auto Add Values + True True + False + True + + 0 + 0 + + + + + + + + + + + + + + + + - 1 - 3 + 2 + + + + + True + False + Config + + + 2 + False + + True + True + + + + + True + True + 3 + 1 + + + + + True + False + 12 + + + True + False + Port: + False - False + True + end + -1 + + + + + True + False + 4 + .. + + + False + True + 0 + + + + + Start + True + True + True + + + + False + True 4 + end 1 + + + 0 + True + True + 92 + 6 + 6 + 502 + + + + + False + True + end + 2 + + + + + True + False + .. + + + False + True + 4 + + - True + False True - 1 + 3 + 2 - + + + + + + + + WORD + + + DWORD + + + DWORD_SWAP + + + FLOAT + + + FLOAT_SWAP + + + BOOL + + + + False - 600 - 400 - testmodbus-server.png - True - - - - - - True + Add Variable or Flag + dialog + + False vertical - - - True + 2 + + False + end - + + gtk-add True - False - _Datei - True - - - True - False - - - gtk-new - True - False - True - True - - - - - - gtk-open - True - False - True - True - - - - - - gtk-save - True - False - True - True - - - - - - gtk-save-as - True - False - True - True - - - - - - True - False - - - - - gtk-quit - True - False - True - True - - - - - + True + True + True + True + + + True + True + 0 + - + + gtk-close True - False - _Hilfe - True - - - True - False - - - gtk-about - True - False - True - True - - - - - + True + True + True + True + + + True + True + 1 + False - True + False 0 - + True - True - True + False + vertical + 10 - - 200 + True False - vertical + 9 + True - + + Coils +[Read/Write] True - False - Netzwerk + True + False + True + True + adddlg_FC2 False @@ -506,393 +676,245 @@ Author: Steffen Pohle - - 200 + + Digital In +[Read] True True - in - 200 - - - True - True - False - - + False + True + True + adddlg_FC1 - True + False True 1 + + + Holding Reg. +[Read/Write] + True + True + False + True + True + adddlg_FC1 + + + False + True + 2 + + + + + Input Reg. +[Read] + True + True + False + True + True + adddlg_FC1 + + + False + True + 3 + + - False - False + False + True + 0 - + + True - True + False + 4 + 4 + 4 + 4 + 4 + 4 - + True False - vertical - - - True - True - in - - - True - True - - - - - - - - True - True - 0 - - - - - True - False - 3 - - - Req. Clear - True - True - True - - - - False - False - 0 - - - - - Enable All - True - True - True - - - - False - False - 1 - - - - - Enable Values - True - True - True - - - - False - False - 2 - - - - - Disable All - True - True - True - - - - False - True - 4 - - + end + Name : + + + 0 + 0 + + + + + True + True + + + 1 + 0 + + + + + True + False + end + Type : + + + 0 + 2 + + + + + True + False + end + Flag / Register : + + + 0 + 1 + + + + + True + False + end + Simmulation : + + + 0 + 3 + + + + + True + True + + + 1 + 1 + + + + + True + False + liststore1 + 0 + on + True + 0 + + + False - - False - True - 4 - 1 - - 1 + 1 + 2 - + True False - Registers + end + Value : - False + 0 + 4 True False - vertical - + True True - in - - - True - True - - - - - - True + False True - 1 + 0 - + + Change True - False - 5 - - - Add - True - True - True - - - - False - False - 0 - - - - - Edit - True - True - True - - - - False - False - 1 - - - - - Delete - True - True - True - - - - False - False - 2 - - + True + False + True False True - 5 - end - 2 + 1 - 1 - - - - - True - False - Values - - - 1 - False + 1 + 4 - - + True False - True - True - - - Auto Add Values - True + 0 + True + + NONE + SAW;t=60;min=0;max=1000 + SIN;t=60;min=-100;max=100; + PULSE;ton=10;toff=60 + + + True - False - True - - 0 - 0 - - - - - - - - - - - - - - - - - - - 2 - - - - - True - False - Config - 2 - False + 1 + 3 - - True - True - - - - - True - True - 3 - 1 - - - - - True - False - 12 - - - True - False - Port: - - - False - True - end - -1 - - - - - True - False - 4 - .. - - - False - True - 0 - - - - - Start - True - True - True - - False - True + False 4 - end 1 - - - 0 - True - True - 92 - 6 - 6 - 502 - - - - - False - True - end - 2 - - - - - True - False - .. - - - False - True - 4 - - - False + True True - 3 - 2 + 1