introduced BusHandler for managing all possible message transfer types
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
Reference in New Issue
Block a user