introduced BusHandler for managing all possible message transfer types

This commit is contained in:
john30
2014-11-29 20:13:25 +01:00
parent 036036e626
commit d90dbe8507
2 changed files with 444 additions and 0 deletions
+281
View File
@@ -0,0 +1,281 @@
/*
* Copyright (C) John Baier 2014 <ebusd@johnm.de>
*
* 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 "bushandler.h"
#include "message.h"
#include "data.h"
#include "result.h"
#include "symbol.h"
#include "appl.h"
#include <string>
#include <vector>
#include <cstring>
using namespace std;
extern Logger& L;
extern Appl& A;
/**
* @brief Return the string corresponding to the @a BusState and send position.
* @param state the @a BusState.
* @param sendPos >=0 while sending data, -1 while receiving data.
* @return the string corresponding to the @a BusState.
*/
const char* getStateCode(BusState state, int sendPos) {
switch (state)
{
case bs_skip: return "skip";
case bs_ready: return "ready";
case bs_command: return sendPos < 0 ? "receive command" : "send command";
case bs_commandAck: return sendPos < 0 ? "receive command ACK" : "send command ACK";
case bs_response: return sendPos < 0 ? "receive response" : "send response";
case bs_responseAck: return sendPos < 0 ? "receive response ACK" : "send response ACK";
//case bs_validTransfer: return sendPos < 0 ? "after complete receive" : "after complete send";
default: return "unknown state";
}
}
void BusHandler::run()
{
result_t result = RESULT_OK;
do {
if (m_port->isOpen() == true) {
result = receiveSymbol();
if (result != RESULT_OK)
L.log(bus, error, " %s", getResultCode(result));
}
else {
// TODO: define max reopen
sleep(10);
result = m_port->open();
if (result != RESULT_OK)
L.log(bus, error, "can't open %s", A.getOptVal<const char*>("device"));
}
} while (isRunning() == true);
}
result_t BusHandler::receiveSymbol()
{
long timeout;
ssize_t count;
if (m_state == bs_skip)
timeout = 0;
else if (m_state == bs_ready)
timeout = SYN_TIMEOUT;
else if (m_sendPos >= 0)
timeout = SLAVE_RECV_TIMEOUT;
else
timeout = SYN_TIMEOUT;
count = m_port->recv(timeout, 1);
if (count < 0)
return setState(bs_skip, RESULT_ERR_DEVICE);
if (count == 0) {
if (m_state == bs_ready)
return RESULT_OK; // TODO keep "no signal" within auto-syn state
return setState(bs_skip, RESULT_ERR_TIMEOUT);
}
unsigned char symbol = m_port->byte();
if (symbol == SYN) {
m_repeat = false;
return setState(bs_ready, RESULT_OK);
}
unsigned char headerLen, crcPos;
result_t result;
switch (m_state)
{
case bs_skip:
return RESULT_OK;
case bs_ready:
if (symbol == ESC)
return setState(bs_skip, RESULT_ERR_ESC);
result = m_command.push_back(symbol);
if (result < RESULT_OK)
return setState(bs_skip, result);
return setState(bs_command, result);
case bs_command:
headerLen = 4;
crcPos = m_command.size() > headerLen ? headerLen + 1 + m_command[headerLen] : 0xff;
result = m_command.push_back(symbol, true, m_command.size() < crcPos);
if (result < RESULT_OK)
return setState(bs_skip, result);
if (result == RESULT_OK && m_command.size() == crcPos + 1) { // CRC received
m_commandCrcValid = m_command[headerLen + 1 + m_command[headerLen]] == m_command.getCRC();
if (m_command[1] == BROADCAST) {
if (m_commandCrcValid) {
transferCompleted(tt_broadcast);
return setState(bs_skip, RESULT_OK);
}
return setState(bs_skip, RESULT_ERR_CRC);
}
/*if (m_command[1] == m_ownSlaveAddress || m_command[1] == m_ownMasterAddress) {
setState(bs_commandAck, RESULT_OK);
m_sendPos = 0;
symbol = m_commandCrcValid ? ACK : NAK;
if (m_port->send(&symbol) <= 0)
return setState(bs_skip, RESULT_ERR_SEND);
}*/
return setState(bs_commandAck, RESULT_OK);
}
return result;
case bs_commandAck:
if (symbol == ESC)
return setState(bs_skip, RESULT_ERR_ESC);
/*if (m_sendPos >= 0) {
if (symbol == ACK && m_commandCrcValid == true)
return setState();
return setState()
}*/
if (symbol == ACK) {
if (m_commandCrcValid == false)
return setState(bs_skip, RESULT_ERR_ACK);
if (isMaster(m_command[1]) == true) {
transferCompleted(tt_masterMaster);
return setState(bs_skip, RESULT_OK);
}
return setState(bs_response, RESULT_OK);
}
if (symbol == NAK) {
if (m_repeat == false) {
m_repeat = true;
return setState(bs_ready, RESULT_ERR_NAK);
}
return setState(bs_skip, RESULT_ERR_NAK);
}
return setState(bs_skip, RESULT_ERR_ACK);
case bs_response:
headerLen = 0;
crcPos = m_response.size() > headerLen ? headerLen + 1 + m_response[headerLen] : 0xff;
result = m_response.push_back(symbol, true, m_response.size() < crcPos);
if (result < RESULT_OK)
return setState(bs_skip, result);
if (result == RESULT_OK && m_response.size() == crcPos + 1) { // CRC received
m_responseCrcValid = m_response[headerLen + 1 + m_response[headerLen]] == m_response.getCRC();
/*if (m_command[1] == m_ownSlaveAddress || m_command[1] == m_ownMasterAddress) {
setState(bs_responseAck, RESULT_OK);
m_sendPos = 0;
symbol = m_responseCrcValid ? ACK : NAK;
if (m_port->send(&symbol) <= 0)
return setState(bs_skip, RESULT_ERR_SEND);
}*/
return setState(bs_responseAck, RESULT_OK);
}
return result;
case bs_responseAck:
if (symbol == ESC)
return setState(bs_skip, RESULT_ERR_ESC);
/*if (m_sendPos >= 0) {
if (symbol == ACK && m_responseCrcValid == true)
return setState();
return setState()
}*/
if (symbol == ACK) {
if (m_responseCrcValid == false)
return setState(bs_skip, RESULT_ERR_ACK);
transferCompleted(tt_masterSlave);
return setState(bs_skip, RESULT_OK);
}
if (symbol == NAK) {
if (m_repeat == false) {
m_repeat = true;
return setState(bs_response, RESULT_ERR_NAK);
}
return setState(bs_skip, RESULT_ERR_NAK);
}
return setState(bs_skip, RESULT_ERR_ACK);
}
return RESULT_OK;
}
result_t BusHandler::setState(BusState state, result_t result)
{
if (state == m_state)
return result;
if (result < RESULT_OK || (result != RESULT_OK && state == bs_skip))
L.log(bus, error, " %s during %s, switching to %s", getResultCode(result), getStateCode(m_state, m_sendPos), getStateCode(state, m_sendPos));
m_state = state;
if (state == bs_ready || state == bs_skip) {
m_command.clear();
m_commandCrcValid = false;
m_response.clear();
m_responseCrcValid = false;
m_sendPos = -1;
}
if (state == bs_skip)
m_repeat = false;
return result;
}
void BusHandler::transferCompleted(TransferType type)
{
switch (type)
{
case tt_broadcast:
L.log(bus, trace, "received broadcast %s", m_command.getDataStr().c_str());
break;
case tt_masterMaster:
L.log(bus, trace, "received master %s", m_command.getDataStr().c_str());
break;
case tt_masterSlave:
L.log(bus, trace, "received master %s, slave %s", m_command.getDataStr().c_str(), m_response.getDataStr().c_str());
break;
default:
return;
}
Message* msg = m_messages->find(m_command);
if (msg != NULL) {
ostringstream output;
result_t result = msg->decode(m_command, m_response, output);
if (result != RESULT_OK)
L.log(bus, error, "unable to parse %s %s: %s", msg->getClass().c_str(), msg->getName().c_str(), getResultCode(result));
else
L.log(bus, trace, "%s %s: %s", msg->getClass().c_str(), msg->getName().c_str(), output.str().c_str());
}
}
+163
View File
@@ -0,0 +1,163 @@
/*
* Copyright (C) John Baier 2014 <ebusd@johnm.de>
*
* 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/.
*/
#ifndef LIBEBUS_BUSHANDLER_H_
#define LIBEBUS_BUSHANDLER_H_
#include "message.h"
#include "data.h"
#include "symbol.h"
#include "result.h"
#include "port.h"
#include "thread.h"
#include <string>
#include <vector>
#include <map>
using namespace std;
/** the maximum allowed time [us] for retrieval of a single symbol from an addressed slave. */
#define SLAVE_RECV_TIMEOUT 10000
/** the maximum allowed time [us] for retrieval of an AUTO-SYN symbol. */
#define SYN_TIMEOUT 50000
/** the possible bus states. */
enum BusState {
bs_skip, // skip all symbols until next @a SYN
bs_ready, // ready for next master (after @a SYN symbol, send/receive QQ)
bs_command, // receive/send command (ZZ, PBSB, master data)
bs_commandAck, // receive/send command ACK/NACK
bs_response, // receive/send response (slave data)
bs_responseAck, // receive/send response ACK/NACK
//bs_validTransfer,// completed a valid message transfer
};
/** the possible message transfer types. */
enum TransferType {
tt_broadcast, // broadcast transfer
tt_masterMaster, // master to master transfer
tt_masterSlave // master to slave transfer
};
/** the possible combinations of participants in a single message exchange. */
enum MessageDirection {
md_thisToAll, // message from us to all (broadcast)
md_thisToMaster, // message from us to another master
md_thisToSlave, // message from us to another slave
md_otherToAll, // message from a master (other than us) to all (broadcast): @a bs_ready, @a bs_recvCmd
md_otherToMaster, // message from a master (other than us) to another master (other than us): @a bs_ready, @a bs_recvCmd, @a bs_recvAck
md_otherToSlave, // message from a master (other than us) to another slave (other than us): @a bs_ready, @a bs_recvCmd, @a bs_recvAck, @a bs_recvResp, @a bs_recvAck
md_otherToThisMaster, // message from a master (other than us) to us (as master)
md_otherToThisSlave, // message from a master (other than us) to us (as slave)
md_undefined,
};
/**
* @brief Handles input from and output to the bus with respect to the ebus protocol.
*/
class BusHandler : public Thread
{
public:
/**
* @brief Construct a new instance.
* @param port the @a Port instance for accessing the bus.
* @param messages the @a MessageMap instance with all known @a Message instances.
* @param ownMasterAddress the own master address to react on master-master messages, or @a SYN to ignore.
* @param ownSlaveAddress the own slave address to react on master-slave messages, or @a SYN to ignore.
*/
BusHandler(Port* port, MessageMap* messages, unsigned char ownMasterAddress,
unsigned char ownSlaveAddress)
: m_port(port), m_messages(messages), m_ownMasterAddress(ownMasterAddress),
m_ownSlaveAddress(ownSlaveAddress), m_state(bs_skip), m_repeat(false),
m_sendPos(-1), m_commandCrcValid(false), m_responseCrcValid(false) {}
/**
* @brief Destructor.
*/
virtual ~BusHandler() {}
/**
* @brief Main thread entry.
*/
virtual void run();
private:
/**
* @brief Receive another symbol from the bus.
* @return RESULT_OK on success, or an error code.
*/
result_t receiveSymbol();
/**
* @brief Set a new @a BusState and add a log message if necessary.
* @param state the new @a BusState.
* @param result the result code.
* @return the result code.
*/
result_t setState(BusState state, result_t result);
/**
* @brief Called when a transfer was successfully completed.
* @param type the @a TransferType.
*/
void transferCompleted(TransferType type);
/** the @a Port instance for accessing the bus. */
Port* m_port;
/** the @a MessageMap instance with all known @a Message instances. */
MessageMap* m_messages;
/** the own master address to react on master-master messages, or @a SYN to ignore. */
unsigned char m_ownMasterAddress;
/** the own slave address to react on master-slave messages, or @a SYN to ignore. */
unsigned char m_ownSlaveAddress;
/** the current @a BusState. */
BusState m_state;
/** whether the current message part is being repeated. */
bool m_repeat;
/*
* the offset of the last sent symbol while sending command/response,
* or 0 while sending ACK/NACK, or -1 if not sending.
*/
int m_sendPos;
/** the received/sent command. */
SymbolString m_command;
/** whether the command CRC is valid. */
bool m_commandCrcValid;
/** the received/sent response. */
SymbolString m_response;
/** whether the response CRC is valid. */
bool m_responseCrcValid;
};
#endif // LIBEBUS_BUSHANDLER_H_