///////////////////////////////////////////////////////////////////////////////// // // nwthread.cc is part of TestModbus-Client. // ///////////////////////////////////////////////////////////////////////////////// #include "tcp.h" #include #if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) #else #include /* close() */ #endif #include #include /* memset() */ #include #include #include #include #include #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; req_mbh.length = 6; printf ("%s:%d build request lenght:%d fc:%d offset:%d number:%d\n", __FILE__, __LINE__, req_mbh.length, req_mbh.fc, req_mbh.offset, req_mbh.number); UnLock(); return 1; } void NetworkThread::GuiSendStatustext(int type, char *txt) { NWTMessage *msg = NULL; if (txt) { int l = strlen (txt)+1; msg = (NWTMessage *) malloc (sizeof (NWTMessage) + l); if (msg == NULL) { fprintf (stderr, "%s:%d %s could not allocate memory : %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno)); exit(-1); } msg->type = type; memcpy (msg->txt, txt, l); 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) { for (cnt = 0, i = 9; i < len && cnt < 0x100 && cnt < mbh_rq->number; cnt++) { memcpy (&res->data[cnt], buffer+i, 2); i += 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_modbusdata, (void *) res); } /* * data must be Thread Locked already * request information must be set already * send a copy of the data to the logging window. */ void NetworkThread::BuildAndSendReadReq(char *buffer, int size) { char *pos; NWTRawData *raw = (NWTRawData*) malloc(sizeof (NWTRawData)); if (raw == NULL) { fprintf (stderr, "%s:%d %s could not allocate memory: %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno)); exit (-1); } // req_mbh.transid++; // with modbus tcp/ip it should always be set to 0 req_mbh.transid = 0; pos = modbustcp_pkt_headerrq(buffer, size, &req_mbh); raw->rawdatalen = pos-buffer; tcp.Write(buffer, raw->rawdatalen); raw->issend = 1; raw->rawdata = (char *) malloc (raw->rawdatalen); if (raw->rawdata == NULL) { fprintf (stderr, "%s:%d %s could not allocate memory: %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno)); exit (-1); } memcpy (raw->rawdata, buffer, raw->rawdatalen); printf ("%s:%d %s send to log issend:%d size:%d data:%p\n", __FILE__, __LINE__, __FUNCTION__, raw->issend, raw->rawdatalen, raw->rawdata); gdk_threads_add_idle(mbcli_thread_cb_net, (void *) raw); }; #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(LOG_CONNECT, (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(LOG_ERROR, strerror(errno)); SetState(NWT_nothing); printf ("%s:%d NetworkThread::Thread Finished on connect\n", __FILE__, __LINE__); return; } if (i == 1) GuiSendStatustext(LOG_CONNECT, (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(); len = tcp.Read(buffer, BUFFER_SIZE); if (len < 0) { state = NWT_close; } else { // send to logging window NWTRawData *raw = (NWTRawData*)malloc (sizeof(NWTRawData)); if (raw == NULL) { fprintf (stderr, "%s:%d %s cloud not allocate memory for RAW info data\n", __FILE__, __LINE__, __FUNCTION__); exit (-1); } raw->issend = 0; raw->rawdatalen = len; raw->rawdata = (char*) malloc (len); if (raw->rawdata == NULL) { fprintf (stderr, "%s:%d %s cloud not allocate memory for RAW data\n", __FILE__, __LINE__, __FUNCTION__); exit (-1); } memcpy (raw->rawdata, buffer, len); gdk_threads_add_idle(mbcli_thread_cb_net, (void *) raw); // // 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; UnLock(); 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(LOG_ERROR, 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(LOG_ERROR, strerror(errno)); } req_mbh.fc = 0; } UnLock(); } // // main loop stopped, clean up i = GetState(); switch (i) { case NWT_error: GuiSendStatustext(LOG_ERROR, strerror(errno)); tcp.Close(); break; case NWT_close: GuiSendStatustext(LOG_CLOSE, (char*)"Connection Closed"); tcp.Close(); break; default: GuiSendStatustext(LOG_ERROR, (char*)"Unknown Error"); tcp.Close(); break; } SetState(NWT_nothing); printf ("%s:%d NetworkThread::Thread Finished\n", __FILE__, __LINE__); };