diff --git a/bin/Makefile.am b/bin/Makefile.am index e525a3c..e7ab902 100644 --- a/bin/Makefile.am +++ b/bin/Makefile.am @@ -13,7 +13,7 @@ VERSION = @VERSION@ AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) -I$(top_srcdir)/src noinst_HEADERS = -bin_PROGRAMS = mbus-tcp-scan mbus-tcp-request-data \ +bin_PROGRAMS = mbus-tcp-scan mbus-tcp-request-data mbus-tcp-request-data-multi-reply \ mbus-tcp-select-secondary mbus-tcp-scan-secondary \ mbus-serial-scan mbus-serial-request-data \ mbus-serial-select-secondary mbus-serial-scan-secondary \ @@ -28,6 +28,10 @@ mbus_tcp_request_data_LDFLAGS = -L$(top_builddir)/mbus mbus_tcp_request_data_LDADD = -lmbus mbus_tcp_request_data_SOURCES = mbus-tcp-request-data.c +mbus_tcp_request_data_multi_reply_LDFLAGS = -L$(top_builddir)/mbus +mbus_tcp_request_data_multi_reply_LDADD = -lmbus +mbus_tcp_request_data_multi_reply_SOURCES = mbus-tcp-request-data-multi-reply.c + mbus_tcp_select_secondary_LDFLAGS = -L$(top_builddir)/mbus mbus_tcp_select_secondary_LDADD = -lmbus mbus_tcp_select_secondary_SOURCES = mbus-tcp-select-secondary.c diff --git a/bin/mbus-serial-request-data.c b/bin/mbus-serial-request-data.c index d7facfc..336cc35 100644 --- a/bin/mbus-serial-request-data.c +++ b/bin/mbus-serial-request-data.c @@ -133,7 +133,13 @@ main(int argc, char **argv) { mbus_frame_print(&reply); } - mbus_frame_data_parse(&reply, &reply_data); + + if (mbus_frame_data_parse(&reply, &reply_data) == -1) + { + fprintf(stderr, "M-bus data parse error.\n"); + return 1; + } + printf("%s", mbus_frame_data_xml(&reply_data)); // manual free diff --git a/bin/mbus-tcp-request-data-multi-reply.c b/bin/mbus-tcp-request-data-multi-reply.c new file mode 100644 index 0000000..5a636e6 --- /dev/null +++ b/bin/mbus-tcp-request-data-multi-reply.c @@ -0,0 +1,153 @@ +//------------------------------------------------------------------------------ +// Copyright (C) 2011, Robert Johansson, Raditex AB +// All rights reserved. +// +// rSCADA +// http://www.rSCADA.se +// info@rscada.se +// +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include +#include + +static int debug = 0; + +//------------------------------------------------------------------------------ +// Execution starts here: +//------------------------------------------------------------------------------ +int +main(int argc, char **argv) +{ + mbus_frame reply, *reply_iter; + mbus_frame_data reply_data; + mbus_handle *handle = NULL; + + char *host, *addr_str, matching_addr[16]; + int port, address; + + memset((void *)&reply, 0, sizeof(mbus_frame)); + memset((void *)&reply_data, 0, sizeof(mbus_frame_data)); + + if (argc == 4) + { + host = argv[1]; + port = atoi(argv[2]); + addr_str = argv[3]; + debug = 0; + } + else if (argc == 5 && strcmp(argv[1], "-d") == 0) + { + host = argv[2]; + port = atoi(argv[3]); + addr_str = argv[4]; + debug = 1; + } + else + { + fprintf(stderr, "usage: %s [-d] host port mbus-address\n", argv[0]); + fprintf(stderr, " optional flag -d for debug printout\n"); + return 0; + } + + if ((handle = mbus_connect_tcp(host, port)) == NULL) + { + fprintf(stderr, "Failed to setup connection to M-bus gateway\n"); + return 1; + } + + if (strlen(addr_str) == 16) + { + // secondary addressing + + int probe_ret; + + probe_ret = mbus_probe_secondary_address(handle, addr_str, matching_addr); + + if (probe_ret == MBUS_PROBE_COLLISION) + { + fprintf(stderr, "%s: Error: The address mask [%s] matches more than one device.\n", __PRETTY_FUNCTION__, addr_str); + return 1; + } + else if (probe_ret == MBUS_PROBE_NOTHING) + { + fprintf(stderr, "%s: Error: The selected secondary address does not match any device [%s].\n", __PRETTY_FUNCTION__, addr_str); + return 1; + } + else if (probe_ret == MBUS_PROBE_ERROR) + { + fprintf(stderr, "%s: Error: Failed to probe secondary address [%s].\n", __PRETTY_FUNCTION__, addr_str); + return 1; + } + // else MBUS_PROBE_SINGLE + + address = 253; + } + else + { + // primary addressing + address = atoi(addr_str); + } + + /* + if (mbus_send_request_frame(handle, address) == -1) + { + fprintf(stderr, "Failed to send M-Bus request frame.\n"); + return 1; + } + + if (mbus_recv_frame(handle, &reply) == -1) + { + fprintf(stderr, "Failed to receive M-Bus response frame.\n"); + return 1; + } + */ + + // instead of the send and recv, use this sendrecv function that + // takes care of the possibility of multi-telegram replies (limit = 16 frames) + if (mbus_sendrecv_request(handle, address, &reply, 16) == -1) + { + fprintf(stderr, "Failed to send/receive M-Bus request.\n"); + return 1; + } + + // + // dump hex data if debug is true + // + if (debug) + { + mbus_frame_print(&reply); + } + + // + // here, figure out how the list of frames should be merged into a single + // XML document, but for now let's just dump all frames as independent XMLs + // + for (reply_iter = &reply; reply_iter; reply_iter = reply_iter->next) + { + if (mbus_frame_data_parse(reply_iter, &reply_data) == -1) + { + fprintf(stderr, "M-bus data parse error.\n"); + return 1; + } + printf("%s", mbus_frame_data_xml(&reply_data)); + + // manual free, all records in the list + if (reply_data.data_var.record) + { + mbus_data_record_free(reply_data.data_var.record); + } + } + + mbus_disconnect(handle); + return 0; +} + + + diff --git a/bin/mbus-tcp-request-data.c b/bin/mbus-tcp-request-data.c index f0f93fd..274829c 100644 --- a/bin/mbus-tcp-request-data.c +++ b/bin/mbus-tcp-request-data.c @@ -32,6 +32,9 @@ main(int argc, char **argv) char *host, *addr_str, matching_addr[16]; int port, address; + memset((void *)&reply, 0, sizeof(mbus_frame)); + memset((void *)&reply_data, 0, sizeof(mbus_frame_data)); + if (argc == 4) { host = argv[1]; @@ -115,7 +118,12 @@ main(int argc, char **argv) { mbus_frame_print(&reply); } - mbus_frame_data_parse(&reply, &reply_data); + + if (mbus_frame_data_parse(&reply, &reply_data) == -1) + { + fprintf(stderr, "M-bus data parse error.\n"); + return 1; + } printf("%s", mbus_frame_data_xml(&reply_data)); // manual free diff --git a/mbus/mbus-protocol-aux.c b/mbus/mbus-protocol-aux.c index fa70273..f7c241b 100644 --- a/mbus/mbus-protocol-aux.c +++ b/mbus/mbus-protocol-aux.c @@ -24,6 +24,8 @@ #define MBUS_DEBUG(...) #endif +static int debug = 1; + typedef struct _mbus_variable_vif { unsigned vif; double exponent; @@ -1347,6 +1349,143 @@ mbus_send_request_frame(mbus_handle * handle, int address) return retval; } +//------------------------------------------------------------------------------ +// send a request from master to slave and collect the reply (replies) +// from the slave. +//------------------------------------------------------------------------------ +int +mbus_sendrecv_request(mbus_handle *handle, int address, mbus_frame *reply, int max_frames) +{ + int retval = 0, more_frames = 1; + mbus_frame_data reply_data; + mbus_frame *frame, *next_frame; + int frame_count = 0; + + frame = mbus_frame_new(MBUS_FRAME_TYPE_SHORT); + + if (frame == NULL) + { + MBUS_ERROR("%s: failed to allocate mbus frame.\n", __PRETTY_FUNCTION__); + return -1; + } + + frame->control = MBUS_CONTROL_MASK_REQ_UD2 | MBUS_CONTROL_MASK_DIR_M2S | MBUS_CONTROL_MASK_FCV; + frame->address = address; + + if (debug) + printf("%s: debug: sending request frame\n", __PRETTY_FUNCTION__); + + if (mbus_send_frame(handle, frame) == -1) + { + MBUS_ERROR("%s: failed to send mbus frame.\n", __PRETTY_FUNCTION__); + mbus_frame_free(frame); + retval = -1; + } + + // + // continue to read until no more records are available (usually only one + // reply frame, but can be more for so-called multi-telegram replies) + // + next_frame = reply; + + while (more_frames) + { + frame_count++; + + if ((max_frames > 0) && + (frame_count > max_frames)) + { + // only readout max_frames + break; + } + + if (debug) + printf("%s: debug: receiving response frame #%d\n", __PRETTY_FUNCTION__, frame_count); + + if (mbus_recv_frame(handle, next_frame) == -1) + { + MBUS_ERROR("%s: Failed to receive M-Bus response frame.\n", __PRETTY_FUNCTION__); + retval = 1; + break; + } + + // + // We need to parse the data in the received frame to be able to tell + // if more records are available or not. + // + if (mbus_frame_data_parse(next_frame, &reply_data) == -1) + { + MBUS_ERROR("%s: M-bus data parse error.\n", __PRETTY_FUNCTION__); + retval = 1; + break; + } + + // + // Continue a cycle of sending requests and reading replies until the + // reply do not have DIF=0x1F in the last record (which signals that + // more records are available. + // + + if (reply_data.type == MBUS_DATA_TYPE_FIXED) + { + // only single frame replies for FIXED type frames + more_frames = 0; + } + else + { + more_frames = 0; + + if (reply_data.data_var.more_records_follow) + { + if (debug) + printf("%s: debug: expecting more frames\n", __PRETTY_FUNCTION__); + + more_frames = 1; + + // allocate new frame and increment next_frame pointer + next_frame->next = mbus_frame_new(MBUS_FRAME_TYPE_ANY); + + if (next_frame->next == NULL) + { + MBUS_ERROR("%s: failed to allocate mbus frame.\n", __PRETTY_FUNCTION__); + retval = -1; + more_frames = 0; + } + + next_frame = next_frame->next; + + // need to send a new request and receive another reply + + if (debug) + printf("%s: debug: resending request frame\n", __PRETTY_FUNCTION__); + + // toogle FCB bit before + frame->control ^= MBUS_CONTROL_MASK_FCB; + if (mbus_send_frame(handle, frame) == -1) + { + MBUS_ERROR("%s: failed to send mbus frame.\n", __PRETTY_FUNCTION__); + retval = -1; + more_frames = 0; + } + } + else + { + if (debug) + printf("%s: debug: no more frames\n", __PRETTY_FUNCTION__); + } + } + + if (reply_data.data_var.record) + { + // free's up the whole list + mbus_data_record_free(reply_data.data_var.record); + } + } + + mbus_frame_free(frame); + return retval; +} + //------------------------------------------------------------------------------ // send a data request packet to from master to slave diff --git a/mbus/mbus-protocol-aux.h b/mbus/mbus-protocol-aux.h index 501bb87..a55e694 100644 --- a/mbus/mbus-protocol-aux.h +++ b/mbus/mbus-protocol-aux.h @@ -198,7 +198,7 @@ int mbus_send_select_frame(mbus_handle * handle, const char *secondary_addr_str) int mbus_send_switch_baudrate_frame(mbus_handle * handle, int address, int baudrate); /** - * Sends data request frame to given slave using "unified" handle + * Sends request frame to given slave using "unified" handle * * @param handle Initialized handle * @param address Address (0-255) @@ -207,6 +207,19 @@ int mbus_send_switch_baudrate_frame(mbus_handle * handle, int address, int baudr */ int mbus_send_request_frame(mbus_handle * handle, int address); +/** + * Sends a request and read replies until no more records available + * or limit is reached. + * + * @param handle Initialized handle + * @param address Address (0-255) + * @param reply pointer to an mbus frame for the reply + * @param max_frames limit of frames to readout (0 = no limit) + * + * @return Zero when successful. + */ +int mbus_sendrecv_request(mbus_handle *handle, int address, mbus_frame *reply, int max_frames); + /** * Sends ping frame to given slave using "unified" handle * diff --git a/mbus/mbus-protocol.c b/mbus/mbus-protocol.c index 83edffc..b46f83a 100644 --- a/mbus/mbus-protocol.c +++ b/mbus/mbus-protocol.c @@ -130,6 +130,9 @@ mbus_frame_free(mbus_frame *frame) { if (frame) { + if (frame->next != NULL) + mbus_frame_free(frame->next); + free(frame); return 0; } @@ -2231,6 +2234,7 @@ mbus_data_variable_parse(mbus_frame *frame, mbus_data_variable *data) { // parse header data->nrecords = 0; + data->more_records_follow = 0; i = sizeof(mbus_data_variable_header); if(frame->data_size < i) return -1; @@ -2253,8 +2257,13 @@ mbus_data_variable_parse(mbus_frame *frame, mbus_data_variable *data) // DIF record->drh.dib.dif = frame->data[i]; - if ((record->drh.dib.dif & 0xFF) == 0x0F || (record->drh.dib.dif & 0xFF) == 0x1F) + if (record->drh.dib.dif == 0x0F || record->drh.dib.dif == 0x1F) { + if ((record->drh.dib.dif & 0xFF) == 0x1F) + { + data->more_records_follow = 1; + } + i++; // just copy the remaining data as it is vendor specific record->data_len = frame->data_size - i; @@ -2316,7 +2325,7 @@ mbus_data_variable_parse(mbus_frame *frame, mbus_data_variable *data) } // re-calculate data length, if of variable length type - if((record->drh.dib.dif & 0x0D) == 0x0D) // flag for variable length data + if ((record->drh.dib.dif & 0x0F) == 0x0D) // flag for variable length data { if(frame->data[i] <= 0xBF) record->data_len = frame->data[i++]; @@ -2708,6 +2717,11 @@ mbus_data_variable_print(mbus_data_variable *data) printf("%.2X ", record->data[j]); } printf("\n"); + + if (record->drh.dib.dif == 0x1F) + { + printf("%s: More records follow in next telegram\n", __PRETTY_FUNCTION__); + } continue; } diff --git a/mbus/mbus-protocol.h b/mbus/mbus-protocol.h index bcc4c9e..9398ca0 100644 --- a/mbus/mbus-protocol.h +++ b/mbus/mbus-protocol.h @@ -86,6 +86,8 @@ typedef struct _mbus_frame { //mbus_frame_data frame_data; + void *next; // pointer to next mbus_frame for multi-telegram replies + } mbus_frame; typedef struct _mbus_slave_data { @@ -175,6 +177,8 @@ typedef struct _mbus_data_variable { u_char *data; size_t data_len; + u_char more_records_follow; + // are these needed/used? u_char mdh; u_char *mfg_data; diff --git a/mbus/mbus-tcp.c b/mbus/mbus-tcp.c index cf594b6..33ad5f7 100644 --- a/mbus/mbus-tcp.c +++ b/mbus/mbus-tcp.c @@ -90,7 +90,7 @@ mbus_tcp_connect(char *host, int port) } // Set a timeout - time_out.tv_sec = 2; //seconds + time_out.tv_sec = 4; //seconds time_out.tv_usec = 0; setsockopt(handle->sock, SOL_SOCKET, SO_SNDTIMEO, &time_out, sizeof(time_out)); setsockopt(handle->sock, SOL_SOCKET, SO_RCVTIMEO, &time_out, sizeof(time_out)); @@ -167,18 +167,13 @@ mbus_tcp_recv_frame(mbus_tcp_handle *handle, mbus_frame *frame) len = 0; do { - //printf("%s: Attempt to read %d bytes [len = %d]", __PRETTY_FUNCTION__, remaining, len); if ((nread = read(handle->sock, &buff[len], remaining)) == -1) { mbus_error_str_set("M-Bus tcp transport layer failed to read data."); - //fprintf(stderr, "%s: aborting recv frame (remaining = %d, len = %d, nread = %d)", - // __PRETTY_FUNCTION__, remaining, len, nread); return -1; } - //printf("%s: Got %d byte [remaining %d, len %d]", __PRETTY_FUNCTION__, nread, remaining, len); - len += nread; } while ((remaining = mbus_parse(frame, buff, len)) > 0);