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.
320 lines
6.6 KiB
320 lines
6.6 KiB
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// nwthread.cc is part of TestModbus-Client.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <errno.h>
|
|
#include <glib.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <string>
|
|
#include <string.h>
|
|
|
|
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
|
|
#else
|
|
#include <unistd.h> /* close() */
|
|
#endif
|
|
|
|
#include "tcp.h"
|
|
#include "nwthread.h"
|
|
#include "client.h"
|
|
|
|
extern NetworkThread netthread;
|
|
|
|
// c / C++ wrapper
|
|
gpointer _Thread (gpointer data) {
|
|
netthread.Thread ();
|
|
return NULL;
|
|
};
|
|
|
|
|
|
|
|
NetworkThread::NetworkThread() {
|
|
g_mutex_init (&mutex);
|
|
thread = NULL;
|
|
state = 0;
|
|
host = "";
|
|
port = "";
|
|
memset (&req_mbh, 0x0, sizeof(modbustcp_rq_header));
|
|
req_new = 0;
|
|
memset (&req_time, 0x0, sizeof (req_time));
|
|
req_timeout = 0;
|
|
};
|
|
|
|
NetworkThread::~NetworkThread() {
|
|
Disconnect();
|
|
g_mutex_clear(&mutex);
|
|
};
|
|
|
|
void NetworkThread::Lock() {
|
|
g_mutex_lock(&mutex);
|
|
};
|
|
|
|
void NetworkThread::UnLock() {
|
|
g_mutex_unlock(&mutex);
|
|
};
|
|
|
|
|
|
/*
|
|
* start network thread and connect
|
|
*/
|
|
int NetworkThread::Connect(std::string dest_host, std::string dest_port) {
|
|
printf ("%s:%d NetworkThread::Connect to %s:%s\n", __FILE__, __LINE__, dest_host.c_str(), dest_port.c_str());
|
|
|
|
//
|
|
// if there is an open connection close this one first
|
|
if (GetState() != NWT_nothing) Disconnect();
|
|
|
|
host = dest_host;
|
|
port = dest_port;
|
|
thread = g_thread_new("network thread", _Thread, NULL);
|
|
|
|
return -1;
|
|
};
|
|
|
|
|
|
/*
|
|
* stop network connection
|
|
*/
|
|
int NetworkThread::Disconnect() {
|
|
printf ("%s:%d NetworkThread::Disconnect\n", __FILE__, __LINE__);
|
|
|
|
Lock();
|
|
if (state != NWT_nothing) state = NWT_close;
|
|
UnLock();
|
|
while (GetState() != NWT_nothing) { usleep(1000); };
|
|
|
|
if (thread != NULL) g_thread_unref(thread);
|
|
thread = NULL;
|
|
|
|
return 0;
|
|
};
|
|
|
|
|
|
void NetworkThread::SetState(int s) {
|
|
Lock();
|
|
state = s;
|
|
UnLock();
|
|
};
|
|
|
|
|
|
int NetworkThread::GetState() {
|
|
int s;
|
|
|
|
Lock();
|
|
s = state;
|
|
UnLock();
|
|
|
|
return s;
|
|
};
|
|
|
|
|
|
int NetworkThread::SendRequestRead(int unitid, int fc, int reg, int num) {
|
|
Lock();
|
|
if (req_mbh.fc != 0 || req_new != 0) {
|
|
printf ("%s%d %s req_new: %d req_mbh.fc: %d\n", __FILE__, __LINE__, __FUNCTION__,
|
|
req_new, req_mbh.fc);
|
|
UnLock();
|
|
return 0;
|
|
}
|
|
|
|
req_new = 1;
|
|
req_mbh.fc = fc;
|
|
req_mbh.offset = reg;
|
|
req_mbh.number = num;
|
|
req_mbh.uid = unitid;
|
|
|
|
printf ("%s:%d build request fc:%d offset:%d number:%d\n", __FILE__, __LINE__,
|
|
req_mbh.fc, req_mbh.offset, req_mbh.number);
|
|
|
|
UnLock();
|
|
return 1;
|
|
}
|
|
|
|
|
|
void NetworkThread::GuiSendStatustext(char *txt) {
|
|
char *msg = NULL;
|
|
if (txt) {
|
|
int l = strlen (txt)+1;
|
|
msg = (char*) malloc(l+1);
|
|
memset (msg, 0x0, l+1);
|
|
strncpy (msg, txt, strlen (txt));
|
|
}
|
|
gdk_threads_add_idle(mbcli_thread_cb_status, msg);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* send response to the gui.
|
|
* the data block must be freed.
|
|
*/
|
|
void NetworkThread::GuiSendResult(char *buffer, int len, modbustcp_rq_header *mbh_rq) {
|
|
NWTReqResult *res = (NWTReqResult*) malloc(sizeof (NWTReqResult));
|
|
int i, cnt;
|
|
uint16_t tmp;
|
|
|
|
res->fc = mbh_rq->fc;
|
|
res->cnt = mbh_rq->number;
|
|
res->reg = mbh_rq->offset;
|
|
|
|
if (res->fc == 1 || res->fc == 2) {
|
|
|
|
}
|
|
else {
|
|
for (cnt = 0, i = 9; i < len && cnt < 0x100 && cnt < mbh_rq->number; cnt++) {
|
|
memcpy (&tmp, buffer+i, 2);
|
|
res->data[cnt] = ntohs(tmp);
|
|
i += 2;
|
|
}
|
|
}
|
|
|
|
gdk_threads_add_idle(mbcli_thread_cb_net, (void *) res);
|
|
}
|
|
|
|
/*
|
|
* data must be Thread Locked already
|
|
* request information must be set already
|
|
*/
|
|
void NetworkThread::BuildAndSendReadReq(char *buffer, int size) {
|
|
char *pos;
|
|
|
|
req_mbh.length = 0;
|
|
req_mbh.transid++;
|
|
pos = modbustcp_pkt_headerrq(buffer, size, &req_mbh);
|
|
tcp.Write(buffer, (pos-buffer));
|
|
};
|
|
|
|
|
|
#define BUFFER_SIZE 0x10000
|
|
void NetworkThread::Thread() {
|
|
int i;
|
|
long int len;
|
|
char buffer[BUFFER_SIZE];
|
|
modbustcp_res_header in_mbh; // incomming mbh data
|
|
char *pos;
|
|
|
|
printf ("%s:%d NetworkThread::Thread Started\n", __FILE__, __LINE__);
|
|
|
|
GuiSendStatustext((char*)"connecting");
|
|
Lock();
|
|
req_mbh.fc = 0;
|
|
req_mbh.transid = 0;
|
|
UnLock();
|
|
|
|
//
|
|
// connect to ip address
|
|
//
|
|
SetState (NWT_connect);
|
|
i = tcp.Connect(host, port);
|
|
if (i < 0) {
|
|
//
|
|
// error on connect, stop thread.
|
|
GuiSendStatustext(strerror(errno));
|
|
|
|
SetState(NWT_nothing);
|
|
GuiSendStatustext(NULL);
|
|
printf ("%s:%d NetworkThread::Thread Finished on connect\n", __FILE__, __LINE__);
|
|
|
|
return;
|
|
}
|
|
if (i == 1) GuiSendStatustext((char*)"connected");
|
|
// FIXME: else: still connecting need some timeout and checks.
|
|
|
|
SetState(NWT_running);
|
|
//
|
|
// connection established
|
|
// go into the main loop
|
|
while (GetState() == NWT_running) {
|
|
//
|
|
// new request?
|
|
Lock();
|
|
if (req_new == 1) {
|
|
BuildAndSendReadReq(buffer, BUFFER_SIZE);
|
|
req_new = 0;
|
|
}
|
|
UnLock();
|
|
|
|
//
|
|
// check for new data
|
|
i = tcp.IsData(10);
|
|
if (i < 0) {
|
|
SetState(NWT_error);
|
|
break;
|
|
}
|
|
else if (i == 0) continue;
|
|
|
|
//
|
|
// we got some data
|
|
Lock();
|
|
printf ("read data\n");
|
|
len = tcp.Read(buffer, BUFFER_SIZE);
|
|
if (len < 0) state = NWT_close;
|
|
else {
|
|
//
|
|
// we could get some data, if busy reset connection
|
|
if (req_mbh.fc == 0) {
|
|
// got data without request? - Protokol error
|
|
errno = EPROTO;
|
|
state = NWT_error;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// return data
|
|
if ((pos = modbustcp_unpkt_headerres(&in_mbh, buffer, len)) == NULL) {
|
|
printf ("%s:%d error on reading modbus header (%s)\n", __FILE__, __LINE__, strerror(errno));
|
|
errno = EPROTO;
|
|
GuiSendStatustext(strerror(errno));
|
|
}
|
|
//
|
|
// process data is valid
|
|
if ((in_mbh.fc == 1 || in_mbh.fc == 2) &&
|
|
(in_mbh.fc == req_mbh.fc &&
|
|
in_mbh.number == (((req_mbh.number-1) / 8)+1) &&
|
|
in_mbh.transid == req_mbh.transid)) {
|
|
GuiSendResult(buffer, len, &req_mbh);
|
|
req_mbh.fc = 0;
|
|
}
|
|
else if ((in_mbh.fc == 3 || in_mbh.fc == 4) &&
|
|
(in_mbh.fc == req_mbh.fc &&
|
|
in_mbh.number/2 == req_mbh.number &&
|
|
in_mbh.transid == req_mbh.transid)) {
|
|
GuiSendResult(buffer, len, &req_mbh);
|
|
req_mbh.fc = 0;
|
|
}
|
|
else {
|
|
printf ("%s:%d incorrect data: fc: %d == %d number: %d == %d(%d) transid: %d == %d\n", __FILE__, __LINE__,
|
|
in_mbh.fc, req_mbh.fc, in_mbh.number, (((req_mbh.number-1) / 8)+1), req_mbh.number, in_mbh.transid, req_mbh.transid);
|
|
errno = EPROTO;
|
|
GuiSendStatustext(strerror(errno));
|
|
}
|
|
req_mbh.fc = 0;
|
|
}
|
|
UnLock();
|
|
}
|
|
|
|
//
|
|
// main loop stopped, clean up
|
|
i = GetState();
|
|
switch (i) {
|
|
case NWT_error:
|
|
GuiSendStatustext(strerror(errno));
|
|
tcp.Close();
|
|
break;
|
|
case NWT_close:
|
|
GuiSendStatustext((char*)"Connection Closed");
|
|
tcp.Close();
|
|
break;
|
|
default:
|
|
GuiSendStatustext((char*)"Unknown Error");
|
|
tcp.Close();
|
|
break;
|
|
}
|
|
|
|
SetState(NWT_nothing);
|
|
printf ("%s:%d NetworkThread::Thread Finished\n", __FILE__, __LINE__);
|
|
};
|
|
|