#include "cwCommon.h"

#include "cwLog.h"
#include "cwCommonImpl.h"

#include "cwMem.h"
#include "cwTime.h"

#include "cwThread.h"
#include "cwSocket.h"
#include "cwUtility.h"

#include "cwEuCon.h"
#include "cwText.h"
#include "cwNumericConvert.h"

#include "dns_sd/dns_sd_const.h"
#include "cwEuConDecls.h"

#define HEART_BEAT "\x04\x00\x00\x00"

#define RESPONSE_1 "\x0a\x00\x00\x00\x6d\x62\x70\x31\x39\x00\x00\x00\x44\x45" \
  "\x00\x00\x78\x3f\x39\xe5\xfe\x7f\x00\x00\xc0\x32\x17\x49\xe2\x7f"    \
  "\x00\x00\xe0\x7d\x2e\x01\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x02\x01\xa8\x03\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x8d\x00\x1f\x0a\xbe\xbd\xea\x31\xf9\x88\xc5\x46\xe2\x7f"    \
  "\x00\x00\x08\x00\x00\x00\x00\x00\x02\x58"

#define RESPONSE_2 "\x0c\x00\x1f\x0a"

#define RESPONSE_3_A "\x19\x00\x06\x00\x00\x00\x04\x14\x00\x00\x00\x00\x00\x80" \
  "\x00\x40\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x54\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\xa8\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x0a\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x19\x01\x06\x00\x00\x00\x04\x14\x00\x00"    \
  "\x00\x00\x00\x80\x00\x40\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

#define RESPONSE_3_B "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x54\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\xa8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00"

#define RESPONSE_4_A "\x19\x02\x06\x00\x00\x00\x04\x14\x00\x00\x00\x00\x00\x80" \
  "\x00\x40\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x54\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\xa8\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x0a\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x19\x03\x06\x00\x00\x00\x04\x14\x00\x00"    \
  "\x00\x00\x00\x80\x00\x40\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

#define RESPONSE_4_B "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x54\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02"    \
  "\x00\x02\x00\x02\xa8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00"    \
  "\x08\x00\x08\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"    \
  "\x00\x00"


namespace cw
{
  namespace eucon
  {
    enum
    {
     kSendHandshake_0_Id,     // send [0x0a, ...]
     kWaitForHandshake_1_Id,  // wait for first heart beat -> then send [0x0c ...]
     kWaitForHandshake_2_Id,  // wait for [0x0d ...] -> then send response_3_a
     kResponse_3_A_Id,
     kResponse_3_B_Id,
     kResponse_4_A_Id,
     kResponse_4_B_Id,
     kRunning_Id
    };

    struct eucon_str;

    typedef struct
    {
      bool muteFl;      
    } ch_t;

    // FBank object
    typedef struct fbank_str
    {
      struct eucon_str*    eucon;
      unsigned             fbIndex;
      unsigned             sockUserId;
      unsigned             protoState;
      unsigned             cbCnt;
      time::spec_t         nextRecvHbTs;
      time::spec_t         nextSendHbTs;
      struct fbank_str*    link;
      uint32_t             remoteAddr;
      ch_t*                chA;
      unsigned             chN;
    } fbank_t;

    // EuCon manager object
    typedef struct eucon_str
    {
      sock::handle_t     sockMgrH;      // socket mgr handle
      fbank_t*           fbankL;        // List of fader banks
      unsigned           maxFaderBankN; // maximum number of fader banks
      unsigned           sockTimeOutMs; // socket time out
      unsigned           fdrTcpPort;    // Fader TCP port TODO: we shouuld be getting this from the MDNS SRV record
      unsigned           heartBeatPeriodMs;
    } eucon_t;

    inline eucon_t* _handleToPtr( handle_t h )
    { return handleToPtr<handle_t,eucon_t>(h); }

    rc_t _destroyFBank( eucon_t* p, fbank_t* fb )
    {
      rc_t rc = kOkRC;
      if((rc = sock::destroy(p->sockMgrH, fb->sockUserId )) != kOkRC )
        rc = cwLogError(rc,"Fader bank (index:%i) destroy failed.", fb->fbIndex );
        
      return rc;          
    }
      
