Merge changes I180c5c1e,Ica7669ea,I63235684
* changes:
Adds missing model type
Removes obsolete Type in OperationVariable
Fixes minor bugs
diff --git a/components/basys.components/basyx.components.docker/basyx.components.sqlregistry/src/test/java/org/eclipse/basyx/regression/registry/ITSQLRegistryRaw.java b/components/basys.components/basyx.components.docker/basyx.components.sqlregistry/src/test/java/org/eclipse/basyx/regression/registry/ITSQLRegistryRaw.java
index 53b9609..9f96f53 100644
--- a/components/basys.components/basyx.components.docker/basyx.components.sqlregistry/src/test/java/org/eclipse/basyx/regression/registry/ITSQLRegistryRaw.java
+++ b/components/basys.components/basyx.components.docker/basyx.components.sqlregistry/src/test/java/org/eclipse/basyx/regression/registry/ITSQLRegistryRaw.java
@@ -8,6 +8,8 @@
import java.util.Collection;
import java.util.Map;
+import javax.ws.rs.NotFoundException;
+
import org.eclipse.basyx.aas.metamodel.map.descriptor.AASDescriptor;
import org.eclipse.basyx.aas.metamodel.map.descriptor.ModelUrn;
import org.eclipse.basyx.components.configuration.BaSyxContextConfiguration;
@@ -96,8 +98,12 @@
@After
public void tearDown() throws UnsupportedEncodingException {
// Delete AAS registration
- client.delete(aasUrl1);
- client.delete(aasUrl2);
+ try {
+ client.delete(aasUrl1);
+ } catch(NotFoundException e) {}
+ try {
+ client.delete(aasUrl2);
+ } catch(NotFoundException e) {}
}
/**
@@ -173,7 +179,7 @@
try {
getResult(client.get(aasUrl2));
fail();
- } catch(Exception e) {}
+ } catch(NotFoundException e) {}
// Create new AAS registration
client.post(registryUrl, serializedDescriptor2);
@@ -190,7 +196,7 @@
try {
getResult(client.get(aasUrl2));
fail();
- } catch(Exception e) {}
+ } catch(NotFoundException e) {}
}
/**
diff --git a/sdks/c++/basys.sdk.cc/include/BaSyx/server/TCPSelectServer.h b/sdks/c++/basys.sdk.cc/include/BaSyx/server/TCPSelectServer.h
new file mode 100644
index 0000000..1a6935c
--- /dev/null
+++ b/sdks/c++/basys.sdk.cc/include/BaSyx/server/TCPSelectServer.h
@@ -0,0 +1,118 @@
+/*
+ * TCPSelectServer.h
+ *
+ * Author: wendel
+ */
+
+#ifndef BASYX_SERVER_BASYXTCPSELECTSERVER_H_
+#define BASYX_SERVER_BASYXTCPSELECTSERVER_H_
+
+#include <BaSyx/vab/core/IModelProvider.h>
+#include <BaSyx/vab/provider/native/frame/BaSyxNativeFrameProcessor.h>
+#include <BaSyx/log/log.h>
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+
+namespace basyx {
+namespace vab {
+namespace provider {
+namespace native {
+
+using socket_t = int;
+
+/**********************************************************************************************/
+/* A non-blocking server that is capable to observe real-time properties. */
+/* To obtain this property the server loops over the ingoing connections, */
+/* instead of waiting actively. */
+/* This implementation is based on an example given on: */
+/* https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzab6/xnonblock.htm */
+/**********************************************************************************************/
+class TCPSelectServer
+{
+public:
+ /**********************************************************************************************/
+ /* Server Constructor */
+ /* */
+ /* @param backend the backen (normally a model provider) */
+ /* @param port the tcp port which should be used for connections */
+ /* @param timeout_ms Time until the server is closed if no connection comes in. */
+ /* @param listen_backlog defines the number of pending connections until server rejects */
+ /* incoming connections. */
+ /**********************************************************************************************/
+ TCPSelectServer(core::IModelProvider * backend, int port,
+ int timeout_ms, int listen_backlog = 32);
+
+ /**********************************************************************************************/
+ /* Destructor. Closes all connections. */
+ /**********************************************************************************************/
+ ~TCPSelectServer();
+
+ /**********************************************************************************************/
+ /* Initializes the server. */
+ /**********************************************************************************************/
+ void Init();
+
+ /**********************************************************************************************/
+ /* Needs to be called periodically. */
+ /* Waiting for incoming connections or data of connected sockets. */
+ /**********************************************************************************************/
+ int Update();
+
+ /**********************************************************************************************/
+ /* Closes all connections. */
+ /**********************************************************************************************/
+ void Close();
+
+ /**********************************************************************************************/
+ /* Currenct server running state. */
+ /**********************************************************************************************/
+ bool isRunning();
+
+private:
+ void clean_up();
+
+ /*************************************************/
+ /* Accept all incoming connections that are */
+ /* queued up on the listening socket before we */
+ /* loop back and call select again. */
+ /*************************************************/
+ void accept_incoming_connections();
+
+ /*************************************************/
+ /* Receive all incoming data on this socket */
+ /* before we loop back and call select again. */
+ /*************************************************/
+ void receive_incoming_data(int fd);
+
+private:
+ bool initialized;
+
+ vab::core::IModelProvider* backend;
+ std::unique_ptr<vab::provider::native::frame::BaSyxNativeFrameProcessor> frame_processor;
+ basyx::log log;
+
+ // Buffers
+ static constexpr std::size_t default_buffer_size = 4096;
+ std::array<char, default_buffer_size> recv_buffer;
+ std::array<char, default_buffer_size> ret_buffer;
+
+ //tcp
+ int port;
+ struct timeval timeout;
+ socket_t listen_sd, max_socket;
+ int listen_backlog;
+ int desc_ready, end_server = 0;
+ struct sockaddr_in addr;
+ fd_set master_set;
+ bool close_connection;
+};
+
+}
+}
+}
+}
+
+#endif
diff --git a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/api/dataspecification/IDataSpecificationIEC61360.h b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/api/dataspecification/IDataSpecificationIEC61360.h
index 6b09971..370b2c0 100644
--- a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/api/dataspecification/IDataSpecificationIEC61360.h
+++ b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/api/dataspecification/IDataSpecificationIEC61360.h
@@ -13,6 +13,8 @@
#include <BaSyx/submodel/api/reference/IReference.h>
+#include <BaSyx/submodel/api/submodelelement/langstring/ILangStringSet.h>
+
#include <BaSyx/shared/object.h>
#include <string>
@@ -43,13 +45,14 @@
public:
virtual ~IDataSpecificationIEC61360() = default;
- virtual std::string getPreferredName() const = 0;
- virtual std::string getShortName() const = 0;
+ virtual std::shared_ptr<ILangStringSet> PreferredName() = 0;
+ virtual std::shared_ptr<ILangStringSet> ShortName() = 0;
+ virtual std::shared_ptr<ILangStringSet> Definition() = 0;
+
virtual std::string getUnit() const = 0;
virtual std::shared_ptr<IReference> getUnitId() const = 0;
virtual std::string getSourceOfDefinition() const = 0;
virtual DataTypeIEC61360 getDataType() const = 0;
- virtual std::string getDefinition() const = 0;
virtual std::string getValueFormat() const = 0;
virtual basyx::object getValueList() const = 0;
virtual std::shared_ptr<submodel::IReference> getValueId() const = 0;
diff --git a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/api/submodelelement/langstring/ILangStringSet.h b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/api/submodelelement/langstring/ILangStringSet.h
index e78c780..df8867b 100644
--- a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/api/submodelelement/langstring/ILangStringSet.h
+++ b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/api/submodelelement/langstring/ILangStringSet.h
@@ -3,15 +3,30 @@
#include <string>
+namespace basyx {
+namespace submodel {
+
class ILangStringSet
{
public:
- virtual ~ILangStringSet() = 0;
+ struct Path {
+ static constexpr char Language[] = "language";
+ static constexpr char Text[] = "text";
+ };
+public: // Constructors / dtor
+ ILangStringSet() = default;
+
+
+ virtual ~ILangStringSet() = 0;
+public:
virtual const std::string & getLangString(const std::string & languageCode) const = 0;
- virtual void addLangString(const std::string & languageCode, const std::string & langString) = 0;
+ virtual void addLangString(const std::string & languageCode, const std::string & langString) = 0 ;
};
inline ILangStringSet::~ILangStringSet() = default;
+}
+}
+
#endif /* _ILANGSTRINGSET_H */
diff --git a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/dataspecification/DataSpecificationContent.h b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/dataspecification/DataSpecificationContent.h
index e427a64..2fbfcb2 100644
--- a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/dataspecification/DataSpecificationContent.h
+++ b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/dataspecification/DataSpecificationContent.h
@@ -13,16 +13,17 @@
namespace submodel {
- class DataSpecificationContent
+class DataSpecificationContent
: public IDataSpecificationContent
, public virtual vab::ElementMap
{
public:
- ~DataSpecificationContent() = default;
+ using vab::ElementMap::ElementMap;
- DataSpecificationContent() = default;
- DataSpecificationContent(basyx::object obj);
- DataSpecificationContent(const IDataSpecificationContent & other);
+ DataSpecificationContent() = default;
+ DataSpecificationContent(const IDataSpecificationContent & other);
+
+ ~DataSpecificationContent() = default;
};
}
diff --git a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/dataspecification/DataSpecificationIEC61360.h b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/dataspecification/DataSpecificationIEC61360.h
index e620217..9f8a99e 100644
--- a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/dataspecification/DataSpecificationIEC61360.h
+++ b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/dataspecification/DataSpecificationIEC61360.h
@@ -25,28 +25,31 @@
, public virtual vab::ElementMap
{
public:
+ using vab::ElementMap::ElementMap;
+ DataSpecificationIEC61360();
~DataSpecificationIEC61360() = default;
+public:
// Inherited via IDataSpecificationIEC61360
- virtual std::string getPreferredName() const override;
- virtual std::string getShortName() const override;
+ virtual std::shared_ptr<ILangStringSet> PreferredName() override;
+ virtual std::shared_ptr<ILangStringSet> ShortName() override;
virtual std::string getUnit() const override;
virtual std::shared_ptr<submodel::IReference> getUnitId() const override;
virtual std::string getSourceOfDefinition() const override;
virtual DataTypeIEC61360 getDataType() const override;
- virtual std::string getDefinition() const override;
+ virtual std::shared_ptr<ILangStringSet> Definition() override;
virtual std::string getValueFormat() const override;
virtual basyx::object getValueList() const override;
virtual std::shared_ptr<submodel::IReference> getValueId() const override;
virtual LevelType getLevelType() const override;
- void setPreferredName(const std::string & preferredName);
- void setShortName(const std::string & shortName);
+// void setPreferredName(const std::string & preferredName);
+// void setShortName(const std::string & shortName);
void setUnit(const std::string & unit);
void setUnitId(const submodel::IReference & unitId);
void setSourceOfDefinition(const std::string & sourceOfDefinition);
void setDataType(const std::string & dataType);
- void setDefinition(const std::string & definition);
+// void setDefinition(const std::string & definition);
void setValueFormat(const std::string & valueFormat);
void setValueList(const basyx::object & valueList);
void setValueId(const IReference & valueId);
diff --git a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/qualifier/HasKind.h b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/qualifier/HasKind.h
index a6ebc29..2aa0d4f 100644
--- a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/qualifier/HasKind.h
+++ b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/qualifier/HasKind.h
@@ -20,11 +20,11 @@
{
public:
// constructors
- HasKind(Kind kind = Kind::NotSpecified);
+ HasKind(Kind kind = Kind::Instance);
HasKind(basyx::object object);
HasKind(const IHasKind & other);
- void Init(Kind kind = Kind::NotSpecified);
+ void Init(Kind kind = Kind::Instance);
~HasKind() = default;
diff --git a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/submodelelement/langstring/LangStringSet.h b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/submodelelement/langstring/LangStringSet.h
index 3e37fa3..9bf839a 100644
--- a/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/submodelelement/langstring/LangStringSet.h
+++ b/sdks/c++/basys.sdk.cc/include/BaSyx/submodel/map/submodelelement/langstring/LangStringSet.h
@@ -8,6 +8,7 @@
#include <BaSyx/vab/ElementMap.h>
#include <string>
+#include <initializer_list>
namespace basyx {
namespace submodel {
@@ -17,8 +18,14 @@
, public virtual vab::ElementMap
{
public:
- LangStringSet();
- LangStringSet(basyx::object object);
+ using langCodeSet_t = const std::vector<std::reference_wrapper<const std::string>>;
+public:
+ using vab::ElementMap::ElementMap;
+
+ LangStringSet();
+ LangStringSet(std::initializer_list<std::pair<std::string, std::string>> il);
+
+ langCodeSet_t getLanguageCodes() const;
const std::string & getLangString(const std::string & languageCode) const;
void addLangString(const std::string & languageCode, const std::string & langString);
@@ -27,4 +34,4 @@
}
}
-#endif /* _LANGSTRINGSET_H */
+#endif /* _LANGSTRINGSET_H */
\ No newline at end of file
diff --git a/sdks/c++/basys.sdk.cc/src/abstraction/abstraction/impl/unix/socket/acceptor_impl.cpp b/sdks/c++/basys.sdk.cc/src/abstraction/abstraction/impl/unix/socket/acceptor_impl.cpp
index dd8ef56..51236c4 100644
--- a/sdks/c++/basys.sdk.cc/src/abstraction/abstraction/impl/unix/socket/acceptor_impl.cpp
+++ b/sdks/c++/basys.sdk.cc/src/abstraction/abstraction/impl/unix/socket/acceptor_impl.cpp
@@ -41,7 +41,7 @@
// Resolve the local address and port to be used by the server
iResult = getaddrinfo(NULL, port.c_str(), &hints, &result);
if (iResult != 0) {
- log.error("getaddrinfo() failed! Error code: %d", iResult);
+ log.error("getaddrinfo() failed! Error code: {}", iResult);
return -1;
}
@@ -49,7 +49,7 @@
socketDesc = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (socketDesc < 0) {
- log.error("socket() failed! Error code: %d", iResult);
+ log.error("socket() failed! Error code: {}", iResult);
freeaddrinfo(result);
return -1;
}
@@ -58,7 +58,7 @@
iResult = setsockopt(socketDesc, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse));
if (iResult < 0) {
- log.error("setsockopt() failed! Error code: %d", iResult);
+ log.error("setsockopt() failed! Error code: {}", iResult);
freeaddrinfo(result);
::close(socketDesc);
return -1;
@@ -69,7 +69,7 @@
iResult = bind(socketDesc, result->ai_addr, (int)result->ai_addrlen);
if (iResult < 0) {
- log.error("bind() failed! Error code: %d", iResult);
+ log.error("bind() failed! Error code: {}", iResult);
freeaddrinfo(result);
::close(socketDesc);
return -1;
@@ -80,7 +80,7 @@
// To listen on a socket
// starts listening to allow clients to connect.
if (::listen(socketDesc, SOMAXCONN) < 0) {
- log.error("listen() failed! Error code: %d", iResult);
+ log.error("listen() failed! Error code: {}", iResult);
::close(socketDesc);
return -1;
}
diff --git a/sdks/c++/basys.sdk.cc/src/abstraction/abstraction/impl/unix/socket/socket_impl.cpp b/sdks/c++/basys.sdk.cc/src/abstraction/abstraction/impl/unix/socket/socket_impl.cpp
index 513dabb..b1ea576 100644
--- a/sdks/c++/basys.sdk.cc/src/abstraction/abstraction/impl/unix/socket/socket_impl.cpp
+++ b/sdks/c++/basys.sdk.cc/src/abstraction/abstraction/impl/unix/socket/socket_impl.cpp
@@ -44,7 +44,7 @@
// Resolve the server address and port
int iResult = getaddrinfo(address.c_str(), port.c_str(), &hints, &result);
if (iResult != 0) {
- log.error("getaddrinfo() failed! Error code: %d", iResult);
+ log.error("getaddrinfo() failed! Error code: {}", iResult);
return -1;
}
@@ -55,7 +55,7 @@
freeaddrinfo(result);
if (this->SocketDesc < 0) {
- log.error("socket() failed! Error code: %d", iResult);
+ log.error("socket() failed! Error code: {}", iResult);
return -1;
}
@@ -63,7 +63,7 @@
// 1. server socket, 2. socket address information, 3. size of socket address information ( of the second parameter)
iResult = ::connect(this->SocketDesc, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult < 0) {
- log.error("connect() failed! Error code: %d", iResult);
+ log.error("connect() failed! Error code: {}", iResult);
::close(this->SocketDesc);
return -1;
}
@@ -87,11 +87,11 @@
int socket_impl::shutdown(enum SocketShutdownDir how)
{
- log.trace("Shutting down socket. Code: %d", how);
+ log.trace("Shutting down socket. Code: {}", how);
auto iResult = ::shutdown(this->SocketDesc, how);
if (iResult < 0) {
- log.error("shutdown() failed! Error code: %d", iResult);
+ log.error("shutdown() failed! Error code: {}", iResult);
return -1;
}
return 0;
@@ -102,7 +102,7 @@
log.trace("Closing socket");
auto iResult = ::close(this->SocketDesc);
if (iResult < 0) {
- log.error("close() failed! Error code: %d", iResult);
+ log.error("close() failed! Error code: {}", iResult);
return -1;
}
this->SocketDesc = 0;
diff --git a/sdks/c++/basys.sdk.cc/src/logging/log/log.cpp b/sdks/c++/basys.sdk.cc/src/logging/log/log.cpp
index 4c08e8a..a0c90d9 100644
--- a/sdks/c++/basys.sdk.cc/src/logging/log/log.cpp
+++ b/sdks/c++/basys.sdk.cc/src/logging/log/log.cpp
@@ -6,7 +6,7 @@
namespace basyx
{
- log::Level log::logLevel = log::Level::Debug;
+ log::Level log::logLevel = log::Level::Trace;
const char * log::printLevel(log::Level level)
{
diff --git a/sdks/c++/basys.sdk.cc/src/server/CMakeLists.txt b/sdks/c++/basys.sdk.cc/src/server/CMakeLists.txt
index b4726ff..275cfb3 100644
--- a/sdks/c++/basys.sdk.cc/src/server/CMakeLists.txt
+++ b/sdks/c++/basys.sdk.cc/src/server/CMakeLists.txt
@@ -23,6 +23,7 @@
target_sources(${BASYX_SERVER_LIB_SUFFIX}
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/server/server.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/server/TCPSelectServer.cpp
)
target_include_directories(${BASYX_SERVER_LIB_SUFFIX} PRIVATE ${PROJECT_SOURCE_DIR})
diff --git a/sdks/c++/basys.sdk.cc/src/server/server/TCPSelectServer.cpp b/sdks/c++/basys.sdk.cc/src/server/server/TCPSelectServer.cpp
new file mode 100644
index 0000000..7586c4e
--- /dev/null
+++ b/sdks/c++/basys.sdk.cc/src/server/server/TCPSelectServer.cpp
@@ -0,0 +1,266 @@
+/*
+ * TCPSelectServer.cpp
+ *
+ * Author: wendel
+ */
+
+#include "include/BaSyx/server/TCPSelectServer.h"
+#include <BaSyx/vab/provider/native/frame/BaSyxNativeFrameHelper.h>
+#include <BaSyx/vab/provider/native/frame/BaSyxNativeFrameProcessor.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <chrono>
+
+namespace basyx {
+namespace vab {
+namespace provider {
+namespace native {
+
+TCPSelectServer::TCPSelectServer(core::IModelProvider *backend, int port, int timeout_ms, int listen_backlog)
+ : port(port)
+ , initialized(false)
+ , backend(backend)
+ , log("TCPSelectServer")
+ , listen_backlog(listen_backlog)
+{
+ frame_processor = std::unique_ptr<frame::BaSyxNativeFrameProcessor>(new frame::BaSyxNativeFrameProcessor(backend));
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+}
+
+TCPSelectServer::~TCPSelectServer()
+{
+ this->clean_up();
+}
+
+void TCPSelectServer::Init()
+{
+ int rc, on = 1;
+ // create socket to accept incoming connections
+ listen_sd = socket(AF_INET, SOCK_STREAM, 0);
+ if (listen_sd < 0)
+ {
+ log.error("socket() failed");
+ exit(-1);
+ }
+
+ // set socket reusable
+ int setsocketopt_state = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
+ if (setsocketopt_state < 0)
+ {
+ log.error("setsockopt() failed");
+ close(listen_sd);
+ exit(-1);
+ }
+
+ // Set socket to nonblocking state (FIONBIO -> non-blocking io)
+ int ioctl_state = ioctl(listen_sd, FIONBIO, (char *) &on);
+ if (ioctl_state < 0)
+ {
+ log.error("ioctl() failed");
+ close(listen_sd);
+ exit(-1);
+ }
+
+ // bind socket
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ memset(&addr.sin_addr, INADDR_ANY, sizeof(INADDR_ANY));
+ //memcpy(&addr.sin_addr, INADDR_ANY, sizeof(INADDR_ANY));
+ addr.sin_port = htons(port);
+
+ int bind_state = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
+ if (bind_state < 0)
+ {
+ log.error("bind() failed");
+ close(listen_sd);
+ exit(-1);
+ }
+
+ int listen_state = listen(listen_sd, listen_backlog);
+ if (listen_state < 0)
+ {
+ log.error("listen() failed");
+ close(listen_sd);
+ exit(-1);
+ }
+
+ //init master filedescriptor
+ FD_ZERO(&master_set);
+ max_socket = listen_sd;
+ FD_SET(listen_sd, &master_set);
+
+ log.info("Select server initialized. Listen socket-descriptor({})", listen_sd);
+ this->initialized = true;
+}
+
+int TCPSelectServer::Update()
+{
+ if (not initialized)
+ log.warn("Select server not initialized");
+
+ int rc;
+ fd_set working_set;
+
+ // copy filedescriptor
+ memcpy(&working_set, &master_set, sizeof(master_set));
+
+ log.info("Waiting on select()...");
+ rc = select(max_socket + 1, &working_set, nullptr, nullptr, &timeout);
+
+ // check select state
+ if (rc < 0)
+ {
+ log.error("select() failed");
+ return -1;
+ }
+ if (rc == 0)
+ {
+ log.info("select() timed out. End program.");
+ return -2;
+ }
+
+ desc_ready = rc;
+ // check which descriptors are readable
+ for (int fd = 0; fd <= max_socket && desc_ready > 0; ++fd)
+ {
+ // Check readiness of descriptors
+ if (FD_ISSET(fd, &working_set))
+ {
+ // decrease number of readable descriptors, if all found stop looking for them
+ desc_ready -= 1;
+
+ if (fd == listen_sd)
+ this->accept_incoming_connections();
+ else //if not listen socket, socket should be readable
+ {
+ log.info("Descriptor {} is readable", fd);
+ close_connection = false;
+ this->receive_incoming_data(fd);
+
+ // if connection is closed, clean up
+ if (close_connection)
+ {
+ close(fd);
+ log.info("Connection {} closed", fd);
+ FD_CLR(fd, &master_set);
+ if (fd == max_socket)
+ {
+ while (FD_ISSET(max_socket, &master_set) == false)
+ {
+ max_socket -= 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void TCPSelectServer::Close()
+{
+ this->clean_up();
+}
+
+bool TCPSelectServer::isRunning()
+{
+ log.warn("Not implemented!");
+ return false;
+}
+
+void TCPSelectServer::clean_up()
+{
+ for (int i = 0; i <= max_socket; ++i)
+ {
+ if (FD_ISSET(i, &master_set))
+ if (FD_ISSET(i, &master_set))
+ {
+ close(i);
+ }
+ }
+}
+
+void TCPSelectServer::accept_incoming_connections()
+{
+ log.info("Listening socket is readable");
+ int new_sd;
+ do
+ {
+ // accept incoming connections
+ new_sd = accept(listen_sd, nullptr, nullptr);
+ if (new_sd < 0)
+ {
+ // if not EWOULDBLOCK -> all incomminng connections are accepted
+ if (errno != EWOULDBLOCK)
+ {
+ log.error("accept() failed");
+ end_server = true;
+ }
+ break;
+ }
+
+ log.info("New incoming connection - {}", new_sd);
+
+ // add incoming connections to master read set
+ FD_SET(new_sd, &master_set);
+ if (new_sd > max_socket)
+ {
+ max_socket = new_sd;
+ }
+ } while (new_sd != -1);
+}
+
+void TCPSelectServer::receive_incoming_data(int fd)
+{
+ do
+ {
+ // receive data
+ int receive_state = recv(fd, recv_buffer.data(), recv_buffer.size(), 0);
+ if (receive_state < 0)
+ {
+ log.debug("receive state {}", receive_state);
+ if (errno != EWOULDBLOCK)
+ {
+ log.error("recv() failed {}", errno);
+ close_connection = true;
+ }
+ break;
+ }
+
+ // if 0, client closed connection
+ if (receive_state == 0)
+ {
+ log.info("Connection closed");
+ close_connection = true;
+ break;
+ }
+
+ int len = receive_state;
+ log.info("{} bytes received", len);
+
+ std::size_t txSize = 0;
+ frame_processor->processInputFrame(recv_buffer.data() + BASYX_FRAMESIZE_SIZE, len - BASYX_FRAMESIZE_SIZE, ret_buffer.data() + BASYX_FRAMESIZE_SIZE, &txSize);
+ txSize += BASYX_FRAMESIZE_SIZE;
+
+ // answer client
+ int send_state = send(fd, ret_buffer.data(), txSize, 0);
+ if (send_state < 0)
+ {
+ log.error("send() failed");
+ close_connection = true;
+ break;
+ }
+
+ } while (true);
+}
+
+}
+}
+}
+}
diff --git a/sdks/c++/basys.sdk.cc/src/submodel/submodel/api/constant_definitions.cpp b/sdks/c++/basys.sdk.cc/src/submodel/submodel/api/constant_definitions.cpp
index c8adb2a..7e1a3d6 100644
--- a/sdks/c++/basys.sdk.cc/src/submodel/submodel/api/constant_definitions.cpp
+++ b/sdks/c++/basys.sdk.cc/src/submodel/submodel/api/constant_definitions.cpp
@@ -24,6 +24,7 @@
#include <BaSyx/submodel/api/submodelelement/property/blob/IBlob.h>
#include <BaSyx/submodel/api/submodelelement/property/file/IFile.h>
+#include <BaSyx/submodel/api/submodelelement/langstring/ILangStringSet.h>
#include <BaSyx/submodel/api/dataspecification/datatypes/DataTypeIEC61360.h>
#include <BaSyx/submodel/api/dataspecification/datatypes/LevelType.h>
@@ -152,6 +153,8 @@
constexpr char IDataSpecificationIEC61360::Path::DataType[];
+constexpr char ILangStringSet::Path::Language[];
+constexpr char ILangStringSet::Path::Text[];
}
}
diff --git a/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/dataspecification/DataSpecificationContent.cpp b/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/dataspecification/DataSpecificationContent.cpp
index 4c1019e..89217bc 100644
--- a/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/dataspecification/DataSpecificationContent.cpp
+++ b/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/dataspecification/DataSpecificationContent.cpp
@@ -9,10 +9,6 @@
namespace basyx {
namespace submodel {
-DataSpecificationContent::DataSpecificationContent(basyx::object obj)
- : vab::ElementMap(obj)
-{}
-
DataSpecificationContent::DataSpecificationContent(const IDataSpecificationContent & other)
: vab::ElementMap()
{
diff --git a/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/dataspecification/DataSpecificationIEC61360.cpp b/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/dataspecification/DataSpecificationIEC61360.cpp
index f681a9b..6f85672 100644
--- a/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/dataspecification/DataSpecificationIEC61360.cpp
+++ b/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/dataspecification/DataSpecificationIEC61360.cpp
@@ -6,33 +6,43 @@
#include <BaSyx/submodel/map/dataspecification/DataSpecificationIEC61360.h>
#include <BaSyx/submodel/map/reference/Reference.h>
+#include <BaSyx/submodel/map/submodelelement/langstring/LangStringSet.h>
namespace basyx {
namespace submodel {
-std::string DataSpecificationIEC61360::getPreferredName() const
+DataSpecificationIEC61360::DataSpecificationIEC61360()
{
- return this->map.getProperty(IDataSpecificationIEC61360::Path::PreferredName).GetStringContent();
+ this->map.insertKey(IDataSpecificationIEC61360::Path::PreferredName, LangStringSet().getMap());
+ this->map.insertKey(IDataSpecificationIEC61360::Path::ShortName, LangStringSet().getMap());
+ this->map.insertKey(IDataSpecificationIEC61360::Path::Definition, LangStringSet().getMap());
}
-std::string DataSpecificationIEC61360::getShortName() const
+std::shared_ptr<ILangStringSet> DataSpecificationIEC61360::PreferredName()
{
- return this->map.getProperty(IDataSpecificationIEC61360::Path::ShortName).GetStringContent();
+ auto langStringObj = this->map.getProperty(IDataSpecificationIEC61360::Path::PreferredName);
+ return std::make_shared<LangStringSet>(langStringObj);
+}
+
+std::shared_ptr<ILangStringSet> DataSpecificationIEC61360::ShortName()
+{
+ auto langStringObj = this->map.getProperty(IDataSpecificationIEC61360::Path::ShortName);
+ return std::make_shared<LangStringSet>(langStringObj);
}
std::string DataSpecificationIEC61360::getUnit() const
{
- return this->map.getProperty(IDataSpecificationIEC61360::Path::Unit).GetStringContent();
+ return this->map.getProperty(IDataSpecificationIEC61360::Path::Unit).GetStringContent();
}
std::shared_ptr<submodel::IReference> DataSpecificationIEC61360::getUnitId() const
{
- return std::make_shared<submodel::Reference>(this->map.getProperty(IDataSpecificationIEC61360::Path::UnitId));
+ return std::make_shared<submodel::Reference>(this->map.getProperty(IDataSpecificationIEC61360::Path::UnitId));
}
std::string DataSpecificationIEC61360::getSourceOfDefinition() const
{
- return this->map.getProperty(IDataSpecificationIEC61360::Path::SourceOfDefinition).GetStringContent();
+ return this->map.getProperty(IDataSpecificationIEC61360::Path::SourceOfDefinition).GetStringContent();
}
DataTypeIEC61360 DataSpecificationIEC61360::getDataType() const
@@ -41,9 +51,10 @@
return util::from_string<DataTypeIEC61360>(dataTypeStr);
}
-std::string DataSpecificationIEC61360::getDefinition() const
+std::shared_ptr<ILangStringSet> DataSpecificationIEC61360::Definition()
{
- return this->map.getProperty(IDataSpecificationIEC61360::Path::Definition).GetStringContent();
+ auto langStringObj = this->map.getProperty(IDataSpecificationIEC61360::Path::Definition);
+ return std::make_shared<LangStringSet>(langStringObj);
}
std::string DataSpecificationIEC61360::getValueFormat() const
@@ -66,16 +77,6 @@
return util::from_string<LevelType>(this->map.getProperty(IDataSpecificationIEC61360::Path::LevelType).GetStringContent());
}
-void DataSpecificationIEC61360::setPreferredName(const std::string & preferredName)
-{
- this->map.insertKey(IDataSpecificationIEC61360::Path::PreferredName, preferredName);
-}
-
-void DataSpecificationIEC61360::setShortName(const std::string & shortName)
-{
- this->map.insertKey(IDataSpecificationIEC61360::Path::ShortName, shortName);
-}
-
void DataSpecificationIEC61360::setUnit(const std::string & unit)
{
this->map.insertKey(IDataSpecificationIEC61360::Path::Unit, unit);
@@ -96,11 +97,6 @@
this->map.insertKey(IDataSpecificationIEC61360::Path::DataType, dataType);
}
-void DataSpecificationIEC61360::setDefinition(const std::string & definition)
-{
- this->map.insertKey(IDataSpecificationIEC61360::Path::Definition, definition);
-}
-
void DataSpecificationIEC61360::setValueFormat(const std::string & valueFormat)
{
this->map.insertKey(IDataSpecificationIEC61360::Path::ValueFormat, valueFormat);
diff --git a/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/modeltype/ModelType.cpp b/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/modeltype/ModelType.cpp
index 79f5a82..507def8 100644
--- a/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/modeltype/ModelType.cpp
+++ b/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/modeltype/ModelType.cpp
@@ -15,6 +15,6 @@
basyx::submodel::ModelType::ModelType(const std::string & type)
: vab::ElementMap{}
{
- this->map.insertKey(Path::Name, type);
this->map.insertKey(Path::ModelType, basyx::object::make_map());
+ this->map.getProperty(Path::ModelType).insertKey(Path::Name, type);
};
diff --git a/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/submodelelement/DataElement.cpp b/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/submodelelement/DataElement.cpp
index 107b65c..3a2c46f 100644
--- a/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/submodelelement/DataElement.cpp
+++ b/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/submodelelement/DataElement.cpp
@@ -15,11 +15,13 @@
{}
DataElement::DataElement(basyx::object object) :
- vab::ElementMap{object}
+ vab::ElementMap{object},
+ ModelType{ IDataElement::Path::ModelType }
{}
DataElement::DataElement(const IDataElement & other) :
- SubmodelElement{other}
+ SubmodelElement{other},
+ ModelType{ IDataElement::Path::ModelType }
{}
}
diff --git a/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/submodelelement/langstring/LangStringSet.cpp b/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/submodelelement/langstring/LangStringSet.cpp
index 7383aa1..6690121 100644
--- a/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/submodelelement/langstring/LangStringSet.cpp
+++ b/sdks/c++/basys.sdk.cc/src/submodel/submodel/map/submodelelement/langstring/LangStringSet.cpp
@@ -1,22 +1,55 @@
#include <BaSyx/submodel/map/submodelelement/langstring/LangStringSet.h>
-basyx::submodel::LangStringSet::LangStringSet()
- : vab::ElementMap{basyx::object::make_list<basyx::object>()}
-{
+const std::string empty{};
+basyx::submodel::LangStringSet::LangStringSet()
+ : vab::ElementMap{basyx::object::make_object_set()}
+{
}
-basyx::submodel::LangStringSet::LangStringSet(basyx::object object)
+basyx::submodel::LangStringSet::LangStringSet(std::initializer_list<std::pair<std::string, std::string>> il)
+ : LangStringSet()
{
-
+ for (const auto & entry : il)
+ this->addLangString(entry.first, entry.second);
}
const std::string & basyx::submodel::LangStringSet::getLangString(const std::string & languageCode) const
{
- return std::string{};
+ auto & objectSet = this->getMap().Get<basyx::object::object_set_t&>();
+
+ for (auto & entry : objectSet)
+ {
+ auto & object = const_cast<basyx::object&>(entry);
+ if (object.getProperty(Path::Language) == languageCode) {
+ return object.getProperty(Path::Text).GetStringContent();
+ }
+ };
+
+ return empty;
}
+basyx::submodel::LangStringSet::langCodeSet_t basyx::submodel::LangStringSet::getLanguageCodes() const
+{
+ auto & objectSet = this->getMap().Get<basyx::object::object_set_t&>();
+
+ std::remove_const<langCodeSet_t>::type ret;
+ ret.reserve(objectSet.size());
+
+ for (auto & entry : objectSet)
+ {
+ auto & object = const_cast<basyx::object&>(entry);
+ ret.emplace_back( std::cref( object.getProperty(Path::Language).GetStringContent()) );
+ };
+
+ return ret;
+};
+
void basyx::submodel::LangStringSet::addLangString(const std::string & languageCode, const std::string & langString)
{
+ auto langStringMap = basyx::object::make_map();
+ langStringMap.insertKey( Path::Language, languageCode );
+ langStringMap.insertKey( Path::Text, langString);
-}
+ this->map.insert(langStringMap);
+}
\ No newline at end of file
diff --git a/sdks/c++/basys.sdk.cc/src/vab/vab/ElementMap.cpp b/sdks/c++/basys.sdk.cc/src/vab/vab/ElementMap.cpp
index c560efb6..a5a07ac 100644
--- a/sdks/c++/basys.sdk.cc/src/vab/vab/ElementMap.cpp
+++ b/sdks/c++/basys.sdk.cc/src/vab/vab/ElementMap.cpp
@@ -14,12 +14,8 @@
{}
ElementMap::ElementMap(basyx::object object)
- : map(basyx::object::make_null())
+ : map(object)
{
- if ( object.InstanceOf<basyx::object::object_map_t>() )
- {
- map = object;
- };
}
ElementMap::ElementMap(const ElementMap & other) :
diff --git a/sdks/c++/basys.sdk.cc/tests/regression/submodel/CMakeLists.txt b/sdks/c++/basys.sdk.cc/tests/regression/submodel/CMakeLists.txt
index 7b1b450..42fcd58 100644
--- a/sdks/c++/basys.sdk.cc/tests/regression/submodel/CMakeLists.txt
+++ b/sdks/c++/basys.sdk.cc/tests/regression/submodel/CMakeLists.txt
@@ -10,23 +10,6 @@
target_sources(${PROJECT_NAME}
PRIVATE
-# support/VABProxyMock.hpp
-# connected/test_ConnectedDataElement.cpp
-# connected/test_ConnectedElement.cpp
-# connected/submodelelement/property/test_ConnectedMapProperty.cpp
-# connected/submodelelement/property/test_ConnectedProperty.cpp
-# connected/submodelelement/property/test_ConnectedReferenceElement.cpp
-# connected/submodelelement/property/test_ConnectedSingleProperty.cpp
-# connected/submodelelement/property/test_ConnectedCollectionProperty.cpp
-# connected/submodelelement/property/file/test_ConnectedFile.cpp
-# connected/submodelelement/property/blob/test_ConnectedBlob.cpp
-# connected/submodelelement/operation/test_ConnectedOperation.cpp
-# connected/submodelelement/test_ConnectedRelationshipElement.cpp
-# connected/submodelelement/test_ConnectedSubmodelElement.cpp
-# connected/submodelelement/test_ConnectedSubmodelElementCollection.cpp
-# connected/submodelelement/property/test_ConnectedPropertyFactory.cpp
-
-
support/KeyMock.hpp
support/ReferenceMock.hpp
support/IdentifiableMock.hpp
@@ -36,7 +19,7 @@
support/AdditionalAssertions.hpp
support/constant_definitions.cpp
- map/test_Submodel.cpp
+ map/test_Submodel.cpp
map/qualifier/test_HasKind.cpp
map/qualifier/test_HasDataSpecification.cpp
map/qualifier/test_HasSemantics.cpp
@@ -49,7 +32,7 @@
map/reference/test_Key.cpp
map/reference/test_Reference.cpp
map/submodelelement/operation/test_OperationVariable.cpp
- #map/submodelelement/property/test_Property.cpp
+ map/submodelelement/test_LangStringSet.cpp
map/submodelelement/test_DataElement.cpp
map/submodelelement/test_ReferenceElement.cpp
)
diff --git a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Constraint.cpp b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Constraint.cpp
index f10f43d..ef77a74 100644
--- a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Constraint.cpp
+++ b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Constraint.cpp
@@ -20,8 +20,6 @@
void TearDown() override
{
- auto map = constraint.getMap().Get<basyx::object::object_map_t>();
- ASSERT_EQ(map.at(ModelType::Path::Name).GetStringContent(), std::string(IConstraint::Path::ModelType));
}
};
@@ -46,5 +44,4 @@
Constraint c2{constraint};
auto map = c2.getMap().Get<basyx::object::object_map_t>();
- ASSERT_EQ(map.at(ModelType::Path::Name).GetStringContent(), std::string(IConstraint::Path::ModelType));
}
diff --git a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Formula.cpp b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Formula.cpp
index da09f42..03db56c 100644
--- a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Formula.cpp
+++ b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Formula.cpp
@@ -21,9 +21,6 @@
void TearDown() override
{
- auto map = formula.getMap().Get<basyx::object::object_map_t>();
- ASSERT_EQ(map.at(ModelType::Path::Name).GetStringContent(), std::string(IFormula::Path::Modeltype));
- ASSERT_NO_THROW(map.at(IFormula::Path::Dependson).Get<basyx::object::object_list_t>());
}
};
diff --git a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Qualifier.cpp b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Qualifier.cpp
index 5c29839..3811613 100644
--- a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Qualifier.cpp
+++ b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/qualifiable/test_Qualifier.cpp
@@ -21,8 +21,6 @@
void TearDown() override
{
- auto map = qualifier.getMap().Get<basyx::object::object_map_t>();
- ASSERT_EQ(map.at(ModelType::Path::Name).GetStringContent(), std::string(IQualifier::Path::Modeltype));
}
};
diff --git a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/test_HasKind.cpp b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/test_HasKind.cpp
index 948135c..a1b5a8f 100644
--- a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/test_HasKind.cpp
+++ b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/qualifier/test_HasKind.cpp
@@ -17,13 +17,13 @@
{}
};
-TEST_F(HasKindTest, TestConstructorKindNotSpecified)
+TEST_F(HasKindTest, TestConstructor)
{
HasKind hasKind;
auto map = hasKind.getMap().Get<basyx::object::object_map_t>();
- ASSERT_EQ(map.at(IHasKind::Path::Kind).GetStringContent(), util::to_string(Kind::NotSpecified));
+ ASSERT_EQ(map.at(IHasKind::Path::Kind).GetStringContent(), util::to_string(Kind::Instance));
}
TEST_F(HasKindTest, TestConstructorKindSpecified)
diff --git a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/operation/test_OperationVariable.cpp b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/operation/test_OperationVariable.cpp
index 3d6cc1a..a7e4515 100644
--- a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/operation/test_OperationVariable.cpp
+++ b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/operation/test_OperationVariable.cpp
@@ -25,8 +25,6 @@
void TearDown() override
{
- auto map = op_var.getMap();
- basyx::assertions::AssertIsModelType(map, IOperationVariable::Path::ModelType);
}
};
diff --git a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_DataElement.cpp b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_DataElement.cpp
index ff8db4a..2ad7f59 100644
--- a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_DataElement.cpp
+++ b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_DataElement.cpp
@@ -23,8 +23,6 @@
void TearDown() override
{
- auto map = data_element.getMap();
- basyx::assertions::AssertIsModelType(map, IDataElement::Path::ModelType);
}
};
diff --git a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_LangStringSet.cpp b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_LangStringSet.cpp
new file mode 100644
index 0000000..0742c35
--- /dev/null
+++ b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_LangStringSet.cpp
@@ -0,0 +1,57 @@
+#include <gtest/gtest.h>
+
+#include <BaSyx/submodel/map/submodelelement/langstring/LangStringSet.h>
+#include <BaSyx/submodel/map/qualifier/qualifiable/Constraint.h>
+#include <BaSyx/submodel/map/modeltype/ModelType.h>
+
+using namespace basyx::submodel;
+
+class LangStringTest : public ::testing::Test
+{
+protected:
+ void SetUp() override
+ {}
+
+ void TearDown() override
+ {
+ }
+};
+
+TEST_F(LangStringTest, TestInitializerListConstruction)
+{
+ LangStringSet langString{ {"DE", "Test" } };
+
+ const auto & deString = langString.getLangString("DE");
+ ASSERT_EQ(deString, "Test");
+}
+
+TEST_F(LangStringTest, TestUnknownLangCode)
+{
+ LangStringSet langString;
+
+ const auto & string = langString.getLangString("UN");
+
+ ASSERT_TRUE(string.empty());
+}
+
+
+TEST_F(LangStringTest, Test)
+{
+ LangStringSet langString;
+
+ constexpr char stringValDe[] = "Das ist ein Test!";
+ constexpr char stringValEn[] = "This is a test!";
+
+ langString.addLangString("DE", stringValDe);
+ langString.addLangString("EN", stringValEn);
+
+ const auto & deString = langString.getLangString("DE");
+ const auto & enString = langString.getLangString("EN");
+
+ ASSERT_EQ(deString, stringValDe);
+ ASSERT_EQ(enString, stringValEn);
+
+ const auto & langCodes = langString.getLanguageCodes();
+
+ ASSERT_EQ(langCodes.size(), 2);
+}
diff --git a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_ReferenceElement.cpp b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_ReferenceElement.cpp
index 42c2476..0e34852 100644
--- a/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_ReferenceElement.cpp
+++ b/sdks/c++/basys.sdk.cc/tests/regression/submodel/map/submodelelement/test_ReferenceElement.cpp
@@ -23,8 +23,6 @@
void TearDown() override
{
- auto map = ref_elem.getMap();
- basyx::assertions::AssertIsModelType(map, IReferenceElement::Path::Modeltype);
}
};
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/aas/aggregator/restapi/AASAggregationProvider.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/aas/aggregator/restapi/AASAggregationProvider.java
index b000110..142a2f8 100644
--- a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/aas/aggregator/restapi/AASAggregationProvider.java
+++ b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/aas/aggregator/restapi/AASAggregationProvider.java
@@ -9,6 +9,9 @@
import org.eclipse.basyx.aas.metamodel.map.descriptor.ModelUrn;
import org.eclipse.basyx.submodel.metamodel.api.identifier.IIdentifier;
import org.eclipse.basyx.submodel.metamodel.map.modeltype.ModelType;
+import org.eclipse.basyx.vab.exception.provider.MalformedRequestException;
+import org.eclipse.basyx.vab.exception.provider.ResourceAlreadyExistsException;
+import org.eclipse.basyx.vab.exception.provider.ResourceNotFoundException;
import org.eclipse.basyx.vab.modelprovider.VABPathTools;
import org.eclipse.basyx.vab.modelprovider.api.IModelProvider;
@@ -34,11 +37,12 @@
* prefix)
* @param path
* @return
+ * @throws MalformedRequestException
*/
- private String stripPrefix(String path) {
+ private String stripPrefix(String path) throws MalformedRequestException {
path = VABPathTools.stripSlashes(path);
if (!path.startsWith(PREFIX)) {
- throw new RuntimeException("Path " + path + " not recognized as aggregator path. Has to start with " + PREFIX);
+ throw new MalformedRequestException("Path " + path + " not recognized as aggregator path. Has to start with " + PREFIX);
}
path = path.replace(PREFIX, "");
path = VABPathTools.stripSlashes(path);
@@ -51,13 +55,14 @@
*
* @param value the AAS Map object
* @return an AAS
+ * @throws MalformedRequestException
*/
@SuppressWarnings("unchecked")
- private AssetAdministrationShell createAASFromMap(Object value) {
+ private AssetAdministrationShell createAASFromMap(Object value) throws MalformedRequestException {
//check if the given value is a Map
if(!(value instanceof Map)) {
- throw new RuntimeException("Given newValue is not a Map");
+ throw new MalformedRequestException("Given newValue is not a Map");
}
Map<String, Object> map = (Map<String, Object>) value;
@@ -68,7 +73,7 @@
//have to accept Objects without modeltype information,
//as modeltype is not part of the public metamodel
if(!AssetAdministrationShell.MODELTYPE.equals(type) && type != null) {
- throw new RuntimeException("Given newValue map has not the correct ModelType");
+ throw new MalformedRequestException("Given newValue map has not the correct ModelType");
}
AssetAdministrationShell aas = AssetAdministrationShell.createAsFacade(map);
@@ -90,7 +95,7 @@
//Throw an Exception if the requested aas does not exist
if(aas == null) {
- throw new RuntimeException("Requested aasID '" + path + "' does not exist.");
+ throw new ResourceNotFoundException("Requested aasID '" + path + "' does not exist.");
}
return aas;
}
@@ -109,16 +114,16 @@
ModelUrn identifier = new ModelUrn(path);
if(!aas.getIdentification().getId().equals(path)) {
- throw new RuntimeException("Given aasID and given AAS do not match");
+ throw new MalformedRequestException("Given aasID and given AAS do not match");
}
if(aggregator.getAAS(identifier) == null) {
- throw new RuntimeException("Can not update non existing value '" + path + "'. Try create instead.");
+ throw new ResourceAlreadyExistsException("Can not update non existing value '" + path + "'. Try create instead.");
}
aggregator.updateAAS(aas);
} else {
- throw new RuntimeException("Set with empty path is not supported by aggregator");
+ throw new MalformedRequestException("Set with empty path is not supported by aggregator");
}
}
@@ -132,12 +137,12 @@
if (path.isEmpty()) { // Creating new entry
if(aggregator.getAAS(aas.getIdentification()) != null) {
- throw new RuntimeException("Value '" + path + "' to be created already exists. Try update instead.");
+ throw new ResourceAlreadyExistsException("Value '" + path + "' to be created already exists. Try update instead.");
}
aggregator.createAAS(aas);
} else {
- throw new RuntimeException("Create was called with an unsupported path: " + path);
+ throw new MalformedRequestException("Create was called with an unsupported path: " + path);
}
}
@@ -153,23 +158,23 @@
IIdentifier identifier = new ModelUrn(path);
if(aggregator.getAAS(identifier) == null) {
- throw new RuntimeException("Value '" + path + "' to be deleted does not exists.");
+ throw new ResourceNotFoundException("Value '" + path + "' to be deleted does not exists.");
}
aggregator.deleteAAS(identifier);
} else {
- throw new RuntimeException("Delete with empty path is not supported by registry");
+ throw new MalformedRequestException("Delete with empty path is not supported by registry");
}
}
@Override
public void deleteValue(String path, Object obj) throws Exception {
- throw new RuntimeException("DeleteValue with parameter not supported by aggregator");
+ throw new MalformedRequestException("DeleteValue with parameter not supported by aggregator");
}
@Override
public Object invokeOperation(String path, Object... parameter) throws Exception {
- throw new RuntimeException("Invoke not supported by aggregator");
+ throw new MalformedRequestException("Invoke not supported by aggregator");
}
}
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/aas/registration/restapi/DirectoryModelProvider.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/aas/registration/restapi/DirectoryModelProvider.java
index 6dc3165..b48cc90 100644
--- a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/aas/registration/restapi/DirectoryModelProvider.java
+++ b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/aas/registration/restapi/DirectoryModelProvider.java
@@ -1,5 +1,6 @@
package org.eclipse.basyx.aas.registration.restapi;
+import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;
import java.util.Set;
@@ -11,6 +12,10 @@
import org.eclipse.basyx.aas.registration.memory.InMemoryRegistry;
import org.eclipse.basyx.submodel.metamodel.api.identifier.IIdentifier;
import org.eclipse.basyx.submodel.metamodel.map.modeltype.ModelType;
+import org.eclipse.basyx.vab.exception.provider.MalformedRequestException;
+import org.eclipse.basyx.vab.exception.provider.ProviderException;
+import org.eclipse.basyx.vab.exception.provider.ResourceAlreadyExistsException;
+import org.eclipse.basyx.vab.exception.provider.ResourceNotFoundException;
import org.eclipse.basyx.vab.modelprovider.VABPathTools;
import org.eclipse.basyx.vab.modelprovider.api.IModelProvider;
@@ -41,11 +46,12 @@
*
* @param path
* @return
+ * @throws MalformedRequestException if path does not start with PERFIX "api/v1/registry"
*/
- private String stripPrefix(String path) {
+ private String stripPrefix(String path) throws MalformedRequestException {
path = VABPathTools.stripSlashes(path);
if (!path.startsWith(PREFIX)) {
- throw new RuntimeException("Path " + path + " not recognized as registry path. Has to start with " + PREFIX);
+ throw new MalformedRequestException("Path " + path + " not recognized as registry path. Has to start with " + PREFIX);
}
path = path.replace(PREFIX, "");
path = VABPathTools.stripSlashes(path);
@@ -57,8 +63,9 @@
*
* @param path the path to be splitted
* @return Array of path elements
+ * @throws MalformedRequestException if path is not valid
*/
- private String[] splitPath(String path) {
+ private String[] splitPath(String path) throws MalformedRequestException {
if(path.isEmpty()) {
return new String[0];
@@ -68,29 +75,43 @@
//Assumes "submodels" is not a valid AASId
if(splitted[0].equals(SUBMODELS)) {
- throw new RuntimeException("Path must not start with " + SUBMODELS);
+ throw new MalformedRequestException("Path must not start with " + SUBMODELS);
}
//If path contains more than one element, the second one has to be "submodels"
if(splitted.length > 1 && !splitted[1].equals(SUBMODELS)) {
- throw new RuntimeException("Second path element must be (if present): " + SUBMODELS);
+ throw new MalformedRequestException("Second path element must be (if present): " + SUBMODELS);
}
return splitted;
}
+ private String[] preparePath(String path) throws MalformedRequestException {
+ try {
+ path = URLDecoder.decode(path, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ //Malformed request because of unsupported encoding
+ throw new MalformedRequestException("Path has to be encoded as UTF-8 string.");
+ }
+
+ path = stripPrefix(path);
+
+ return splitPath(path);
+ }
+
/**
* Checks if a given Object is a Map and checks if it has the correct modelType
*
* @param expectedModelType the modelType the Object is expected to have
* @param value the Object to be checked and casted
* @return the object casted to a Map
+ * @throws MalformedRequestException
*/
@SuppressWarnings("unchecked")
- private Map<String, Object> checkModelType(String expectedModelType, Object value) {
+ private Map<String, Object> checkModelType(String expectedModelType, Object value) throws MalformedRequestException {
//check if the given value is a Map
if(!(value instanceof Map)) {
- throw new RuntimeException("Given newValue is not a Map");
+ throw new MalformedRequestException("Given newValue is not a Map");
}
Map<String, Object> map = (Map<String, Object>) value;
@@ -101,7 +122,7 @@
//have to accept Objects without modeltype information,
//as modeltype is not part of the public metamodel
if(!expectedModelType.equals(type) && type != null) {
- throw new RuntimeException("Given newValue map has not the correct ModelType");
+ throw new MalformedRequestException("Given newValue map has not the correct ModelType");
}
return map;
@@ -113,8 +134,9 @@
*
* @param value the AAS Map object
* @return an AAS
+ * @throws MalformedRequestException
*/
- private AASDescriptor createAASDescriptorFromMap(Object value) {
+ private AASDescriptor createAASDescriptorFromMap(Object value) throws MalformedRequestException {
Map<String, Object> map = checkModelType(AASDescriptor.MODELTYPE, value);
AASDescriptor aasDescriptor = new AASDescriptor(map);
return aasDescriptor;
@@ -126,32 +148,30 @@
*
* @param value the AAS Map object
* @return an AAS
+ * @throws MalformedRequestException
*/
- private SubmodelDescriptor createSMDescriptorFromMap(Object value) {
+ private SubmodelDescriptor createSMDescriptorFromMap(Object value) throws MalformedRequestException {
Map<String, Object> map = checkModelType(SubmodelDescriptor.MODELTYPE, value);
SubmodelDescriptor smDescriptor = new SubmodelDescriptor(map);
return smDescriptor;
}
@Override
- public Object getModelPropertyValue(String path) throws Exception {
- path = stripPrefix(path);
- path = URLDecoder.decode(path, "UTF-8");
+ public Object getModelPropertyValue(String path) throws ProviderException {
+ String[] splitted = preparePath(path);
//Path is empty, request for all AASDescriptors
- if (path.isEmpty()) {
+ if (splitted.length == 0) {
return registry.lookupAll();
} else {
- String[] splitted = splitPath(path);
-
//Given path consists only of an AAS Id
if(splitted.length == 1) {
- AASDescriptor descriptor = registry.lookupAAS(new ModelUrn(path));
+ AASDescriptor descriptor = registry.lookupAAS(new ModelUrn(splitted[0]));
//Throw an Exception if the requested AAS does not exist
if(descriptor == null) {
- throw new RuntimeException("Specified AASid '" + path + "' does not exist.");
+ throw new ResourceNotFoundException("Specified AASid '" + splitted[0] + "' does not exist.");
}
return descriptor;
@@ -165,28 +185,24 @@
} else if(splitted.length == 3) {
SubmodelDescriptor smDescriptor = getSmDescriptorFromAAS(new ModelUrn(splitted[0]), splitted[2]);
if(smDescriptor == null) {
- throw new RuntimeException("Specified SubmodelId '" + splitted[2] + "' does not exist in AAS '" + splitted[0] + "'.");
+ throw new ResourceNotFoundException("Specified SubmodelId '" + splitted[2] + "' does not exist in AAS '" + splitted[0] + "'.");
}
return smDescriptor;
}
//path has more than three elements and is therefore invalid
- throw new RuntimeException("Given path '" + path + "' contains more than three path elements and is therefore invalid.");
+ throw new MalformedRequestException("Given path '" + path + "' contains more than three path elements and is therefore invalid.");
}
}
@Override
- public void setModelPropertyValue(String path, Object newValue) throws Exception {
- path = stripPrefix(path);
- path = URLDecoder.decode(path, "UTF-8");
+ public void setModelPropertyValue(String path, Object newValue) throws ProviderException {
+ String[] splitted = preparePath(path);
- if (!path.isEmpty()) { // Overwriting existing entry
-
- String[] splitted = splitPath(path);
-
+ if (splitted.length > 0) { // Overwriting existing entry
//if path contains more or less than an aasID after the prefix
if(splitted.length != 1) {
- throw new RuntimeException("Path '" + path + "' is invalid for updating an aas.");
+ throw new MalformedRequestException("Path '" + path + "' is invalid for updating an aas.");
}
// Decode encoded path
@@ -194,23 +210,20 @@
//aas to be updated does not exist
if(registry.lookupAAS(identifier) == null) {
- throw new RuntimeException("AAS '" + path + "' to be updated does not exist. Try create instead.");
+ throw new ResourceNotFoundException("AAS '" + path + "' to be updated does not exist. Try create instead.");
}
//delete old value and create the new one
registry.delete(identifier);
registry.register(createAASDescriptorFromMap(newValue));
} else {
- throw new RuntimeException("Set with empty path is not supported by registry");
+ throw new MalformedRequestException("Set with empty path is not supported by registry");
}
}
@Override
- public void createValue(String path, Object newEntity) throws Exception {
- path = stripPrefix(path);
- path = URLDecoder.decode(path, "UTF-8");
-
- String[] splitted = splitPath(path);
+ public void createValue(String path, Object newEntity) throws ProviderException {
+ String[] splitted = preparePath(path);
// Creating new entry
if (splitted.length == 0) {
@@ -219,7 +232,7 @@
//aas to be created already exists
if(registry.lookupAAS(aas.getIdentifier()) != null) {
- throw new RuntimeException("AAS with Id '" +
+ throw new ResourceAlreadyExistsException("AAS with Id '" +
aas.getIdentifier().getId() + "' already exists. Try update instead.");
}
@@ -235,23 +248,20 @@
//a submodel with this Id already exists in given aas
//getSmDescriptorFromAAS also checks if aas exists
if(getSmDescriptorFromAAS(aasId, smDescriptor.getIdShort()) != null) {
- throw new RuntimeException("A Submodel with id '" + smDescriptor.getIdShort() +
+ throw new ResourceAlreadyExistsException("A Submodel with id '" + smDescriptor.getIdShort() +
"' already exists in aas '" + splitted[0] + "'. Try update instead.");
}
registry.register(aasId, smDescriptor);
} else {
- throw new RuntimeException("Create was called with an unsupported path: " + path);
+ throw new MalformedRequestException("Create was called with an unsupported path: " + path);
}
}
@Override
- public void deleteValue(String path) throws Exception {
- path = stripPrefix(path);
- path = URLDecoder.decode(path, "UTF-8");
-
- String[] splitted = splitPath(path);
+ public void deleteValue(String path) throws ProviderException {
+ String[] splitted = preparePath(path);
if (splitted.length == 1) { //delete an aas
@@ -259,7 +269,7 @@
//aas to be deleted does not exist
if(registry.lookupAAS(aasId) == null) {
- throw new RuntimeException("AAS '" + splitted[0] + "' to be deleted does not exist.");
+ throw new ResourceNotFoundException("AAS '" + splitted[0] + "' to be deleted does not exist.");
}
registry.delete(aasId);
@@ -272,25 +282,25 @@
//a submodel with this Id does not exist in given aas
//getSmDescriptorFromAAS also checks if aas exists
if(getSmDescriptorFromAAS(aasId, smId) == null) {
- throw new RuntimeException("A Submodel with id '" + smId +
+ throw new ResourceNotFoundException("A Submodel with id '" + smId +
"' does not exist in aas '" + splitted[0] + "'.");
}
registry.delete(aasId, smId);
} else {
- throw new RuntimeException("Delete with empty path is not supported by registry");
+ throw new MalformedRequestException("Delete with empty path is not supported by registry");
}
}
@Override
public void deleteValue(String path, Object obj) throws Exception {
- throw new RuntimeException("DeleteValue with parameter not supported by registry");
+ throw new MalformedRequestException("DeleteValue with parameter not supported by registry");
}
@Override
public Object invokeOperation(String path, Object... parameter) throws Exception {
- throw new RuntimeException("Invoke not supported by registry");
+ throw new MalformedRequestException("Invoke not supported by registry");
}
/**
@@ -299,11 +309,12 @@
*
* @param id id of the aas
* @return Set of contained SubmodelDescriptor objects
+ * @throws ResourceNotFoundException if the AAS does not exist
*/
- private Set<SubmodelDescriptor> getSmDescriptorsFromAAS(IIdentifier id) {
+ private Set<SubmodelDescriptor> getSmDescriptorsFromAAS(IIdentifier id) throws ResourceNotFoundException {
AASDescriptor aasDescriptor = registry.lookupAAS(id);
if(aasDescriptor == null) {
- throw new RuntimeException("Specified AASid '" + id.getId() + "' does not exist.");
+ throw new ResourceNotFoundException("Specified AASid '" + id.getId() + "' does not exist.");
}
return aasDescriptor.getSubModelDescriptors();
}
@@ -315,11 +326,13 @@
* @param aasId id of the aas
* @param smId id of the submodel
* @return the SubmodelDescriptor with the given id
+ * @throws ResourceNotFoundException if aasId does not exist
*/
- private SubmodelDescriptor getSmDescriptorFromAAS(IIdentifier aasId, String smId) {
+ private SubmodelDescriptor getSmDescriptorFromAAS(IIdentifier aasId, String smId)
+ throws ResourceNotFoundException {
AASDescriptor aasDescriptor = registry.lookupAAS(aasId);
if(aasDescriptor == null) {
- throw new RuntimeException("Specified AASid '" + aasId.getId() + "' does not exist.");
+ throw new ResourceNotFoundException("Specified AASid '" + aasId.getId() + "' does not exist.");
}
return aasDescriptor.getSubmodelDescriptorFromIdShort(smId);
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/coder/json/provider/JSONProvider.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/coder/json/provider/JSONProvider.java
index 09cc270..5056bd1 100644
--- a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/coder/json/provider/JSONProvider.java
+++ b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/coder/json/provider/JSONProvider.java
@@ -11,6 +11,8 @@
import org.eclipse.basyx.vab.coder.json.serialization.GSONToolsFactory;
import org.eclipse.basyx.vab.exception.LostHTTPRequestParameterException;
import org.eclipse.basyx.vab.exception.ServerException;
+import org.eclipse.basyx.vab.exception.provider.MalformedRequestException;
+import org.eclipse.basyx.vab.exception.provider.ProviderException;
import org.eclipse.basyx.vab.modelprovider.api.IModelProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -137,29 +139,26 @@
/**
- * Send JSON encoded response
- */
- private void sendJSONResponse(PrintWriter outputStream, String jsonValue) {
- // Write result to output stream
- outputStream.write(jsonValue);
- outputStream.flush();
- }
-
-
- /**
* Send Error
* @param e
* @param path
* @param resp
*/
- private void sendException(PrintWriter resp, Exception e) {
- logger.error("Exception in sendException", e);
+ private void sendException(PrintWriter resp, Exception e) throws ProviderException {
// Serialize Exception
String jsonString = serialize(e);
- // Send error response
- sendJSONResponse(resp, jsonString);
+ resp.write(jsonString);
+
+ //If the Exception is a ProviderException, just rethrow it
+ if(e instanceof ProviderException) {
+ throw (ProviderException) e;
+ }
+
+ //If the Exception is not a ProviderException encapsulate it in one and log it
+ logger.error("Unknown Exception in JSONProvider", e);
+ throw new ProviderException(e);
}
@@ -170,24 +169,31 @@
* @param serializedJSONValue
* @param outputStream
* @return
+ * @throws MalformedRequestException
* @throws LostHTTPRequestParameterException
* @throws ServerException
*/
- private Object extractParameter(String path, String serializedJSONValue, PrintWriter outputStream) {
+ private Object extractParameter(String path, String serializedJSONValue, PrintWriter outputStream) throws MalformedRequestException {
// Return value
Object result = null;
- // Deserialize json body
- result = serializer.deserialize(serializedJSONValue);
-
+ try {
+ // Deserialize json body
+ result = serializer.deserialize(serializedJSONValue);
+ } catch (Exception e) {
+ //JSON could not be deserialized
+ throw new MalformedRequestException(e);
+ }
+
return result;
}
/**
* Process a BaSys get operation, return JSON serialized result
+ * @throws ProviderException
*/
- public void processBaSysGet(String path, PrintWriter outputStream) {
+ public void processBaSysGet(String path, PrintWriter outputStream) throws ProviderException {
try {
// Get requested value from provider backend
@@ -197,7 +203,7 @@
String jsonString = serialize(true, value, null);
// Send response
- sendJSONResponse(outputStream, jsonString);
+ outputStream.write(jsonString);
} catch (Exception e) {
sendException(outputStream, e);
}
@@ -210,8 +216,9 @@
* @param path
* @param serializedJSONValue
* @param outputStream
+ * @throws ProviderException
*/
- public void processBaSysSet(String path, String serializedJSONValue, PrintWriter outputStream) {
+ public void processBaSysSet(String path, String serializedJSONValue, PrintWriter outputStream) throws ProviderException {
// Try to set value of BaSys VAB element
try {
@@ -226,7 +233,7 @@
String jsonString = serialize(true);
// Send response
- sendJSONResponse(outputStream, jsonString);
+ outputStream.write(jsonString);
} catch (Exception e) {
sendException(outputStream, e);
@@ -236,9 +243,10 @@
/**
* Process a BaSys invoke operation
+ * @throws ProviderException
*/
@SuppressWarnings("unchecked")
- public void processBaSysInvoke(String path, String serializedJSONValue, PrintWriter outputStream) {
+ public void processBaSysInvoke(String path, String serializedJSONValue, PrintWriter outputStream) throws ProviderException {
try {
@@ -271,7 +279,7 @@
String jsonString = serialize(true, result, null);
// Send response
- sendJSONResponse(outputStream, jsonString);
+ outputStream.write(jsonString);
} catch (Exception e) {
sendException(outputStream, e);
@@ -285,8 +293,9 @@
* @param path
* @param serializedJSONValue If this parameter is not null (basystype),we remove an element from a collection by index / remove from map by key. We assume that the parameter only contains 1 element
* @param outputStream
+ * @throws ProviderException
*/
- public void processBaSysDelete(String path, String serializedJSONValue, PrintWriter outputStream) {
+ public void processBaSysDelete(String path, String serializedJSONValue, PrintWriter outputStream) throws ProviderException {
try {
@@ -304,7 +313,7 @@
String jsonString = serialize(true);
// Send response
- sendJSONResponse(outputStream, jsonString);
+ outputStream.write(jsonString);
} catch (Exception e) {
sendException(outputStream, e);
@@ -318,8 +327,9 @@
* @param path
* @param parameter
* @param outputStream
+ * @throws ProviderException
*/
- public void processBaSysCreate(String path, String serializedJSONValue, PrintWriter outputStream) {
+ public void processBaSysCreate(String path, String serializedJSONValue, PrintWriter outputStream) throws ProviderException {
try {
// Deserialize json body.
@@ -332,7 +342,7 @@
String jsonString = serialize(true);
// Send response
- sendJSONResponse(outputStream, jsonString);
+ outputStream.write(jsonString);
} catch (Exception e) {
sendException(outputStream, e);
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/ResourceNotFoundException.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/ResourceNotFoundException.java
deleted file mode 100644
index ac79925..0000000
--- a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/ResourceNotFoundException.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.eclipse.basyx.vab.exception;
-
-
-/**
- * Exception that indicates that a requested resource (AAS, sub model, property) was not found
- *
- * @author kuhn
- *
- */
-public class ResourceNotFoundException extends RuntimeException {
-
-
- /**
- * Version information for serialized instances
- */
- private static final long serialVersionUID = 1L;
-
-
- /**
- * Store message
- */
- protected String message = null;
-
-
-
- /**
- * Constructor
- */
- public ResourceNotFoundException(String msg) {
- // Store message
- message = msg;
- }
-
-
-
- /**
- * Return detailed message
- */
- @Override
- public String getMessage() {
- return message;
- }
-}
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/MalformedRequestException.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/MalformedRequestException.java
new file mode 100644
index 0000000..02f92a0
--- /dev/null
+++ b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/MalformedRequestException.java
@@ -0,0 +1,30 @@
+package org.eclipse.basyx.vab.exception.provider;
+
+/**
+ * Used to indicate by a ModelProvider,
+ * that a given request was malformed. <br/>
+ * e.g. an invalid path or a invalid JSON.
+ *
+ * @author conradi
+ *
+ */
+public class MalformedRequestException extends ProviderException {
+
+
+ /**
+ * Version information for serialized instances
+ */
+ private static final long serialVersionUID = 1L;
+
+
+ /**
+ * Constructor
+ */
+ public MalformedRequestException(String msg) {
+ super(msg);
+ }
+
+ public MalformedRequestException(Exception e) {
+ super(e);
+ }
+}
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/ProviderException.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/ProviderException.java
new file mode 100644
index 0000000..418a81e
--- /dev/null
+++ b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/ProviderException.java
@@ -0,0 +1,45 @@
+package org.eclipse.basyx.vab.exception.provider;
+
+/**
+ * Used to indicate a general exception in a ModelProvider
+ *
+ * @author conradi
+ *
+ */
+public class ProviderException extends Exception {
+
+
+ /**
+ * Version information for serialized instances
+ */
+ private static final long serialVersionUID = 1L;
+
+
+ /**
+ * Store message
+ */
+ protected String message = null;
+
+
+ /**
+ * Constructor
+ */
+ public ProviderException(String msg) {
+ // Store message
+ message = msg;
+ }
+
+
+ public ProviderException(Exception e) {
+ super(e);
+ }
+
+
+ /**
+ * Return detailed message
+ */
+ @Override
+ public String getMessage() {
+ return message;
+ }
+}
\ No newline at end of file
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/ResourceAlreadyExistsException.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/ResourceAlreadyExistsException.java
new file mode 100644
index 0000000..5289ae0
--- /dev/null
+++ b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/ResourceAlreadyExistsException.java
@@ -0,0 +1,29 @@
+package org.eclipse.basyx.vab.exception.provider;
+
+/**
+ * Used to indicate by a ModelProvider,
+ * that a resource to be created already exists
+ *
+ * @author conradi
+ *
+ */
+public class ResourceAlreadyExistsException extends ProviderException {
+
+
+ /**
+ * Version information for serialized instances
+ */
+ private static final long serialVersionUID = 1L;
+
+
+ /**
+ * Constructor
+ */
+ public ResourceAlreadyExistsException(String msg) {
+ super(msg);
+ }
+
+ public ResourceAlreadyExistsException(Exception e) {
+ super(e);
+ }
+}
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/ResourceNotFoundException.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/ResourceNotFoundException.java
new file mode 100644
index 0000000..9c58147
--- /dev/null
+++ b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/exception/provider/ResourceNotFoundException.java
@@ -0,0 +1,29 @@
+package org.eclipse.basyx.vab.exception.provider;
+
+
+/**
+ * Exception that indicates that a requested resource (AAS, sub model, property) was not found
+ *
+ * @author kuhn
+ *
+ */
+public class ResourceNotFoundException extends ProviderException {
+
+
+ /**
+ * Version information for serialized instances
+ */
+ private static final long serialVersionUID = 1L;
+
+
+ /**
+ * Constructor
+ */
+ public ResourceNotFoundException(String msg) {
+ super(msg);
+ }
+
+ public ResourceNotFoundException(Exception e) {
+ super(e);
+ }
+}
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/basyx/server/VABBaSyxTCPInterface.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/basyx/server/VABBaSyxTCPInterface.java
index b13416b..4d1fd95 100644
--- a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/basyx/server/VABBaSyxTCPInterface.java
+++ b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/basyx/server/VABBaSyxTCPInterface.java
@@ -8,6 +8,7 @@
import java.nio.channels.SocketChannel;
import org.eclipse.basyx.vab.coder.json.provider.JSONProvider;
+import org.eclipse.basyx.vab.exception.provider.ProviderException;
import org.eclipse.basyx.vab.modelprovider.api.IModelProvider;
import org.eclipse.basyx.vab.protocol.basyx.CoderTools;
import org.slf4j.Logger;
@@ -92,11 +93,20 @@
String path = new String(rxFrame, 1 + 4, pathLen);
// Forward request to provider
- providerBackend.processBaSysGet(path, output);
+ try {
+ providerBackend.processBaSysGet(path, output);
+ } catch (ProviderException e) {
+ logger.error("Exception in BASYX_GET", e);
+ // Catch Exceptions from JSONProvider
+ // No further action here, as the current version
+ // of the TCP-Mapping states, that always Statuscode 0x00
+ // should be returned with Exceptions encoded in returned String
+ }
// System.out.println("Processed GET:"+path);
// Send response frame
+ output.flush();
sendResponseFrame(byteArrayOutput);
break;
@@ -111,9 +121,18 @@
String jsonValue = new String(rxFrame, 1 + 4 + pathLen + 4, jsonValueLen);
// Invoke get operation
- providerBackend.processBaSysSet(path, jsonValue, output);
+ try {
+ providerBackend.processBaSysSet(path, jsonValue, output);
+ } catch (ProviderException e) {
+ logger.error("Exception in BASYX_SET", e);
+ // Catch Exceptions from JSONProvider
+ // No further action here, as the current version
+ // of the TCP-Mapping states, that always Statuscode 0x00
+ // should be returned with Exceptions encoded in returned String
+ }
// Send response frame
+ output.flush();
sendResponseFrame(byteArrayOutput);
break;
@@ -128,9 +147,18 @@
String jsonValue = new String(rxFrame, 1 + 4 + pathLen + 4, jsonValueLen);
// Invoke get operation
- providerBackend.processBaSysCreate(path, jsonValue, output);
+ try {
+ providerBackend.processBaSysCreate(path, jsonValue, output);
+ } catch (ProviderException e) {
+ logger.error("Exception in BASYX_CREATE", e);
+ // Catch Exceptions from JSONProvider
+ // No further action here, as the current version
+ // of the TCP-Mapping states, that always Statuscode 0x00
+ // should be returned with Exceptions encoded in returned String
+ }
// Send response frame
+ output.flush();
sendResponseFrame(byteArrayOutput);
break;
@@ -154,9 +182,18 @@
}
// Invoke delete operation
- providerBackend.processBaSysDelete(path, jsonValue, output);
+ try {
+ providerBackend.processBaSysDelete(path, jsonValue, output);
+ } catch (ProviderException e) {
+ logger.error("Exception in BASYX_DELETE", e);
+ // Catch Exceptions from JSONProvider
+ // No further action here, as the current version
+ // of the TCP-Mapping states, that always Statuscode 0x00
+ // should be returned with Exceptions encoded in returned String
+ }
// Send response frame
+ output.flush();
sendResponseFrame(byteArrayOutput);
break;
@@ -170,9 +207,18 @@
int jsonValueLen = CoderTools.getInt32(rxFrame, 1 + 4 + pathLen);
String jsonValue = new String(rxFrame, 1 + 4 + pathLen + 4, jsonValueLen);
// Invoke get operation
- providerBackend.processBaSysInvoke(path, jsonValue, output);
+ try {
+ providerBackend.processBaSysInvoke(path, jsonValue, output);
+ } catch (ProviderException e) {
+ logger.error("Exception in BASYX_INVOKE", e);
+ // Catch Exceptions from JSONProvider
+ // No further action here, as the current version
+ // of the TCP-Mapping states, that always Statuscode 0x00
+ // should be returned with Exceptions encoded in returned String
+ }
// Send response frame
+ output.flush();
sendResponseFrame(byteArrayOutput);
break;
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/http/server/ExceptionToHTTPCodeMapper.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/http/server/ExceptionToHTTPCodeMapper.java
new file mode 100644
index 0000000..454b9dd
--- /dev/null
+++ b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/http/server/ExceptionToHTTPCodeMapper.java
@@ -0,0 +1,36 @@
+package org.eclipse.basyx.vab.protocol.http.server;
+
+import org.eclipse.basyx.vab.exception.provider.MalformedRequestException;
+import org.eclipse.basyx.vab.exception.provider.ProviderException;
+import org.eclipse.basyx.vab.exception.provider.ResourceAlreadyExistsException;
+import org.eclipse.basyx.vab.exception.provider.ResourceNotFoundException;
+
+/**
+ * Maps Exceptions from providers to HTTP-Codes
+ *
+ * @author conradi
+ *
+ */
+public class ExceptionToHTTPCodeMapper {
+
+
+ /**
+ * Maps ProviderExceptions to HTTP-Codes
+ *
+ * @param e The thrown ProviderException
+ * @return HTTP-Code
+ */
+ public static int mapException(ProviderException e) {
+
+ if(e instanceof MalformedRequestException) {
+ return 400;
+ } else if(e instanceof ResourceAlreadyExistsException) {
+ return 422;
+ } else if(e instanceof ResourceNotFoundException) {
+ return 404;
+ }
+ return 500;
+
+ }
+
+}
diff --git a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/http/server/VABHTTPInterface.java b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/http/server/VABHTTPInterface.java
index cdb470e..8d7ba63 100644
--- a/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/http/server/VABHTTPInterface.java
+++ b/sdks/java/basys.sdk/src/main/java/org/eclipse/basyx/vab/protocol/http/server/VABHTTPInterface.java
@@ -3,13 +3,13 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
-
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.basyx.vab.coder.json.provider.JSONProvider;
+import org.eclipse.basyx.vab.exception.provider.ProviderException;
import org.eclipse.basyx.vab.modelprovider.VABPathTools;
import org.eclipse.basyx.vab.modelprovider.api.IModelProvider;
@@ -91,9 +91,22 @@
// Setup HTML response header
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
-
- // Process get request
- providerBackend.processBaSysGet(path, resp.getWriter());
+
+ resp.setStatus(200);
+
+ PrintWriter responseWriter = resp.getWriter();
+
+ try {
+ // Process get request
+ providerBackend.processBaSysGet(path, responseWriter);
+ responseWriter.flush();
+ } catch(ProviderException e) {
+ int httpCode = ExceptionToHTTPCodeMapper.mapException(e);
+ resp.setStatus(httpCode);
+ responseWriter.flush();
+ logger.error("Exception in HTTP-GET. Response-code: " + httpCode, e);
+ }
+
}
@@ -106,7 +119,19 @@
String serValue = extractSerializedValue(req);
logger.trace("DoPut: {}", serValue);
- providerBackend.processBaSysSet(path, serValue.toString(), resp.getWriter());
+ resp.setStatus(200);
+
+ PrintWriter responseWriter = resp.getWriter();
+
+ try {
+ providerBackend.processBaSysSet(path, serValue.toString(), responseWriter);
+ responseWriter.flush();
+ } catch(ProviderException e) {
+ int httpCode = ExceptionToHTTPCodeMapper.mapException(e);
+ resp.setStatus(httpCode);
+ responseWriter.flush();
+ logger.error("Exception in HTTP-PUT. Response-code: " + httpCode, e);
+ }
}
@@ -123,16 +148,35 @@
logger.trace("DoPost: {}", serValue);
// Setup HTML response header
+ resp.setStatus(201);
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
+
+ PrintWriter responseWriter = resp.getWriter();
// Check if request is for property creation or operation invoke
if (VABPathTools.isOperationPath(path)) {
// Invoke BaSys VAB 'invoke' primitive
- providerBackend.processBaSysInvoke(path, serValue, resp.getWriter());
+ try {
+ providerBackend.processBaSysInvoke(path, serValue, responseWriter);
+ responseWriter.flush();
+ } catch(ProviderException e) {
+ int httpCode = ExceptionToHTTPCodeMapper.mapException(e);
+ resp.setStatus(httpCode);
+ responseWriter.flush();
+ logger.error("Exception in HTTP-POST. Response-code: " + httpCode, e);
+ }
} else {
// Invoke the BaSys 'create' primitive
- providerBackend.processBaSysCreate(path, serValue, resp.getWriter());
+ try {
+ providerBackend.processBaSysCreate(path, serValue, responseWriter);
+ responseWriter.flush();
+ } catch(ProviderException e) {
+ int httpCode = ExceptionToHTTPCodeMapper.mapException(e);
+ resp.setStatus(httpCode);
+ responseWriter.flush();
+ logger.error("Exception in HTTP-POST. Response-code: " + httpCode, e);
+ }
}
}
@@ -147,7 +191,19 @@
String serValue = extractSerializedValue(req);
logger.trace("DoPatch: {}", serValue);
- providerBackend.processBaSysDelete(path, serValue, resp.getWriter());
+ resp.setStatus(200);
+
+ PrintWriter responseWriter = resp.getWriter();
+
+ try {
+ providerBackend.processBaSysDelete(path, serValue, responseWriter);
+ responseWriter.flush();
+ } catch(ProviderException e) {
+ int httpCode = ExceptionToHTTPCodeMapper.mapException(e);
+ resp.setStatus(httpCode);
+ responseWriter.flush();
+ logger.error("Exception in HTTP-PATCH. Response-code: " + httpCode, e);
+ }
}
@@ -161,7 +217,19 @@
// No parameter to read! Provide serialized null
String nullParam = "";
- providerBackend.processBaSysDelete(path, nullParam, resp.getWriter());
+ resp.setStatus(200);
+
+ PrintWriter responseWriter = resp.getWriter();
+
+ try {
+ providerBackend.processBaSysDelete(path, nullParam, responseWriter);
+ responseWriter.flush();
+ } catch(ProviderException e) {
+ int httpCode = ExceptionToHTTPCodeMapper.mapException(e);
+ resp.setStatus(httpCode);
+ responseWriter.flush();
+ logger.error("Exception in HTTP-DELETE. Response-code: " + httpCode, e);
+ }
}
diff --git a/sdks/java/basys.sdk/src/test/java/org/eclipse/basyx/testsuite/regression/vab/coder/json/IBasyxConnectorFacade.java b/sdks/java/basys.sdk/src/test/java/org/eclipse/basyx/testsuite/regression/vab/coder/json/IBasyxConnectorFacade.java
index 4183b75..50ffb75 100644
--- a/sdks/java/basys.sdk/src/test/java/org/eclipse/basyx/testsuite/regression/vab/coder/json/IBasyxConnectorFacade.java
+++ b/sdks/java/basys.sdk/src/test/java/org/eclipse/basyx/testsuite/regression/vab/coder/json/IBasyxConnectorFacade.java
@@ -3,6 +3,7 @@
import java.io.FileNotFoundException;
import org.eclipse.basyx.vab.coder.json.provider.JSONProvider;
+import org.eclipse.basyx.vab.exception.provider.ProviderException;
import org.eclipse.basyx.vab.modelprovider.api.IModelProvider;
import org.eclipse.basyx.vab.protocol.api.IBaSyxConnector;
import org.slf4j.Logger;
@@ -39,7 +40,7 @@
provider.processBaSysGet(path, outputstream);
return outputstream.getResult();
- } catch (FileNotFoundException e) {
+ } catch (FileNotFoundException | ProviderException e) {
logger.error("[TEST] Exception in getModelPropertyValue", e);
}
return null;