gtdc/tdc_parser.c
2008-04-18 21:04:25 +02:00

333 lines
9.4 KiB
C

/***************************************************************************
* 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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <glib.h>
#include <gdk/gdk.h>
#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);
}