    rc_t _destroy( eucon_t* p)
    {
      rc_t rc = kOkRC;
      rc_t rc0;
        
      fbank_t* fb = p->fbankL;
      while( fb!=nullptr )
      {
        fbank_t* fb0 = fb->link;
          
        if((rc0 = _destroyFBank(p,fb)) != kOkRC )
          rc = rc0;;

        mem::release(fb);
          
        fb = fb0;
      }
        
      if( p->sockMgrH.isValid() )
        if((rc = sock::destroyMgr(p->sockMgrH)) != kOkRC )
          return rc;
        
      mem::release(p);
        
      return rc;
    }

    rc_t _disconnect( fbank_t* fb )
    {
      rc_t rc = kOkRC;
      
      //if((rc = sock::destroy(fb->eucon->sockMgrH, fb->sockUserId)) != kOkRC )
      //  rc = cwLogError(rc,"Socket destroy failed on disconnect attempt on fader bank index:%i.",fb->fbIndex);
        
      fb->protoState = kSendHandshake_0_Id;

      return rc;
          
    }

    rc_t _send_response( fbank_t* fb, const char* buf, unsigned bufByteN )
    {
      rc_t rc = kOkRC;
        
      if((rc = sock::send( fb->eucon->sockMgrH, fb->sockUserId, kInvalidId, buf, bufByteN )) != kOkRC )
      {
        rc = cwLogError(rc,"TCP send failed on fader bank index:%i proto:%i Disconnecting.",fb->fbIndex,fb->protoState);

        _disconnect(fb);
      }
        
      return rc;
    }
      
    rc_t _sendHandshake_0( fbank_t* fb )
    {
      printf("%i : Sent HS-0: 0x0a\n", fb->fbIndex);
      return _send_response(fb,RESPONSE_1,sizeof(RESPONSE_1)-1);
    }

    rc_t _sendHandshake_1( fbank_t* fb )
    { return _send_response(fb,RESPONSE_2,sizeof(RESPONSE_2)-1); }


    rc_t _send_app_msg( fbank_t* fb, uint16_t chIdx, uint16_t msgTypeId, uint16_t value )
    {
      if( fb->protoState != kRunning_Id )
        return kOkRC;
      
      typedef struct fields_str
      {
        uint16_t channel;
        uint16_t typeId;
        uint16_t zero;
        uint16_t value;
      } fields_t;
      
      typedef struct
      {
        union
        {
          uint8_t buf[8];
          fields_t f;
        } u;
      } buf_t;

      uint16_t v = value;

      buf_t b;
      b.u.f.channel = chIdx;
      b.u.f.typeId  = msgTypeId;
      b.u.f.zero    = 0;
      b.u.f.value   = ((v & 0xff00) >> 8) + ((v & 0x00ff) << 8);
      
      return _send_response(fb,(char*)(b.u.buf),sizeof(b.u.buf));
    }

    fbank_t* _createFBank( eucon_t* p, unsigned fbIndex )
    {
      fbank_t* fb = mem::allocZ<fbank_t>();

      fb->eucon      = p;
      fb->fbIndex    = fbIndex;
      fb->sockUserId = kBaseSockUserId + fbIndex;
      fb->protoState = kSendHandshake_0_Id;
      fb->link       = p->fbankL;
      fb->chN        = 8;
      fb->chA        = mem::allocZ<ch_t>(fb->chN);
      p->fbankL      = fb;
      

      return fb;
        
    }

    fbank_t* _fbIndexToFBank( eucon_t* p, unsigned fbIndex, bool showErrorFl=true )
    {
      fbank_t* fb = p->fbankL;
      for(; fb!=nullptr; fb=fb->link)
        if( fb->fbIndex == fbIndex )
          return fb;

      if( showErrorFl )
        cwLogError(kInvalidId,"Fader bank index %i is not valid.", fbIndex );
        
      return nullptr;
    }

    fbank_t* _ipAddrToFBank( eucon_t* p, uint32_t  ipAddr, bool showErrorFl=true )
    {
      fbank_t* fb = p->fbankL;
      for(; fb!=nullptr; fb=fb->link)
        if( fb->remoteAddr == ipAddr )
          return fb;

      if( showErrorFl )
        cwLogError(kInvalidId,"Fader bank with dest. addr 0x%x was not found.", ipAddr );
        
      return nullptr;        
    }


