[DEBUG] protocol work again ...

This commit is contained in:
Edouard DUPIN 2016-07-05 22:21:17 +02:00
parent 8b21709a01
commit 009448cac4
7 changed files with 59 additions and 87 deletions

View File

@ -72,15 +72,11 @@ void zeus::Buffer::appendBuffer(const ememory::SharedPtr<zeus::Buffer>& _obj) {
appendBufferData(std::static_pointer_cast<zeus::BufferData>(_obj)); appendBufferData(std::static_pointer_cast<zeus::BufferData>(_obj));
} }
size_t zeus::Buffer::getSize() {
return sizeof(headerBin);
}
bool zeus::Buffer::writeOn(enet::WebSocket& _interface) { bool zeus::Buffer::writeOn(enet::WebSocket& _interface) {
uint64_t size = getSize(); if (_interface.configHeader(false) == false) {
if (_interface.writeHeader(size, false) == false) {
return false; return false;
} }
_interface.writeData((uint8_t*)&m_header, sizeof(headerBin));
return true; return true;
} }
@ -109,7 +105,7 @@ void zeus::Buffer::generateDisplay(std::ostream& _os) const {
_os << " id=" << etk::to_string(getTransactionId()); _os << " id=" << etk::to_string(getTransactionId());
_os << " cId=" << etk::to_string(getClientId()); _os << " cId=" << etk::to_string(getClientId());
if (getPartFinish() == true) { if (getPartFinish() == true) {
_os << "finish"; _os << " finish";
} }
enum zeus::Buffer::typeMessage type = getType(); enum zeus::Buffer::typeMessage type = getType();
switch (type) { switch (type) {
@ -149,7 +145,9 @@ void zeus::BufferCall::generateDisplay(std::ostream& _os) const {
void zeus::BufferAnswer::generateDisplay(std::ostream& _os) const { void zeus::BufferAnswer::generateDisplay(std::ostream& _os) const {
zeus::Buffer::generateDisplay(_os); zeus::Buffer::generateDisplay(_os);
if (getNumberParameter() != 0) {
_os << " '" + simpleStringParam(0) + "'"; _os << " '" + simpleStringParam(0) + "'";
}
if (m_errorType.size() != 0) { if (m_errorType.size() != 0) {
_os << " Error='" + m_errorType + "'"; _os << " Error='" + m_errorType + "'";
}if (m_errorHelp.size() != 0) { }if (m_errorHelp.size() != 0) {
@ -159,8 +157,8 @@ void zeus::BufferAnswer::generateDisplay(std::ostream& _os) const {
void zeus::BufferData::generateDisplay(std::ostream& _os) const { void zeus::BufferData::generateDisplay(std::ostream& _os) const {
zeus::Buffer::generateDisplay(_os); zeus::Buffer::generateDisplay(_os);
_os << " paramId=" << etk::to_string(m_partId); _os << " paramId=" << etk::to_string(m_parameterId);
_os << " part=" << etk::to_string(m_parameterId); _os << " part=" << etk::to_string(m_partId);
_os << " nbData=" << etk::to_string(m_data.size()); _os << " nbData=" << etk::to_string(m_data.size());
} }
@ -200,31 +198,20 @@ enum zeus::Buffer::typeMessage zeus::Buffer::getType() const {
// ------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------
// -- Multiple parameter // -- Multiple parameter
// ------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------
bool zeus::BufferParameter::writeOn(enet::WebSocket& _interface) {
size_t zeus::BufferParameter::parameterGetSize() {
size_t size = sizeof(uint16_t);
for (auto &it : m_parameter) {
size += sizeof(uint32_t); // parameter size
size += it.second.size();
}
return size;
}
bool zeus::BufferParameter::parameterWriteOn(enet::WebSocket& _interface) {
uint8_t* data = nullptr; uint8_t* data = nullptr;
uint32_t dataSize = 0; uint32_t dataSize = 0;
uint16_t numberOfParameter = m_parameter.size(); uint16_t nbParameters = m_parameter.size();
size_t size = _interface.writeData((uint8_t*)&numberOfParameter, sizeof(uint16_t)); size_t size = _interface.writeData((uint8_t*)&nbParameters, sizeof(uint16_t));
for (auto &it : m_parameter) { for (auto &it : m_parameter) {
uint32_t paramSize = it.second.size(); uint32_t paramSize = it.second.size();
size = _interface.writeData((uint8_t*)&paramSize, sizeof(uint32_t)); size = _interface.writeData((uint8_t*)&paramSize, sizeof(uint32_t));
size = _interface.writeData(&it.second[0], it.second.size() * sizeof(uint8_t)); size += _interface.writeData(&it.second[0], it.second.size() * sizeof(uint8_t));
} }
return true; return true;
} }
void zeus::BufferParameter::parameterComposeWith(const uint8_t* _buffer, uint32_t _lenght) { void zeus::BufferParameter::composeWith(const uint8_t* _buffer, uint32_t _lenght) {
m_parameter.clear(); m_parameter.clear();
uint16_t nbParameters = 0; uint16_t nbParameters = 0;
if (_lenght < sizeof(uint16_t)) { if (_lenght < sizeof(uint16_t)) {
@ -385,14 +372,10 @@ void zeus::BufferCall::setCall(const std::string& _value) {
m_callName = _value; m_callName = _value;
} }
size_t zeus::BufferCall::getSize() {
// name + \0 + parameters ...
return m_callName.size()+1 + parameterGetSize();
}
bool zeus::BufferCall::writeOn(enet::WebSocket& _interface) { bool zeus::BufferCall::writeOn(enet::WebSocket& _interface) {
zeus::Buffer::writeOn(_interface);
_interface.writeData((uint8_t*)m_callName.c_str(), m_callName.size() + 1); _interface.writeData((uint8_t*)m_callName.c_str(), m_callName.size() + 1);
return parameterWriteOn(_interface); return BufferParameter::writeOn(_interface);
} }
void zeus::BufferCall::composeWith(const uint8_t* _buffer, uint32_t _lenght) { void zeus::BufferCall::composeWith(const uint8_t* _buffer, uint32_t _lenght) {
@ -401,12 +384,13 @@ void zeus::BufferCall::composeWith(const uint8_t* _buffer, uint32_t _lenght) {
uint32_t pos = 0; uint32_t pos = 0;
m_callName.clear(); m_callName.clear();
while( pos < _lenght while( pos < _lenght
&& _buffer[pos] != '\0') { && (char)_buffer[pos] != '\0') {
m_callName += _buffer[pos]; m_callName += _buffer[pos];
pos++; pos++;
} }
pos++;
// parse parameters: // parse parameters:
parameterComposeWith(&_buffer[pos], _lenght-pos); BufferParameter::composeWith(&_buffer[pos], _lenght-pos);
} }
void zeus::BufferCall::appendBufferData(const ememory::SharedPtr<zeus::BufferData>& _obj) { void zeus::BufferCall::appendBufferData(const ememory::SharedPtr<zeus::BufferData>& _obj) {
@ -434,21 +418,13 @@ void zeus::BufferAnswer::addError(const std::string& _value, const std::string&
m_errorHelp = _comment; m_errorHelp = _comment;
} }
size_t zeus::BufferAnswer::getSize() {
// name + \0 + parameters ...
size_t size = m_errorType.size()+1 + parameterGetSize();
if (m_errorType.size() != 0) {
size += m_errorHelp.size() + 1;
}
return size;
}
bool zeus::BufferAnswer::writeOn(enet::WebSocket& _interface) { bool zeus::BufferAnswer::writeOn(enet::WebSocket& _interface) {
zeus::Buffer::writeOn(_interface);
_interface.writeData((uint8_t*)m_errorType.c_str(), m_errorType.size() + 1); _interface.writeData((uint8_t*)m_errorType.c_str(), m_errorType.size() + 1);
if (m_errorType.size() != 0) { if (m_errorType.size() != 0) {
_interface.writeData((uint8_t*)m_errorHelp.c_str(), m_errorHelp.size() + 1); _interface.writeData((uint8_t*)m_errorHelp.c_str(), m_errorHelp.size() + 1);
} }
return parameterWriteOn(_interface); return BufferParameter::writeOn(_interface);
} }
void zeus::BufferAnswer::composeWith(const uint8_t* _buffer, uint32_t _lenght) { void zeus::BufferAnswer::composeWith(const uint8_t* _buffer, uint32_t _lenght) {
@ -462,15 +438,17 @@ void zeus::BufferAnswer::composeWith(const uint8_t* _buffer, uint32_t _lenght) {
m_errorType += _buffer[pos]; m_errorType += _buffer[pos];
pos++; pos++;
} }
pos++;
if (m_errorType.size() != 0) { if (m_errorType.size() != 0) {
while( pos < _lenght while( pos < _lenght
&& _buffer[pos] != '\0') { && _buffer[pos] != '\0') {
m_errorHelp += _buffer[pos]; m_errorHelp += _buffer[pos];
pos++; pos++;
} }
pos++;
} }
// parse parameters: // parse parameters:
parameterComposeWith(&_buffer[pos], _lenght-pos); BufferParameter::composeWith(&_buffer[pos], _lenght-pos);
} }
void zeus::BufferAnswer::appendBufferData(const ememory::SharedPtr<zeus::BufferData>& _obj) { void zeus::BufferAnswer::appendBufferData(const ememory::SharedPtr<zeus::BufferData>& _obj) {
@ -500,12 +478,8 @@ void zeus::BufferData::setPartId(uint32_t _value) {
m_partId = _value; m_partId = _value;
} }
size_t zeus::BufferData::getSize() {
// name + \0 + parameters ...
return sizeof(uint32_t) + sizeof(uint16_t) + m_data.size();
}
bool zeus::BufferData::writeOn(enet::WebSocket& _interface) { bool zeus::BufferData::writeOn(enet::WebSocket& _interface) {
zeus::Buffer::writeOn(_interface);
_interface.writeData((uint8_t*)&m_partId, sizeof(uint32_t)); _interface.writeData((uint8_t*)&m_partId, sizeof(uint32_t));
_interface.writeData((uint8_t*)&m_parameterId, sizeof(uint16_t)); _interface.writeData((uint8_t*)&m_parameterId, sizeof(uint16_t));
_interface.writeData((uint8_t*)&m_data[0], m_data.size()); _interface.writeData((uint8_t*)&m_data[0], m_data.size());
@ -551,7 +525,7 @@ ememory::SharedPtr<zeus::Buffer> zeus::Buffer::create(const std::vector<uint8_t>
} }
value->setTransactionId(header.transactionID); value->setTransactionId(header.transactionID);
value->setClientId(header.clientID); value->setClientId(header.clientID);
value->setClientId((header.flags & ZEUS_BUFFER_FLAG_FINISH) != 0); value->setPartFinish((header.flags & ZEUS_BUFFER_FLAG_FINISH) != 0);
value->composeWith(&_buffer[sizeof(headerBin)], value->composeWith(&_buffer[sizeof(headerBin)],
_buffer.size() - sizeof(headerBin)); _buffer.size() - sizeof(headerBin));
return value; return value;
@ -564,7 +538,7 @@ ememory::SharedPtr<zeus::Buffer> zeus::Buffer::create(const std::vector<uint8_t>
} }
value->setTransactionId(header.transactionID); value->setTransactionId(header.transactionID);
value->setClientId(header.clientID); value->setClientId(header.clientID);
value->setClientId((header.flags & ZEUS_BUFFER_FLAG_FINISH) != 0); value->setPartFinish((header.flags & ZEUS_BUFFER_FLAG_FINISH) != 0);
value->composeWith(&_buffer[sizeof(headerBin)], value->composeWith(&_buffer[sizeof(headerBin)],
_buffer.size() - sizeof(headerBin)); _buffer.size() - sizeof(headerBin));
return value; return value;
@ -577,7 +551,7 @@ ememory::SharedPtr<zeus::Buffer> zeus::Buffer::create(const std::vector<uint8_t>
} }
value->setTransactionId(header.transactionID); value->setTransactionId(header.transactionID);
value->setClientId(header.clientID); value->setClientId(header.clientID);
value->setClientId((header.flags & ZEUS_BUFFER_FLAG_FINISH) != 0); value->setPartFinish((header.flags & ZEUS_BUFFER_FLAG_FINISH) != 0);
value->composeWith(&_buffer[sizeof(headerBin)], value->composeWith(&_buffer[sizeof(headerBin)],
_buffer.size() - sizeof(headerBin)); _buffer.size() - sizeof(headerBin));
return value; return value;

View File

@ -231,7 +231,6 @@ namespace zeus {
* @return true of no error appear * @return true of no error appear
*/ */
virtual bool writeOn(enet::WebSocket& _interface); virtual bool writeOn(enet::WebSocket& _interface);
virtual size_t getSize();
virtual void generateDisplay(std::ostream& _os) const ; virtual void generateDisplay(std::ostream& _os) const ;
}; };
class BufferParameter: class BufferParameter:
@ -283,14 +282,8 @@ namespace zeus {
* @return readable string * @return readable string
*/ */
std::string simpleStringParam(uint32_t _id) const; std::string simpleStringParam(uint32_t _id) const;
/** void composeWith(const uint8_t* _buffer, uint32_t _lenght) override;
* @brief When receive new data form websocket, it might be added by this input (set all the frame ...) bool writeOn(enet::WebSocket& _interface) override;
* @param[in] _buffer Pointer on the data to add.
* @param[in] _lenght number of octet to add.
*/
void parameterComposeWith(const uint8_t* _buffer, uint32_t _lenght);
bool parameterWriteOn(enet::WebSocket& _interface);
size_t parameterGetSize();
protected: protected:
/** /**
* @brief Add a parameter at a specific position * @brief Add a parameter at a specific position
@ -321,7 +314,6 @@ namespace zeus {
void composeWith(const uint8_t* _buffer, uint32_t _lenght) override; void composeWith(const uint8_t* _buffer, uint32_t _lenght) override;
void appendBufferData(const ememory::SharedPtr<zeus::BufferData>& _obj) override; void appendBufferData(const ememory::SharedPtr<zeus::BufferData>& _obj) override;
bool writeOn(enet::WebSocket& _interface) override; bool writeOn(enet::WebSocket& _interface) override;
size_t getSize() override;
void generateDisplay(std::ostream& _os) const override; void generateDisplay(std::ostream& _os) const override;
public: public:
/** /**
@ -361,7 +353,6 @@ namespace zeus {
void composeWith(const uint8_t* _buffer, uint32_t _lenght) override; void composeWith(const uint8_t* _buffer, uint32_t _lenght) override;
void appendBufferData(const ememory::SharedPtr<zeus::BufferData>& _obj) override; void appendBufferData(const ememory::SharedPtr<zeus::BufferData>& _obj) override;
bool writeOn(enet::WebSocket& _interface) override; bool writeOn(enet::WebSocket& _interface) override;
size_t getSize() override;
void generateDisplay(std::ostream& _os) const override; void generateDisplay(std::ostream& _os) const override;
public: public:
/** /**
@ -430,7 +421,6 @@ namespace zeus {
void composeWith(const uint8_t* _buffer, uint32_t _lenght) override; void composeWith(const uint8_t* _buffer, uint32_t _lenght) override;
// TODO :... void appendBufferData(const ememory::SharedPtr<zeus::BufferData>& _obj) override; // TODO :... void appendBufferData(const ememory::SharedPtr<zeus::BufferData>& _obj) override;
bool writeOn(enet::WebSocket& _interface) override; bool writeOn(enet::WebSocket& _interface) override;
size_t getSize() override;
void generateDisplay(std::ostream& _os) const override; void generateDisplay(std::ostream& _os) const override;
public: public:
/** /**

View File

@ -58,6 +58,8 @@ bool zeus::Client::connectTo(const std::string& _address) {
ret.wait(); ret.wait();
if (ret.hasError() == true) { if (ret.hasError() == true) {
ZEUS_WARNING("Can not connect to user named: '" << _address << "' ==> return error"); ZEUS_WARNING("Can not connect to user named: '" << _address << "' ==> return error");
ZEUS_WARNING(" error: '" << ret.getErrorType() << "'");
ZEUS_WARNING(" help: '" << ret.getErrorHelp() << "'");
return false; return false;
} }
if (ret.get() == true) { if (ret.get() == true) {

View File

@ -42,6 +42,7 @@ zeus::FutureBase::FutureBase(uint32_t _transactionId, const ememory::SharedPtr<z
} }
m_data->m_sendTime = std::chrono::steady_clock::now(); m_data->m_sendTime = std::chrono::steady_clock::now();
m_data->m_transactionId = _transactionId; m_data->m_transactionId = _transactionId;
m_data->m_clientId = _clientId;
m_data->m_isSynchronous = false; m_data->m_isSynchronous = false;
m_data->m_returnData = _returnData; m_data->m_returnData = _returnData;
m_data->m_callbackFinish = _callback; m_data->m_callbackFinish = _callback;
@ -124,7 +125,7 @@ bool zeus::FutureBase::hasError() const {
|| m_data->m_returnData == nullptr) { || m_data->m_returnData == nullptr) {
return true; return true;
} }
if (m_data->m_returnData->getType() == zeus::Buffer::typeMessage::answer) { if (m_data->m_returnData->getType() != zeus::Buffer::typeMessage::answer) {
return true; return true;
} }
return static_cast<zeus::BufferAnswer*>(m_data->m_returnData.get())->hasError(); return static_cast<zeus::BufferAnswer*>(m_data->m_returnData.get())->hasError();
@ -135,7 +136,7 @@ std::string zeus::FutureBase::getErrorType() const {
|| m_data->m_returnData == nullptr) { || m_data->m_returnData == nullptr) {
return "NULL_PTR"; return "NULL_PTR";
} }
if (m_data->m_returnData->getType() == zeus::Buffer::typeMessage::answer) { if (m_data->m_returnData->getType() != zeus::Buffer::typeMessage::answer) {
return "NOT_ANSWER_MESSAGE"; return "NOT_ANSWER_MESSAGE";
} }
return static_cast<zeus::BufferAnswer*>(m_data->m_returnData.get())->getError(); return static_cast<zeus::BufferAnswer*>(m_data->m_returnData.get())->getError();
@ -146,7 +147,7 @@ std::string zeus::FutureBase::getErrorHelp() const {
|| m_data->m_returnData == nullptr) { || m_data->m_returnData == nullptr) {
return "This is a nullptr future"; return "This is a nullptr future";
} }
if (m_data->m_returnData->getType() == zeus::Buffer::typeMessage::answer) { if (m_data->m_returnData->getType() != zeus::Buffer::typeMessage::answer) {
return "This answer is not a anwser type"; return "This answer is not a anwser type";
} }
return static_cast<zeus::BufferAnswer*>(m_data->m_returnData.get())->getErrorHelp(); return static_cast<zeus::BufferAnswer*>(m_data->m_returnData.get())->getErrorHelp();

View File

@ -35,8 +35,10 @@ void zeus::Service::onClientData(const ememory::SharedPtr<zeus::Buffer>& _value)
if (_value == nullptr) { if (_value == nullptr) {
return; return;
} }
ZEUS_WARNING("BUFFER" << _value);
uint32_t tmpID = _value->getTransactionId(); uint32_t tmpID = _value->getTransactionId();
uint32_t clientId = _value->getClientId();; uint32_t clientId = _value->getClientId();
if (_value->getType() == zeus::Buffer::typeMessage::data) {
auto it = m_callMultiData.begin(); auto it = m_callMultiData.begin();
while (it != m_callMultiData.end()) { while (it != m_callMultiData.end()) {
if ( it->getTransactionId() == tmpID if ( it->getTransactionId() == tmpID
@ -52,11 +54,16 @@ void zeus::Service::onClientData(const ememory::SharedPtr<zeus::Buffer>& _value)
} }
++it; ++it;
} }
ZEUS_ERROR("Un-associated data ...");
return;
}
ZEUS_WARNING("direct call");
zeus::FutureBase futData(tmpID, _value, nullptr, clientId); zeus::FutureBase futData(tmpID, _value, nullptr, clientId);
if (futData.isFinished() == true) { if (futData.isFinished() == true) {
ZEUS_INFO("Call Binary .."); ZEUS_INFO("Call Binary ..");
callBinary(futData.getRaw()); callBinary(futData.getRaw());
} else { } else {
ZEUS_INFO("ADD ...");
m_callMultiData.push_back(futData); m_callMultiData.push_back(futData);
} }
} }

View File

@ -190,7 +190,7 @@ namespace zeus {
* @param[in] * @param[in]
* @return * @return
*/ */
virtual void callBinary2(const std::string& _call, const ememory::SharedPtr<zeus::Buffer>& _obj) = 0; virtual void callBinary2(const std::string& _call, const ememory::SharedPtr<zeus::BufferCall>& _obj) = 0;
/** /**
* @brief * @brief
* @param[in] * @param[in]
@ -361,7 +361,7 @@ namespace zeus {
* @param[in] * @param[in]
* @return * @return
*/ */
void callBinary2(const std::string& _call, const ememory::SharedPtr<zeus::Buffer>& _obj) { void callBinary2(const std::string& _call, const ememory::SharedPtr<zeus::BufferCall>& _obj) {
auto it = m_interface.find(_obj->getClientId()); auto it = m_interface.find(_obj->getClientId());
if (it == m_interface.end()) { if (it == m_interface.end()) {
m_interfaceClient->answerError(_obj->getTransactionId(), "CLIENT-UNKNOW", "", _obj->getClientId()); m_interfaceClient->answerError(_obj->getTransactionId(), "CLIENT-UNKNOW", "", _obj->getClientId());

View File

@ -157,7 +157,9 @@ int32_t zeus::WebServer::writeBinary(const ememory::SharedPtr<zeus::Buffer>& _ob
if (_obj->haveAsync() == true) { if (_obj->haveAsync() == true) {
_obj->setPartFinish(false); _obj->setPartFinish(false);
} }
ZEUS_VERBOSE("Send :" << _obj);
if (_obj->writeOn(m_connection) == true) { if (_obj->writeOn(m_connection) == true) {
m_connection.send();
if (_obj->haveAsync() == true) { if (_obj->haveAsync() == true) {
addAsync(SendAsyncBinary(_obj->getTransactionId(), _obj->getServiceId(), std::move(_obj->moveAsync()))); addAsync(SendAsyncBinary(_obj->getTransactionId(), _obj->getServiceId(), std::move(_obj->moveAsync())));
} }
@ -301,7 +303,6 @@ zeus::FutureBase zeus::WebServer::callBinary(uint64_t _transactionId,
const ememory::SharedPtr<zeus::Buffer>& _obj, const ememory::SharedPtr<zeus::Buffer>& _obj,
zeus::FutureData::ObserverFinish _callback, zeus::FutureData::ObserverFinish _callback,
const uint32_t& _serviceId) { const uint32_t& _serviceId) {
ZEUS_VERBOSE("Send [START] ");
if (isActive() == false) { if (isActive() == false) {
ZEUS_ERROR("Send [STOP] ==> not connected (no TCP)"); ZEUS_ERROR("Send [STOP] ==> not connected (no TCP)");
ememory::SharedPtr<zeus::BufferAnswer> obj = zeus::BufferAnswer::create(); ememory::SharedPtr<zeus::BufferAnswer> obj = zeus::BufferAnswer::create();
@ -314,7 +315,6 @@ zeus::FutureBase zeus::WebServer::callBinary(uint64_t _transactionId,
m_pendingCall.push_back(std::make_pair(uint64_t(0), tmpFuture)); m_pendingCall.push_back(std::make_pair(uint64_t(0), tmpFuture));
} }
writeBinary(_obj); writeBinary(_obj);
ZEUS_VERBOSE("Send [STOP]");
return tmpFuture; return tmpFuture;
} }
@ -322,7 +322,6 @@ zeus::FutureBase zeus::WebServer::callForward(uint32_t _clientId,
const ememory::SharedPtr<zeus::Buffer>& _buffer, const ememory::SharedPtr<zeus::Buffer>& _buffer,
uint64_t _singleReferenceId, uint64_t _singleReferenceId,
zeus::FutureData::ObserverFinish _callback) { zeus::FutureData::ObserverFinish _callback) {
ZEUS_VERBOSE("Call Forward [START]");
//zeus::FutureBase ret = callBinary(id, _Buffer, async, _callback); //zeus::FutureBase ret = callBinary(id, _Buffer, async, _callback);
//ret.setSynchronous(); //ret.setSynchronous();
@ -341,7 +340,6 @@ zeus::FutureBase zeus::WebServer::callForward(uint32_t _clientId,
m_pendingCall.push_back(std::make_pair(_singleReferenceId, tmpFuture)); m_pendingCall.push_back(std::make_pair(_singleReferenceId, tmpFuture));
} }
writeBinary(_buffer); writeBinary(_buffer);
ZEUS_VERBOSE("Send Forward [STOP]");
return tmpFuture; return tmpFuture;
} }