diff --git a/.gitattributes b/.gitattributes index 895f7b97edae90dab83135bf125de428053be93a..13bce7cc5d0b344ad0dcab25285e6326b8149dad 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1730,6 +1730,7 @@ LCS/WinCCWrapper/include/CMakeLists.txt -text LCS/WinCCWrapper/include/WinCCManager.h -text LCS/WinCCWrapper/include/WinCCResources.h -text LCS/WinCCWrapper/include/WinCCWrapper.h -text +LCS/WinCCWrapper/include/exceptions.h -text LCS/WinCCWrapper/src/CMakeLists.txt -text LCS/WinCCWrapper/src/ConnectWaitForAnswer.cc -text LCS/WinCCWrapper/src/ConnectWaitForAnswer.h -text @@ -1738,8 +1739,10 @@ LCS/WinCCWrapper/src/WinCCResources.cc -text LCS/WinCCWrapper/src/WinCCWrapper.cc -text LCS/WinCCWrapper/src/WinCCWrapper_boost_python.cc -text LCS/WinCCWrapper/src/__init__.py -text +LCS/WinCCWrapper/src/exceptions.h -text LCS/WinCCWrapper/test/CMakeLists.txt -text LCS/WinCCWrapper/test/WinCCGet.cc -text +LCS/WinCCWrapper/test/WinCCQuery.cc -text LCS/WinCCWrapper/test/WinCCSet.cc -text LCS/WinCCWrapper/test/mock.py -text LCS/doc/package.dox -text diff --git a/LCS/WinCCWrapper/CMakeLists.txt b/LCS/WinCCWrapper/CMakeLists.txt index 84b66b8cfbc355c245611b34f932984b92be3958..d00636a4e7e1406b9101c7420197ba7662c25c4a 100644 --- a/LCS/WinCCWrapper/CMakeLists.txt +++ b/LCS/WinCCWrapper/CMakeLists.txt @@ -4,7 +4,7 @@ lofar_package(WinCCWrapper 1.0 ) include(LofarFindPackage) #hard-coded path where wincc api can be found on build systems -set(WINCC_ROOT_DIR /opt/WinCC_OA/3.15 CACHE PATH "root dir where the WinCC_OA api can be found") +#set(WINCC_ROOT_DIR /opt/WinCC_OA/3.15 CACHE PATH "root dir where the WinCC_OA api can be found") lofar_find_package(WINCC) IF(WINCC_FOUND) diff --git a/LCS/WinCCWrapper/include/CMakeLists.txt b/LCS/WinCCWrapper/include/CMakeLists.txt index 9341a3420be5b55ea0c821bc02652445231d2430..48366da002dc0da6ca064036ffaf96e0f46532a4 100644 --- a/LCS/WinCCWrapper/include/CMakeLists.txt +++ b/LCS/WinCCWrapper/include/CMakeLists.txt @@ -1,5 +1,5 @@ # Create symbolic link to include directory. -execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink +execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include/${PACKAGE_NAME}) @@ -8,4 +8,5 @@ install(FILES WinCCManager.h WinCCResources.h WinCCWrapper.h + exceptions.h DESTINATION include/${PACKAGE_NAME}) diff --git a/LCS/WinCCWrapper/include/WinCCManager.h b/LCS/WinCCWrapper/include/WinCCManager.h index 69e70cbb947cf731c2a3bbf24224aa4ed7c16b5c..c29d37cefaad6c4d20936064db3ae4c074cf68b4 100644 --- a/LCS/WinCCWrapper/include/WinCCManager.h +++ b/LCS/WinCCWrapper/include/WinCCManager.h @@ -31,6 +31,7 @@ #include <Variable.hxx> #include <boost/python.hpp> +#include <boost/any.hpp> namespace LOFAR { namespace WINCCWRAPPER { @@ -41,7 +42,6 @@ class WinCCManager : public Manager public: //! default constructor WinCCManager(); - //! run the manager, connect to the running wincc instance. void run(); //! exit the manager, disconnect from the running wincc instance. @@ -49,8 +49,15 @@ public: //! subscribe for changes on this list of datapoints. Whenever any of these datapoints changes value, the callback function from set_connect_datapoints_callback is called with the changed datapoint name and value. void connect_datapoints(const std::vector<std::string> &data_points); + //! subscribe for changes on this list of datapoints. Whenever any of these datapoints change all the values from these datapoints gets returned in one call. + void connect_datapoints_multiple(const std::vector<std::string> &data_points); + //! unsubscribe for changes on this list of datapoints. + void disconnect_datapoints(const std::vector<std::string> &data_points); + //! provide your callback function which is called whenever any of the connected datapoints is changed. + void set_connect_datapoints_callback(std::function<void(std::string name, std::string value)> callback_functor) {callback = callback_functor;}; //! provide your callback function which is called whenever any of the connected datapoints is changed. - void set_connect_datapoints_callback(std::function<void(std::string name, std::string value)> callback) {callback = callback;}; + void set_connect_datapoints_callback(std::function<void(std::map<std::string, std::string>)> callback_functor) {callback_multi = callback_functor;}; + //! set the datapoint with given name to the given value, returns true upon success. /*! set the datapoint with given name to the given value @@ -75,7 +82,10 @@ public: bool get_datapoint(const std::string &name, boost::python::list &value); //! get the datapoint with the given name and return it's std::vector<int> value in parameter value. returns true upon success. bool get_datapoint(const std::string &name, std::vector<int> &value); - + //! get the datapoint with a any type + bool get_datapoint_any(const std::string &datapoint_name, boost::any &value); + //! set the datapoint with a any type + bool set_datapoint_any(const std::string &datapoint_name, const boost::any &value); //! mark the datapoint with given name valid. returns true upon success. bool set_datapoint_valid(const std::string &name) { return set_datapoint_validity(name, true, nullptr); } //! mark the datapoint with given name invalid. returns true upon success. @@ -83,24 +93,32 @@ public: //! set the datapoint with given name valid/invalid. returns true upon success. bool set_datapoint_validity(const std::string &name, bool validity, const Variable *value=nullptr); + bool get_query(const std::string &query, std::vector<std::vector<std::string>> & result); //! handle signals to exit nicely virtual void signalHandler(int sig); + + void wait_for_event(long sec, long microSec); + private: void init(); friend class ConnectWaitForAnswer; void handle_hotlink(const std::string &name, const std::string &value); + void handle_hotlink(const std::map<std::string, std::string> &); + void handle_get(const std::string &name, Variable *&value); bool request_datapoint(const std::string &name); + bool request_query_result(const std::string &query, PVSSulong & identifier); template <typename Tval> bool _get_datapoint(const std::string &name, Tval &value); template <typename Tval> bool _get_datapoint(const std::string &name, Tval *value); + bool get_datapoint_variable(const std::string &name, Variable *&value); bool has_received_variable(const std::string &name); @@ -109,6 +127,7 @@ private: volatile static bool doExit; std::function<void(std::string name, std::string value)> callback; + std::function<void(std::map<std::string, std::string>)> callback_multi; std::mutex mtx; std::condition_variable cv; std::map<std::string, Variable *> values; diff --git a/LCS/WinCCWrapper/include/WinCCResources.h b/LCS/WinCCWrapper/include/WinCCResources.h index 552e9fcf771938a27fdad189e915440db5f91500..b387368eb42f7c379894621960b07de3db0ea905 100644 --- a/LCS/WinCCWrapper/include/WinCCResources.h +++ b/LCS/WinCCWrapper/include/WinCCResources.h @@ -27,9 +27,12 @@ namespace LOFAR { class WinCCResources { public: - WinCCResources(const std::string &projec_name); + WinCCResources(const std::string &project_name); + + WinCCResources(const std::string &program_name, const std::string &project_name, const int num); private: - void init(const std::string &projec_name); + void init(const std::string &project_name); + void init(const std::string &program_name, const std::string &project_name, const int num); }; } // namespace WINCCWRAPPER diff --git a/LCS/WinCCWrapper/include/WinCCWrapper.h b/LCS/WinCCWrapper/include/WinCCWrapper.h index 26fa060d6064ee4fcff3f620e44b86a90c52327a..4e57aa17166f24a9fc34c283d9dce20047f61ee1 100644 --- a/LCS/WinCCWrapper/include/WinCCWrapper.h +++ b/LCS/WinCCWrapper/include/WinCCWrapper.h @@ -21,6 +21,7 @@ #define WINCCSERVICES_WINCC_WRAPPER_H #include <string> +#include <boost/variant.hpp> #include <vector> #include <ctime> #include <functional> @@ -29,6 +30,7 @@ #include "WinCCManager.h" #include <boost/python.hpp> +#include <boost/any.hpp> namespace LOFAR { namespace WINCCWRAPPER { @@ -40,6 +42,9 @@ class WinCCWrapper public: //! default constructor WinCCWrapper(const std::string &project_name); + + //! default constructor + WinCCWrapper(const std::string &program_name, const std::string &project_name, const int num); //! disconnect from the running wincc instance. void exit(); //! connect to the running wincc instance. @@ -47,14 +52,21 @@ public: //! subscribe for changes on this list of datapoints. Whenever any of these datapoints changes value, the callback function from set_connect_datapoints_callback is called with the changed datapoint name and value. void connect_datapoints(const std::vector<std::string> &data_points); + //! subscribe for changes on this list of datapoints. Whenever any of these datapoints changes value, the callback function from set_connect_datapoints_callback is called with all the specified values. + void connect_datapoints_multi(const std::vector<std::string> &data_points); + + //! unsubscribe for changes on the given list of datapoints. + void disconnect_datapoints(const std::vector<std::string> &data_points); //! provide your callback function which is called whenever any of the connected datapoints is changed. void set_connect_datapoints_callback(std::function<void(std::string name, std::string value)> callback); + //! provide your callback function which is called whenever any of the connected datapoints is changed. It returns all the specified datapoints even if only one changes. + void set_connect_datapoints_callback(std::function<void(std::map<std::string, std::string>)> callback); //! set the datapoint with given name to the given int value, mark it valid/invalid, returns true upon success. bool set_datapoint(const std::string &name, int value, bool valid=true); //! set the datapoint with given name to the given boost::python::list value, mark it valid/invalid, returns true upon success. bool set_datapoint(const std::string &name, boost::python::list &value, bool valid=true); - //! set the datapoint with given name to the given boost::python::tuple value, mark it valid/invalid, returns true upon success. + //! set the datapoint with given name to the given boost::python::tuple value, mark it valid/invalid, returns true upon success. bool set_datapoint(const std::string &name, boost::python::tuple &value, bool valid=true); //! set the datapoint with given name to the given std::vector<int> value, mark it valid/invalid, returns true upon success. bool set_datapoint(const std::string &name, std::vector<int> &value, bool valid=true); @@ -70,11 +82,14 @@ public: //! set the datapoint with given name to the given time_t value, mark it valid/invalid, returns true upon success. bool set_datapoint_time(const std::string &name, time_t value, bool valid=true); + bool set_datapoint_any(const std::string &name, boost::any value); //! mark the datapoint with given name valid. bool set_datapoint_valid(const std::string &name); //! mark the datapoint with given name invalid. bool set_datapoint_invalid(const std::string &name); + //! provide the boost::any for the given datapoint name + boost::any get_datapoint_any (const std::string &name); //! get the datapoint with the given name and return it as an int value if possible, otherwise an exception is raised. int get_datapoint_int(const std::string &name); //! get the datapoint with the given name and return it as a long value if possible, otherwise an exception is raised. @@ -88,11 +103,17 @@ public: //! get the datapoint with the given name and return it as a time_t value if possible, otherwise an exception is raised. time_t get_datapoint_time(const std::string &name); //! get the datapoint with the given name and return it as a boost::python::list value if possible, otherwise an exception is raised. - boost::python::list get_datapoint_list(const std::string &name); + boost::python::list get_datapoint_list(const std::string &name); + //! get the datapoint last set time + time_t get_datapoint_set_time(const std::string &name); //! get the datapoint with the given name and return it as a std::vector<int> value if possible, otherwise an exception is raised. //! this method is used in the WinCCGet test std::vector<int> get_datapoint_vector(const std::string &name); + //! get the datapoint with the given name and return it as a std::vector<std::string> value. + std::vector<std::string> get_datapoint_vector_string(const std::string &name); + std::string get_formatted_datapoint(const std::string &name); + void wait_for_event(long sec, long microSec); private: // get_datapoint template <typename T> diff --git a/LCS/WinCCWrapper/include/exceptions.h b/LCS/WinCCWrapper/include/exceptions.h new file mode 100644 index 0000000000000000000000000000000000000000..998ab30809e845243ed043bdc8159bbb7cd7a5bd --- /dev/null +++ b/LCS/WinCCWrapper/include/exceptions.h @@ -0,0 +1,23 @@ +#ifndef WINCC_EXCEPTIONS +#define WINCC_EXCEPTIONS + +namespace LOFAR{ +namespace WINCCWRAPPER{ + +class DatapointNameNotFound : public std::exception +{ +private: + std::string message; +public: + DatapointNameNotFound(const std::string & datapointName): + message{"Datapoint " + datapointName + " not found"}{} + const char * what () const throw () + { + return message.c_str(); + } +}; + +} +} + +#endif diff --git a/LCS/WinCCWrapper/src/ConnectWaitForAnswer.cc b/LCS/WinCCWrapper/src/ConnectWaitForAnswer.cc index 767ea576d761ca1ee4e99202ba2f07af82c3ba11..b35c3cfe0396b49532f141ca1161d76e17e9a9aa 100644 --- a/LCS/WinCCWrapper/src/ConnectWaitForAnswer.cc +++ b/LCS/WinCCWrapper/src/ConnectWaitForAnswer.cc @@ -25,13 +25,22 @@ namespace LOFAR { using namespace std; +ConnectWaitForAnswer::ConnectWaitForAnswer(const bool handle_multi): HotLinkWaitForAnswer{}, multi{handle_multi}{ + +} + +ConnectWaitForAnswer::ConnectWaitForAnswer(): HotLinkWaitForAnswer{}{ + +} + void ConnectWaitForAnswer::hotLinkCallBack(DpMsgAnswer &answer) { + const std::string answer_id{std::to_string(answer.getAnswerId())}; for (AnswerGroup *grpPtr = answer.getFirstGroup(); grpPtr; grpPtr = answer.getNextGroup()) { if (grpPtr->getError()) { - cout << "Error!" << endl; + cout <<grpPtr->getError()->toString()<< endl; } else { @@ -42,6 +51,8 @@ void ConnectWaitForAnswer::hotLinkCallBack(DpMsgAnswer &answer) if(varPtr) { string name = get_datapoint_name(item); + if(name.compare("") == 0) name += answer_id; + Variable *value = varPtr->clone(); //WinCCManager should delete cloned pointer ((WinCCManager *) Manager::getManPtr())->handle_get(name, value); @@ -52,6 +63,14 @@ void ConnectWaitForAnswer::hotLinkCallBack(DpMsgAnswer &answer) } void ConnectWaitForAnswer::hotLinkCallBack(DpHLGroup &group) +{ + if(multi){ + handle_multi(group); + }else{ + handle_one_by_one(group); + } +} +void ConnectWaitForAnswer::handle_one_by_one(DpHLGroup &group) { for (DpVCItem *item = group.getFirstItem(); item; item = group.getNextItem()) { @@ -59,6 +78,25 @@ void ConnectWaitForAnswer::hotLinkCallBack(DpHLGroup &group) } } +void ConnectWaitForAnswer::handle_multi(DpHLGroup &group) +{ + + std::map<std::string, std::string> values; + for (DpVCItem *item = group.getFirstItem(); item; item = group.getNextItem()) + { + Variable *varPtr = item->getValuePtr(); + + if (varPtr){ + const string name = get_datapoint_name(item); + const string value = varPtr->formatValue().c_str(); + + values[name] = value; + } + } + ((WinCCManager *) Manager::getManPtr())->handle_hotlink(values); +} + + void ConnectWaitForAnswer::handle_group_item(const DpVCItem* const item) { Variable *varPtr = item->getValuePtr(); @@ -66,6 +104,7 @@ void ConnectWaitForAnswer::handle_group_item(const DpVCItem* const item) if (varPtr) { string name = get_datapoint_name(item); + string value = varPtr->formatValue().c_str(); ((WinCCManager *) Manager::getManPtr())->handle_hotlink(name, value); diff --git a/LCS/WinCCWrapper/src/ConnectWaitForAnswer.h b/LCS/WinCCWrapper/src/ConnectWaitForAnswer.h index 28dd3f0138a5c84e25374ade4ee2bea51ad9745a..dba485153cad7e3848ec381da58588b903639bb6 100644 --- a/LCS/WinCCWrapper/src/ConnectWaitForAnswer.h +++ b/LCS/WinCCWrapper/src/ConnectWaitForAnswer.h @@ -32,12 +32,18 @@ namespace LOFAR { class ConnectWaitForAnswer : public HotLinkWaitForAnswer { public: + ConnectWaitForAnswer(); + ConnectWaitForAnswer(const bool handle_multi); using HotLinkWaitForAnswer::hotLinkCallBack; virtual void hotLinkCallBack(DpMsgAnswer &answer); virtual void hotLinkCallBack(DpHLGroup &group); private: + bool multi; + void handle_one_by_one(DpHLGroup &group); + void handle_multi(DpHLGroup &group); void handle_group_item(const DpVCItem* const item); std::string get_datapoint_name(const DpVCItem* const item); + }; } // namespace WINCCWRAPPER diff --git a/LCS/WinCCWrapper/src/WinCCManager.cc b/LCS/WinCCWrapper/src/WinCCManager.cc index 6aa6c34b42ef58025ee2c2e37199c6820783e7f0..4dd1eaff04e8bfa83054f5a10332b49ab2f33a3d 100644 --- a/LCS/WinCCWrapper/src/WinCCManager.cc +++ b/LCS/WinCCWrapper/src/WinCCManager.cc @@ -34,10 +34,18 @@ #include <TextVar.hxx> #include <DynVar.hxx> #include <DynPtrArray.hxx> +#include <Resources.hxx> #include <cassert> #include <vector> #include <boost/python.hpp> +#include <Manager.hxx> +#include <DpElement.hxx> +#include <DpIdentification.hxx> +#include <DpIdentificationResultType.hxx> +#include <boost/any.hpp> +#include <exceptions.h> + namespace LOFAR { namespace WINCCWRAPPER { @@ -100,12 +108,77 @@ void WinCCManager::connect_datapoints(const std::vector<std::string> &dataPoints } } +void WinCCManager::connect_datapoints_multiple(const std::vector<std::string> &dataPoints) +{ + + DpIdentList dpList; + for(vector<string>::const_iterator it = dataPoints.begin(); it != dataPoints.end(); it++) + { + DpIdentifier dpIdConnect; + + if (Manager::getId(it->c_str(), dpIdConnect) == PVSS_FALSE) + { + // This name was unknown + ErrHdl::error(ErrClass::PRIO_SEVERE, + ErrClass::ERR_PARAM, + ErrClass::UNEXPECTEDSTATE, + "PublishManager", + "connect_datapoints", + CharString("Datapoint ") + CharString(it->c_str()) + + CharString(" missing")); + } + else + { + dpList.append(dpIdConnect); + + } + } + // We give the dpConnect a nice naked pointer because it will delete it when the manager stops. + HotLinkWaitForAnswer* wait = new ConnectWaitForAnswer(true); + Manager::dpConnect(dpList, wait); +} + + +void WinCCManager::disconnect_datapoints(const std::vector<std::string> &dataPoints) +{ + for(vector<string>::const_iterator it = dataPoints.begin(); it != dataPoints.end(); it++) + { + DpIdentifier dpIdConnect; + + if (Manager::getId(it->c_str(), dpIdConnect) == PVSS_FALSE) + { + // This name was unknown + ErrHdl::error(ErrClass::PRIO_SEVERE, + ErrClass::ERR_PARAM, + ErrClass::UNEXPECTEDSTATE, + "PublishManager", + "connect_datapoints", + CharString("Datapoint ") + CharString(it->c_str()) + + CharString(" missing")); + } + else + { + // We give the dpConnect a nice naked pointer because it will delete it when the manager stops. + HotLinkWaitForAnswer* wait = new ConnectWaitForAnswer(); + Manager::dpDisconnect(dpIdConnect, wait); + } + } +} + bool WinCCManager::set_datapoint(const std::string &name, const Variable &value, bool valid) { //reuse the set_datapoint_validity, and explicitly set the value to the given value return set_datapoint_validity(name, valid, &value); } + +// request the query (async). is called by _get_query which makes it blocking (synchronous) by waiting for the answer. +bool WinCCManager::request_query_result(const std::string &query, PVSSulong & identifier) +{ + HotLinkWaitForAnswer* wait = new ConnectWaitForAnswer(); + return (PVSS_TRUE == Manager::dpQuery(query.c_str(), identifier, wait)); +} + // request the datapoint (async). is called by _get_datapoint which makes it blocking (synchronous) by waiting for the answer. bool WinCCManager::request_datapoint(const std::string &name) { @@ -333,6 +406,72 @@ Variable::ConvertResult convert(Variable *var, struct tm &value, Variable *&conv return cr; } +boost::any convert_any(Variable * variable, boost::any & value){ + Variable * converted_variable{nullptr}; + switch(variable->isA()){ + case VariableType::INTEGER_VAR:{ + int casted_variable; + convert(variable, casted_variable, converted_variable); + value = casted_variable; + + break; + } + case VariableType::FLOAT_VAR: { + float casted_variable; + convert(variable, casted_variable, converted_variable); + value = casted_variable; + + break; + } + case VariableType::TEXT_VAR: { + std::string casted_variable; + convert(variable, casted_variable, converted_variable); + value = casted_variable; + + break; + } + case VariableType::LONG_VAR:{ + // Find the wincc guy who though this out + long casted_variable; + convert(variable, casted_variable, converted_variable); + value = casted_variable; + + break; + } + case VariableType::BIT_VAR: { + bool casted_variable; + convert(variable, casted_variable, converted_variable); + value = casted_variable; + + break; + } + case VariableType::TIME_VAR: { + time_t casted_variable; + convert(variable, casted_variable, converted_variable); + value = casted_variable; + + break; + } + case VariableType::ANYTYPE_VAR: { + const AnyTypeVar * converted_var{static_cast<AnyTypeVar*>(variable)}; + convert_any(converted_var->getVar(), value); + break; + } + case VariableType::DPIDENTIFIER_VAR: { + value = std::string{variable->formatValue().c_str()}; + break; + } + default: + CharString value_str = variable->formatValue(); + std::cerr<<value_str; + std::cerr<<"Datapoint type still not supported: "<<std::hex<<variable->isA()<<std::endl; + value = std::string{value_str.c_str()}; + } + + if(converted_variable != nullptr) delete converted_variable; + return true; +} + //internal generic method to get the typed (Tval) value of a datapoint //used by the public strictly typed methods template <typename Tval> @@ -355,6 +494,42 @@ bool WinCCManager::_get_datapoint(const std::string &name, Tval &value) return false; } +bool convert_to_vector2(Variable * variable, std::vector<std::vector<std::string>> & result){ + if(variable->isA(VariableType::DYN_VAR)){ + const DynVar * rows{static_cast<DynVar *>(variable)}; + for(DynPtrArrayIndex i=0; i < rows->getNumberOfItems(); i++){ + const DynVar * row{static_cast<DynVar *>(rows->getAt(i))}; + std::vector<std::string> * current_row{new std::vector<std::string>{}}; + Variable * column{static_cast<DynVar *>(row->getNext())}; + while(column){ + current_row->push_back(column->formatValue().c_str()); + column = static_cast<DynVar *>(row->getNext()); + } + result.push_back(*current_row); + row = static_cast<DynVar *>(rows->getNext()); + } + return true; + } + return false; +} + +//internal generic method to query a set of WinCC data points. Since the type is not previously know +bool WinCCManager::get_query(const std::string &query, std::vector<std::vector<std::string>> & result){ + Variable * variable_value = nullptr; + PVSSulong identifier; + if(request_query_result(query, identifier)) + { + std::string identifier_str{std::to_string(identifier)}; + if(wait_for_received_variable(identifier_str, 1000)) { + if(get_received_variable(identifier_str, variable_value)) { + convert_to_vector2(variable_value, result); + return true; + } + } + } + + return false; +} //below, a few strictly type methods for get_datapoint are defined //they just call the templated _get_datapoint, so why not just use the one and only templated method? @@ -401,10 +576,11 @@ bool WinCCManager::get_datapoint(const std::string &name, std::vector<int> &valu return _get_datapoint(name, value); } + bool WinCCManager::set_datapoint_validity(const std::string &name, bool validity, const Variable *value) { DpIdentifier dpId; - + if (Manager::getId(name.c_str(), dpId) == PVSS_FALSE) { // This name was unknown. @@ -434,8 +610,48 @@ bool WinCCManager::set_datapoint_validity(const std::string &name, bool validity infoBits.set(DriverBits::DRV_VALID_INVALID); } - DrvManager *drvman = DrvManager::getSelfPtr(); - drvman ->sendVCMsg(dpId, value!=NULL ? *value : *last_known_value, now, infoBits); + // get VariableType from DpIdentifier + // assign values + VariableType dpId_VariableType; + DpElementType dpId_ElementType; + + DpIdentification temp_dpId; // make a temp dpId to use the 'getElementType' method + DpIdentificationResult dir = temp_dpId.getElementType(dpId, dpId_ElementType); + + // check that the dpIdResult is OK + if(DpIdentificationResult::DpIdentOK == dir) { + dpId_VariableType = DpElement::getVariableType(dpId_ElementType); + } + else { + return false; + } + + // check that the VariableType of the datapoint identifier matches the VariableType of the value given. + // if not, convert value to the appropriate datapoint type + if (value->isA() != dpId_VariableType){ + + VariablePtr converted_var = NULL; + Variable::ConvertResult cr = value->convert(dpId_VariableType, converted_var); + + // if conversion succeeds, send the converted var + if(Variable::ConvertResult::OK == cr) { + DrvManager *drvman = DrvManager::getSelfPtr(); + drvman ->sendVCMsg(dpId, converted_var!=NULL ? *converted_var : *last_known_value, now, infoBits); + delete converted_var; //delete it, since it was a newly created variable in var->convert + } + + // Either 'out_of_bounds' error or 'conv_not_defined' error in converting + else { + delete converted_var; //delete it, since it was a newly created variable in var->convert + return false; + } + } + + // no need for conversion + else { + DrvManager *drvman = DrvManager::getSelfPtr(); + drvman ->sendVCMsg(dpId, value!=NULL ? *value : *last_known_value, now, infoBits); + } //process, wait max 10 ms long sec=0, usec=10000; @@ -472,6 +688,10 @@ void WinCCManager::run() } } +void WinCCManager::wait_for_event(long sec, long microSec){ + dispatch(sec, microSec); +} + void WinCCManager::exit() { Manager::exit(0); @@ -485,6 +705,14 @@ void WinCCManager::handle_hotlink(const std::string &name, const std::string &va } } +void WinCCManager::handle_hotlink(const std::map<std::string, std::string> & values) +{ + if(callback_multi) + { + callback_multi(values); + } +} + void WinCCManager::signalHandler(int sig) { if ((sig == SIGINT) || (sig == SIGTERM)) @@ -497,6 +725,81 @@ void WinCCManager::signalHandler(int sig) } } +bool WinCCManager::set_datapoint_any(const std::string &datapoint_name, const boost::any &value){ + Variable * variable{nullptr}; + + if(get_datapoint_variable(datapoint_name, variable)){ + switch(variable->isA()){ + case VariableType::INTEGER_VAR: { + IntegerVar converted_value{boost::any_cast<int>(value)}; + set_datapoint(datapoint_name, converted_value); + break; + } + case VariableType::FLOAT_VAR: { + FloatVar converted_value{boost::any_cast<float>(value)}; + set_datapoint(datapoint_name, converted_value); + break; + } + case VariableType::TEXT_VAR: { + TextVar converted_value{CharString(boost::any_cast<std::string>(value).c_str())}; + set_datapoint(datapoint_name, converted_value); + break; + } + case VariableType::LONG_VAR:{ + LongVar converted_value{boost::any_cast<long>(value)}; + set_datapoint(datapoint_name, converted_value); + + break; + } + case VariableType::BIT_VAR: { + BitVar converted_value{boost::any_cast<bool>(value)}; + set_datapoint(datapoint_name, converted_value); + + break; + } + case VariableType::TIME_VAR: { + TimeVar converted_value(0,0); + converted_value.setSeconds(boost::any_cast<time_t>(value)); + set_datapoint(datapoint_name, converted_value); + + break; + } + default: + std::cerr<<"Datapoint type still not supported: "<<std::hex<<variable->isA()<<std::endl; + return false; + } + }else{ + return false; + } + + return true; +} + +bool WinCCManager::get_datapoint_any(const std::string &datapoint_name, boost::any & value){ + // Yeah I know... WinCC API is getting a &* to allocate the memory space... + Variable * variable{nullptr}; + bool returnValue{true}; + + if(!get_datapoint_variable(datapoint_name, variable)){ + // This name was unknown. + ErrHdl::error(ErrClass::PRIO_SEVERE, + ErrClass::ERR_PARAM, + ErrClass::UNEXPECTEDSTATE, + Resources::getProgName(), + "get_datapoint", + CharString("Datapoint ") + CharString(datapoint_name.c_str()) + + CharString(" missing")); + throw DatapointNameNotFound{datapoint_name}; + } + + convert_any(variable, value); + + if(variable != nullptr) delete variable; + + return returnValue; + +} + } // namespace WINCCWRAPPER } // namespace LOFAR diff --git a/LCS/WinCCWrapper/src/WinCCResources.cc b/LCS/WinCCWrapper/src/WinCCResources.cc index 5eb7dbf8d1e3935abb068f6aa843514ba7b1c84a..a35accb448a859744f62a74adeeb8755ad2bc5f7 100644 --- a/LCS/WinCCWrapper/src/WinCCResources.cc +++ b/LCS/WinCCWrapper/src/WinCCResources.cc @@ -31,11 +31,62 @@ WinCCResources::WinCCResources(const std::string &project_name) init(project_name); } -void WinCCResources::init(const std::string & /*project_name*/) +WinCCResources::WinCCResources(const::std::string &program_name, const std::string &project_name, const int num) { - // TODO set specific project instead of current project. - char* ownArgv[] = {(char *)"WinCCWrapper", (char *)"-currentproj", (char *)"-log", (char *)"+stderr"}; - int ownArgc = sizeof ownArgv / sizeof ownArgv[0]; + init(program_name, project_name, num); +} + + +void WinCCResources::init(const::std::string &program_name, const std::string & project_name, const int num) +{ + std::vector<std::string> args{program_name}; + + if(project_name.compare("-currentproj") == 0){ + args.push_back("-currentproj"); + }else{ + args.push_back("-proj"); + args.push_back(project_name); + } + args.push_back("-log"); + args.push_back("+stderr"); + + args.push_back("-num"); + args.push_back(std::to_string(num)); + + + int ownArgc = args.size(); + + char * ownArgv[ownArgc]; + + for(int i=0; i<ownArgc; i++){ + ownArgv[i] = const_cast<char *>(args.at(i).c_str()); + } + + Resources::init(ownArgc, ownArgv); +} + + + +void WinCCResources::init(const std::string & project_name) +{ + std::vector<std::string> args{"WinCCWrapper"}; + + if(project_name.compare("-currentproj") == 0){ + args.push_back("-currentproj"); + }else{ + args.push_back("-proj"); + args.push_back(project_name); + } + args.push_back("-log"); + args.push_back("+stderr"); + + int ownArgc = args.size(); + + char * ownArgv[ownArgc]; + + for(int i=0; i<ownArgc; i++){ + ownArgv[i] = const_cast<char *>(args.at(i).c_str()); + } Resources::init(ownArgc, ownArgv); } diff --git a/LCS/WinCCWrapper/src/WinCCWrapper.cc b/LCS/WinCCWrapper/src/WinCCWrapper.cc index ab6191dc8d62d0f793b0c91541d59e3e2ecd4986..d8e129f59b8b81ac596a8ae80b6e7c423bd58c0f 100644 --- a/LCS/WinCCWrapper/src/WinCCWrapper.cc +++ b/LCS/WinCCWrapper/src/WinCCWrapper.cc @@ -43,6 +43,9 @@ using namespace std; //! Each datapoint has a human readable name in the wincc database, but the actual value is stored in a sub-item. Append that to each set/get datapoint name. static const string DP_SUFFIX = ":_original.._value"; +//! This property refers to the last set time +static const string DP_SUFFIX_STIME = ":_original.._stime"; + WinCCWrapper::WinCCWrapper(const std::string &project_name) : resources(project_name) @@ -50,6 +53,12 @@ WinCCWrapper::WinCCWrapper(const std::string &project_name) : manager = new WinCCManager(); } +WinCCWrapper::WinCCWrapper(const std::string &program_name, const std::string &project_name, const int num) : + resources(program_name, project_name, num) +{ + manager = new WinCCManager(); +} + void WinCCWrapper::run() { manager->run(); @@ -65,11 +74,31 @@ void WinCCWrapper::connect_datapoints(const std::vector<std::string> &data_point manager->connect_datapoints(data_points); } +void WinCCWrapper::connect_datapoints_multi(const std::vector<std::string> &data_points) +{ + manager->connect_datapoints_multiple(data_points); +} + +void WinCCWrapper::disconnect_datapoints(const std::vector<std::string> &data_points) +{ + manager->connect_datapoints(data_points); +} + + +void WinCCWrapper::wait_for_event(long sec, long microSec){ + manager->wait_for_event(sec, microSec); +} + void WinCCWrapper::set_connect_datapoints_callback(std::function<void(std::string name, std::string value)> callback) { manager->set_connect_datapoints_callback(callback); } +void WinCCWrapper::set_connect_datapoints_callback(std::function<void(std::map<std::string, std::string>)> callback) +{ + manager->set_connect_datapoints_callback(callback); +} + // set_datapoint bool WinCCWrapper::set_datapoint(const std::string &name, int value, bool valid) { @@ -99,7 +128,7 @@ bool WinCCWrapper::set_datapoint(const std::string &name, boost::python::tuple & bool WinCCWrapper::set_datapoint(const std::string &name, std::vector<int> &value, bool valid) { DynVar variable(VariableType::INTEGER_VAR); - + for(auto iter = value.cbegin(); iter != value.cend(); iter++) { IntegerVar elem{*iter}; variable.append(elem);} @@ -142,6 +171,12 @@ bool WinCCWrapper::set_datapoint_time(const std::string &name, time_t value, boo return manager->set_datapoint(name + DP_SUFFIX, variable, valid); } +boost::any WinCCWrapper::get_datapoint_any (const std::string &name){ + boost::any datapoint_value; + manager->get_datapoint_any(name + DP_SUFFIX, datapoint_value); + return datapoint_value; +} + // get_datapoint template <typename T> bool WinCCWrapper::get_datapoint(const std::string &name, T &value) @@ -213,6 +248,21 @@ time_t WinCCWrapper::get_datapoint_time(const std::string &name) throw std::runtime_error("Could not get datapoint"); } +std::string WinCCWrapper::get_formatted_datapoint(const std::string &name){ + std::string value; + if(manager->get_datapoint(name, value)){ + return value; + } + throw std::runtime_error("Could not get datapoint"); +} + +time_t WinCCWrapper::get_datapoint_set_time(const std::string &name){ + struct tm value; + if(manager->get_datapoint(name + DP_SUFFIX_STIME, value)) + return mktime(&value); + throw std::runtime_error("Could not get datapoint"); +} + bool WinCCWrapper::set_datapoint_valid(const std::string &name) { return manager->set_datapoint_valid(name + DP_SUFFIX); @@ -223,5 +273,8 @@ bool WinCCWrapper::set_datapoint_invalid(const std::string &name) return manager->set_datapoint_invalid(name + DP_SUFFIX); } +bool WinCCWrapper::set_datapoint_any(const std::string &name, boost::any value){ + return manager->set_datapoint_any(name + DP_SUFFIX, value); +} } // namespace WINCCWRAPPER } // namespace LOFAR diff --git a/LCS/WinCCWrapper/src/exceptions.h b/LCS/WinCCWrapper/src/exceptions.h new file mode 100644 index 0000000000000000000000000000000000000000..cf0da769433fabc453caea25a37d76f885ff211f --- /dev/null +++ b/LCS/WinCCWrapper/src/exceptions.h @@ -0,0 +1,12 @@ +class DatapointNameNotFound : public std::exception +{ +private: + std::string message; +public: + DatapointNameNotFound(const std::string & datapointName): + message{"Datapoint " + datapointName + " not found"}{} + const char * what () const throw () + { + return message.c_str(); + } +} diff --git a/LCS/WinCCWrapper/test/CMakeLists.txt b/LCS/WinCCWrapper/test/CMakeLists.txt index 93e3e67b7f1dd434c11c41fd399d18972c55937f..08c5036d1acb50498adbf4f295eefeb1bc8b999c 100644 --- a/LCS/WinCCWrapper/test/CMakeLists.txt +++ b/LCS/WinCCWrapper/test/CMakeLists.txt @@ -8,6 +8,15 @@ IF(BUILD_TESTING) lofar_add_bin_program(WinCCSet WinCCSet.cc) lofar_add_bin_program(WinCCGet WinCCGet.cc) + lofar_add_bin_program(WinCCQuery WinCCQuery.cc) + find_package(Boost COMPONENTS program_options system REQUIRED) + + target_link_libraries(WinCCQuery + wincc_wrapper + ${Boost_LIBRARIES} + + ) + ENDIF(WINCC_FOUND) ENDIF(BUILD_TESTING) diff --git a/LCS/WinCCWrapper/test/WinCCGet.cc b/LCS/WinCCWrapper/test/WinCCGet.cc index 5958d82e915ae681f7e11d206b062c0eebeb1983..aa10b76c422d4866cbec3a7e3bfa14e29dc745ae 100644 --- a/LCS/WinCCWrapper/test/WinCCGet.cc +++ b/LCS/WinCCWrapper/test/WinCCGet.cc @@ -3,28 +3,28 @@ #include <WinCCWrapper.h> #include <vector> #include <iostream> +#include <Resources.hxx> using namespace LOFAR::WINCCWRAPPER; using namespace std; void get_help(){ cout << "Usage:" << endl; - cout << "WinCCGet \"datapoint_name\" datapoint_type" << endl; + cout << "WinCCGet \"datapoint_name\" datapoint_type project_name" << endl; cout << "Accepted datapoint types:" << endl; cout << " int, float, string, list (for int lists)" << endl; } int main(int argc, char * argv[]) { - bool asking_for_help = ((argc == 2) && (string(argv[1]) == "--help" || string(argv[1]) == "--h")); - bool invalid_args = (argc != 3); + bool asking_for_help = ((argc < 4) && (string(argv[1]) == "--help" || string(argv[1]) == "--h")); + bool invalid_args = (argc != 4); if (asking_for_help || invalid_args){ get_help(); return 0; } - - WinCCWrapper wrapper{""}; + WinCCWrapper wrapper{std::string(argv[3])}; string dpname{argv[1]}; if (string(argv[2]) == "int") { diff --git a/LCS/WinCCWrapper/test/WinCCQuery.cc b/LCS/WinCCWrapper/test/WinCCQuery.cc new file mode 100644 index 0000000000000000000000000000000000000000..108c626d304c56e1cb22ecbd3075328732488cee --- /dev/null +++ b/LCS/WinCCWrapper/test/WinCCQuery.cc @@ -0,0 +1,68 @@ + + + +#include <cstdlib> +#include <string> +#include <WinCCManager.h> +#include <WinCCResources.h> + +#include <vector> +#include <iostream> +#include <Resources.hxx> + +#include <boost/program_options.hpp> +#include <boost/any.hpp> + + + +using namespace LOFAR::WINCCWRAPPER; +using namespace std; +namespace po = boost::program_options; + +void set_options(po::options_description & description){ + description.add_options() + ("help", "produce help message") + ("sql", po::value<std::string>(), "SQL_query") + ("project", po::value<std::string>(), "WinCC project"); + +} + +int main(int argc, char * argv[]) +{ + // Declare the supported options. + po::options_description desc("Query the WinCCDatabase"); + set_options(desc); + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + + if (vm.count("help")) { + cout << desc << "\n"; + return 1; + } + + if (vm.count("sql") == 1 && vm.count("project") == 1) { + + const string sql{vm["sql"].as<string>()}; + + WinCCResources resource{"WinCCQuery", vm["project"].as<string>(), 0}; + WinCCManager manager; + cout << "The SQL is: [" << sql << "].\n"; + + std::vector<std::vector<std::string>> queryResult; + manager.get_query(sql, queryResult); + std::cout<< "RESULTS ----"<<"\n\n"; + std::cout<< "datapoint" << "\t"; + for(auto & row : queryResult){ + for(std::string & column : row){ + std::cout<<column<<"\t"; + } + std::cout<<"\n"; + } + + } else { + cout << desc; + } +}