    // Heatbeat: 0x03 0x00 0x00 0x00
    // 
    // channel    status    zero      value
    // --------- --------- --------- ---------
    // 0x00 0x01 0x00 0x00 0x00 0x00 0x02 0x0b  fader
    // 0x00 0x01 0x02 0x00 0x00 0x00 0x00 0x01  mute
    // 0x00 0x01 0x00 0x01 0x00 0x00 0x00 0x00  touch
    // 0x00 0x01 0x04 0x00 0x00 0x00 0x00 0x00  ping

  
    void _app_msg_decode(eucon_t* p, fbank_t* fb, const uint8_t* buf, unsigned bufByteN )
    {
      unsigned bi = 0;
      
      while( bi<bufByteN )
      {
          char     type  = 'U';
          uint16_t numb  = 0;
          uint16_t id    = 0;
          uint16_t chIdx = 0;
          unsigned incr  = 8;
          

          // if this is a heartbeat msg
          if( buf[bi] == kChHb_EuProtoId )
          {
            type='H';
            incr = 4;
          }
          else
          {
            uint16_t* v = (uint16_t*)(buf+bi);
            chIdx = ntohs(v[0]);
            id    = ntohs(v[1]);
            numb  = ntohs(v[3]);
            
            switch(id )
            {
              case kFPosnEuconId:
                type = 'F';
                break;
                
              case kTouchEuconId:
                type = 'T';
                break;

              case kFdrMuteEuconId:
                {
                  type = 'M';
                  
                  fb->chA[chIdx].muteFl = !fb->chA[chIdx].muteFl;
                  
                  if(_send_app_msg(fb, chIdx, kEucMuteEuconId, !fb->chA[chIdx].muteFl ) != kOkRC )
                    cwLogError(kOpFailRC,"Send mute msg failed.");
                }
                break;

              case kPingEuconId:
                type = 'P';
                _send_response(fb,(const char*)(buf+bi),8);
                break;

              default:
                incr = 1;
                
            }
          }
          if(  type != 'H' )
            printf("%i %i : %c (0x%x) %i (0x%x)\n",fb->fbIndex, chIdx, type,id,numb,numb);

          bi += incr;
      }
    }
    
    void _tcpCallback( void* arg, sock::cbOpId_t cbOpId, unsigned userId, unsigned connId, const void* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr )
    {
      eucon_t* p = static_cast<eucon_t*>(arg);
      fbank_t* fb;

      switch( cbOpId )
      {
        case sock::kConnectCbId:
          cwLogInfo("Connected: user:%i conn:%i", userId, connId );
          return;
            
        case sock::kDisconnectCbId:
          cwLogInfo("Disconnected: user:%i conn:%i", userId, connId );
          return;

        case sock::kReceiveCbId:
          break;

        default:
          cwLogError(kInvalidIdRC,"An invalid socket callback id (%i) was received.",cbOpId);
          return;
      }

      // get the fader bank this message is intended for
      if((fb = _ipAddrToFBank( p, fromAddr->sin_addr.s_addr ))  == nullptr )
      {
        cwLogError(kOpFailRC,"Fader bank not found. TCP message not delivered.");
        return;
      }

      // 
      if( data!=nullptr && dataByteCnt >= 4 )
      {
        //printHex(data,dataByteCnt);
            
        unsigned hdr = *(const unsigned*)data;

        switch( fb->protoState )
        {
          case kWaitForHandshake_1_Id:
            if( hdr == kHs1_b_EuProtoId )
            {
              fb->protoState = kWaitForHandshake_2_Id;
              _sendHandshake_1( fb );
              cwLogInfo("%i : Rcvd (0x0b) HS 1  - sent 0x0c\n", fb->fbIndex);
            }
            break;

          case kWaitForHandshake_2_Id:
            if( hdr == kHs3_d_EuProtoId )
            {
              fb->protoState = kResponse_3_A_Id;
              cwLogInfo("%i : Rcvd (0x0d) HS 2 - Sending setup data\n",fb->fbIndex);
            }
            break;

        }            
      }

      switch(fb->protoState)
      {
        case kResponse_3_A_Id:                
          _send_response(fb,RESPONSE_3_A,sizeof(RESPONSE_3_A)-1);                
          fb->protoState = kResponse_3_B_Id;
          break;
                
        case kResponse_3_B_Id:
          _send_response(fb,RESPONSE_3_B,sizeof(RESPONSE_3_B)-1);                
          fb->protoState = kResponse_4_A_Id;
          break;
                
        case kResponse_4_A_Id:
          _send_response(fb,RESPONSE_4_A,sizeof(RESPONSE_4_A)-1);                
          fb->protoState = kResponse_4_B_Id;
          break;
                
        case kResponse_4_B_Id:
          _send_response(fb,RESPONSE_4_B,sizeof(RESPONSE_4_B)-1);
          
          fb->protoState = kRunning_Id;
          // set the initial next heart-beat times for this fader bank
          time::futureMs(fb->nextSendHbTs,fb->eucon->heartBeatPeriodMs);
          break;

        case kRunning_Id:
          {
            _app_msg_decode(p, fb, (const uint8_t*)data, dataByteCnt );
          
            //printf("%i : Rcv: %i : ",fb->fbIndex, dataByteCnt );
            //for(unsigned i=0; i<dataByteCnt; ++i)
            //  printf("0x%02x ",((uint8_t*)data)[i]);
            //printf("\n");
          }
      }

      //printf("fbi: %i : proto:%i rcv\n",fb->fbIndex,fb->protoState);
      time::futureMs(fb->nextRecvHbTs,fb->eucon->heartBeatPeriodMs*2);
        
    }

