Skip to content

Commit

Permalink
Merge branch 'cia-state-machine' into apocanlypse
Browse files Browse the repository at this point in the history
  • Loading branch information
PeterBowman committed Sep 13, 2019
2 parents 7af8b63 + 3c1e282 commit 972f875
Show file tree
Hide file tree
Showing 15 changed files with 556 additions and 199 deletions.
9 changes: 5 additions & 4 deletions libraries/CanBusSharerLib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@ if(ENABLE_CanBusSharerLib)
SdoClient.hpp
SdoClient-inl.hpp
SdoClient.cpp
SdoSemaphore.hpp
SdoSemaphore.cpp
PdoProtocol.hpp
PdoProtocol.cpp
EmcyConsumer.hpp
EmcyConsumer.cpp
NmtProtocol.hpp
NmtProtocol.cpp
DriveStatusMachine.hpp
DriveStatusMachine.hpp
StateObserver.hpp
StateObserver.cpp
CanUtils.hpp
CanUtils.cpp)

target_link_libraries(CanBusSharerLib PUBLIC YARP::YARP_dev
PRIVATE YARP::YARP_OS
ROBOTICSLAB::ColorDebug)
PRIVATE ROBOTICSLAB::ColorDebug)

target_include_directories(CanBusSharerLib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

Expand Down
13 changes: 8 additions & 5 deletions libraries/CanBusSharerLib/CanOpen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

using namespace roboticslab;

CanOpen::CanOpen(unsigned int id, double sdoTimeout, CanSenderDelegate * sender)
CanOpen::CanOpen(unsigned int id, double sdoTimeout, double stateTimeout, CanSenderDelegate * sender)
: _id(id),
_sdo(new SdoClient(_id, 0x600, 0x580, sdoTimeout, sender)),
_rpdo1(new ReceivePdo(_id, 0x200, 1, _sdo, sender)),
Expand All @@ -16,13 +16,17 @@ CanOpen::CanOpen(unsigned int id, double sdoTimeout, CanSenderDelegate * sender)
_tpdo3(new TransmitPdo(_id, 0x380, 3, _sdo)),
_tpdo4(new TransmitPdo(_id, 0x480, 4, _sdo)),
_emcy(new EmcyConsumer(_sdo)),
_nmt(new NmtProtocol(_id, _sdo, sender))
_nmt(new NmtProtocol(_id, _sdo, sender)),
_driveStatus(new DriveStatusMachine(_rpdo1, stateTimeout))
{
_tpdo1->registerHandler<std::uint16_t>([this](std::uint16_t statusword) { _driveStatus->update(statusword); });
}

CanOpen::~CanOpen()
{
delete _sdo;
delete _emcy;
delete _nmt;
delete _driveStatus;

delete _rpdo1;
delete _rpdo2;
Expand All @@ -34,8 +38,7 @@ CanOpen::~CanOpen()
delete _tpdo3;
delete _tpdo4;

delete _emcy;
delete _nmt;
delete _sdo;
}

void CanOpen::configureSender(CanSenderDelegate * sender)
Expand Down
10 changes: 8 additions & 2 deletions libraries/CanBusSharerLib/CanOpen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "PdoProtocol.hpp"
#include "EmcyConsumer.hpp"
#include "NmtProtocol.hpp"
#include "DriveStatusMachine.hpp"

namespace roboticslab
{
Expand All @@ -19,8 +20,10 @@ class CanOpen final
{
public:
static constexpr double SDO_TIMEOUT = 0.1; // seconds
static constexpr double STATE_MACHINE_TIMEOUT = 2.0; // seconds

CanOpen(unsigned int id, double sdoTimeout = SDO_TIMEOUT, CanSenderDelegate * sender = nullptr);
CanOpen(unsigned int id, double sdoTimeout = SDO_TIMEOUT,
double stateTimeout = STATE_MACHINE_TIMEOUT, CanSenderDelegate * sender = nullptr);

CanOpen(const CanOpen &) = delete;
CanOpen & operator=(const CanOpen &) = delete;
Expand Down Expand Up @@ -59,6 +62,9 @@ class CanOpen final
NmtProtocol * nmt() const
{ return _nmt; }

DriveStatusMachine * driveStatus() const
{ return _driveStatus; }

bool consumeMessage(std::uint16_t cobId, const std::uint8_t * data, std::size_t size) const;

private:
Expand All @@ -77,8 +83,8 @@ class CanOpen final
TransmitPdo * _tpdo4;

EmcyConsumer * _emcy;

NmtProtocol * _nmt;
DriveStatusMachine * _driveStatus;
};

} // namespace roboticslab
Expand Down
164 changes: 164 additions & 0 deletions libraries/CanBusSharerLib/DriveStatusMachine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*-

#include "DriveStatusMachine.hpp"

#include <bitset>
#include <unordered_map>

using namespace roboticslab;

namespace
{
DriveState parseDriveState(const std::bitset<16> & bits)
{
if (!bits.test(0) && !bits.test(1) && !bits.test(2) && !bits.test(3) && !bits.test(6))
{
return DriveState::NOT_READY_TO_SWITCH_ON; // xxxx.xxxx.x0xx.0000
}
else if (!bits.test(0) && !bits.test(1) && !bits.test(2) && !bits.test(3) && bits.test(6))
{
return DriveState::SWITCH_ON_DISABLED; // xxxx.xxxx.x1xx.0000
}
else if (bits.test(0) && !bits.test(1) && !bits.test(2) && !bits.test(3) && bits.test(5) && !bits.test(6))
{
return DriveState::READY_TO_SWITCH_ON; // xxxx.xxxx.x01x.0001
}
else if (bits.test(0) && bits.test(1) && !bits.test(2) && !bits.test(3) && bits.test(5) && !bits.test(6))
{
return DriveState::SWITCHED_ON; // xxxx.xxxx.x01x.0011
}
else if (bits.test(0) && bits.test(1) && bits.test(2) && !bits.test(3) && bits.test(5) && !bits.test(6))
{
return DriveState::OPERATION_ENABLED; // xxxx.xxxx.x01x.0111
}
else if (bits.test(0) && bits.test(1) && bits.test(2) && !bits.test(3) && !bits.test(5) && !bits.test(6))
{
return DriveState::QUICK_STOP_ACTIVE; // xxxx.xxxx.x00x.0111
}
else if (bits.test(0) && bits.test(1) && bits.test(2) && bits.test(3) && !bits.test(6))
{
return DriveState::FAULT_REACTION_ACTIVE; // xxxx.xxxx.x0xx.1111
}
else if (!bits.test(0) && !bits.test(1) && !bits.test(2) && bits.test(3) && !bits.test(6))
{
return DriveState::FAULT; // xxxx.xxxx.x0xx.1000
}
else
{
return DriveState::NOT_READY_TO_SWITCH_ON; // assume drive is dead
}
}

void prepareDriveTransition(DriveTransition transition, std::bitset<16> & bits)
{
switch (transition)
{
case DriveTransition::SHUTDOWN: // xxxx.xxxx.0xxx.x110
bits.reset(0);
bits.set(1);
bits.set(2);
bits.reset(7);
break;
case DriveTransition::SWITCH_ON: // xxxx.xxxx.0xxx.0111
case DriveTransition::DISABLE_OPERATION:
bits.set(0);
bits.set(1);
bits.set(2);
bits.reset(3);
bits.reset(7);
break;
case DriveTransition::DISABLE_VOLTAGE: // xxxx.xxxx.0xxx.xx0x
bits.reset(1);
bits.reset(7);
break;
case DriveTransition::QUICK_STOP: // xxxx.xxxx.0xxx.x01x
bits.set(1);
bits.reset(2);
bits.reset(7);
break;
case DriveTransition::ENABLE_OPERATION: // xxxx.xxxx.0xxx.1111
bits.set(0);
bits.set(1);
bits.set(2);
bits.set(3);
bits.reset(7);
break;
case DriveTransition::FAULT_RESET: // xxxx.xxxx.^xxx.xxxx
bits.set(7); // FIXME: check this
break;
}
}

std::unordered_map<std::pair<DriveState, DriveTransition>, DriveState> transitionToState = {
{std::make_pair(DriveState::SWITCH_ON_DISABLED, DriveTransition::SHUTDOWN), DriveState::READY_TO_SWITCH_ON}, // 2
{std::make_pair(DriveState::READY_TO_SWITCH_ON, DriveTransition::SWITCH_ON), DriveState::SWITCHED_ON}, // 3
{std::make_pair(DriveState::SWITCHED_ON, DriveTransition::ENABLE_OPERATION), DriveState::OPERATION_ENABLED}, // 4
{std::make_pair(DriveState::OPERATION_ENABLED, DriveTransition::DISABLE_OPERATION), DriveState::SWITCHED_ON}, // 5
{std::make_pair(DriveState::OPERATION_ENABLED, DriveTransition::SWITCH_ON), DriveState::SWITCHED_ON}, // 5 (alias)
{std::make_pair(DriveState::SWITCHED_ON, DriveTransition::SHUTDOWN), DriveState::READY_TO_SWITCH_ON}, // 6
{std::make_pair(DriveState::READY_TO_SWITCH_ON, DriveTransition::DISABLE_VOLTAGE), DriveState::SWITCH_ON_DISABLED}, // 7
{std::make_pair(DriveState::OPERATION_ENABLED, DriveTransition::SHUTDOWN), DriveState::READY_TO_SWITCH_ON}, // 8
{std::make_pair(DriveState::OPERATION_ENABLED, DriveTransition::DISABLE_VOLTAGE), DriveState::SWITCH_ON_DISABLED}, // 9
{std::make_pair(DriveState::SWITCHED_ON, DriveTransition::DISABLE_VOLTAGE), DriveState::SWITCH_ON_DISABLED}, // 10
{std::make_pair(DriveState::OPERATION_ENABLED, DriveTransition::QUICK_STOP), DriveState::QUICK_STOP_ACTIVE}, // 11
{std::make_pair(DriveState::QUICK_STOP_ACTIVE, DriveTransition::DISABLE_VOLTAGE), DriveState::SWITCH_ON_DISABLED}, // 12
{std::make_pair(DriveState::QUICK_STOP_ACTIVE, DriveTransition::ENABLE_OPERATION), DriveState::OPERATION_ENABLED}, // 16
};
}

bool DriveStatusMachine::update(std::uint16_t statusword)
{
{
std::lock_guard<std::mutex> lock(stateMutex);
_statusword = statusword;
}

return stateObserver.notify();
}

std::bitset<16> & DriveStatusMachine::controlword()
{
return _controlword;
}

const std::bitset<16> & DriveStatusMachine::statusword() const
{
std::lock_guard<std::mutex> lock(stateMutex);
return _statusword;
}

DriveState DriveStatusMachine::getCurrentState() const
{
return parseDriveState(statusword());
}

bool DriveStatusMachine::requestTransition(DriveTransition transition, bool wait)
{
prepareDriveTransition(transition, _controlword);
std::uint16_t data = _controlword.to_ulong();

DriveState initialState = getCurrentState();

if (!rpdo->write(data))
{
return false;
}

if (wait)
{
auto op = std::make_pair(initialState, transition);
std::uint16_t resp;

if (!stateObserver.await())
{
return false;
}

if (transitionToState[op] != resp)
{
return false;
}
}

return true;
}
68 changes: 68 additions & 0 deletions libraries/CanBusSharerLib/DriveStatusMachine.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*-

#ifndef __DRIVE_STATUS_MACHINE_HPP__
#define __DRIVE_STATUS_MACHINE_HPP__

#include <cstdint>

#include <bitset>
#include <mutex>

#include "PdoProtocol.hpp"
#include "StateObserver.hpp"

namespace roboticslab
{

// associated to statusword
enum class DriveState
{
NOT_READY_TO_SWITCH_ON,
SWITCH_ON_DISABLED,
READY_TO_SWITCH_ON,
SWITCHED_ON,
OPERATION_ENABLED,
QUICK_STOP_ACTIVE,
FAULT_REACTION_ACTIVE,
FAULT
};

// associated to controlword
enum class DriveTransition
{
SHUTDOWN,
SWITCH_ON,
DISABLE_VOLTAGE,
QUICK_STOP,
ENABLE_OPERATION,
DISABLE_OPERATION, // same as switch on
FAULT_RESET
};

class DriveStatusMachine
{
public:
DriveStatusMachine(ReceivePdo * rpdo, double timeout)
: rpdo(rpdo), stateObserver(timeout)
{ }

void configureRpdo(ReceivePdo * rpdo)
{ this->rpdo = rpdo; }

bool update(std::uint16_t statusword);
std::bitset<16> & controlword();
const std::bitset<16> & statusword() const;
DriveState getCurrentState() const;
bool requestTransition(DriveTransition transition, bool wait = true);

private:
std::bitset<16> _controlword;
std::bitset<16> _statusword;
ReceivePdo * rpdo;
StateObserver stateObserver;
mutable std::mutex stateMutex;
};

} // namespace roboticslab

#endif // __DRIVE_STATUS_MACHINE_HPP__
2 changes: 1 addition & 1 deletion libraries/CanBusSharerLib/SdoClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ bool SdoClient::performTransfer(const std::string & name, const std::uint8_t * r

CD_INFO("SDO client request/indication (\"%s\"). %s\n", name.c_str(), reqStr.c_str());

bool success = sdoSemaphore.await(resp);
bool success = stateObserver.await(resp);
const std::string & respStr = msgToStr(cobTx, resp);

if (!success)
Expand Down
8 changes: 4 additions & 4 deletions libraries/CanBusSharerLib/SdoClient.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#include <type_traits>

#include "CanSenderDelegate.hpp"
#include "SdoSemaphore.hpp"
#include "StateObserver.hpp"

namespace roboticslab
{
Expand All @@ -19,7 +19,7 @@ class SdoClient final
{
public:
SdoClient(std::uint8_t id, std::uint16_t cobRx, std::uint16_t cobTx, double timeout, CanSenderDelegate * sender = nullptr)
: id(id), cobRx(cobRx), cobTx(cobTx), sender(sender), sdoSemaphore(timeout)
: id(id), cobRx(cobRx), cobTx(cobTx), sender(sender), stateObserver(timeout)
{}

std::uint16_t getCobIdRx() const
Expand All @@ -32,7 +32,7 @@ class SdoClient final
{ this->sender = sender; }

bool notify(const std::uint8_t * raw)
{ return sdoSemaphore.notify(raw); }
{ return stateObserver.notify(raw, 8); }

template<typename T>
bool upload(const std::string & name, T * data, std::uint16_t index, std::uint8_t subindex = 0x00)
Expand Down Expand Up @@ -68,7 +68,7 @@ class SdoClient final
std::uint16_t cobTx;

CanSenderDelegate * sender;
SdoSemaphore sdoSemaphore;
TypedStateObserver<std::uint8_t> stateObserver;
};

} // namespace roboticslab
Expand Down
Loading

0 comments on commit 972f875

Please sign in to comment.