/*************************************************************************** * Copyright (C) 04/2008 by Olaf Rempel * * razzor@kopf-tisch.de * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; version 2 of the License * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include "sockaddr.h" #include "tcpsocket.h" #include "tdc_parser.h" #include "tdc_proto.h" #include "tdc_store.h" #include "tdc_variable.h" static int socket_fd; static gint socket_tag; static void (*socket_status_cb)(int status); static GByteArray *socket_stream; static int tdcparser_send(struct tdc_pkt_header *pkt) { return write(socket_fd, pkt, pkt->size); } void tdcparser_send_hello(void) { int addr; for (addr = 1; addr < 8; addr++) { struct tdc_pkt_header pkt; pkt.cmd = (addr << 4) | TDC_HELLO; pkt.size = sizeof(pkt); tdcparser_send(&pkt); } } int tdcparser_send_getvars(int address) { struct tdc_pkt_header pkt; pkt.cmd = (address << 4) | TDC_GETVARS; pkt.size = sizeof(pkt); return tdcparser_send(&pkt); } int tdcparser_send_getvalue(int address, int id) { struct tdc_getvalue_request pkt; pkt.cmd = (address << 4) | TDC_GETVALUE; pkt.size = sizeof(pkt); pkt.id = (id & 0xFF); return tdcparser_send((struct tdc_pkt_header *)&pkt); } int tdcparser_send_setvalue(int address, int id) { struct tdc_var *var = tdcstore_get_var(address, id); if (var == NULL) return -1; int datasize = (var->flags & TDC_SIZEMASK); struct tdc_setvalue_request *pkt; pkt = g_malloc0(sizeof(*pkt) + datasize); pkt->cmd = (address << 4) | TDC_SETVALUE; pkt->size = sizeof(*pkt) + datasize; pkt->id = (id & 0xFF); memcpy(pkt->data, &var->data, datasize); int retval = tdcparser_send((struct tdc_pkt_header *)pkt); g_free(pkt); return retval; } int tdcparser_send_request(int address, int interval, uint32_t *bitmap) { struct tdc_reqvalues_request pkt; pkt.cmd = (address << 4) | TDC_REQVALUES; pkt.size = sizeof(pkt); pkt.interval = interval; memcpy(pkt.varmap, bitmap, sizeof(pkt.varmap)); return tdcparser_send((struct tdc_pkt_header *)&pkt); } static int check_size_helper(int available, int packet_size, int expected_base_size, int packet_data_size, const char *name) { /* not enough data in buffer */ if (available < expected_base_size) { // printf("received incomplete %s\n", name); return 0; } /* retest, after we know that packet_data_size is valid */ if (available < (expected_base_size + packet_data_size)) { // printf("received incomplete %s\n", name); return 0; } /* packet has wrong size */ if (packet_size != (expected_base_size + packet_data_size)) { // printf("received invalid %s\n", name); return -1; } // printf("received %s\n", name); return packet_size; } static int tdcparser_parse(void) { int address = (socket_stream->data[0] & TDC_ADDRMASK) >> 4; int remove = 1; switch (socket_stream->data[0] & (TDC_REPLY | TDC_OPCODEMASK)) { case TDC_HELLO: { struct tdc_pkt_header *pkt = (struct tdc_pkt_header *)socket_stream->data; remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), 0, "TDC_HELLO"); /* nothing to do */ } break; case (TDC_HELLO | TDC_REPLY): { struct tdc_hello_reply *pkt = (struct tdc_hello_reply *)socket_stream->data; remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), 0, "TDC_HELLO REPLY"); if (remove <= 0) break; tdcstore_create_board(address, pkt->name); tdcstore_refresh_values(address, 250); tdcstore_graph_refresh(address, 20); tdcparser_send_getvars(address); } break; case TDC_GETVARS: { struct tdc_pkt_header *pkt = (struct tdc_pkt_header *)socket_stream->data; remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), 0, "TDC_GETVARS"); /* nothing to do */ } break; case (TDC_GETVARS | TDC_REPLY): { struct tdc_getvars_reply *pkt = (struct tdc_getvars_reply *)socket_stream->data; remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), pkt->name_len, "TDC_GETVARS REPLY"); if (remove <= 0) break; tdcstore_create_var(address, tdcvar_create(pkt->id, pkt->flags, pkt->name, pkt->name_len)); } break; case TDC_GETVALUE: { struct tdc_getvalue_request *pkt = (struct tdc_getvalue_request *)socket_stream->data; remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), 0, "TDC_GETVALUE"); /* nothing to do */ } break; case (TDC_GETVALUE | TDC_REPLY): { struct tdc_getvalue_reply *pkt = (struct tdc_getvalue_reply *)socket_stream->data; remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), 0, "TDC_GETVALUE REPLY (1)"); /* at this point: invalid packet */ if (remove > 0) remove = 1; /* retry with more bytes */ else if (remove == 0) break; /* base packet is complete, but exact size is wrong, get it from storage */ struct tdc_var *var = tdcstore_get_var(address, pkt->id); if (var == NULL) { printf("unknown variable %d\n", pkt->id); remove = 1; break; } int varsize = (var->flags & TDC_SIZEMASK); remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), varsize, "TDC_GETVALUE REPLY (2)"); if (remove <= 0) break; tdcstore_update_var(address, pkt->id, pkt->data, varsize); } break; case TDC_SETVALUE: { struct tdc_setvalue_request *pkt = (struct tdc_setvalue_request *)socket_stream->data; remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), 0, "TDC_SETVALUE REPLY (1)"); /* at this point: invalid packet */ if (remove > 0) remove = 1; /* retry with more bytes */ else if (remove == 0) break; /* base packet is complete, but exact size is wrong, get it from storage */ struct tdc_var *var = tdcstore_get_var(address, pkt->id); if (var == NULL) { printf("unknown variable %d\n", pkt->id); remove = 1; break; } int varsize = (var->flags & TDC_SIZEMASK); remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), varsize, "TDC_SETVALUE REPLY (2)"); if (remove <= 0) break; /* nothing to do */ } break; case (TDC_SETVALUE | TDC_REPLY): break; case TDC_REQVALUES: { struct tdc_reqvalues_request *pkt = (struct tdc_reqvalues_request *)socket_stream->data; remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), 0, "TDC_REQVALUES"); /* nothing to do */ } break; case (TDC_REQVALUES | TDC_REPLY): { struct tdc_reqvalues_reply *pkt = (struct tdc_reqvalues_reply *)socket_stream->data; remove = check_size_helper(socket_stream->len, pkt->size, sizeof(*pkt), 0, "TDC_REQVALUES"); if (remove <= 0) break; // TODO: output pkt->timestamp, pkt->cnt } break; case TDC_TERMINAL: case (TDC_TERMINAL | TDC_REPLY): break; } if (remove != 0) g_byte_array_remove_range(socket_stream, 0, (remove > 0) ? remove : -remove); return remove; } static void tdcparser_read_cb(gpointer data, gint source, GdkInputCondition condition) { char buf[256]; int len = read(socket_fd, buf, sizeof(buf)); if (len > 0) { g_byte_array_append(socket_stream, (guint8 *)buf, len); while (socket_stream->len >= 2) { if (tdcparser_parse() == 0) break; } } else { g_warning("gtdc:tdcparser_read_cb:read:%s", g_strerror(errno)); tdcparser_disconnect(); } } static void tdcparser_connect_cb(gpointer data, gint source, GdkInputCondition condition) { gdk_input_remove(socket_tag); if (tcp_connect_error(source) < 0) { g_warning("gtdc:tdcparser_connect_cb:tcp_connect_error:%s", g_strerror(errno)); socket_status_cb(STAT_DISCONNECTED); close(socket_fd); } else { socket_tag = gdk_input_add(socket_fd, GDK_INPUT_READ, tdcparser_read_cb, NULL); socket_status_cb(STAT_CONNECTED); if (socket_stream == NULL) socket_stream = g_byte_array_new(); else if (socket_stream->len) g_byte_array_remove_range(socket_stream, 0, socket_stream->len); tdcstore_flush(); tdcparser_send_hello(); } } void tdcparser_connect(char *addr_str, void (*cb)(int status)) { struct sockaddr_in sa; parse_sockaddr(addr_str, &sa); socket_status_cb = cb; socket_status_cb(STAT_CONNECTING); socket_fd = tcp_connect_nonblock(&sa); socket_tag = gdk_input_add(socket_fd, GDK_INPUT_WRITE, tdcparser_connect_cb, NULL); } void tdcparser_disconnect(void) { socket_status_cb(STAT_DISCONNECTED); gdk_input_remove(socket_tag); close(socket_fd); }