diff --git a/jus/AbstractFunction.cpp b/jus/AbstractFunction.cpp index 573fe2a..df4e05a 100644 --- a/jus/AbstractFunction.cpp +++ b/jus/AbstractFunction.cpp @@ -107,6 +107,15 @@ namespace jus { } return out; } + template<> ejson::Value convertToJson(const jus::FileServer& _value) { + ejson::Array out; + /* + for (auto &it : _value) { + out.add(ejson::String(it)); + } + */ + return out; + } template<> bool convertStringTo(const std::string& _value) { return etk::string_to_bool(_value); @@ -149,6 +158,9 @@ namespace jus { JUS_TODO("Convert string to vs"); return out; } + template<> jus::FileServer convertStringTo(const std::string& _value) { + return jus::FileServer(); + } } diff --git a/jus/AbstractFunction.h b/jus/AbstractFunction.h index 41c25a1..697592b 100644 --- a/jus/AbstractFunction.h +++ b/jus/AbstractFunction.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace jus { class AbstractFunction { public: @@ -55,7 +56,7 @@ namespace jus { virtual std::string getPrototype() const = 0; virtual std::string getPrototypeReturn() const = 0; virtual std::vector getPrototypeParam() const = 0; - virtual ejson::Value executeJson(const ejson::Array& _params, void* _class=nullptr) = 0; + virtual void executeJson(const ememory::SharedPtr& _interfaceClient, uint64_t _transactionId, uint64_t _clientId, const ejson::Array& _params, void* _class=nullptr) = 0; virtual std::string executeString(const std::vector& _params, void* _class=nullptr) = 0; }; diff --git a/jus/AbstractFunctionTypeClass.h b/jus/AbstractFunctionTypeClass.h index dde876e..b52d906 100644 --- a/jus/AbstractFunctionTypeClass.h +++ b/jus/AbstractFunctionTypeClass.h @@ -12,23 +12,63 @@ #include namespace jus { template - ejson::Value executeClassCallJson(JUS_CLASS_TYPE* _pointer, JUS_RETURN (JUS_CLASS_TYPE::*_func)(JUS_TYPES...), const ejson::Array& _params) { + void executeClassCallJson(const ememory::SharedPtr& _interfaceClient, + uint64_t _transactionId, + uint64_t _clientId, + JUS_CLASS_TYPE* _pointer, + JUS_RETURN (JUS_CLASS_TYPE::*_func)(JUS_TYPES...), + const ejson::Array& _params) { #if defined(__clang__) // clang generate a basic warning: // warning: multiple unsequenced modifications to 'idParam' [-Wunsequenced] int32_t idParam = 0; - return convertToJson((*_pointer.*_func)((convertJsonTo(_params[idParam++]))...)); + ejson::Value ret = convertToJson((*_pointer.*_func)((convertJsonTo(_params[idParam++]))...)); #elif defined(__GNUC__) || defined(__GNUG__) || defined(_MSC_VER) int32_t idParam = int32_t(sizeof...(JUS_TYPES))-1; - return convertToJson((*_pointer.*_func)(convertJsonTo(_params[idParam--])...)); + ejson::Value ret = convertToJson((*_pointer.*_func)(convertJsonTo(_params[idParam--])...)); #else #error Must be implemented ... + ejson::Value ret = ejson::Null(); + return; #endif - return ejson::Null(); + ejson::Object answer; + answer.add("id", ejson::Number(_transactionId)); + answer.add("client-id", ejson::Number(_clientId)); + answer.add("return", ret); + JUS_INFO("Answer: " << answer.generateHumanString()); + _interfaceClient->write(answer.generateMachineString()); + } + + template + void executeClassCallJson(const ememory::SharedPtr& _interfaceClient, + uint64_t _transactionId, + uint64_t _clientId, + JUS_CLASS_TYPE* _pointer, + jus::FileServer (JUS_CLASS_TYPE::*_func)(JUS_TYPES...), + const ejson::Array& _params) { + #if defined(__clang__) + // clang generate a basic warning: + // warning: multiple unsequenced modifications to 'idParam' [-Wunsequenced] + int32_t idParam = 0; + jus::FileServer tmpElem = (*_pointer.*_func)((convertJsonTo(_params[idParam++]))...); + #elif defined(__GNUC__) || defined(__GNUG__) || defined(_MSC_VER) + int32_t idParam = int32_t(sizeof...(JUS_TYPES))-1; + jus::FileServer tmpElem = (*_pointer.*_func)(convertJsonTo(_params[idParam--])...); + #else + #error Must be implemented ... + jus::FileServer tmpElem; + return; + #endif + JUS_ERROR("Must be implemented in a worker ..."); } template - ejson::Value executeClassCallJson(JUS_CLASS_TYPE* _pointer, void (JUS_CLASS_TYPE::*_func)(JUS_TYPES...), const ejson::Array& _params) { + void executeClassCallJson(const ememory::SharedPtr& _interfaceClient, + uint64_t _transactionId, + uint64_t _clientId, + JUS_CLASS_TYPE* _pointer, + void (JUS_CLASS_TYPE::*_func)(JUS_TYPES...), + const ejson::Array& _params) { ejson::Object out; #if defined(__clang__) // clang generate a basic warning: @@ -40,12 +80,21 @@ namespace jus { (*_pointer.*_func)(convertJsonTo(_params[idParam--])...); #else #error Must be implemented ... + ejson::Value ret = ejson::Null(); + return; #endif - return ejson::Null(); + ejson::Object answer; + answer.add("id", ejson::Number(_transactionId)); + answer.add("client-id", ejson::Number(_clientId)); + answer.add("return", ejson::Null()); + JUS_INFO("Answer: " << answer.generateHumanString()); + _interfaceClient->write(answer.generateMachineString()); } template - std::string executeClassCallString(JUS_CLASS_TYPE* _pointer, JUS_RETURN (JUS_CLASS_TYPE::*_func)(JUS_TYPES...), const std::vector& _params) { + std::string executeClassCallString(JUS_CLASS_TYPE* _pointer, + JUS_RETURN (JUS_CLASS_TYPE::*_func)(JUS_TYPES...), + const std::vector& _params) { #if defined(__clang__) // clang generate a basic warning: // warning: multiple unsequenced modifications to 'idParam' [-Wunsequenced] @@ -60,7 +109,9 @@ namespace jus { return ""; } template - std::string executeClassCallString(JUS_CLASS_TYPE* _pointer, void (JUS_CLASS_TYPE::*_func)(JUS_TYPES...), const std::vector& _params) { + std::string executeClassCallString(JUS_CLASS_TYPE* _pointer, + void (JUS_CLASS_TYPE::*_func)(JUS_TYPES...), + const std::vector& _params) { ejson::Object out; #if defined(__clang__) // clang generate a basic warning: @@ -113,37 +164,49 @@ namespace jus { } return out; } - ejson::Value executeJson(const ejson::Array& _params, void* _class) override { + void executeJson(const ememory::SharedPtr& _interfaceClient, + uint64_t _transactionId, + uint64_t _clientId, + const ejson::Array& _params, + void* _class) override { JUS_CLASS_TYPE* tmpClass = nullptr; if (_class != nullptr) { tmpClass = (JUS_CLASS_TYPE*)_class; } - ejson::Object out; + // check parameter number if (_params.size() != sizeof...(JUS_TYPES)) { JUS_ERROR("Wrong number of Parameters ..."); - out.add("error", ejson::String("WRONG-PARAMETER-NUMBER")); + ejson::Object answer; + answer.add("id", ejson::Number(_transactionId)); + answer.add("client-id", ejson::Number(_clientId)); + answer.add("error", ejson::String("WRONG-PARAMETER-NUMBER")); std::string help = "request "; help += etk::to_string(_params.size()); help += " parameters and need "; help += etk::to_string(sizeof...(JUS_TYPES)); help += " parameters. prototype function:"; help += getPrototype(); - out.add("error-help", ejson::String(help)); - return out; + answer.add("error-help", ejson::String(help)); + JUS_INFO("Answer: " << answer.generateHumanString()); + _interfaceClient->write(answer.generateMachineString()); + return; } // check parameter compatibility for (size_t iii=0; iiiwrite(answer.generateMachineString()); + return; } } // execute cmd: - ejson::Value retVal = jus::executeClassCallJson(tmpClass, m_function, _params); - out.add("return", retVal); - return out; + jus::executeClassCallJson(_interfaceClient, _transactionId, _clientId, tmpClass, m_function, _params); } std::string executeString(const std::vector& _params, void* _class) override { JUS_CLASS_TYPE* tmpClass = (JUS_CLASS_TYPE*)_class; diff --git a/jus/AbstractFunctionTypeDirect.h b/jus/AbstractFunctionTypeDirect.h index 6b3ba21..55b7ac9 100644 --- a/jus/AbstractFunctionTypeDirect.h +++ b/jus/AbstractFunctionTypeDirect.h @@ -12,23 +12,37 @@ #include namespace jus { template - ejson::Value executeCallJson(JUS_RETURN (*_func)(JUS_TYPES...), const ejson::Array& _params) { + void executeCallJson(const ememory::SharedPtr& _interfaceClient, + uint64_t _transactionId, + uint64_t _clientId, + JUS_RETURN (*_func)(JUS_TYPES...), + const ejson::Array& _params) { #if defined(__clang__) // clang generate a basic warning: // warning: multiple unsequenced modifications to 'idParam' [-Wunsequenced] int32_t idParam = 0; - return convertToJson(_func((convertJsonTo(_params[idParam++]))...)); + ejson::Value ret = convertToJson(_func((convertJsonTo(_params[idParam++]))...)); #elif defined(__GNUC__) || defined(__GNUG__) || defined(_MSC_VER) int32_t idParam = int32_t(sizeof...(JUS_TYPES))-1; - return convertToJson(_func(convertJsonTo(_params[idParam--])...)); + ejson::Value ret = convertToJson(_func(convertJsonTo(_params[idParam--])...)); #else #error Must be implemented ... + ejson::Value ret = ejson::Null(); #endif - return ejson::Null(); + ejson::Object answer; + answer.add("id", ejson::Number(_transactionId)); + answer.add("client-id", ejson::Number(_clientId)); + answer.add("return", ret); + JUS_INFO("Answer: " << answer.generateHumanString()); + _interfaceClient->write(answer.generateMachineString()); } template - ejson::Value executeCallJson(void (*_func)(JUS_TYPES...), const ejson::Array& _params) { + void executeCallJson(const ememory::SharedPtr& _interfaceClient, + uint64_t _transactionId, + uint64_t _clientId, + void (*_func)(JUS_TYPES...), + const ejson::Array& _params) { ejson::Object out; #if defined(__clang__) // clang generate a basic warning: @@ -41,7 +55,16 @@ namespace jus { #else #error Must be implemented ... #endif - return ejson::Null(); + _interfaceClient->addAsync([=](TcpString* _interface) { + ejson::Object answer; + answer.add("id", ejson::Number(_transactionId)); + answer.add("client-id", ejson::Number(_clientId)); + answer.add("return", ejson::Null()); + JUS_INFO("Answer: " << answer.generateHumanString()); + _interface->write(answer.generateMachineString()); + return true; + }); + } } template @@ -113,33 +136,43 @@ namespace jus { } return out; } - ejson::Value executeJson(const ejson::Array& _params, void* _class) override { - ejson::Object out; + void executeJson(const ememory::SharedPtr& _interfaceClient, + uint64_t _transactionId, + uint64_t _clientId, + const ejson::Array& _params, + void* _class) override { // check parameter number if (_params.size() != sizeof...(JUS_TYPES)) { - JUS_ERROR("Wrong number of Parameters ..."); - out.add("error", ejson::String("WRONG-PARAMETER-NUMBER")); + ejson::Object answer; + answer.add("id", ejson::Number(_transactionId)); + answer.add("client-id", ejson::Number(_clientId)); + answer.add("error", ejson::String("WRONG-PARAMETER-NUMBER")); std::string help = "request "; help += etk::to_string(_params.size()); help += " parameters and need "; help += etk::to_string(sizeof...(JUS_TYPES)); help += " parameters. prototype function:"; help += getPrototype(); - out.add("error-help", ejson::String(help)); - return out; + answer.add("error-help", ejson::String(help)); + JUS_INFO("Answer: " << answer.generateHumanString()); + _interfaceClient->write(answer.generateMachineString()); + return; } // check parameter compatibility for (size_t iii=0; iiiwrite(answer.generateMachineString()); + return; } } // execute cmd: - ejson::Value retVal = jus::executeCallJson(m_function, _params); - out.add("return", retVal); - return out; + jus::executeCallJson(_interfaceClient, _transactionId, _clientId, m_function, _params); } std::string executeString(const std::vector& _params, void* _class) override { std::string out; diff --git a/jus/File.cpp b/jus/File.cpp new file mode 100644 index 0000000..40b6a8b --- /dev/null +++ b/jus/File.cpp @@ -0,0 +1,38 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2016, Edouard DUPIN, all right reserved + * @license APACHE v2.0 (see license file) + */ +#include +#include +#include + + + +jus::File::File() { + +} + +jus::File::File(const std::string& _filename) { + +} + +jus::File::File(const std::string& _mineType, std::vector _data) { + +} +jus::FileServer::FileServer() { + +} +jus::FileServer::FileServer(const std::string& _filename) { + m_name = _filename; +} + + +namespace etk { + template<> + std::string to_string(jus::FileServer const& _obj) { + return ""; + } +} + + diff --git a/jus/File.h b/jus/File.h new file mode 100644 index 0000000..b14a7da --- /dev/null +++ b/jus/File.h @@ -0,0 +1,33 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2016, Edouard DUPIN, all right reserved + * @license APACHE v2.0 (see license file) + */ +#pragma once +#include + +namespace jus { + class File { + private: + std::string m_mineType; + std::vector m_data; + public: + File(); + File(const std::string& _filename); + File(const std::string& _mineType, std::vector _data); + const std::string& getMineType() { + return m_mineType; + } + const std::vector& getData() { + return m_data; + } + }; + class FileServer { + private: + std::string m_name; + public: + FileServer(); + FileServer(const std::string& _filename); + }; +} + diff --git a/jus/Future.cpp b/jus/Future.cpp index 78f14f8..18e928c 100644 --- a/jus/Future.cpp +++ b/jus/Future.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace jus { template<> @@ -488,6 +489,33 @@ namespace jus { } return out; } + template<> + jus::File jus::Future::get() { + jus::File out; + if (m_data == nullptr) { + return out; + } + // TODO :... + /* + ejson::Value val = m_data->m_returnData["return"]; + if (val.exist() == false) { + JUS_WARNING("No Return value ..."); + return out; + } + if (val.isArray() == false) { + JUS_WARNING("Wrong return Type get '" << val.getType() << " instead of 'Array'"); + return out; + } + for (auto it : val.toArray()) { + if (it.isBoolean() == false) { + JUS_WARNING("Wrong return Type (part of array) get '" << it.getType() << " instead of 'Boolean'"); + continue; + } + out.push_back(it.toBoolean().get()); + } + */ + return out; + } } diff --git a/jus/FutureBase.cpp b/jus/FutureBase.cpp index e961fdd..d8ef93a 100644 --- a/jus/FutureBase.cpp +++ b/jus/FutureBase.cpp @@ -24,6 +24,7 @@ jus::FutureBase::FutureBase(uint64_t _transactionId, jus::FutureData::ObserverFi m_data->m_sendTime = std::chrono::steady_clock::now(); m_data->m_transactionId = _transactionId; m_data->m_isFinished = false; + m_data->m_isSynchronous = false; m_data->m_callbackFinish = _callback; } @@ -42,6 +43,7 @@ jus::FutureBase::FutureBase(uint64_t _transactionId, bool _isFinished, ejson::Ob m_data->m_sendTime = std::chrono::steady_clock::now(); m_data->m_transactionId = _transactionId; m_data->m_isFinished = _isFinished; + m_data->m_isSynchronous = false; m_data->m_returnData = _returnData; m_data->m_callbackFinish = _callback; if (m_data->m_isFinished == true) { @@ -67,17 +69,50 @@ jus::FutureBase jus::FutureBase::operator= (const jus::FutureBase& _base) { return *this; } -void jus::FutureBase::setAnswer(const ejson::Object& _returnValue) { +bool jus::FutureBase::setAnswer(const ejson::Object& _returnValue) { if (m_data == nullptr) { JUS_ERROR(" Not a valid future ..."); - return; + return true; } m_data->m_receiveTime = std::chrono::steady_clock::now(); + if (m_data->m_isSynchronous == true) { + m_data->m_returnData = _returnValue; + if (m_data->m_callbackFinish != nullptr) { + return m_data->m_callbackFinish(*this); + } + return true; + } + if (_returnValue.valueExist("part") == true) { + uint64_t idPart = _returnValue["part"].toNumber().getU64(); + if (idPart == 0) { + m_data->m_returnData = _returnValue; + } else { + m_data->m_returnDataPart.push_back(_returnValue["data"]); + } + if (_returnValue.valueExist("finish") == true) { + if (_returnValue["finish"].toBoolean().get() == true) { + m_data->m_isFinished = true; + if (m_data->m_callbackFinish != nullptr) { + return m_data->m_callbackFinish(*this); + } + return true; + } + // finish is false ==> normal case ... + } + return false; + } m_data->m_returnData = _returnValue; m_data->m_isFinished = true; if (m_data->m_callbackFinish != nullptr) { - m_data->m_callbackFinish(*this); + return m_data->m_callbackFinish(*this); } + return true; +} +void jus::FutureBase::setSynchronous() { + if (m_data == nullptr) { + return; + } + m_data->m_isSynchronous = true; } uint64_t jus::FutureBase::getTransactionId() { diff --git a/jus/FutureBase.h b/jus/FutureBase.h index 70aeba2..16d00d8 100644 --- a/jus/FutureBase.h +++ b/jus/FutureBase.h @@ -17,7 +17,8 @@ namespace jus { FutureBase(uint64_t _transactionId, jus::FutureData::ObserverFinish _callback=nullptr); FutureBase(uint64_t _transactionId, bool _isFinished, ejson::Object _returnData, jus::FutureData::ObserverFinish _callback=nullptr); jus::FutureBase operator= (const jus::FutureBase& _base); - void setAnswer(const ejson::Object& _returnValue); + bool setAnswer(const ejson::Object& _returnValue); + void setSynchronous(); uint64_t getTransactionId(); bool hasError(); std::string getErrorType(); diff --git a/jus/FutureData.h b/jus/FutureData.h index d6aba8a..bc4d334 100644 --- a/jus/FutureData.h +++ b/jus/FutureData.h @@ -12,11 +12,13 @@ namespace jus { class FutureBase; class FutureData { public: - using ObserverFinish = std::function; //!< Define an Observer: function pointer + using ObserverFinish = std::function; //!< Define an Observer: function pointer public: uint64_t m_transactionId; + bool m_isSynchronous; bool m_isFinished; ejson::Object m_returnData; + std::vector m_returnDataPart; ObserverFinish m_callbackFinish; std::chrono::steady_clock::time_point m_sendTime; std::chrono::steady_clock::time_point m_receiveTime; diff --git a/jus/GateWayClient.cpp b/jus/GateWayClient.cpp index 53f5501..419a9d5 100644 --- a/jus/GateWayClient.cpp +++ b/jus/GateWayClient.cpp @@ -362,6 +362,14 @@ void jus::GateWayClient::onClientData(std::string _value) { tmpp["id"].toNumber().set(data["id"].toNumber().getU64()); JUS_VERBOSE(" msg=" << tmpp.generateMachineString()); m_interfaceClient.write(tmpp.generateMachineString()); + if (tmpp.valueExist("part") == true) { + // multiple send element ... + if (tmpp.valueExist("finish") == true) { + return tmpp["finish"].toBoolean().get(); + } + return false; + } + return true; }); } } @@ -371,7 +379,9 @@ void jus::GateWayClient::onClientData(std::string _value) { jus::FutureBase jus::GateWayClient::callActionForward(uint64_t _callerId, ememory::SharedPtr _srv, const std::string& _functionName, ejson::Array _params, jus::FutureData::ObserverFinish _callback) { uint64_t id = getId(); ejson::Object callElem = jus::createCallJson(id, _functionName, _params); - return callJson(_callerId, _srv, id, callElem, _callback); + jus::FutureBase ret = callJson(_callerId, _srv, id, callElem, _callback); + ret.setSynchronous(); + return ret; } uint64_t jus::GateWayClient::getId() { @@ -400,24 +410,24 @@ jus::FutureBase jus::GateWayClient::callJson(uint64_t _callerId, ememory::Shared void jus::GateWayClient::returnMessage(ejson::Object _data) { jus::FutureBase future; - { - uint64_t tid = _data["id"].toNumber().get(); - if (tid == 0) { - if (_data["error"].toString().get() == "PROTOCOL-ERROR") { - JUS_ERROR("Get a Protocol error ..."); - std::unique_lock lock(m_mutex); - for (auto &it : m_pendingCall) { - if (it.isValid() == false) { - continue; - } - it.setAnswer(_data); + uint64_t tid = _data["id"].toNumber().get(); + if (tid == 0) { + if (_data["error"].toString().get() == "PROTOCOL-ERROR") { + JUS_ERROR("Get a Protocol error ..."); + std::unique_lock lock(m_mutex); + for (auto &it : m_pendingCall) { + if (it.isValid() == false) { + continue; } - m_pendingCall.clear(); - } else { - JUS_ERROR("call with no ID ==> error ..."); + it.setAnswer(_data); } - return; + m_pendingCall.clear(); + } else { + JUS_ERROR("call with no ID ==> error ..."); } + return; + } + { std::unique_lock lock(m_mutex); auto it = m_pendingCall.begin(); while (it != m_pendingCall.end()) { @@ -429,8 +439,8 @@ void jus::GateWayClient::returnMessage(ejson::Object _data) { ++it; continue; } + // TODO : Do it better ... future = *it; - it = m_pendingCall.erase(it); break; } } @@ -438,6 +448,22 @@ void jus::GateWayClient::returnMessage(ejson::Object _data) { JUS_WARNING("Action to do ..."); return; } - future.setAnswer(_data); + bool ret = future.setAnswer(_data); + if (ret == true) { + std::unique_lock lock(m_mutex); + auto it = m_pendingCall.begin(); + while (it != m_pendingCall.end()) { + if (it->isValid() == false) { + it = m_pendingCall.erase(it); + continue; + } + if (it->getTransactionId() != tid) { + ++it; + continue; + } + it = m_pendingCall.erase(it); + break; + } + } } diff --git a/jus/ParamType.cpp b/jus/ParamType.cpp index 6489912..f93e5af 100644 --- a/jus/ParamType.cpp +++ b/jus/ParamType.cpp @@ -6,6 +6,7 @@ #include #include +#include jus::ParamType::ParamType(const char* _name): @@ -55,4 +56,10 @@ generate_basic_type(std::vector, "vector:uint64"); generate_basic_type(std::vector, "vector:uint32"); generate_basic_type(std::vector, "vector:uint16"); generate_basic_type(std::vector, "vector:uint8"); -generate_basic_type(std::vector, "vector:string"); \ No newline at end of file +generate_basic_type(std::vector, "vector:string"); + + +generate_basic_type(jus::File, "file"); +generate_basic_type(jus::FileServer, "file"); + + diff --git a/jus/Service.cpp b/jus/Service.cpp index cde150d..0381c45 100644 --- a/jus/Service.cpp +++ b/jus/Service.cpp @@ -15,7 +15,8 @@ jus::Service::Service() : propertyIp(this, "ip", "127.0.0.1", "Ip to connect server", &jus::Service::onPropertyChangeIp), propertyPort(this, "port", 1982, "Port to connect server", &jus::Service::onPropertyChangePort) { - m_interfaceClient.connect(this, &jus::Service::onClientData); + m_interfaceClient = std::make_shared(); + m_interfaceClient->connect(this, &jus::Service::onClientData); advertise("getExtention", &jus::Service::getExtention); setLastFuncDesc("Get List of availlable extention of this service"); @@ -35,16 +36,18 @@ std::vector jus::Service::getExtention() { void jus::Service::onClientData(std::string _value) { ejson::Object request(_value); - ejson::Value tmpID = request["id"]; - request.remove("id"); + uint64_t tmpID = request["id"].toNumber().getU64(); + //request.remove("id"); JUS_INFO("Request: " << _value); - ejson::Value answer = callJson(request); + callJson(tmpID, request); + /* // check if an answer is needed if (answer.isNull() == false) { answer.toObject().add("id", tmpID); JUS_INFO("Answer: " << answer.generateHumanString()); - m_interfaceClient.write(answer.generateMachineString()); + m_interfaceClient->write(answer.generateMachineString()); } + */ } void jus::Service::onPropertyChangeIp() { @@ -64,29 +67,29 @@ void jus::Service::connect(const std::string& _serviceName, uint32_t _numberRetr JUS_DEBUG("connect [STOP] ==> can not connect"); return; } - m_interfaceClient.setInterface(std::move(connection)); - m_interfaceClient.connect(); - m_interfaceClient.write(std::string("{\"connect-service\":\"") + _serviceName + "\"}"); + m_interfaceClient->setInterface(std::move(connection)); + m_interfaceClient->connect(); + m_interfaceClient->write(std::string("{\"connect-service\":\"") + _serviceName + "\"}"); JUS_DEBUG("connect [STOP]"); } void jus::Service::disconnect(){ JUS_DEBUG("disconnect [START]"); - m_interfaceClient.disconnect(); + m_interfaceClient->disconnect(); JUS_DEBUG("disconnect [STOP]"); } bool jus::Service::GateWayAlive() { - return m_interfaceClient.isActive(); + return m_interfaceClient->isActive(); } void jus::Service::pingIsAlive() { - if (std::chrono::steady_clock::now() - m_interfaceClient.getLastTimeSend() >= std::chrono::seconds(30)) { - m_interfaceClient.write("{\"event\":\"IS-ALIVE\"}"); + if (std::chrono::steady_clock::now() - m_interfaceClient->getLastTimeSend() >= std::chrono::seconds(30)) { + m_interfaceClient->write("{\"event\":\"IS-ALIVE\"}"); } } -ejson::Value jus::Service::callJson(const ejson::Object& _obj) { +void jus::Service::callJson(uint64_t _transactionId, const ejson::Object& _obj) { if (_obj.valueExist("event") == true) { std::string event = _obj["event"].toString().get(); if (event == "IS-ALIVE") { @@ -104,22 +107,27 @@ ejson::Value jus::Service::callJson(const ejson::Object& _obj) { } else { JUS_ERROR("Unknow event: '" << event << "'"); } - return ejson::Null(); + return; } - ejson::Object tmpp; + ejson::Object answer; uint64_t clientId = _obj["client-id"].toNumber().getU64(); if (_obj.valueExist("call") == true) { std::string call = _obj["call"].toString().get(); if (isFunctionAuthorized(clientId, call) == true) { - tmpp = callJson2(clientId, _obj); - tmpp.add("client-id", ejson::Number(clientId)); - return tmpp; + callJson2(_transactionId, clientId, call, _obj["param"].toArray()); + return; } else { - tmpp.add("error", ejson::String("NOT-AUTHORIZED-FUNCTION")); + answer.add("id", ejson::Number(_transactionId)); + answer.add("client-id", ejson::Number(clientId)); + answer.add("error", ejson::String("NOT-AUTHORIZED-FUNCTION")); + JUS_INFO("Answer: " << answer.generateHumanString()); + m_interfaceClient->write(answer.generateMachineString()); + return; } - } else { - tmpp.add("error", ejson::String("NOT-IMPLEMENTED-FUNCTION")); } - tmpp.add("client-id", ejson::Number(clientId)); - return tmpp; + answer.add("id", ejson::Number(_transactionId)); + answer.add("client-id", ejson::Number(clientId)); + answer.add("error", ejson::String("NOT-IMPLEMENTED-FUNCTION")); + JUS_INFO("Answer: " << answer.generateHumanString()); + m_interfaceClient->write(answer.generateMachineString()); } diff --git a/jus/Service.h b/jus/Service.h index 2066e14..f6b2e0a 100644 --- a/jus/Service.h +++ b/jus/Service.h @@ -57,8 +57,8 @@ namespace jus { public: eproperty::Value propertyIp; eproperty::Value propertyPort; - private: - jus::TcpString m_interfaceClient; + protected: + ememory::SharedPtr m_interfaceClient; uint32_t m_id; std::vector m_newData; public: @@ -77,15 +77,15 @@ namespace jus { void onPropertyChangePort(); /** * @brief A extern client connect on specific user - * @param[in] _clientSessionID Source session Id on the client + * @param[in] _clientId Source session Id on the client * @param[in] _userName User name of the client to connect * @todo Set a relur like ==> service not availlable / service close / service maintenance / service right reject */ - virtual void clientConnect(size_t _clientSessionID, const std::string& _userName, const std::string& _clientName, const std::vector& _groups) = 0; - virtual void clientDisconnect(size_t _clientSessionID) = 0; + virtual void clientConnect(uint64_t _clientId, const std::string& _userName, const std::string& _clientName, const std::vector& _groups) = 0; + virtual void clientDisconnect(uint64_t _clientId) = 0; // Genenric function call: - ejson::Value callJson(const ejson::Object& _obj); - virtual ejson::Object callJson2(size_t _clientId, const ejson::Object& _obj) = 0; + void callJson(uint64_t _transactionId, const ejson::Object& _obj); + virtual void callJson2(uint64_t _transactionId, uint64_t _clientId, const std::string& _call, const ejson::Array& _obj) = 0; std::vector getExtention(); public: // Add Local fuction (depend on this class) @@ -154,21 +154,21 @@ namespace jus { m_getUserInterface(_interface) { } - bool isFunctionAuthorized(uint64_t _clientSessionID, const std::string& _funcName) { - auto it = m_interface.find(_clientSessionID); + bool isFunctionAuthorized(uint64_t _clientId, const std::string& _funcName) { + auto it = m_interface.find(_clientId); if (it == m_interface.end()) { return false; } return it->second.first->isFunctionAuthorized(_funcName); } - void clientConnect(uint64_t _clientSessionID, const std::string& _userName, const std::string& _clientName, const std::vector& _groups) { + void clientConnect(uint64_t _clientId, const std::string& _userName, const std::string& _clientName, const std::vector& _groups) { std::unique_lock lock(m_mutex); - JUS_DEBUG("connect: " << _clientSessionID << " to '" << _userName << "'"); + JUS_DEBUG("connect: " << _clientId << " to '" << _userName << "'"); JUS_DEBUG(" client name='" << _clientName << "'"); JUS_DEBUG(" groups=" << etk::to_string(_groups)); ememory::SharedPtr tmpProperty = std::make_shared(_clientName, _groups); ememory::SharedPtr tmpSrv = std::make_shared(m_getUserInterface.getUser(_userName), tmpProperty); - m_interface.insert(std::make_pair(_clientSessionID, std::make_pair(tmpProperty, tmpSrv))); + m_interface.insert(std::make_pair(_clientId, std::make_pair(tmpProperty, tmpSrv))); // enable list of function availlable: for (auto &it : m_listFunction) { if (it == nullptr) { @@ -177,72 +177,85 @@ namespace jus { tmpProperty->addAuthorized(it->getName()); } } - void clientDisconnect(uint64_t _clientSessionID) { + void clientDisconnect(uint64_t _clientId) { std::unique_lock lock(m_mutex); - JUS_DEBUG("disconnect: " << _clientSessionID); - auto it = m_interface.find(_clientSessionID); + JUS_DEBUG("disconnect: " << _clientId); + auto it = m_interface.find(_clientId); if (it == m_interface.end()) { - JUS_WARNING("disconnect ==> Not find Client ID " << _clientSessionID); + JUS_WARNING("disconnect ==> Not find Client ID " << _clientId); // noting to do ==> user never conected. return; } m_interface.erase(it); } - void clientSetName(uint64_t _clientSessionID, const std::string& _clientName) { + void clientSetName(uint64_t _clientId, const std::string& _clientName) { std::unique_lock lock(m_mutex); - auto it = m_interface.find(_clientSessionID); + auto it = m_interface.find(_clientId); if (it == m_interface.end()) { JUS_ERROR("Change the client property but client was not created ..."); return; } it->second.first->setName(_clientName); } - void clientSetGroup(uint64_t _clientSessionID, const std::vector& _clientGroups) { + void clientSetGroup(uint64_t _clientId, const std::vector& _clientGroups) { std::unique_lock lock(m_mutex); - auto it = m_interface.find(_clientSessionID); + auto it = m_interface.find(_clientId); if (it == m_interface.end()) { JUS_ERROR("Change the client property but client was not created ..."); return; } it->second.first->setGroups(_clientGroups); } - ejson::Object callJson2(uint64_t _clientSessionID, const ejson::Object& _obj) { - ejson::Object out; - auto it = m_interface.find(_clientSessionID); + void callJson2(uint64_t _transactionId, uint64_t _clientId, const std::string& _call, const ejson::Array& _params) { + auto it = m_interface.find(_clientId); if (it == m_interface.end()) { - out.add("error", ejson::String("CLIENT-UNKNOW")); - return out; + ejson::Object answer; + answer.add("id", ejson::Number(_transactionId)); + answer.add("client-id", ejson::Number(_clientId)); + answer.add("error", ejson::String("CLIENT-UNKNOW")); + JUS_INFO("Answer: " << answer.generateHumanString()); + m_interfaceClient->write(answer.generateMachineString()); + return; } - std::string call = _obj["call"].toString().get(); - const ejson::Array param = _obj["param"].toArray(); for (auto &it2 : m_listFunction) { if (it2 == nullptr) { continue; } - if (it2->getName() != call) { + if (it2->getName() != _call) { continue; } switch (it2->getType()) { case jus::AbstractFunction::type::object: { JUS_TYPE_SERVICE* elem = it->second.second.get(); - return it2->executeJson(param, (void*)elem).toObject(); + it2->executeJson(m_interfaceClient, _transactionId, _clientId, _params, (void*)elem); + return; } case jus::AbstractFunction::type::local: { - return it2->executeJson(param, (void*)((RemoteProcessCall*)this)).toObject(); + it2->executeJson(m_interfaceClient, _transactionId, _clientId, _params, (void*)((RemoteProcessCall*)this)); + return; } case jus::AbstractFunction::type::service: { - return it2->executeJson(param, (void*)this).toObject(); + it2->executeJson(m_interfaceClient, _transactionId, _clientId, _params, (void*)this); + return; } case jus::AbstractFunction::type::global: { - return it2->executeJson(param, nullptr).toObject(); + it2->executeJson(m_interfaceClient, _transactionId, _clientId, _params, nullptr); + return; } case jus::AbstractFunction::type::unknow: JUS_ERROR("Can not call unknow type ..."); break; } } - out.add("error", ejson::String("FUNCTION-UNKNOW")); - return out; + { + ejson::Object answer; + answer.add("id", ejson::Number(_transactionId)); + answer.add("client-id", ejson::Number(_clientId)); + answer.add("error", ejson::String("FUNCTION-UNKNOW")); + JUS_INFO("Answer: " << answer.generateHumanString()); + m_interfaceClient->write(answer.generateMachineString()); + } + return; } }; } diff --git a/jus/TcpString.cpp b/jus/TcpString.cpp index df6580c..6eebafb 100644 --- a/jus/TcpString.cpp +++ b/jus/TcpString.cpp @@ -11,15 +11,19 @@ jus::TcpString::TcpString(enet::Tcp _connection) : m_connection(std::move(_connection)), m_thread(nullptr), - m_obsercerElement(nullptr) { + m_obsercerElement(nullptr), + m_threadAsync(nullptr) { m_threadRunning = false; + m_threadAsyncRunning = false; } jus::TcpString::TcpString() : m_connection(), m_thread(nullptr), - m_obsercerElement(nullptr) { + m_obsercerElement(nullptr), + m_threadAsync(nullptr) { m_threadRunning = false; + m_threadAsyncRunning = false; } void jus::TcpString::setInterface(enet::Tcp _connection) { @@ -66,6 +70,14 @@ void jus::TcpString::connect(bool _async){ JUS_ERROR("creating callback thread!"); return; } + m_threadAsyncRunning = true; + m_threadAsync = new std::thread([&](void *){ this->threadAsyncCallback();}, nullptr); + if (m_threadAsync == nullptr) { + m_threadAsyncRunning = false; + JUS_ERROR("creating async sender thread!"); + return; + } + while ( _async == false && m_threadRunning == true && m_connection.getConnectionStatus() != enet::Tcp::status::link) { @@ -81,9 +93,8 @@ void jus::TcpString::connect(bool _async){ void jus::TcpString::disconnect(bool _inThreadStop){ JUS_DEBUG("disconnect [START]"); - if (m_thread != nullptr) { - m_threadRunning = false; - } + m_threadRunning = false; + m_threadAsyncRunning = false; if (m_connection.getConnectionStatus() == enet::Tcp::status::link) { uint32_t size = 0xFFFFFFFF; m_connection.write(&size, 4); @@ -91,6 +102,11 @@ void jus::TcpString::disconnect(bool _inThreadStop){ if (m_connection.getConnectionStatus() != enet::Tcp::status::unlink) { m_connection.unlink(); } + if (m_threadAsync != nullptr) { + m_threadAsync->join(); + delete m_threadAsync; + m_threadAsync = nullptr; + } if (_inThreadStop == false) { if (m_thread != nullptr) { m_thread->join(); @@ -147,3 +163,36 @@ std::string jus::TcpString::read() { return out; } + +void jus::TcpString::threadAsyncCallback() { + ethread::setName("Async-sender"); + // get datas: + while ( m_threadAsyncRunning == true + && m_connection.getConnectionStatus() == enet::Tcp::status::link) { + if (m_threadAsyncList.size() == 0) { + usleep(10000); + continue; + } + std::unique_lock lock(m_threadAsyncMutex); + auto it = m_threadAsyncList.begin(); + while (it != m_threadAsyncList.end()) { + bool ret = m_threadAsyncList(this); + if (ret == true) { + // Remove it ... + it = m_threadAsyncList.erase(it); + } else { + ++it; + } + } + JUS_VERBOSE("Receive data: '" << data << "'"); + if (data.size() != 0) { + m_lastReceive = std::chrono::steady_clock::now(); + if (m_obsercerElement != nullptr) { + m_obsercerElement(std::move(data)); + } + } + } + m_threadRunning = false; + JUS_DEBUG("End of thread"); +} + diff --git a/jus/TcpString.h b/jus/TcpString.h index d0c2238..6a6d94b 100644 --- a/jus/TcpString.h +++ b/jus/TcpString.h @@ -55,6 +55,19 @@ namespace jus { const std::chrono::steady_clock::time_point& getLastTimeSend() { return m_lastSend; } + private: + using ActionAsync = std::function; + std::mutex m_threadAsyncMutex; + std::thread* m_threadAsync; + bool m_threadAsyncRunning; + std::vector m_threadAsyncList; + private: + void threadAsyncCallback(); + public: + void addAsync(ActionAsync _elem) { + std::unique_lock lock(m_threadAsyncMutex); + m_threadAsyncList.push_back(_elem); + } }; } diff --git a/lutin_jus.py b/lutin_jus.py index 66ea843..90b9d0b 100644 --- a/lutin_jus.py +++ b/lutin_jus.py @@ -37,6 +37,7 @@ def create(target, module_name): 'jus/AbstractFunctionTypeClass.cpp', 'jus/FutureBase.cpp', 'jus/Future.cpp', + 'jus/File.cpp', 'jus/ParamType.cpp', 'jus/Client.cpp', 'jus/GateWay.cpp', @@ -54,6 +55,7 @@ def create(target, module_name): 'jus/FutureData.h', 'jus/FutureBase.h', 'jus/Future.h', + 'jus/File.h', 'jus/ParamType.h', 'jus/debug.h', 'jus/Client.h', diff --git a/test/client/appl/main.cpp b/test/client/appl/main.cpp index f2057b3..d4992a1 100644 --- a/test/client/appl/main.cpp +++ b/test/client/appl/main.cpp @@ -145,6 +145,10 @@ int main(int _argc, const char *_argv[]) { jus::Future retCount = remoteServicePicture.call("getAlbumCount", it).wait(); if (retCount.get() != 0) { APPL_INFO(" - " << it << " / " << retCount.get() << " images"); + jus::Future> retListImage = remoteServicePicture.call("getAlbumListPicture", it).wait(); + for (auto &it3 : retListImage.get()) { + APPL_INFO(" - " << it3); + } } else { APPL_INFO(" - " << it); } @@ -153,6 +157,14 @@ int main(int _argc, const char *_argv[]) { jus::Future retCount2 = remoteServicePicture.call("getAlbumCount", it2).wait(); if (retCount2.get() != 0) { APPL_INFO(" - " << it2 << " / " << retCount2.get() << " images"); + jus::Future> retListImage = remoteServicePicture.call("getAlbumListPicture", it2).wait(); + for (auto &it3 : retListImage.get()) { + APPL_INFO(" - " << it3); + jus::Future retListImage = remoteServicePicture.call("getAlbumPicture", it3).wait(); + jus::File tmpFile = retListImage.get(); + APPL_INFO(" mine-type: " << tmpFile.getMineType()); + APPL_INFO(" size: " << tmpFile.getData().size()); + } } else { APPL_INFO(" - " << it2); } diff --git a/tools/picture/appl/main.cpp b/tools/picture/appl/main.cpp index bc78a46..49da458 100644 --- a/tools/picture/appl/main.cpp +++ b/tools/picture/appl/main.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -19,46 +20,23 @@ namespace appl { std::mutex m_mutex; std::string m_userName; std::string m_basePath; + ejson::Document m_database; + std::map m_listFile; public: User(const std::string& _userName) : m_userName(_userName) { std::unique_lock lock(m_mutex); - APPL_WARNING("new USER: " << m_userName); + APPL_WARNING("new USER: " << m_userName << " [START]"); m_basePath = std::string("USERDATA:") + m_userName + "/"; - } - ~User() { - std::unique_lock lock(m_mutex); - APPL_WARNING("delete USER [START]"); - APPL_WARNING("delete USER [STOP]"); - } - // Return the list of root albums - std::vector getAlbums() { - return getSubAlbums(""); - } - // Get the list of sub album - std::vector getSubAlbums(const std::string& _parrentAlbum) { - std::unique_lock lock(m_mutex); - std::vector out; - etk::FSNode node(m_basePath + _parrentAlbum); - std::vector tmpList = node.folderGetSubList(false, true, false, false); - for (auto &it : tmpList) { - if (it == nullptr) { - continue; - } - if ( _parrentAlbum.size() == 0 - || _parrentAlbum[_parrentAlbum.size()-1] == '/') { - out.push_back(_parrentAlbum + it->getNameFile()); - } else { - out.push_back(_parrentAlbum + "/" + it->getNameFile()); - } + APPL_WARNING("new USER: " << m_userName); + bool ret = m_database.load(m_basePath + "database.json"); + if (ret == false) { + APPL_WARNING(" ==> LOAD error"); } - return out; - } - uint32_t getAlbumCount(const std::string& _album) { - std::unique_lock lock(m_mutex); - etk::FSNode node(m_basePath + _album); + // Load all files (image and video ...) + etk::FSNode node(m_basePath); std::vector tmpList = node.folderGetSubList(false, false, true, false); - uint32_t nbElem = 0; + APPL_WARNING("Find " << tmpList.size() << " files"); for (auto &it : tmpList) { if (it == nullptr) { continue; @@ -66,24 +44,163 @@ namespace appl { if ( etk::end_with(it->getNameFile(), ".svg", false) == true || etk::end_with(it->getNameFile(), ".bmp", false) == true || etk::end_with(it->getNameFile(), ".png", false) == true - || etk::end_with(it->getNameFile(), ".jpg", false) == true) { - nbElem++; + || etk::end_with(it->getNameFile(), ".jpg", false) == true + || etk::end_with(it->getNameFile(), ".tga", false) == true + || etk::end_with(it->getNameFile(), ".mp4", false) == true + || etk::end_with(it->getNameFile(), ".avi", false) == true + || etk::end_with(it->getNameFile(), ".mov", false) == true + || etk::end_with(it->getNameFile(), ".mkv", false) == true) { + // TODO : Do it better (proto ..) + std::string idString = it->getNameFile(); + idString.resize(idString.size()-4); + uint64_t id = 0; + std::stringstream ss; + ss << std::hex << idString; + ss >> id; + if (id <= 1024) { + APPL_WARNING(" ==> REJECTED file " << it->getNameFile() << " with ID = " << id); + } else { + m_listFile.insert(std::make_pair(id, it->getNameFile())); + APPL_WARNING(" ==> load file " << it->getNameFile() << " with ID = " << id); + } + } else { + APPL_WARNING(" ==> REJECT file " << it->getNameFile()); } } - return nbElem; + APPL_WARNING("new USER: " << m_userName << " [STOP]"); } - // Return the list of the album files - std::vector getAlbumListPicture(const std::string& _album, uint32_t _startId, uint32_t _stopId) { + ~User() { + std::unique_lock lock(m_mutex); + APPL_WARNING("delete USER [START]"); + APPL_DEBUG("Store User Info:"); + bool ret = m_database.storeSafe(m_basePath + "database.json"); + if (ret == false) { + APPL_WARNING(" ==> Store error"); + } + APPL_WARNING("delete USER [STOP]"); + } + // Return the list of root albums + std::vector getAlbums() { std::unique_lock lock(m_mutex); std::vector out; - return std::vector(); + ejson::Array globalGroups = m_database["group-global"].toArray(); + if (globalGroups.exist() == false) { + return out; + } + ejson::Object groups = m_database["groups"].toObject(); + if (groups.exist() == false) { + return out; + } + for (auto it: globalGroups) { + std::string tmpString = it.toString().get(); + if (tmpString == "") { + continue; + } + out.push_back(tmpString); + } + return out; + /* + ejson::Object groups = m_database["groups"].toObject(); + if (groups.exist() == false) { + return std::vector(); + } + groups + return getSubAlbums(""); + */ + } + // Get the list of sub album + std::vector getSubAlbums(const std::string& _album) { + std::unique_lock lock(m_mutex); + std::vector out; + ejson::Object groups = m_database["groups"].toObject(); + if (groups.exist() == false) { + return out; + } + // find parrentAlbum ==> to get sub group + /* + for (size_t iii=0; iii lock(m_mutex); + ejson::Object groups = m_database["groups"].toObject(); + if (groups.exist() == false) { + // TODO : Throw an error ... + return 0; + } + ejson::Object group = groups[_album].toObject(); + if (group.exist() == false) { + // TODO : Throw an error ... + return 0; + } + ejson::Array groupSubs = group["files"].toArray(); + // TODO: Check right + return groupSubs.size(); + /* + for (auto it: groupSubs) { + uint64_t id = it.toNumber().getU64(); + if (id == 0) { + continue; + } + out.push_back(id); + } + */ + } + // Return the list of the album files + std::vector getAlbumListPicture(const std::string& _album) {//, uint32_t _startId, uint32_t _stopId) { + std::unique_lock lock(m_mutex); + std::vector out; + ejson::Object groups = m_database["groups"].toObject(); + if (groups.exist() == false) { + // TODO : Throw an error ... + return out; + } + ejson::Object group = groups[_album].toObject(); + if (group.exist() == false) { + // TODO : Throw an error ... + return out; + } + ejson::Array groupSubs = group["files"].toArray(); + + for (auto it: groupSubs) { + uint64_t id = it.toNumber().getU64(); + /* + auto itImage = m_listFile.find(id); + if (itImage == m_listFile.end()) { + + }*/ + if (id == 0) { + continue; + } + out.push_back(etk::to_string(id)); + } + return out; + } + // Return a File Data (might be a picture .tiff/.png/.jpg) + jus::FileServer getAlbumPicture(const std::string& _pictureName) { + std::unique_lock lock(m_mutex); + return jus::FileServer(); } /* - // Return a File Data (might be a picture .tiff/.png/.jpg) - jus::File getAlbumPicture(const std::string& _pictureName) { - std::unique_lock lock(m_mutex); - return jus::File(); - } // Return a global UTC time jus::Time getAlbumPictureTime(const std::string& _pictureName) { std::unique_lock lock(m_mutex); @@ -94,6 +211,11 @@ namespace appl { std::unique_lock lock(m_mutex); return jus::Geo(); } + jus::FileId addElement(const jus::File& _file) { + std::unique_lock lock(m_mutex); + std::vector out; + return std::vector(); + } */ }; @@ -148,14 +270,14 @@ namespace appl { return m_user->getAlbumCount(_album); } // Return the list of the album files - std::vector getAlbumListPicture(std::string _album, uint32_t _startId, uint32_t _stopId) { - return m_user->getAlbumListPicture(_album, _startId, _stopId); + std::vector getAlbumListPicture(std::string _album) {//, uint32_t _startId, uint32_t _stopId) { + return m_user->getAlbumListPicture(_album);//, _startId, _stopId); } - /* // Return a File Data (might be a picture .tiff/.png/.jpg) - jus::File getAlbumPicture(std::string _pictureName) { + jus::FileServer getAlbumPicture(std::string _pictureName) { return m_user->getAlbumPicture(_pictureName); } + /* // Return a global UTC time jus::Time getAlbumPictureTime(std::string _pictureName) { return m_user->getAlbumPictureTime(_pictureName); @@ -209,6 +331,7 @@ int main(int _argc, const char *_argv[]) { serviceInterface.advertise("getSubAlbums", &appl::PictureService::getSubAlbums); serviceInterface.advertise("getAlbumCount", &appl::PictureService::getAlbumCount); serviceInterface.advertise("getAlbumListPicture", &appl::PictureService::getAlbumListPicture); + serviceInterface.advertise("getAlbumPicture", &appl::PictureService::getAlbumPicture); /* serviceInterface.advertise("getAlbumPicture", &appl::PictureService::getAlbumPicture); serviceInterface.advertise("getAlbumPictureTime", &appl::PictureService::getAlbumPictureTime);