    rc_t _on_McMix_DNS_SD_TXT( eucon_t* p, const char* numberText, unsigned numberTextCharN, const struct sockaddr_in* fromAddr )
    {
      rc_t               rc         = kOkRC;
      unsigned           tcpFlags   = sock::kTcpFl | sock::kBlockingFl | sock::kStreamFl | sock::kReuseAddrFl | sock::kReusePortFl;
      fbank_t*           fb         = nullptr;
      unsigned           fbIndex    = 0;
      char               fbIP[ INET_ADDRSTRLEN+1 ];

      // copy the "MC Mix" suffix number into a zero terminated string
      char numbBuf[ numberTextCharN + 1 ];
      strncpy(numbBuf,numberText,numberTextCharN);
      numbBuf[numberTextCharN] = '\0';
        
      // convert the "MC Mix" suffix to a number
      if((rc = string_to_number<unsigned>(numbBuf,fbIndex)) != kOkRC )
        return cwLogError(kSyntaxErrorRC,"The 'MC Mix' suffix number could not be parsed.");
        
      // validate the value
      if( !(0 < fbIndex && fbIndex <= p->maxFaderBankN ) )
        return cwLogError(kInvalidArgRC,"The fader bank number %i is not valid.", fbIndex );

      // convert fbIndex to a zero based index        
      fbIndex -= 1; 

      // If this fader bank does not already exist ....
      if((fb = _fbIndexToFBank(p,fbIndex,false)) == nullptr )
      {
        // ... then create it
        if((fb = _createFBank(p,fbIndex)) == nullptr )
          return cwLogError(kOpFailRC,"The fader bank index %i failed.", fbIndex );          
      }

      if( fb->protoState == kSendHandshake_0_Id )
      {
        // convert the fromAddr to a string
        if((rc = sock::addrToString( fromAddr, fbIP, INET_ADDRSTRLEN)) != kOkRC )
          return cwLogError(rc,"IP address to string conversion failed.", fbIndex);
        
        // create the TCP socket
        if((rc = sock::create( p->sockMgrH, fb->sockUserId, sock::kInvalidPortNumber, tcpFlags, p->sockTimeOutMs, _tcpCallback, p, fbIP, p->fdrTcpPort )) != kOkRC )
          return cwLogError(rc,"The TCP socket for fader bank index %i failed. ", fbIndex);

        fb->remoteAddr = fromAddr->sin_addr.s_addr;

        time::futureMs(fb->nextRecvHbTs,fb->eucon->heartBeatPeriodMs*2);
        
        // Send the initial handshake to the fader bank
        _sendHandshake_0( fb );

        fb->protoState = kWaitForHandshake_1_Id;
      }
        
      return rc;
    }

      
      
