[DEV] continue better management of connection and add display of current object list

This commit is contained in:
Edouard DUPIN 2016-12-08 22:13:16 +01:00
parent fe4ba85a05
commit 0efb75039b
20 changed files with 258 additions and 97 deletions

View File

@ -245,7 +245,32 @@ void appl::GateWay::cleanIO() {
}
if (tmpIDToRemove.size() != 0) {
APPL_TODO("Remove Ids ... " << tmpIDToRemove);
for (auto &it : m_listIO) {
if (it == nullptr) {
continue;
}
if (it->isConnected() == false) {
continue;
}
// send the message the interface has been removed
zeus::WebServer* iface = it->getInterface();
uint16_t id = it->getId();
iface->event(ZEUS_ID_GATEWAY, uint32_t(id)<<16, "removeInterface", tmpIDToRemove);
APPL_WARNING("Send it to :" << id);
}
}
// Simply display All active objkect in all interfaces:
for( auto &it : m_listIO) {
if (it == nullptr) {
continue;
}
zeus::WebServer* tmpp = it->getInterface();
if (tmpp == nullptr) {
continue;
}
tmpp->listObjects();
}
}
void appl::GateWay::onClientConnect(const bool& _value) {

View File

@ -69,6 +69,18 @@ void appl::IOInterface::receive(ememory::SharedPtr<zeus::Message> _value) {
// Check if we are the destinated Of this message
if ( _value->getDestinationId() == ZEUS_ID_GATEWAY
&& _value->getDestinationObjectId() == ZEUS_ID_GATEWAY_OBJECT) {
if (_value->getType() == zeus::message::type::event) {
ememory::SharedPtr<zeus::message::Event> eventObj = ememory::staticPointerCast<zeus::message::Event>(_value);
std::string event = eventObj->getCall();
if (event == "removeRouterClient") {
// TODO : Broadcast that an IO is remoed ...
m_state = appl::clientState::unconnect;
} else {
APPL_ERROR("Protocol error ==>missing 'call'");
answerProtocolError(transactionId, "missing parameter: 'event' ... ");
}
return;
}
if (_value->getType() != zeus::message::type::call) {
APPL_ERROR("Protocol error ==>missing 'call'");
answerProtocolError(transactionId, "missing parameter: 'call' / wrong type 'call'");

View File

@ -18,15 +18,15 @@ static const std::string protocolError = "PROTOCOL-ERROR";
appl::clientSpecificInterface::clientSpecificInterface() {
m_uid = 0;
m_state = appl::clientState::unconnect;
APPL_INFO("----------------");
APPL_INFO("-- NEW Client --");
APPL_INFO("----------------");
APPL_INFO("-------------------------------");
APPL_INFO("-- NEW Client (threw router) --");
APPL_INFO("-------------------------------");
}
appl::clientSpecificInterface::~clientSpecificInterface() {
APPL_INFO("-------------------");
APPL_INFO("-- DELETE Client --");
APPL_INFO("-------------------");
APPL_INFO("----------------------------------");
APPL_INFO("-- DELETE Client (threw router) --");
APPL_INFO("----------------------------------");
}
zeus::WebServer* appl::clientSpecificInterface::getInterface() {
@ -37,6 +37,9 @@ bool appl::clientSpecificInterface::isConnected() {
if (m_interfaceWeb == nullptr) {
return false;
}
if (m_state == appl::clientState::unconnect) {
return false;
}
return m_interfaceWeb->isActive();
};

View File

@ -28,7 +28,7 @@ namespace appl {
class RouterInterface {
private:
enum clientState m_state; // state machine ..
std::vector<ememory::SharedPtr<clientSpecificInterface>> m_listClients;
std::vector<ememory::SharedPtr<clientSpecificInterface>> m_listClients; // TODO : Maybe remove this ...
private:
appl::GateWay* m_gateway;
zeus::WebServer m_interfaceWeb;

View File

@ -155,7 +155,8 @@ int main(int _argc, const char *_argv[]) {
uint32_t iii = 0;
while(m_client.isAlive() == true) {
m_client.pingIsAlive();
std::this_thread::sleep_for(std::chrono::seconds(10));
m_client.displayConnectedObject();
std::this_thread::sleep_for(std::chrono::seconds(1));
APPL_INFO("service in waiting ... " << iii << "/inf");
iii++;
}

View File

@ -17,7 +17,8 @@ static const std::string protocolError = "PROTOCOL-ERROR";
appl::ClientInterface::ClientInterface(enet::Tcp _connection, appl::Router* _routerInterface) :
m_routerInterface(_routerInterface),
m_interfaceClient(std::move(_connection), true) {
m_interfaceClient(std::move(_connection), true),
m_uid(0) {
APPL_INFO("----------------");
APPL_INFO("-- NEW Client --");
APPL_INFO("----------------");
@ -66,9 +67,13 @@ void appl::ClientInterface::stop() {
if (m_interfaceClient.isActive() == true) {
m_interfaceClient.disconnect();
}
if (m_userGateWay != nullptr) {
m_userGateWay->rmClient(sharedFromThis());
}
}
bool appl::ClientInterface::isAlive() {
//APPL_INFO("is alive : " << m_interfaceClient.isActive());
return m_interfaceClient.isActive();
}
@ -91,7 +96,7 @@ void appl::ClientInterface::onClientData(ememory::SharedPtr<zeus::Message> _valu
}
// check correct SourceID
if (_value->getSourceId() != m_uid) {
answerProtocolError(transactionId, "message with the wrong source ID");
answerProtocolError(transactionId, "message with the wrong source ID : " + etk::to_string(_value->getSourceId()) + " != " + etk::to_string(m_uid));
return;
}
// Check gateway corectly connected
@ -131,17 +136,5 @@ void appl::ClientInterface::send(ememory::SharedPtr<zeus::Message> _data) {
return;
}
m_interfaceClient.writeBinary(_data);
/*
if (_data->getType() == zeus::Message::type::ctrl) {
std::string value = static_cast<zeus::MessageCtrl*>(_data.get())->getCtrl();
if (value == "DISCONNECT") {
m_interfaceClient.disconnect(true);
return;
}
APPL_ERROR("Receive message from GateWay CTRL : '" << value << "' ==> not managed" );
return;
}
APPL_ERROR("Get call from the Service to the user ... " << _data);
*/
}

View File

@ -31,6 +31,9 @@ namespace appl {
bool checkId(uint16_t _id) const {
return m_uid == _id;
}
uint16_t getId() const {
return m_uid;
}
bool isAlive();
void answerProtocolError(uint32_t _transactionId, const std::string& _errorHelp);

View File

@ -83,6 +83,10 @@ uint16_t appl::GateWayInterface::addClient(ememory::SharedPtr<appl::ClientInterf
}
void appl::GateWayInterface::rmClient(ememory::SharedPtr<appl::ClientInterface> _value) {
if (_value == nullptr) {
return;
}
uint16_t id = _value->getId();
auto it = m_clientConnected.begin();
while (it != m_clientConnected.end()) {
if (*it == _value) {
@ -91,6 +95,7 @@ void appl::GateWayInterface::rmClient(ememory::SharedPtr<appl::ClientInterface>
++it;
}
}
m_interfaceClient.event(uint32_t(id)<<16, ZEUS_ID_GATEWAY, "removeRouterClient", id);
}
void appl::GateWayInterface::onServiceData(ememory::SharedPtr<zeus::Message> _value) {

View File

@ -155,7 +155,9 @@ void appl::Router::cleanIO() {
while (it2 != m_clientList.end()) {
if (*it2 != nullptr) {
if ((*it2)->isAlive() == false) {
(*it2)->stop();
it2 = m_clientList.erase(it2);
APPL_INFO("remove DONE ... ");
continue;
}
} else {

View File

@ -95,6 +95,18 @@ void zeus::Client::onClientData(ememory::SharedPtr<zeus::Message> _value) {
}
m_interfaceWeb->answerError(transactionId, _value->getDestination(), _value->getSource(), "UNKNOW-SERVICE");
}
} else if (_value->getType() == zeus::message::type::event) {
ememory::SharedPtr<zeus::message::Event> eventObj = ememory::staticPointerCast<zeus::message::Event>(_value);
std::string callFunction = eventObj->getCall();
if (callFunction != "removeInterface") {
answerProtocolError(transactionId, "interact with client, musty only call: removeInterface");
return;
}
if (callFunction == "removeInterface") {
ZEUS_VERBOSE("Remove Object : " << eventObj);
m_interfaceWeb->interfaceRemoved(eventObj->getParameter<std::vector<uint16_t>>(0));
}
return;
}
m_interfaceWeb->answerError(transactionId, _value->getDestination(), _value->getSource(), "UNKNOW-ACTION");
return;
@ -293,3 +305,10 @@ void zeus::Client::pingIsAlive() {
}
}
void zeus::Client::displayConnectedObject() {
if (m_interfaceWeb== nullptr) {
return;
}
m_interfaceWeb->listObjects();
}

View File

@ -148,6 +148,10 @@ namespace zeus {
* @return
*/
bool isAlive();
/**
* @brief Display all connected object remote and local ...
*/
void displayConnectedObject();
};
}

View File

@ -15,9 +15,15 @@ zeus::ObjectRemoteBase::ObjectRemoteBase(const ememory::SharedPtr<zeus::WebServe
m_remoteAddress(_address),
m_isLinked(false) {
m_isLinked = true;
ZEUS_INFO("[" << m_id << "/" << m_objectId << "] create => to remote [" << (m_remoteAddress>>16) << "/" << (m_remoteAddress&0xFFFF) << "]");
}
void zeus::ObjectRemoteBase::display() {
ZEUS_INFO(" - [" << m_id << "/" << m_objectId << "] => [" << (m_remoteAddress>>16) << "/" << (m_remoteAddress&0xFFFF) << "]");
}
zeus::ObjectRemoteBase::~ObjectRemoteBase() {
ZEUS_INFO("[" << m_id << "/" << m_objectId << "] DESTROY => to remote [" << (m_remoteAddress>>16) << "/" << (m_remoteAddress&0xFFFF) << "]");
if (m_isLinked == true) {
uint32_t tmpLocalService = m_remoteAddress;
// little hack : Call the service manager with the service ID=0 ...

View File

@ -35,7 +35,7 @@ namespace zeus {
*/
ObjectRemoteBase():
zeus::WebObj(nullptr, 0, 0) {
ZEUS_INFO("[XX/YY] Create");
}
/**
* @brief
@ -61,6 +61,22 @@ namespace zeus {
* @return
*/
const std::string& getName() const;
/**
* @brief Get the remote interface ID
* @return The Id of the remote interface
*/
uint16_t getRemoteInterfaceId() {
return m_remoteAddress>>16;
}
/**
* @brief Set the Remote object has been removed
*/
void setRemoteObjectRemoved() {
ZEUS_WARNING("The object remote has been removed : " << m_remoteAddress);
m_isLinked = false;
m_remoteAddress = 0;
}
void display();
};
/**
* @brief

View File

@ -21,7 +21,13 @@ namespace zeus {
m_interfaceWeb(_iface),
m_id(_id),
m_objectId(_objectId) {
ZEUS_INFO("[" << m_id << "/" << m_objectId << "] Create");
}
virtual ~WebObj() {
ZEUS_INFO("[" << m_id << "/" << m_objectId << "] Delete");
}
uint32_t getInterfaceId() {
return m_id;
}
uint32_t getFullId() {
return (uint32_t(m_id) << 16 ) + m_objectId;
@ -29,6 +35,9 @@ namespace zeus {
virtual void receive(ememory::SharedPtr<zeus::Message> _value) {
ZEUS_ERROR("Receive a message ==> not implemented magaging ..." << _value);
}
virtual void display() {
ZEUS_INFO(" - [" << m_id << "/" << m_objectId << "]");
}
};
}

View File

@ -5,13 +5,24 @@
*/
#include <zeus/WebServer.hpp>
#include <zeus/debug.hpp>
#include <zeus/ObjectRemote.hpp>
#include <ethread/tools.hpp>
#include <zeus/message/Data.hpp>
ememory::SharedPtr<zeus::message::Call> zeus::createBaseCall(const ememory::SharedPtr<zeus::WebServer>& _iface, uint64_t _transactionId, const uint32_t& _source, const uint32_t& _destination, const std::string& _functionName) {
ememory::SharedPtr<zeus::message::Call> obj = zeus::message::Call::create(_iface);
ememory::SharedPtr<zeus::message::Call> zeus::createBaseCall(bool _isEvent,
const ememory::SharedPtr<zeus::WebServer>& _iface,
uint64_t _transactionId,
const uint32_t& _source,
const uint32_t& _destination,
const std::string& _functionName) {
ememory::SharedPtr<zeus::message::Call> obj;
if (_isEvent == false) {
obj = zeus::message::Call::create(_iface);
} else {
obj = zeus::message::Event::create(_iface);
}
if (obj == nullptr) {
return nullptr;
}
@ -94,6 +105,31 @@ void zeus::WebServer::addWebObj(ememory::SharedPtr<zeus::WebObj> _obj) {
m_listObject.push_back(_obj);
}
void zeus::WebServer::addWebObjRemote(ememory::SharedPtr<zeus::ObjectRemoteBase> _obj) {
m_listRemoteObject.push_back(_obj);
}
void zeus::WebServer::interfaceRemoved(std::vector<uint16_t> _list) {
for (int32_t iii=0; iii < _list.size(); ++iii) {
// Call local object
for (auto it=m_listRemoteObject.begin();
it != m_listRemoteObject.end();
/* no increment */) {
ememory::SharedPtr<zeus::ObjectRemoteBase> tmp = it->lock();
if (tmp == nullptr) {
it = m_listRemoteObject.erase(it);
continue;
}
if (tmp->getRemoteInterfaceId() == _list[iii]) {
tmp->setRemoteObjectRemoved();
it = m_listRemoteObject.erase(it);
continue;
}
++it;
}
}
}
bool zeus::WebServer::isActive() const {
return m_connection.isAlive();
@ -326,6 +362,7 @@ void zeus::WebServer::newMessage(ememory::SharedPtr<zeus::Message> _buffer) {
// Not find a pen,ding call ==> execute it ...
if (future.isValid() == false) {
uint32_t dest = _buffer->getDestination();
// Call local object
for (auto &it : m_listObject) {
if (it == nullptr) {
continue;
@ -343,6 +380,25 @@ void zeus::WebServer::newMessage(ememory::SharedPtr<zeus::Message> _buffer) {
return;
}
}
// call local map object on remote object
for (auto &it : m_listRemoteObject) {
ememory::SharedPtr<zeus::ObjectRemoteBase> tmp = it.lock();
if (tmp == nullptr) {
continue;
}
if (tmp->getFullId() == dest) {
// send in an other async to syncronize the
m_processingPool.async(
[=](){
ememory::SharedPtr<zeus::WebObj> tmpObj = tmp;
ZEUS_INFO("PROCESS : " << _buffer);
tmpObj->receive(_buffer);
},
dest
);
return;
}
}
if (m_observerElement != nullptr) {
m_processingPool.async(
[=](){
@ -380,6 +436,27 @@ void zeus::WebServer::newMessage(ememory::SharedPtr<zeus::Message> _buffer) {
}
void zeus::WebServer::listObjects() {
if ( m_listObject.size() == 0
&& m_listRemoteObject.size() == 0) {
return;
}
ZEUS_INFO("[" << m_interfaceId << "] Interface WebServer:");
for (auto &it : m_listObject) {
if (it == nullptr) {
continue;
}
it->display();
}
for (auto &it : m_listRemoteObject) {
ememory::SharedPtr<zeus::ObjectRemoteBase> tmpp = it.lock();
if (tmpp == nullptr) {
continue;
}
tmpp->display();
}
}
void zeus::WebServer::addAsync(zeus::WebServer::ActionAsync _elem) {
std::unique_lock<std::mutex> lock(m_threadAsyncMutex);
m_threadAsyncList2.push_back(_elem);
@ -436,20 +513,6 @@ zeus::FutureBase zeus::WebServer::callBinary(uint64_t _transactionId,
return tmpFuture;
}
/*
void zeus::WebServer::sendCtrl(uint32_t _source, uint32_t _destination, const std::string& _ctrlValue) {
auto ctrl = zeus::MessageCtrl::create(sharedFromThis());
if (ctrl == nullptr) {
return;
}
ctrl->setTransactionId(getId());
ctrl->setSource(_source);
ctrl->setDestination(_destination);
ctrl->setCtrl(_ctrlValue);
writeBinary(ctrl);
}
*/
void zeus::WebServer::answerError(uint32_t _clientTransactionId, uint32_t _source, uint32_t _destination, const std::string& _errorValue, const std::string& _errorHelp) {
auto answer = zeus::message::Answer::create(sharedFromThis());
if (answer == nullptr) {

View File

@ -6,6 +6,8 @@
#pragma once
#include <zeus/message/Message.hpp>
#include <zeus/message/Answer.hpp>
#include <zeus/message/Event.hpp>
#include <zeus/message/Call.hpp>
#include <enet/WebSocket.hpp>
#include <thread>
#include <ememory/memory.hpp>
@ -25,16 +27,18 @@
namespace zeus {
class WebServer;
class ObjectRemoteBase;
/**
* @brief
* @param[in]
* @return
*/
ememory::SharedPtr<zeus::message::Call> createBaseCall(const ememory::SharedPtr<zeus::WebServer>& _iface,
uint64_t _transactionId,
const uint32_t& _source,
const uint32_t& _destination,
const std::string& _functionName);
ememory::SharedPtr<zeus::message::Call> createBaseCall(bool _isEvent,
const ememory::SharedPtr<zeus::WebServer>& _iface,
uint64_t _transactionId,
const uint32_t& _source,
const uint32_t& _destination,
const std::string& _functionName);
/**
* @brief
* @param[in]
@ -79,15 +83,14 @@ namespace zeus {
* @return
*/
template<class... _ARGS>
ememory::SharedPtr<zeus::message::Call> createCall(const ememory::SharedPtr<zeus::WebServer>& _iface, uint64_t _transactionId, const uint32_t& _source, const uint32_t& _destination, const std::string& _functionName, _ARGS&&... _args) {
ememory::SharedPtr<zeus::message::Call> callElem = createBaseCall(_iface, _transactionId, _source, _destination, _functionName);
ememory::SharedPtr<zeus::message::Call> createCall(bool _isEvent, const ememory::SharedPtr<zeus::WebServer>& _iface, uint64_t _transactionId, const uint32_t& _source, const uint32_t& _destination, const std::string& _functionName, _ARGS&&... _args) {
ememory::SharedPtr<zeus::message::Call> callElem = createBaseCall(_isEvent, _iface, _transactionId, _source, _destination, _functionName);
if (callElem == nullptr) {
return nullptr;
}
createParam(_iface, 0, callElem, std::forward<_ARGS>(_args)...);
return callElem;
}
/**
* @brief
*/
@ -112,8 +115,14 @@ namespace zeus {
}
private:
std::vector<ememory::SharedPtr<zeus::WebObj>> m_listObject;
std::vector<ememory::WeakPtr<zeus::ObjectRemoteBase>> m_listRemoteObject;
public:
void addWebObj(ememory::SharedPtr<zeus::WebObj> _obj);
void addWebObjRemote(ememory::SharedPtr<zeus::ObjectRemoteBase> _obj);
/**
* @brief Set the list of interface that has been removed ...
*/
void interfaceRemoved(std::vector<uint16_t> _list);
private:
uint32_t m_interfaceId;
uint16_t m_transmissionId;
@ -291,28 +300,20 @@ namespace zeus {
template<class... _ARGS>
zeus::FutureBase call(const uint32_t& _source, const uint32_t& _destination, const std::string& _functionName, _ARGS&&... _args) {
uint16_t id = getId();
ememory::SharedPtr<zeus::message::Call> callElem = zeus::createCall(sharedFromThis(), id, _source, _destination, _functionName, std::forward<_ARGS>(_args)...);
ememory::SharedPtr<zeus::message::Call> callElem = zeus::createCall(false, sharedFromThis(), id, _source, _destination, _functionName, std::forward<_ARGS>(_args)...);
return callBinary(id, callElem);
}
public:
#if 0
/**
* @brief
* @param[in]
* @return
*/
zeus::FutureBase callForward(uint32_t _source,
ememory::SharedPtr<zeus::Message> _Message,
uint64_t _singleReferenceId);
/**
* @brief
* @param[in]
* @return
*/
void callForwardMultiple(uint32_t _source,
ememory::SharedPtr<zeus::Message> _Message,
uint64_t _singleReferenceId);
#endif
template<class... _ARGS>
void event(const uint32_t& _source, const uint32_t& _destination, const std::string& _eventName, _ARGS&&... _args) {
uint16_t id = getId();
ememory::SharedPtr<zeus::message::Call> callElem = zeus::createCall(true, sharedFromThis(), id, _source, _destination, _eventName, std::forward<_ARGS>(_args)...);
callBinary(id, callElem);
}
public: // answers ...
/**
* @brief
@ -350,14 +351,9 @@ namespace zeus {
* @param[in] _srcObjectId Client to send control
*/
void answerError(uint32_t _clientTransactionId, uint32_t _source, uint32_t _destination, const std::string& _errorValue, const std::string& _errorComment="");
/**
* @brief Send a control on the Interface
* @param[in] _source Source of the message
* @param[in] _destination Destination of the message
* @param[in] _ctrlValue Control to send
* @return
*/
//void sendCtrl(uint32_t _source, uint32_t _destination, const std::string& _ctrlValue);
public:
// for debug only:
void listObjects();
};
}

View File

@ -10,13 +10,10 @@
#include <etk/stdTools.hpp>
#include <zeus/message/Event.hpp>
/*
// ------------------------------------------------------------------------------------
// -- Factory
// ------------------------------------------------------------------------------------
ememory::SharedPtr<zeus::MessageEvent> zeus::MessageEvent::create() {
return ememory::SharedPtr<zeus::MessageEvent>(new zeus::MessageEvent);
ememory::SharedPtr<zeus::message::Event> zeus::message::Event::create(ememory::SharedPtr<zeus::WebServer> _iface) {
return ememory::SharedPtr<zeus::message::Event>(new zeus::message::Event(_iface));
}
*/

View File

@ -4,38 +4,32 @@
* @license APACHE v2.0 (see license file)
*/
#pragma once
#include <etk/types.hpp>
#include <enet/WebSocket.hpp>
#include <zeus/message/ParamType.hpp>
#include <zeus/message/Call.hpp>
namespace zeus {
namespace message {
/*
class MessageEvent :
public message::Parameter {
class Event :
public message::Call {
friend class zeus::Message;
protected:
/ **
* @brief basic constructor (hidden to force the use of ememory::SharedPtr) @ref zeus::MessageEvent::create
* /
MessageEvent() {
/**
* @brief basic constructor (hidden to force the use of ememory::SharedPtr) @ref zeus::message::Call::create
*/
Event(ememory::SharedPtr<zeus::WebServer> _iface):
zeus::message::Call(_iface) {
m_header.flags = ZEUS_BUFFER_FLAG_FINISH + uint8_t(zeus::message::type::event);
};
void composeWith(const uint8_t* _buffer, uint32_t _lenght) override;
void appendMessageData(ememory::SharedPtr<zeus::message::Data> _obj) override;
public:
/ **
* @brief Create a shared pointer on the MessageEvent
/**
* @brief Create a shared pointer on the MessageCall
* @return Allocated Message.
* /
static ememory::SharedPtr<zeus::MessageEvent> create();
*/
static ememory::SharedPtr<zeus::message::Event> create(ememory::SharedPtr<zeus::WebServer> _iface);
public:
enum zeus::message::type getType() const override {
return zeus::message::type::event;
}
};
*/
}
}

View File

@ -250,8 +250,21 @@ ememory::SharedPtr<zeus::Message> zeus::Message::create(ememory::SharedPtr<zeus:
return value;
}
break;
case zeus::message::type::event:
case zeus::message::type::event: {
ememory::SharedPtr<zeus::message::Event> value = zeus::message::Event::create(_iface);
if (value == nullptr) {
return nullptr;
}
value->setTransactionId(header.transactionId);
value->setSourceId(header.sourceId);
value->setSourceObjectId(header.sourceObjectId);
value->setDestinationId(header.destinationId);
value->setDestinationObjectId(header.destinationObjectId);
value->setPartFinish((header.flags & ZEUS_BUFFER_FLAG_FINISH) != 0);
value->composeWith(&_buffer[sizeof(zeus::message::headerBin)],
_buffer.size() - sizeof(zeus::message::headerBin));
return value;
}
break;
}
return nullptr;

View File

@ -1473,7 +1473,7 @@ namespace zeus {
uint16_t id = _iface2->getAddress();
uint16_t idObj = _iface2->getNewObjectId();
out = ememory::makeShared<zeus::ObjectRemoteBase>(_iface, id, idObj, serviceAddress, type.getName());
_iface2->addWebObj(out);
_iface2->addWebObjRemote(out);
} else {
ZEUS_ERROR("missing interface to crate object: '" << type << "'");
}