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; either version 3 of the License, or (at your option) any later version.
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
Demonstrates usage of the EIBnetmux monitoring function.
It produces a trace of requests seen on the KNX bus.
/* * eibtrace - eib packet trace * * eibnetmux - eibnet/ip multiplexer * Copyright (C) 2006-2009 Urs Zurbuchen <going_nuts@users.sourceforge.net> * * 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; either version 3 of the License, or * (at your option) any later version. * * 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 * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <libgen.h> #include <getopt.h> #include <errno.h> #include <time.h> #include <math.h> #include <arpa/inet.h> #include <sys/time.h> #include <eibnetmux/enmx_lib.h> #include <mylib.h> /* * EIB constants */ #define EIB_DAF_GROUP 0x80 #define A_RESPONSE_VALUE_REQ 0x0040 #define A_WRITE_VALUE_REQ 0x0080 /* * Global variables */ ENMX_HANDLE sock_con = 0; unsigned char conn_state = 0; /* * local function declarations */ static void Usage( char *progname ); static char *knx_physical( uint16_t phy_addr ); static char *knx_group( uint16_t grp_addr ); /* * EIB request frame */ typedef struct __attribute__((packed)) { uint8_t code; uint8_t zero; uint8_t ctrl; uint8_t ntwrk; uint16_t saddr; uint16_t daddr; uint8_t length; uint8_t tpci; uint8_t apci; uint8_t data[16]; } CEMIFRAME; static void Usage( char *progname ) { fprintf( stderr, "Usage: %s [options] [hostname[:port]]\n" "where:\n" " hostname[:port] defines eibnetmux server with default port of 4390\n" "\n" "options:\n" " -u user name of user default: -\n" " -c count stop after count number of requests default: endless\n" " -q no verbose output (default: no)\n" "\n", basename( progname )); } int main( int argc, char **argv ) { uint16_t value_size; struct timeval tv; struct tm *ltime; uint16_t buflen; unsigned char *buf; CEMIFRAME *cemiframe; int enmx_version; int c; int quiet = 0; int total = -1; int count = 0; int spaces = 1; char *user = NULL; char pwd[255]; char *target; char *eis_types; int type; int seconds; unsigned char value[20]; uint32_t *p_int = 0; double *p_real; opterr = 0; while( ( c = getopt( argc, argv, "c:u:q" )) != -1 ) { switch( c ) { case 'c': total = atoi( optarg ); break; case 'u': user = strdup( optarg ); break; case 'q': quiet = 1; break; default: fprintf( stderr, "Invalid option: %c\n", c ); Usage( argv[0] ); exit( -1 ); } } if( optind == argc ) { target = NULL; } else if( optind + 1 == argc ) { target = argv[optind]; } else { Usage( argv[0] ); exit( -1 ); } // catch signals for shutdown signal( SIGINT, Shutdown ); signal( SIGTERM, Shutdown ); // request monitoring connection enmx_version = enmx_init(); sock_con = enmx_open( target, "eibtrace" ); if( sock_con < 0 ) { fprintf( stderr, "Connect to eibnetmux failed (%d): %s\n", sock_con, enmx_errormessage( sock_con )); exit( -2 ); } // authenticate if( user != NULL ) { if( getpassword( pwd ) != 0 ) { fprintf( stderr, "Error reading password - cannot continue\n" ); exit( -6 ); } if( enmx_auth( sock_con, user, pwd ) != 0 ) { fprintf( stderr, "Authentication failure\n" ); exit( -3 ); } } if( quiet == 0 ) { printf( "Connection to eibnetmux '%s' established\n", enmx_gethost( sock_con )); } buf = malloc( 10 ); buflen = 10; if( total != -1 ) { spaces = floor( log10( total )) +1; } while( total == -1 || count < total ) { buf = enmx_monitor( sock_con, 0xffff, buf, &buflen, &value_size ); if( buf == NULL ) { switch( enmx_geterror( sock_con )) { case ENMX_E_COMMUNICATION: case ENMX_E_NO_CONNECTION: case ENMX_E_WRONG_USAGE: case ENMX_E_NO_MEMORY: fprintf( stderr, "Error on write: %s\n", enmx_errormessage( sock_con )); enmx_close( sock_con ); exit( -4 ); break; case ENMX_E_INTERNAL: fprintf( stderr, "Bad status returned\n" ); break; case ENMX_E_SERVER_ABORTED: fprintf( stderr, "EOF reached: %s\n", enmx_errormessage( sock_con )); enmx_close( sock_con ); exit( -4 ); break; case ENMX_E_TIMEOUT: fprintf( stderr, "No value received\n" ); break; } } else { count++; cemiframe = (CEMIFRAME *) buf; gettimeofday( &tv, NULL ); ltime = localtime( &tv.tv_sec ); if( total != -1 ) { printf( "%*d: ", spaces, count ); } printf( "%04d/%02d/%02d %02d:%02d:%02d:%03d - ", ltime->tm_year + 1900, ltime->tm_mon +1, ltime->tm_mday, ltime->tm_hour, ltime->tm_min, ltime->tm_sec, (uint32_t)tv.tv_usec / 1000 ); printf( "%8s ", knx_physical( cemiframe->saddr )); if( cemiframe->apci & A_WRITE_VALUE_REQ ) { printf( "W " ); } else if( cemiframe->apci & A_RESPONSE_VALUE_REQ ) { printf( "A " ); } else { printf( "R " ); } printf( "%8s", (cemiframe->ntwrk & EIB_DAF_GROUP) ? knx_group( cemiframe->daddr ) : knx_physical( cemiframe->daddr )); if( cemiframe->apci & (A_WRITE_VALUE_REQ | A_RESPONSE_VALUE_REQ) ) { printf( " : " ); p_int = (uint32_t *)value; p_real = (double *)value; switch( cemiframe->length ) { case 1: // EIS 1, 2, 7, 8 type = enmx_frame2value( 1, cemiframe, value ); printf( "%s | ", (*p_int == 0) ? "off" : "on" ); type = enmx_frame2value( 2, cemiframe, value ); printf( "%d | ", *p_int ); type = enmx_frame2value( 7, cemiframe, value ); printf( "%d | ", *p_int ); type = enmx_frame2value( 8, cemiframe, value ); printf( "%d", *p_int ); eis_types = "1, 2, 7, 8"; break; case 2: // 6, 13, 14 type = enmx_frame2value( 6, cemiframe, value ); printf( "%d%% | %d", *p_int * 100 / 255, *p_int ); type = enmx_frame2value( 13, cemiframe, value ); if( *p_int >= 0x20 && *p_int < 0x7f ) { printf( " | %c", *p_int ); eis_types = "6, 14, 13"; } else { eis_types = "6, 14"; } break; case 3: // 5, 10 type = enmx_frame2value( 5, cemiframe, value ); printf( "%.2f | ", *p_real ); type = enmx_frame2value( 10, cemiframe, value ); printf( "%d", *p_int ); eis_types = "5, 10"; break; case 4: // 3, 4 type = enmx_frame2value( 3, cemiframe, value ); seconds = *p_int; ltime->tm_hour = seconds / 3600; seconds %= 3600; ltime->tm_min = seconds / 60; seconds %= 60; ltime->tm_sec = seconds; printf( "%02d:%02d:%02d | ", ltime->tm_hour, ltime->tm_min, ltime->tm_sec ); type = enmx_frame2value( 4, cemiframe, value ); ltime = localtime( (time_t *)p_int ); printf( "%04d/%02d/%02d", ltime->tm_year + 1900, ltime->tm_mon +1, ltime->tm_mday ); eis_types = "3, 4"; break; case 5: // 9, 11, 12 type = enmx_frame2value( 11, cemiframe, value ); printf( "%d | ", *p_int ); type = enmx_frame2value( 9, cemiframe, value ); printf( "%.2f", *p_real ); type = enmx_frame2value( 12, cemiframe, value ); // printf( "12: <->" ); eis_types = "9, 11, 12"; break; default: // 15 // printf( "%s", string ); eis_types = "15"; break; } if( cemiframe->length == 1 ) { printf( " (%s", hexdump( &cemiframe->apci, 1, 1 )); } else { printf( " (%s", hexdump( (unsigned char *)(&cemiframe->apci) +1, cemiframe->length -1, 1 )); } printf( " - eis types: %s)", eis_types ); } printf( "\n" ); } } return( 0 ); } /* * Return representation of physical device KNX address as string */ static char *knx_physical( uint16_t phy_addr ) { static char textual[64]; int area; int line; int device; phy_addr = ntohs( phy_addr ); area = (phy_addr & 0xf000) >> 12; line = (phy_addr & 0x0f00) >> 8; device = phy_addr & 0x00ff; sprintf( textual, "%d.%d.%d", area, line, device ); return( textual ); } /* * Return representation of logical KNX group address as string */ static char *knx_group( uint16_t grp_addr ) { static char textual[64]; int top; int sub; int group; grp_addr = ntohs( grp_addr ); top = (grp_addr & 0x7800) >> 11; sub = (grp_addr & 0x0700) >> 8; group = grp_addr & 0x00ff; sprintf( textual, "%d/%d/%d", top, sub, group ); return( textual ); }