You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

467 lines
13 KiB

/////////////////////////////////////////////////////////////////////////////////
//
// client.cc is part of TestModbus-Client.
//
/////////////////////////////////////////////////////////////////////////////////
#include <sys/time.h>
#include "config.h"
#include "client.h"
#include "nwthread.h"
#include "modbus.h"
#define BUILDER_FILE "testmodbus-client.ui"
/************************************************************************************
* Global Variables
*/
GtkBuilder *_builder_ = NULL; // work around for the thread situation
gboolean mbcli_thread_cb_net(gpointer data); // callback thread network data
gboolean mbcli_thread_cb_error(gpointer data); // callback thread error
void mbcli_connect_btn_sensitive (bool enable); // enable/disable connect buttons
int old_refreshtime = -1;
int num_errors = 0; // error counter
int num_requests = 0; // request counter
NetworkThread netthread;
std::string to_hex8 (uint8_t v) {
char HEX[] = "0123456789ABCDEF";
int i = v;
int n;
std::string txt = "";
for (n = 0; n < 2; n++) {
txt = HEX[i%16]+ txt;
i = i / 16;
}
return txt;
}
std::string to_hex16 (uint16_t v) {
char HEX[] = "0123456789ABCDEF";
int i = v;
int n;
std::string txt = "";
for (n = 0; n < 4; n++) {
txt = HEX[i%16]+ txt;
i = i / 16;
}
return txt;
}
int main (int argc, char **argv) {
GtkBuilder *builder;
GObject *window;
#ifdef BUILD_WINDOWS
char buffer[16];
setvbuf (stdout, buffer, _IONBF, 16);
#endif
printf ("TestModbus-Client - %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
//
window = gtk_builder_get_object (builder, "testmodbus-client");
gtk_widget_show_all (GTK_WIDGET(window));
gtk_main ();
return 0;
}
void mbcli_cb_show(GtkWidget *widget, gpointer data) {
GtkTextTag *tag_addr;
GtkTextTag *tag_values;
GtkTextTag *tag_head;
GtkWidget *textview = GTK_WIDGET(gtk_builder_get_object (_builder_, "cli_RawResult"));
GtkTextBuffer *textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
GtkWidget *lbVersion = GTK_WIDGET(gtk_builder_get_object (_builder_, "cli_LabelVersion"));
gtk_label_set_label (GTK_LABEL(lbVersion), "Version: " VERSION);
tag_head = gtk_text_buffer_create_tag (textbuffer, "Head",
"foreground", "green", "style", PANGO_WEIGHT_HEAVY, "family", "Monospace", NULL);
tag_addr = gtk_text_buffer_create_tag (textbuffer, "Addr",
"foreground", "blue", "style", PANGO_WEIGHT_BOLD, "family", "Monospace", NULL);
tag_values = gtk_text_buffer_create_tag (textbuffer, "Data",
"foreground", "black", "style", PANGO_WEIGHT_NORMAL, "family", "Monospace", NULL);
}
/*
* got some data
*/
gboolean mbcli_thread_cb_net(gpointer data) {
NWTRawData *res = (NWTRawData*) data;
if (data == NULL) {
fprintf (stderr, "%s:%d %s data not set\n", __FILE__, __LINE__, __FUNCTION__);
return FALSE;
}
printf ("%s:%d %s issend:%d size:%d data:%p\n", __FILE__, __LINE__, __FUNCTION__,
res->issend, res->rawdatalen, res->rawdata);
if (res->issend) log_addtext(LOG_TX, (std::string)(char*)"TCP Send:", (NWTRawData *)data);
else log_addtext(LOG_TX, (std::string)(char*)"TCP Read:", (NWTRawData *)data);
if (res->rawdata != NULL) free (res->rawdata);
free (res);
return FALSE;
};
/*
* got some data
*/
gboolean mbcli_thread_cb_modbusdata(gpointer data) {
NWTReqResult *res = (NWTReqResult*) data;
GtkWidget *textview = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_RawResult"));
GtkTextBuffer *textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
GtkTextIter start, end;
char *tmpdata = (char *) res->data;
std::string text;
float f1, f2;
char tmp[255];
if (data == NULL) {
fprintf (stderr, "%s:%d %s data not set\n", __FILE__, __LINE__, __FUNCTION__);
return FALSE;
}
gtk_text_buffer_get_start_iter(textbuffer, &start);
gtk_text_buffer_get_end_iter(textbuffer, &end);
gtk_text_buffer_delete(textbuffer, &start, &end);
if (res->fc == 1 || res->fc == 2) {
//
// binary data
//
gtk_text_buffer_get_end_iter(textbuffer, &end);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &end, " Addr [HEX] Registers\n", -1, "Head", NULL);
for (int i = 0; i < res->cnt; i++) {
if ((i % 32) == 0) {
if (i > 0) {
text += "\n";
gtk_text_buffer_get_end_iter(textbuffer, &end);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &end, text.c_str(), -1, "Data", NULL);
}
text = "| ";
snprintf (tmp, 255, "%5d [%s] ", res->reg + i, to_hex16(res->reg+i).c_str());
gtk_text_buffer_get_end_iter(textbuffer, &end);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &end, tmp, -1, "Addr", NULL);
}
else if ((i % 16) == 0) text += " : ";
else if ((i % 8) == 0) text += " ";
if (tmpdata[i/8] & (1<<i%8)) text += "1";
else text += "0";
}
gtk_text_buffer_get_end_iter(textbuffer, &end);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &end, text.c_str(), -1, "Data", NULL);
}
else {
//
// integer values (16bit words)
//
gtk_text_buffer_get_end_iter(textbuffer, &end);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &end, " Addr RawData Integer Float Float(SW)\n", -1, "Head", NULL);
for (int i = 0; i < res->cnt; i++) {
// add addr to textbuffer
snprintf (tmp, 255, "%5d ", res->reg + i);
gtk_text_buffer_get_end_iter(textbuffer, &end);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &end, tmp, -1, "Addr", NULL);
// add data to textbuffer
text = to_hex16((int) res->data[i]);
if ((res->reg + i)%2 == 0 && i < res->cnt-1) {
memcpy (((char*)&f1) + 0, &res->data[i], 2);
memcpy (((char*)&f1) + 2, &res->data[i+1], 2);
memcpy (((char*)&f2) + 2, &res->data[i], 2);
memcpy (((char*)&f2) + 0, &res->data[i+1], 2);
snprintf (tmp, 255, " | %6d | %9.3g | %9.3g", res->data[i], f1, f2);
text = text + tmp;
}
else {
snprintf (tmp, 255, " | %6d | | ", res->data[i]);
text = text + tmp;
}
gtk_text_buffer_get_end_iter(textbuffer, &end);
gtk_text_buffer_insert_with_tags_by_name(textbuffer, &end, text.c_str(), -1, "Data", NULL);
gtk_text_buffer_get_end_iter(textbuffer, &end);
gtk_text_buffer_insert(textbuffer, &end, (char *) "\n", -1);
}
}
free (res);
return FALSE;
};
/*
* display error if somethin happened within a thread
*/
gboolean mbcli_thread_cb_error(gpointer data) {
if (data == NULL) {
displayerror ("unknown error");
}
else {
std::string text = (char*)data;
free (data);
displayerror (text);
}
return FALSE;
};
void displayerror (std::string error) {
GtkWidget *dialog;
GtkWidget *window = GTK_WIDGET (gtk_builder_get_object (_builder_, "testmodbus-client"));
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);
}
/*
* network thread callback
*/
gboolean mbcli_thread_cb_status(gpointer data) {
NWTMessage *msg = (NWTMessage *) data;
mbcli_connect_btn_sensitive (netthread.GetState() == NWT_nothing);
if (msg) {
log_addtext(msg->type, (std::string)(char*)msg->txt, NULL);
free (msg);
}
return FALSE;
};
void mbcli_connect_btn_sensitive (bool enable) {
GtkWidget *txthost = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_host"));
GtkWidget *txtport = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_port"));
GtkWidget *btnconnect = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_btnConnect"));
GtkWidget *btndisconnect = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_btnDisconnect"));
gtk_widget_set_sensitive(txthost, enable);
gtk_widget_set_sensitive(txtport, enable);
gtk_widget_set_sensitive(btnconnect, enable);
gtk_widget_set_sensitive(btndisconnect, !enable);
};
void mbcli_cb_connect (GtkWidget *widget, gpointer data) {
GtkWidget *txthost = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_host"));
GtkWidget *txtport = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_port"));
mbcli_connect_btn_sensitive(false);
netthread.Connect(gtk_entry_get_text(GTK_ENTRY(txthost)), gtk_entry_get_text(GTK_ENTRY(txtport)));
};
void mbcli_cb_disconnect (GtkWidget *widget, gpointer data) {
GtkWidget *tb_update = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_Update"));
GtkWidget *tb_reconnect = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_Reconnect"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb_update), FALSE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb_reconnect), FALSE);
netthread.Disconnect();
};
/*
* returns the currently selected FC
*/
int mbcli_get_FC () {
GtkWidget *FC_n[4] = { NULL, NULL, NULL, NULL };
int fc = 0, i = 0;
FC_n[fc++] = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_FC1"));
FC_n[fc++] = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_FC2"));
FC_n[fc++] = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_FC3"));
FC_n[fc++] = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_FC4"));
for (i = 0, fc = -1; i < 4; i++)
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(FC_n[i]))) fc = i+1;
return fc;
}
/*
* returns the refresh time
*/
int mbcli_get_refreshtime() {
GtkWidget *rd_refresh = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_refreshtime"));
return atoi(gtk_entry_get_text(GTK_ENTRY(rd_refresh)));
}
/*
* returns the register
*/
int mbcli_get_register() {
GtkWidget *rd_refresh = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_register"));
return atoi(gtk_entry_get_text(GTK_ENTRY(rd_refresh)));
}
/*
* returns the number
*/
int mbcli_get_number() {
GtkWidget *rd_refresh = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_number"));
return atoi(gtk_entry_get_text(GTK_ENTRY(rd_refresh)));
}
/*
* returns the unitid
*/
int mbcli_get_unitid() {
GtkWidget *rd_refresh = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_unitid"));
return atoi(gtk_entry_get_text(GTK_ENTRY(rd_refresh)));
}
/*
* returns status of the update checkbox
*/
int mbcli_get_update () {
GtkWidget *tb_update = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_Update"));
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tb_update))) return 1;
return 0;
}
/*
* returns status of the reconnect checkbox
*/
int mbcli_get_autoconnect () {
GtkWidget *tb_update = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_Reconnect"));
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tb_update))) return 1;
return 0;
}
/*
* sets the update checkbox
*/
void mbcli_set_update (int active) {
GtkWidget *tb_update = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_Update"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb_update), active);
}
/*
* returns status of the dont_use_FC1516 button
*/
int mbcli_get_FC1516 () {
// GtkWidget *tb_update = GTK_WIDGET (gtk_builder_get_object (_builder_, "cli_dontuse_FC1516"));
// if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tb_update))) return 1;
return 0;
}
/*
* toglebutton to read some data is changed.
*/
void mbcli_cb_updateread (GtkToggleButton *togglebutton, gpointer user_data) {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (mbcli_get_update()) {
num_requests = 0;
num_errors = 0;
g_timeout_add(mbcli_get_refreshtime(), mbcli_refreshtimeout, NULL);
if (mbcli_get_FC1516()) mbcli_set_update(0);
}
}
gboolean mbcli_refreshtimeout(gpointer data) {
int timeout = mbcli_get_refreshtime();
int update = mbcli_get_update();
int fc1516 = mbcli_get_FC1516();
int fc = mbcli_get_FC();
int reg = mbcli_get_register();
int num = mbcli_get_number();
int unitid = mbcli_get_unitid();
int state = netthread.GetState();
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (state == NWT_running) {
num_requests++;
if (netthread.SendRequestRead(unitid, fc, reg, num) == 0) {
num_errors++;
log_addtext(LOG_ERROR, (std::string)(char*)"Busy, another request in progress. Close connection.", NULL);
netthread.Disconnect();
}
else {
log_addtext(LOG_TX, (std::string)(char*)"Request Send", NULL);
}
}
else
if (mbcli_get_autoconnect()) mbcli_cb_connect(NULL, NULL); // reconnect if needed
//
// need to continue
if (fc1516) {
mbcli_set_update(0);
return FALSE;
}
if (!update) return FALSE;
//
// refresh time changed?
if (timeout != old_refreshtime) {
old_refreshtime = timeout;
g_timeout_add(timeout, mbcli_refreshtimeout, NULL);
return FALSE;
}
return TRUE; // continue with timer
};