    void _udpCallback( void* arg, sock::cbOpId_t cbId, unsigned userId, unsigned connId, const void* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr )
    {
      rc_t            rc = kOkRC;
      eucon_t*        p  = (eucon_t*)arg;
      const uint16_t* u  = (const uint16_t*)data;

      // if this is a DNS-SD reply
      if( cwIsFlag(ntohs(u[1]), kReplyHdrDnsFl ) )
      {
        const char* name  = (const char*)(u+6);
        const char* label = "MC Mix - ";
        unsigned    sn    = strlen(label);
        printf("%.*s|%i\n", name[0], name+1, sn );

        // if this a 'MC Mix' DNS-SD SRV reply
        if( strncmp(label, name+1, strlen(label)) == 0 )
        {
          unsigned n = strlen(label);
            
          if((rc = _on_McMix_DNS_SD_TXT( p, name+n+1, name[0]-n, fromAddr )) != kOkRC )
            cwLogError(rc,"%.*s initialization failed.",name[0],name+1);
            
        }
      }
    }      
  } 
}



cw::rc_t cw::eucon::create( handle_t& hRef, const args_t& args )
{
  rc_t     rc       = kOkRC;
  unsigned udpFlags = sock::kBlockingFl | sock::kReuseAddrFl | sock::kReusePortFl | sock::kMultiCastTtlFl | sock::kMultiCastLoopFl;
    
  if((rc = destroy(hRef)) != kOkRC )
    return rc;

  eucon_t* p         = mem::allocZ<eucon_t>();

  // create the socket manager
  if((rc = sock::createMgr(p->sockMgrH, args.recvBufByteN, args.maxSockN)) != kOkRC )
  {
    rc = cwLogError(rc,"Socket manager create failed during eucon mgr. allocation.");
    goto errLabel;
  }

  // create the MDNS/DNS SD UDP socket 
  if((rc = sock::create( p->sockMgrH, kUdpSockUserId, args.mdnsPort, udpFlags, args.sockTimeOutMs, _udpCallback, p )) != kOkRC )
  {
    rc = cwLogError(rc,"The Eucon controller UDP socket creation failed. ");
    goto errLabel;
  }

  // add the mDNS socket to the multicast group
  if((rc =  join_multicast_group( p->sockMgrH, kUdpSockUserId, args.mdnsIP )) != kOkRC )
  {
    rc = cwLogError(rc,"The Eucon controller UDP socket could not be added to the multicast group. ");
    goto errLabel;
  }
    
  // set the TTL for multicast 
  if((rc = set_multicast_time_to_live( p->sockMgrH, kUdpSockUserId, 255 )) != kOkRC )
  {
    rc = cwLogError(rc,"The multicast assignment to Eucon controller UDP socket failed. ");
    goto errLabel;
  }

  p->maxFaderBankN     = args.maxFaderBankN;
  p->sockTimeOutMs     = args.sockTimeOutMs;
  p->fdrTcpPort      = args.fdrTcpPort;
  p->heartBeatPeriodMs = args.heartBeatPeriodMs;
  hRef.set(p);
  
 errLabel:
  if( rc != kOkRC )
    _destroy(p);

  return rc;
}

cw::rc_t cw::eucon::destroy( handle_t& hRef )
{
  rc_t rc = kOkRC;
  
  if( !hRef.isValid() )
    return rc;

  eucon_t* p = _handleToPtr(hRef);
  if((rc = _destroy(p)) != kOkRC )
    return rc;
  
  hRef.clear();
  return rc;  
}

cw::rc_t cw::eucon::exec( handle_t h, unsigned sockTimeOutMs )
{
  rc_t         rc             = kOkRC;
  eucon_t*     p              = _handleToPtr(h);
  unsigned     totalReadByteN = 0;
  time::spec_t t0;

  // poll sockets for incoming EuCon messages
  if((rc = sock::receive_all(p->sockMgrH, sockTimeOutMs, totalReadByteN )) != kOkRC )
    return rc;

  switch( rc )
  {
    case kOkRC:
    case kTimeOutRC:
      rc = kOkRC;
      break;
    
    default:
      rc = cwLogError(rc,"EuCon network receive failed.");
  };

  // get the current time
  time::get(t0);

  
  // check the health of each fader bank
  for(fbank_t* fb = p->fbankL; fb!=nullptr; fb=fb->link)
    if( fb->protoState != kSendHandshake_0_Id )
    {

      // has it been more than 'heartBeatPerioMs' millisecnods since we received a msg from this fader bank
      if( time::isGTE( t0, fb->nextRecvHbTs ) )
      {
        cwLogInfo("Missed heart-beat disconnecting fader bank index %i.",fb->fbIndex );
        _disconnect(fb);
      }
      // is it time to send a heart-beat to this fader bank
      if( time::isGTE( t0, fb->nextSendHbTs ) )
      {        
        _send_response( fb, HEART_BEAT, sizeof(HEART_BEAT)-1 );
        time::futureMs(fb->nextSendHbTs,fb->eucon->heartBeatPeriodMs);
      }
    }
  
  return rc;
  
}

