From 06af73bc90feab48733da2f455f382bc2575114b Mon Sep 17 00:00:00 2001 From: jason416 Date: Fri, 24 May 2019 11:37:39 +0800 Subject: [PATCH 1/4] use timed_mutex. for all communication ordered, a simple mutex works fine --- src/client/binary_client.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/client/binary_client.cpp b/src/client/binary_client.cpp index 274f632b..8f1a7d46 100644 --- a/src/client/binary_client.cpp +++ b/src/client/binary_client.cpp @@ -83,8 +83,12 @@ class RequestCallback public: RequestCallback(const Common::Logger::SharedPtr & logger) : Logger(logger) - , lock(m) { + m.lock(); + } + ~RequestCallback() + { + m.unlock(); } void OnData(std::vector data, ResponseHeader h) @@ -92,12 +96,12 @@ class RequestCallback //std::cout << ToHexDump(data); Data = std::move(data); this->header = std::move(h); - doneEvent.notify_all(); + m.unlock(); } T WaitForData(std::chrono::milliseconds msec) { - if (doneEvent.wait_for(lock, msec) == std::cv_status::timeout) + if (m.try_lock_for(msec) == false) { throw std::runtime_error("Response timed out"); } T result; @@ -122,9 +126,7 @@ class RequestCallback Common::Logger::SharedPtr Logger; std::vector Data; ResponseHeader header; - std::mutex m; - std::unique_lock lock; - std::condition_variable doneEvent; + std::timed_mutex m; }; class CallbackThread @@ -883,6 +885,7 @@ class BinaryClient catch (std::exception & ex) { //Remove the callback on timeout + LOG_DEBUG(Logger, "binary_client | send exception: handle: {}", request.Header.RequestHandle); std::unique_lock lock(Mutex); Callbacks.erase(request.Header.RequestHandle); lock.unlock(); From 5795406a63206f63c2720de686452e80a62c59cc Mon Sep 17 00:00:00 2001 From: jason416 Date: Thu, 25 Jul 2019 16:05:44 +0800 Subject: [PATCH 2/4] catch SIGPIPE durring Disconnect(), add more type in ToVariant2() --- include/opc/ua/client/client.h | 4 ++ python/src/py_opcua_variant.h | 72 ++++++++++++++++++++++++++++------ src/client/client.cpp | 52 ++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 12 deletions(-) diff --git a/include/opc/ua/client/client.h b/include/opc/ua/client/client.h index f891a0e4..bc8e21fa 100644 --- a/include/opc/ua/client/client.h +++ b/include/opc/ua/client/client.h @@ -99,6 +99,10 @@ class UaClient // close communication with OPC-UA server, close all threads and subscriptions void Disconnect(); + /// @brief Deal with Disconnection problem when TCP LINK DOWN. + static void test_handler(int sign); + static Common::Logger::SharedPtr Logger_bak; + /// @brief Abort server connection // abort communication with OPC-UA server, close all threads and subcsriptions // Like Disconnect() but without CloseSession() call, which is not possible on faulty connection anyway diff --git a/python/src/py_opcua_variant.h b/python/src/py_opcua_variant.h index 0b8b4b51..5c7f8892 100644 --- a/python/src/py_opcua_variant.h +++ b/python/src/py_opcua_variant.h @@ -182,19 +182,42 @@ Variant ToVariant2(const object & obj, VariantType vtype) { case VariantType::BOOLEAN: var = ToVector(obj); - return var; - + break; + + case VariantType::SBYTE: + var = ToVector(obj); + break; + case VariantType::INT16: + var = ToVector(obj); + break; + case VariantType::INT32: + var = ToVector(obj); + break; + case VariantType::INT64: + var = ToVector(obj); + break; + + case VariantType::BYTE: + var = ToVector(obj); + break; case VariantType::UINT16: + var = ToVector(obj); + break; case VariantType::UINT32: var = ToVector(obj); - return var; - + break; + case VariantType::UINT64: + var = ToVector(obj); + break; case VariantType::FLOAT: var = ToVector(obj); - return var; + break; + case VariantType::DOUBLE: + var = ToVector(obj); + break; default: - return ToVariant(obj); + var = ToVariant(obj); } } } @@ -205,21 +228,46 @@ Variant ToVariant2(const object & obj, VariantType vtype) { case VariantType::BOOLEAN: var = extract(obj)(); - return var; - + break; + + case VariantType::SBYTE: + var = extract(obj)(); + break; + case VariantType::INT16: + var = extract(obj)(); + break; + case VariantType::INT32: + var = extract(obj)(); + break; + case VariantType::INT64: + var = extract(obj)(); + break; + + case VariantType::BYTE: + var = extract(obj)(); + break; case VariantType::UINT16: + var = extract(obj)(); + break; case VariantType::UINT32: var = extract(obj)(); - return var; - + break; + case VariantType::UINT64: + var = extract(obj)(); + break; case VariantType::FLOAT: var = extract(obj)(); - return var; + break; + case VariantType::DOUBLE: + var = extract(obj)(); + break; default: - return ToVariant(obj); + var = ToVariant(obj); } } + + return var; } diff --git a/src/client/client.cpp b/src/client/client.cpp index 0c82ddc1..8a5683bb 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -338,12 +338,26 @@ UaClient::~UaClient() Disconnect();//Do not leave any thread or connection running } +/* + * Deal with SIGPIPE(broken pipe) when call Disconnect(with TCP LINK DOWN). + */ +#include +void UaClient::test_handler(int sign) +{ + LOG_INFO(Logger_bak, "ua_client | triggered SIGPIPE signal"); +} + +/* Save Logger */ +Common::Logger::SharedPtr UaClient::Logger_bak = 0; + void UaClient::Disconnect() { KeepAlive.Stop(); + Logger_bak = Logger; // for static member test_handler use if (Server.get()) { +#if 0 CloseSessionResponse response = Server->CloseSession(); LOG_INFO(Logger, "ua_client | CloseSession response is {}", ToString(response.Header.ServiceResult)); @@ -351,9 +365,47 @@ void UaClient::Disconnect() CloseSecureChannel(); Server.reset(); } +#endif + /* + * Catch exception which raised by SIGPIPE durring CloseSession(which call tcp_send). + */ + try + { + if(signal(SIGPIPE, test_handler) == SIG_ERR) + throw std::runtime_error("bind signal handler error!"); + + CloseSessionResponse response = Server->CloseSession(); + + LOG_INFO(Logger, "ua_client | CloseSession response is {}", ToString(response.Header.ServiceResult)); + + CloseSecureChannel(); + Server.reset(); + } + catch(std::exception &ex) + { + std::string temp_str(ex.what()); + + // if exception code is SIGPIPE, clean resources + if(temp_str.find(" Broken pipe") != std::string::npos) + { + Server.reset(); + } + else // if not, throw to higher level + { + if(signal(SIGPIPE, SIG_DFL) == SIG_ERR) // recovery default signal handle + throw std::runtime_error("bind default signal handler error!"); + throw; + } + } + } + if(signal(SIGPIPE, SIG_DFL) == SIG_ERR) // recovery default signal handle + throw std::runtime_error("bind default signal handler error!"); + + LOG_INFO(Logger, "ua_client | Disconnectd"); } + void UaClient::Abort() { KeepAlive.Stop(); From 0dd2885d3d5ef8ac7f0814fb03eb826f86e0e6c2 Mon Sep 17 00:00:00 2001 From: jason416 Date: Fri, 26 Jul 2019 17:32:05 +0800 Subject: [PATCH 3/4] fix add_variable & add_property error, the same reason as last patch(python data -> Variant) --- python/src/py_opcua_module.cpp | 44 +++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/python/src/py_opcua_module.cpp b/python/src/py_opcua_module.cpp index 5b13f4ed..6a83512d 100644 --- a/python/src/py_opcua_module.cpp +++ b/python/src/py_opcua_module.cpp @@ -238,6 +238,24 @@ static void DataValue_set_server_picoseconds(DataValue & self, uint16_t ps) static void Node_SetValue(Node & self, const object & obj, VariantType vtype) { self.SetValue(ToVariant2(obj, vtype)); } +static Node Node_AddVariable(Node & self, const NodeId & variableId, const QualifiedName & browsename, const object & obj, VariantType vtype) +{ return self.AddVariable(variableId, browsename, ToVariant2(obj, vtype)); } + +static Node Node_AddVariable(Node & self, const std::string & nodeId, const std::string & browseName, const object & obj, VariantType vtype) +{ return self.AddVariable(nodeId, browseName, ToVariant2(obj, vtype)); } + +static Node Node_AddVariable(Node & self, uint32_t namespaceidx, const std::string & BrowseName, const object & obj, VariantType vtype) +{ return self.AddVariable(namespaceidx, BrowseName, ToVariant2(obj, vtype)); } + +static Node Node_AddProperty(Node & self, const NodeId & propertyId, const QualifiedName & browsename, const object & obj, VariantType vtype) +{ return self.AddProperty(propertyId, browsename, ToVariant2(obj, vtype)); } + +static Node Node_AddProperty(Node & self, const std::string & nodeid, const std::string & browseName, const object & obj, VariantType vtype) +{ return self.AddProperty(nodeid, browseName, ToVariant2(obj, vtype)); } + +static Node Node_AddProperty(Node & self, uint32_t namespaceidx, const std::string & browseName, const object & obj, VariantType vtype) +{ return self.AddProperty(namespaceidx, browseName, ToVariant2(obj, vtype)); } + //-------------------------------------------------------------------------- // UaClient helpers //-------------------------------------------------------------------------- @@ -439,8 +457,8 @@ BOOST_PYTHON_MODULE(opcua) .def("get_attribute", &Node::GetAttribute) .def("set_attribute", &Node::SetAttribute) .def("get_value", &Node::GetValue) - .def("set_value", (void(Node::*)(const DataValue &) const) &Node::SetValue) - .def("set_value", (void(Node::*)(const Variant &) const) &Node::SetValue) + //.def("set_value", (void(Node::*)(const DataValue &) const) &Node::SetValue) + //.def("set_value", (void(Node::*)(const Variant &) const) &Node::SetValue) .def("set_value", &Node_SetValue) .def("get_properties", &Node::GetProperties) .def("get_variables", &Node::GetVariables) @@ -454,12 +472,22 @@ BOOST_PYTHON_MODULE(opcua) .def("add_object", (Node(Node::*)(const NodeId &, const QualifiedName &) const) &Node::AddObject) .def("add_object", (Node(Node::*)(const std::string &, const std::string &) const) &Node::AddObject) .def("add_object", (Node(Node::*)(uint32_t, const std::string &) const) &Node::AddObject) - .def("add_variable", (Node(Node::*)(const NodeId &, const QualifiedName &, const Variant &) const) &Node::AddVariable) - .def("add_variable", (Node(Node::*)(const std::string &, const std::string &, const Variant &) const) &Node::AddVariable) - .def("add_variable", (Node(Node::*)(uint32_t, const std::string &, const Variant &) const) &Node::AddVariable) - .def("add_property", (Node(Node::*)(const NodeId &, const QualifiedName &, const Variant &) const) &Node::AddProperty) - .def("add_property", (Node(Node::*)(const std::string &, const std::string &, const Variant &) const) &Node::AddProperty) - .def("add_property", (Node(Node::*)(uint32_t, const std::string &, const Variant &) const) &Node::AddProperty) + //.def("add_variable", (Node(Node::*)(const NodeId &, const QualifiedName &, const Variant &) const) &Node::AddVariable) + //.def("add_variable", (Node(Node::*)(const std::string &, const std::string &, const Variant &) const) &Node::AddVariable) + //.def("add_variable", (Node(Node::*)(uint32_t, const std::string &, const Variant &) const) &Node::AddVariable) + //.def("add_property", (Node(Node::*)(const NodeId &, const QualifiedName &, const Variant &) const) &Node::AddProperty) + //.def("add_property", (Node(Node::*)(const std::string &, const std::string &, const Variant &) const) &Node::AddProperty) + //.def("add_property", (Node(Node::*)(uint32_t, const std::string &, const Variant &) const) &Node::AddProperty) + + /* + * Must GIVE data type, for python is type-less + */ + .def("add_variable", (Node (*)(Node &, const NodeId &, const QualifiedName &, const object &, VariantType)) &Node_AddVariable) + .def("add_variable", (Node (*)(Node &, uint32_t, const std::string &, const object &, VariantType)) &Node_AddVariable) + .def("add_variable", (Node (*)(Node &, const std::string &, const std::string &, const object &, VariantType)) &Node_AddVariable) + .def("add_property", (Node(*)(Node &, const NodeId &, const QualifiedName &, const object &, VariantType)) &Node_AddProperty) + .def("add_property", (Node(*)(Node &, const std::string &, const std::string &, const object &, VariantType)) &Node_AddProperty) + .def("add_property", (Node(*)(Node &, uint32_t, const std::string &, const object &, VariantType)) &Node_AddProperty) .def(str(self)) .def(repr(self)) .def(self == self) From bac2cfe961b10a93c160f4f9221fca9e6596103f Mon Sep 17 00:00:00 2001 From: jason416 Date: Fri, 23 Aug 2019 15:25:32 +0800 Subject: [PATCH 4/4] fix std::logic_err when client receive one request two times; add srcipt --- my_configure.sh | 17 +++++++++++++++++ python/examples/server.py | 3 ++- python/my_build.sh | 22 ++++++++++++++++++++++ src/client/binary_client.cpp | 6 ++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100755 my_configure.sh create mode 100755 python/my_build.sh diff --git a/my_configure.sh b/my_configure.sh new file mode 100755 index 00000000..c26dafd5 --- /dev/null +++ b/my_configure.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +export PATH=$PATH:/opt/buildroot-2017.02.5/output/host/usr/bin + + +./configure CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ --host=arm-linux CPPFLAGS="-I/home/jason/lib/arm-python/include -I/home/jason/lib/libxml2/build/include/libxml2 -I/home/jason/lib/libxml2/build/include -I/home/jason/lib/boost" LDFLAGS="-L/home/jason/lib/arm-python/lib -L/home/jason/lib/libxml2/build/lib -L/home/jason/lib/boost/stage/lib" --enable-static=no --prefix=/home/jason/workspace/freeopcua/output + +# After configure, DO EDIT Makefile +# 1. change LIBS +# 2. change /usr/include/libxmle to you libxml2 include path, use: %s/\/usr.... +# 3. then do make + + +# Build python ext: +# 1. export CC=arm-linux-gnueabihf-gcc +# 2. export LDSHARED="arm-linux-gnueabihf-gcc -shared" +# 3. python2 setup.py build_ext --include-dirs="/home/jason/lib/boost:/home/jason/lib/arm-python/include/python2.7:/home/jason/lib/libxml2/build/include/libxml2:/home/jason/lib/libxml2/build/include" --library-dirs="/home/jason/lib/arm-python/lib:/home/jason/lib/libxml2/build/lib:/home/jason/lib/boost/stage/lib" --libraries=boost_atomic:boost_python:python2.7 diff --git a/python/examples/server.py b/python/examples/server.py index ffc521ea..3e4ac80d 100644 --- a/python/examples/server.py +++ b/python/examples/server.py @@ -3,7 +3,7 @@ import sys sys.path.append(".") -from IPython import embed +# from IPython import embed import opcua class SubHandler(opcua.SubscriptionHandler): @@ -19,6 +19,7 @@ def event(self, handle, event): server = opcua.Server(True) server.set_endpoint("opc.tcp://localhost:4841/freeopcua/server/") server.set_server_name("FreeOpcUa Example Server") + server.set_uri("urn://exampleserver.freeopcua.github.io") # start the server server.start() diff --git a/python/my_build.sh b/python/my_build.sh new file mode 100755 index 00000000..4866a7ad --- /dev/null +++ b/python/my_build.sh @@ -0,0 +1,22 @@ +#!/bin/bash +python2 setup.py clean +rm -rf build +export PATH=$PATH:/opt/buildroot-2017.02.5/output/host/usr/bin + +export CC=arm-linux-gnueabihf-gcc +export LDSHARED="arm-linux-gnueabihf-gcc -shared" + +sudo cp /usr/bin/g++ /usr/bin/g++.bak +sudo ln -sf /opt/buildroot-2017.02.5/output/host/usr/bin/arm-linux-gnueabihf-g++ /etc/alternatives/c++ +#sudo ln -sf /opt/buildroot-2017.02.5/output/host/usr/bin/arm-linux-gnueabihf-g++ /usr/bin/g++ + +python2 setup.py build_ext --include-dirs="/home/jason/lib/boost:/home/jason/lib/arm-python/include/python2.7:/home/jason/lib/libxml2/build/include/libxml2:/home/jason/lib/libxml2/build/include" --library-dirs="/home/jason/lib/arm-python/lib:/home/jason/lib/libxml2/build/lib:/home/jason/lib/boost/stage/lib" --libraries="boost_atomic boost_python python2.7" + +echo "======================build done===========================" +sudo ln -sf /usr/bin/g++ /etc/alternatives/c++ +sudo cp /usr/bin/g++.bak /usr/bin/g++ +OBJ=build/lib.linux-x86_64-2.7/opcua.so +arm-linux-gnueabihf-strip $OBJ +echo "======================cp to tftpboot=======================" +cp $OBJ /tftpboot + diff --git a/src/client/binary_client.cpp b/src/client/binary_client.cpp index 329cff71..4fff3acb 100644 --- a/src/client/binary_client.cpp +++ b/src/client/binary_client.cpp @@ -983,6 +983,12 @@ class BinaryClient if (callbackIt == Callbacks.end()) { LOG_WARN(Logger, "binary_client | no callback found for message id: {}, handle: {}", id, header.RequestHandle); + /* 2019-8-22: Changed by jason416, Do clear buffer + * Once we recieve a request alreay handled, buffer must BE CLEANED, otherwise there will be two packet laies in buffer. + * As above, if two buffer are diffrent type request, it will cause logic_error excpetion. + * SO ADD DO CLEAR buffer when wrong packet receive. + */ + messageBuffer.clear(); return; }