Files
ebusd/src/lib/ebus/bus.cpp
T
2014-11-06 16:08:11 +01:00

389 lines
7.7 KiB
C++

/*
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>
*
* This file is part of ebusd.
*
* ebusd 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.
*
* ebusd 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 ebusd. If not, see http://www.gnu.org/licenses/.
*/
#include "bus.h"
#include <iostream>
#include <iomanip>
namespace libebus
{
Bus::Bus(const std::string deviceName, const bool noDeviceCheck, const long recvTimeout,
const std::string dumpFile, const long dumpSize, const bool dumpState)
: m_sstr(), m_recvTimeout(recvTimeout), m_dumpState(dumpState),
m_busLocked(false), m_busPriorRetry(false)
{
m_port = new Port(deviceName, noDeviceCheck);
m_dump = new Dump(dumpFile, dumpSize);
}
Bus::~Bus()
{
if (isConnected() == true)
disconnect();
delete m_port;
delete m_dump;
}
void Bus::printBytes() const
{
unsigned char byte;
ssize_t bytes_read;
bytes_read = m_port->recv(0);
for (int i = 0; i < bytes_read; i++) {
byte = m_port->byte();
std::cout << std::nouppercase << std::hex << std::setw(2)
<< std::setfill('0') << static_cast<unsigned>(byte);
if (byte == SYN)
std::cout << std::endl;
}
}
int Bus::proceed()
{
unsigned char byte;
ssize_t nbytes;
// fetch new message and get bus
if (m_sendBuffer.size() != 0 && m_sstr.size() == 0) {
BusCommand* busCommand = m_sendBuffer.front();
return getBus(busCommand->getCommand()[0]);
}
// wait for new data
nbytes = m_port->recv(0);
if (nbytes < 0)
return RESULT_ERR_DEVICE;
for (int i = 0; i < nbytes; i++) {
// fetch next byte
byte = recvByte();
// store byte
return proceedCycData(byte); // TODO what if more than one byte was received?
}
return RESULT_SYN;
}
int Bus::proceedCycData(const unsigned char byte)
{
if (byte != SYN) {
m_sstr.push_back(byte, true, false);
if (m_busLocked == true)
m_busLocked = false;
return RESULT_DATA;
}
if (byte == SYN && m_sstr.size() != 0) {
// lock bus after SYN-BYTE-SYN Sequence
if (m_sstr.size() == 1 && m_busPriorRetry == false)
m_busLocked = true;
m_cycBuffer.push(m_sstr);
m_sstr.clear();
if (m_busLocked == true)
return RESULT_BUS_LOCKED;
}
return RESULT_SYN;
}
SymbolString Bus::getCycData()
{
SymbolString data;
if (m_cycBuffer.empty() == false) {
data = m_cycBuffer.front();
m_cycBuffer.pop();
}
return data;
}
int Bus::getBus(const unsigned char byte_sent)
{
unsigned char byte_recv;
ssize_t bytes_sent, bytes_recv;
// send QQ
bytes_sent = m_port->send(&byte_sent, 1);
if (bytes_sent <= 0)
return RESULT_ERR_SEND;
// receive 1 byte - must be QQ
bytes_recv = m_port->recv(0, 1);
if (bytes_recv < 0)
return RESULT_ERR_DEVICE;
// fetch next byte
byte_recv = recvByte();
// compare sent and received byte
if (bytes_recv == 1 && byte_sent == byte_recv) {
m_busPriorRetry = false;
return RESULT_BUS_ACQUIRED;
}
// store byte
int ret = proceedCycData(byte_recv);
if (ret >= 0)
return ret;
// TODO this needs to be re-designed with above proceedCycData()
// compare prior nibble for retry
if (bytes_recv == 1 && (byte_sent & 0x0F) == (byte_recv & 0x0F)) {
m_busPriorRetry = true;
return RESULT_BUS_PRIOR_RETRY;
}
m_busLocked = true;
return RESULT_ERR_BUS_LOST;
}
BusCommand* Bus::sendCommand()
{
unsigned char byte_recv;
ssize_t bytes_recv;
std::string result;
SymbolString slaveData;
int retval = RESULT_OK;
BusCommand* busCommand = m_sendBuffer.front();
m_sendBuffer.pop();
// send ZZ PB SB NN Dx CRC
SymbolString command = busCommand->getCommand();
for (size_t i = 1; i < command.size(); i++) {
retval = sendByte(command[i]);
if (retval < 0)
goto on_exit;
}
// BC -> send SYN
if (busCommand->getType() == broadcast) {
sendByte(SYN);
goto on_exit;
}
// receive ACK
bytes_recv = m_port->recv(m_recvTimeout);
if (bytes_recv > 1) {
retval = RESULT_ERR_EXTRA_DATA;
goto on_exit;
} else if (bytes_recv < 0) {
retval = RESULT_ERR_TIMEOUT;
goto on_exit;
}
byte_recv = recvByte();
// is received byte SYN?
if (byte_recv == SYN) {
retval = RESULT_ERR_SYN;
goto on_exit;
}
// is slave ACK negative?
if (byte_recv == NAK) {
// send QQ ZZ PB SB NN Dx CRC again
for (size_t i = 0; i < command.size(); i++) {
retval = sendByte(command[i]);
if (retval < 0)
goto on_exit;
}
// receive ACK
bytes_recv = m_port->recv(m_recvTimeout);
if (bytes_recv > 1) {
retval = RESULT_ERR_EXTRA_DATA;
goto on_exit;
} else if (bytes_recv < 0) {
retval = RESULT_ERR_TIMEOUT;
goto on_exit;
}
byte_recv = recvByte();
// is received byte SYN?
if (byte_recv == SYN) {
retval = RESULT_ERR_SYN;
goto on_exit;
}
// is slave ACK negative?
if (byte_recv == NAK) {
retval = sendByte(SYN);
if (retval == 0)
retval = RESULT_ERR_NAK;
goto on_exit;
}
}
// MM -> send SYN
if (busCommand->getType() == masterMaster) {
sendByte(SYN);
goto on_exit;
}
// receive NN, Dx, CRC
retval = recvSlaveDataAndCRC(slaveData);
// are calculated and received CRC equal?
if (retval == RESULT_ERR_CRC) {
// send NAK
retval = sendByte(NAK);
if (retval < 0)
goto on_exit;
// receive NN, Dx, CRC
slaveData.clear();
retval = recvSlaveDataAndCRC(slaveData);
// are calculated and received CRC equal?
if (retval == RESULT_ERR_CRC) {
// send NAK
retval = sendByte(NAK);
if (retval >= 0)
retval = RESULT_ERR_CRC;
}
}
if (retval < 0)
goto on_exit;
// send ACK
retval = sendByte(ACK);
if (retval == -1) {
retval = RESULT_ERR_ACK;
goto on_exit;
}
// MS -> send SYN
sendByte(SYN);
on_exit:
// empty receive buffer
while (m_port->size() != 0)
byte_recv = recvByte();
busCommand->setResult(slaveData, retval);
return busCommand;
}
BusCommand* Bus::delCommand()
{
BusCommand* busCommand = m_sendBuffer.front();
m_sendBuffer.pop();
busCommand->setResult(SymbolString(), RESULT_ERR_BUS_LOST);
return busCommand;
}
int Bus::sendByte(const unsigned char byte_sent)
{
unsigned char byte_recv;
ssize_t bytes_sent, bytes_recv;
bytes_sent = m_port->send(&byte_sent, 1);
// receive 1 byte - must be equal
bytes_recv = m_port->recv(RECV_TIMEOUT);
if (bytes_sent != bytes_recv)
return RESULT_ERR_EXTRA_DATA;
byte_recv = recvByte();
if (byte_sent != byte_recv)
return RESULT_ERR_SEND;
return RESULT_OK;
}
unsigned char Bus::recvByte()
{
unsigned char byte_recv;
// fetch byte
byte_recv = m_port->byte();
if (m_dumpState == true)
m_dump->write((const char*) &byte_recv);
return byte_recv;
}
int Bus::recvSlaveDataAndCRC(SymbolString& result)
{
unsigned char byte_recv, crc_calc = 0;
ssize_t bytes_recv;
size_t NN = 0;
bool updateCrc = true;
int retval = 0;
for (size_t i = 0, needed = 1; i < needed; i++) {
bytes_recv = m_port->recv(RECV_TIMEOUT, 1);
if (bytes_recv < 0)
return RESULT_ERR_TIMEOUT;
byte_recv = recvByte();
retval = result.push_back(byte_recv, true, updateCrc);
if (retval < 0)
return retval;
if (retval == RESULT_IN_ESC)
needed++;
else if (result.size() == 1) { // NN received
NN = result[0];
needed += NN;
}
else if (NN > 0 && result.size() == 1+NN) {// all data received
updateCrc = false;
crc_calc = result.getCRC();
needed++;
}
}
if (retval == RESULT_IN_ESC)
return RESULT_ERR_ESC;
if (updateCrc || crc_calc != result[result.size()-1])
return RESULT_ERR_CRC;
return RESULT_OK;
}
} //namespace