bool  cw::eucon::areMsgsWaiting( handle_t h )
{
  //eucon_t* p = _handleToPtr(h);
  return false;
}
        
cw::rc_t  cw::eucon::getMsgs( handle_t h, msgCallback_t cbFunc, void* cbArg )
{
  rc_t     rc = kOkRC;
  //eucon_t* p  = _handleToPtr(h);

  return rc;
}


cw::rc_t cw::eucon::sendMsg( handle_t h, unsigned fbIndex, unsigned fbChIndex, unsigned msgTypeId, unsigned value )
{
  rc_t     rc = kOkRC;

  eucon_t* p  = _handleToPtr(h);

  fbank_t* fb = p->fbankL;
  while( fb != nullptr )
    if( fb->fbIndex == fbIndex )
    {
      rc = _send_app_msg(fb, fbChIndex, msgTypeId, value );
      break;
    }

  return rc;
}
    



namespace cw
{
  namespace eucon
  {
    typedef struct app_str
    {
      bool             quitFl;
      handle_t         h;
      thread::handle_t thH;
      unsigned         sockTimeOutMs;
    } app_t;
    
    bool appThreadFunc( void* arg )
    {
      app_t* app = static_cast<app_t*>(arg);

      exec(app->h,app->sockTimeOutMs);

      return true;
    }

    rc_t appBegin( const args_t& args, app_t& app )
    {
      rc_t rc = kOkRC;
      
      // Create the EuCon controller
      if((rc = create(  app.h, args )) != kOkRC )
        return cwLogError(rc,"Unable to create EuCon server.");

      // Create the application thread
      if((rc = thread::create( app.thH, appThreadFunc, &app, "eu_con" )) != kOkRC )
        return cwLogError(rc,"App thread create failed.");

      // Start the application thread
      if((rc = thread::unpause( app.thH )) != kOkRC )
        return cwLogError(rc,"App thread start failed.");

      app.sockTimeOutMs = args.sockTimeOutMs;
      
      return rc;
    }

    rc_t appEnd( app_t& app )
    {
      rc_t rc = kOkRC;

      // Destroy the application thread
      if((rc = thread::destroy(app.thH)) != kOkRC )
        cwLogError(rc,"EuCon app thread destroy failed.");

      // Destroy the EuCon controller
      if((rc = destroy(app.h)) != kOkRC )
        cwLogError(rc,"Eucon app object destroy failed.");
      
      return rc;
    }

  }
}



cw::rc_t cw::eucon::test()
{
  rc_t           rc    = kOkRC;
  app_t          app;
  args_t         args;
  const unsigned sbufN            = 31;
  char           sbuf[ sbufN+1 ];  

  memset(&args,0,sizeof(app));

  args.recvBufByteN  = 4096;
  args.mdnsIP        = "224.0.0.251";
  args.mdnsPort      = 5353;
  args.sockTimeOutMs = 50;
  args.maxFaderBankN = 8;
  args.fdrTcpPort  = 49168;
  args.maxSockN      = 50;
  args.heartBeatPeriodMs = 4000;

  if((rc = appBegin(args,app)) != kOkRC )
    return 1;

  
  printf("'quit' to exit\n");

  // readline loop
  while( true )
  {
    printf("? ");
    if( std::fgets(sbuf,sbufN,stdin) == sbuf )
    {
      printf("Sending:%s",sbuf);

      if( strcmp(sbuf,"quit\n") == 0)
        break;
    }
  }
  

  appEnd(app);

  

  return rc;
}