libcw/dns_sd/fader.cpp
2024-12-01 14:35:24 -05:00

404 lines
9.9 KiB
C++

//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org>
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
#include "config.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "rpt.h"
#include "fader.h"
#ifdef OS_LINUX
#include <arpa/inet.h>
#endif
#ifdef ARDUINO
#include <Ethernet.h>
#include <utility/w5100.h>
#endif
fader::msgRef_t fader::_msgRefA[] =
{
{ 0x0a, 88 }, // initial handshake
{ 0x0c, 4 }, // secondary handshake
{ 0x00, 8 }, //
{ 0x19, 1044 }, // 0x19 messages are variable length
{ 0x04, 4 }, //
{ 0xff, 0 } // end-of-list sentinel (both id and byteN are invalid)
};
fader::fader(
printCallback_t printCbFunc,
const unsigned char faderMac[6],
uint32_t faderInetAddr,
euConCbFunc_t euconCbFunc,
void* euconCbArg,
physCtlCbFunc_t physCtlCbFunc,
void* physCtlCbArg,
unsigned ticksPerHeartBeat,
unsigned chN )
: _printCbFunc(printCbFunc),
_inetAddr(faderInetAddr),
_tickN(0),
_chArray(nullptr),
_euconCbFunc(euconCbFunc),
_euconCbArg(euconCbArg),
_physCtlCbFunc(physCtlCbFunc),
_physCtlCbArg(physCtlCbArg),
_protoState(kWaitForHandshake_0_Id),
_ticksPerHeartBeat(ticksPerHeartBeat),
_msgTypeId(0xff),
_msgByteIdx(0),
_msgByteN(0),
_mbi(0)
{
memcpy(_mac,faderMac,6);
_chArray = new ch_t[chN];
_chN = chN;
reset();
}
fader::~fader()
{
delete[] _chArray;
}
void fader::reset()
{
_protoState = kWaitForHandshake_0_Id;
_msgTypeId = 0xff;
_msgByteIdx = 0;
_msgByteN = 0;
_mbi = 0;
for(unsigned i=0; i<_chN; ++i)
{
_chArray[i].position = 0;
_chArray[i].muteFl = false;
_chArray[i].touchFl = false;
}
}
fader::rc_t fader::receive_from_eucon( const void* buf, unsigned bufByteN )
{
rc_t rc = kOkRC;
const uint8_t* b = (const uint8_t*)buf; // current msg ptr
const uint8_t* bend = b + bufByteN; // end of buffer ptr
while(b<bend)
{
// if this is the start of a new msg
if( _msgByteN == 0 )
{
// if this is an 0x19 msg which started on the previous packet
if( _msgTypeId == 0x19 )
{
// if not already filled
for(int i=0; (_msgByteIdx+i < 8) && ((b+i)<bend); ++i)
_msg[_msgByteIdx+i] = b[i];
// get the count of bytes associated with this 0x19 msg
_msgByteN = _get_eucon_msg_byte_count( _msgTypeId, _msg, _msg+8 );
//rpt(_printCbFunc,"0x19 end: %i\n",_msgByteN);
}
else
{
_msgTypeId = b[0]; // the first byte always contains the type of the msg
_msgByteIdx = 0; // empty the _msg[] index
_msgByteN = _get_eucon_msg_byte_count( _msgTypeId, b, bend ); // get the length of this mesg
// if this is a type 0x19 msg then we have to wait 8 bytes for the msg byte count
if( _msgByteN == 0 && _msgTypeId == 0x19 )
{
//_printCbFunc("0x19 begin\n");
_msgByteIdx = bend - b;
break;
}
}
// store the size and type of this message
if( _msgByteN == 0 && _msgTypeId != 0x19 )
{
rpt(_printCbFunc,"Unk Type:0x%x %i\n",_msgTypeId,bufByteN);
break;
}
//rpt(_printCbFunc,"T:0x%x %i\n",_msgTypeId,_msgByteN);
}
// if this is a channel message or the start of a 0x19 msg ...
if( _msgTypeId == 0 || (_msgTypeId == 0x19 && _msgByteN==0) )
{
// copy it into _msg[]
for(int i=0; (_msgByteIdx+i < 8) && ((b+i)<bend); ++i)
_msg[_msgByteIdx+i] = b[i];
}
// if the end, (and possibly the beginning) of the current msg is fully contained in the buffer ...
if( (_msgByteN - _msgByteIdx) <= (bend - b) )
{
_on_eucon_recv_msg_complete(_msgTypeId);
b += _msgByteN - _msgByteIdx; // then we have reached the end of the msg
_msgByteN = 0;
_msgByteIdx = 0;
_msgTypeId = 0xff;
}
else // this msg overflows to the next TCP packet
{
//_printCbFunc("Ovr:\n");
_msgByteIdx += bend - b;
b = bend;
}
}
//rpt(_printCbFunc,"D:\n");
return rc;
}
fader::rc_t fader::tick()
{
rc_t rc = kOkRC;
switch( _protoState )
{
case kWaitForHandshake_Tick_Id:
//_printCbFunc("HS Tick ");
_send_heartbeat_to_eucon();
_protoState = kWaitForHandshake_1_Id;
break;
case kWaitForHandshake_0_Id:
case kWaitForHandshake_1_Id:
case kWaitForHeartBeat_Id:
break;
}
_tickN += 1;
if( _tickN == _ticksPerHeartBeat )
{
_tickN = 0;
if( _protoState == kWaitForHeartBeat_Id )
_send_heartbeat_to_eucon();
}
return rc;
}
void fader::physical_control_changed( const uint8_t msg[3] )
{
// TODO: mask off invalid values (e.g. chan>7, value>0x3ff)
uint8_t type = (msg[0] & 0x70) >> 4;
uint8_t chan = (msg[0] & 0x0f);
uint16_t value = msg[1];
value <<= 7;
value += msg[2];
rpt(_printCbFunc,"T:%i Ch:%i V:%x\n",type,chan,value);
switch( type )
{
case kPhysTouchTId:
_send_touch_to_eucon(chan,value != 0);
break;
case kPhysFaderTId:
_send_fader_to_eucon(chan,value);
break;
case kPhysMuteTId:
_send_mute_to_eucon(chan,value == 0);
break;
default:
rpt(_printCbFunc,"Unknown physical ctl id.");
}
}
void fader::_send_to_eucon( const void* buf, unsigned bufByteN )
{
return _euconCbFunc(_euconCbArg,buf,bufByteN);
}
void fader::_send_response_0_to_eucon()
{
unsigned char buf[] =
{ 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x02,0x03,0xfc,0x01,0x05,
0x06,0x00,
0x00,0x00,0x00,0x00,0x00,0x00, // mac: offset 16
0x01,0x00,
0x00,0x00,0x00,0x00, // ip: offset 24
0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x03,0xff,0x00,0x30,0x08,0x00,0x00,0x80,0x00,0x40,0x01,0x01,0x00,0x00,0x00,0x00,
0x00,0x00
};
// set the mac address
memcpy(buf+16,_mac,6);
// set the 32 bit ip address
memcpy((unsigned char *)(buf+24),(unsigned char*)&_inetAddr, 4);
_send_to_eucon(buf,sizeof(buf));
}
void fader::_send_response_1_to_eucon()
{
unsigned char buf[] = { 0x0d,0x00,0x00,0x00, 0x00,0x00,0x00,0x08 };
_send_to_eucon(buf,sizeof(buf));
}
void fader::_send_heartbeat_to_eucon()
{
const unsigned char buf[] = { 0x03, 0x00, 0x00, 0x00 };
_send_to_eucon(buf,sizeof(buf));
}
void fader::_send_fader_to_eucon( uint16_t chIdx, uint16_t pos )
{
_chArray[chIdx].position = pos;
if( _chArray[chIdx].touchFl )
{
uint16_t buf[] = { htons(chIdx),htons(0), 0, htons(pos) };
_send_to_eucon(buf,sizeof(buf));
}
}
void fader::_send_touch_to_eucon( uint16_t chIdx, uint16_t touchFl )
{
_chArray[chIdx].touchFl = touchFl;
uint16_t buf[] = { htons(chIdx),htons(1),0, htons((uint16_t)touchFl) };
_send_to_eucon(buf,sizeof(buf));
}
void fader::_send_mute_to_eucon( uint16_t chIdx, uint16_t muteFl )
{
_chArray[chIdx].muteFl = muteFl;
uint16_t buf[] = { htons(chIdx),htons(0x200),0, htons((uint16_t)(!muteFl)) };
_send_to_eucon(buf,sizeof(buf));
}
uint16_t fader::_get_eucon_msg_byte_count( uint8_t msgTypeId, const uint8_t* b, const uint8_t* bend )
{
if( msgTypeId == 0x19 )
{
const uint16_t* u = (const uint16_t*)b;
if( bend < (const uint8_t*)(u+4) )
{
//_printCbFunc("0x19 short\n");
return 0;
}
uint16_t v = u[3];
return ntohs(v);
}
for(int i=0; _msgRefA[i].byteN != 0; ++i)
if( msgTypeId == _msgRefA[i].id )
return _msgRefA[i].byteN;
return 0;
}
void fader:: _send_to_phys_control( uint8_t ctlTypeId, uint8_t ch, uint16_t value )
{
uint8_t msg[3];
msg[0] = 0x80 + (ctlTypeId << 4) + (ch); // status byte always has high bit set
msg[1] = (uint8_t)((value & 0x3f80) >> 7); // get high 7 bits of value (high bit is always cleared)
msg[2] = (uint8_t)(value & 0x007f); // get low 7 bits of value (high bit is always cleared)
_physCtlCbFunc( _physCtlCbArg, msg, sizeof(msg) );
}
// called when a new msg is received, b[0] is the msg type id
void fader::_on_eucon_recv_msg_complete( const uint8_t typeId )
{
switch( typeId )
{
case 0x0a:
if( _protoState == kWaitForHandshake_0_Id )
{
_printCbFunc("HS 0 ");
_send_response_0_to_eucon(); // send [ 0x0b ... ]
_protoState = kWaitForHandshake_Tick_Id;
}
break;
case 0x0c:
//rpt(_printCbFunc,"HB\n");
break;
case 0x00:
{
const uint16_t* u = (const uint16_t*)_msg;
uint8_t ch = _msg[1];
if( ch < 8 )
{
switch( ntohs(u[1]) )
{
case 0:
_chArray[ch].position = ntohs(u[3]);
//rpt(_printCbFunc,"F: %2i %4i \n",(int)_msg[1],ntohs(u[3]));
_send_to_phys_control(kPhysFaderTId,ch,ntohs(u[3]));
break;
case 0x201:
_chArray[ch].muteFl = ntohs(u[3]);
//rpt(_printCbFunc,"M: %2i %4i \n",(int)_msg[1],ntohs(u[3]));
_send_to_phys_control(kPhysMuteTId,ch,ntohs(u[3]));
break;
}
}
}
break;
case 0x04:
//rpt(_printCbFunc,"HB:\n");
break;
case 0x19:
//rpt(_printCbFunc,"Skip:\n");
break;
default:
rpt(_printCbFunc,"Unknown msg type.");
}
// Any msg will trigger change of state from
// kWaitForHandshake_1_Id to kWaitForHeartBeat_Id
if( _protoState == kWaitForHandshake_1_Id )
{
_printCbFunc("HS 1\n ");
_send_response_1_to_eucon(); //send [ 0x0d, .... ]
_protoState = kWaitForHeartBeat_Id;
}
}