diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c11b39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.ci_config + +# Vim +.*.swp +.*.swo diff --git a/include/odva_ethernetip/connection.h b/include/odva_ethernetip/connection.h index 17ad9df..8c3b8bf 100644 --- a/include/odva_ethernetip/connection.h +++ b/include/odva_ethernetip/connection.h @@ -66,6 +66,20 @@ class Connection EIP_UINT o_to_t_buffer_size; EIP_UINT t_to_o_buffer_size; + bool o_to_t_variable_size; + bool t_to_o_variable_size; + + CONN_PRIORITY_T o_to_t_priority; + CONN_PRIORITY_T t_to_o_priority; + + CONN_TYPE_T o_to_t_type; + CONN_TYPE_T t_to_o_type; + + bool o_to_t_shared; + bool t_to_o_shared; + + EIP_BYTE connection_type_; + /** * Create a connection instance using the directional values and reasonable defaults * @param o_to_t Originator to Target info @@ -73,6 +87,23 @@ class Connection */ Connection(const EIP_CONNECTION_INFO_T& o_to_t, const EIP_CONNECTION_INFO_T& t_to_o); + /** + * Create a connection instance using the directional values and reasonable defaults + * @param o_to_t Originator to Target info + * @param t_to_o Target to Originator info + * @param connection_type Byte + */ + Connection(const EIP_CONNECTION_INFO_T& o_to_t, const EIP_CONNECTION_INFO_T& t_to_o, EIP_BYTE connection_type); + + /** + * Create a connection instance using the directional values and reasonable defaults + * @param o_to_t Originator to Target info + * @param t_to_o Target to Originator info + * @param connection_type Byte + * @param path Path already containing segments + */ + Connection(const EIP_CONNECTION_INFO_T& o_to_t, const EIP_CONNECTION_INFO_T& t_to_o, EIP_BYTE connection_type, const Path *path); + /** * Get the path in the given message router request * @return reference to the current Path @@ -92,7 +123,17 @@ class Connection /** * Create the forward open request from the data in this connection object */ - shared_ptr createForwardOpenRequest(); + shared_ptr createForwardOpenRequest() + { + // Maintains backwards compatability + return createForwardOpenRequest(false); + } + + /** + * Create the forward open request from the data in this connection object + * @param use_legacy_forward_open_request use 16 bit connection parameters instead of news 32 bit parameters + */ + shared_ptr createForwardOpenRequest(bool use_legacy_forward_open_request); /** * Create a forward close request from the data in this connection object diff --git a/include/odva_ethernetip/eip_types.h b/include/odva_ethernetip/eip_types.h index f3774b1..5c450a5 100644 --- a/include/odva_ethernetip/eip_types.h +++ b/include/odva_ethernetip/eip_types.h @@ -94,8 +94,25 @@ typedef enum EIP_ITEM_SEQ_ADDRESS = 0x8002, } EIP_ITEM_T; +typedef enum +{ + CONN_PRIORITY_LOW = 0, + CONN_PRIORITY_HIGH = 1, + CONN_PRIORITY_SCHEDULED = 2, + CONN_PRIORITY_URGENT = 3, +} CONN_PRIORITY_T; + +typedef enum +{ + CONN_TYPE_NULL = 0, + CONN_TYPE_MULTICAST = 1, + CONN_TYPE_P2P = 2, + CONN_TYPE_RESERVED = 3, +} CONN_TYPE_T; + typedef struct { + virtual void f() {}; // This allows dynamic_cast /// Assembly ID for this endpoint of the connection EIP_USINT assembly_id; /// Buffer size to be used for routing @@ -104,4 +121,16 @@ typedef struct EIP_UDINT rpi; } EIP_CONNECTION_INFO_T; +typedef struct _EIP_CONNECTION_INFO_ADVANCED_T : EIP_CONNECTION_INFO_T +{ + // Variable Size: default o_to_t false, default t_to_o true + bool variable_size; + // Priority: default CONN_PRIORITY_SCHEDULED + CONN_PRIORITY_T priority; + //Connection Type: default CONN_TYPE_P2P + CONN_TYPE_T type; + // Connection Type: default false + bool shared; +} EIP_CONNECTION_INFO_ADVANCED_T; + #endif // ODVA_ETHERNETIP_EIP_TYPES_H diff --git a/include/odva_ethernetip/forward_open_request.h b/include/odva_ethernetip/forward_open_request.h index 6489282..088ec37 100644 --- a/include/odva_ethernetip/forward_open_request.h +++ b/include/odva_ethernetip/forward_open_request.h @@ -43,22 +43,6 @@ using serialization::Serializable; using serialization::Reader; using serialization::Writer; -typedef enum -{ - CONN_PRIORITY_LOW = 0, - CONN_PRIORITY_HIGH = 1, - CONN_PRIORITY_SCHEDULED = 2, - CONN_PRIORITY_URGENT = 3, -} CONN_PRIORITY_T; - -typedef enum -{ - CONN_TYPE_NULL = 0, - CONN_TYPE_MULTICAST = 1, - CONN_TYPE_P2P = 2, - CONN_TYPE_RESERVED = 3, -} CONN_TYPE_T; - /** * Class to encapsulate a ForwardOpenRequest data. Note that this is currently * only LARGE forward open, but could be easily changed to support both. @@ -77,12 +61,26 @@ class ForwardOpenRequest : public Serializable EIP_USINT timeout_multiplyer; EIP_UDINT o_to_t_rpi; EIP_DWORD o_to_t_conn_params; + EIP_WORD o_to_t_conn_params_legacy; EIP_UDINT t_to_o_rpi; EIP_DWORD t_to_o_conn_params; + EIP_WORD t_to_o_conn_params_legacy; EIP_BYTE conn_type; + bool use_legacy_forward_open_request; + + ForwardOpenRequest() + : use_legacy_forward_open_request(false) + { + } + + ForwardOpenRequest(bool use_legacy_forward_open_request) + : use_legacy_forward_open_request(use_legacy_forward_open_request) + { + } + /** - * Helper to calculate connection parameters + * Helper to calculate connection parameters for current 32 bit connection parameters * @param size Maximum size of the messages in the connection in byte * @param variable if set to true, variable message sizes * @param priority Priority value for the connection @@ -96,13 +94,35 @@ class ForwardOpenRequest : public Serializable | (type & 0x03) << 29 | (shared ? 0x80000000 : 0); } + /** + * Helper to calculate connection parameters for legacy 16 bit connection parameters + * @param size Maximum size of the messages in the connection in byte + * @param variable if set to true, variable message sizes + * @param priority Priority value for the connection + * @param type Connection type / class info + * @param shared If set to true, then a shared connection + */ + static EIP_WORD calcConnectionParamsLegacy(EIP_UINT size, bool variable, EIP_BYTE priority, + EIP_BYTE type, bool shared) + { + return (size & 0x1FF) | (variable ? 0x200 : 0) | (priority & 0x03) << 10 + | (type & 0x03) << 13 | (shared ? 0x8000 : 0); + } + /** * Shortcut to set the origin to target parameters. */ EIP_DWORD setOriginToTargetParams(EIP_UINT size, bool variable, EIP_BYTE priority, EIP_BYTE type, bool shared) { - o_to_t_conn_params = calcConnectionParams(size, variable, priority, type, shared); + if (use_legacy_forward_open_request) + { + o_to_t_conn_params_legacy = calcConnectionParamsLegacy(size, variable, priority, type, shared); + } + else + { + o_to_t_conn_params = calcConnectionParams(size, variable, priority, type, shared); + } return 0; } @@ -112,7 +132,14 @@ class ForwardOpenRequest : public Serializable EIP_DWORD setTargetToOriginParams(EIP_UINT size, bool variable, EIP_BYTE priority, EIP_BYTE type, bool shared) { - t_to_o_conn_params = calcConnectionParams(size, variable, priority, type, shared); + if (use_legacy_forward_open_request) + { + t_to_o_conn_params_legacy = calcConnectionParamsLegacy(size, variable, priority, type, shared); + } + else + { + t_to_o_conn_params = calcConnectionParams(size, variable, priority, type, shared); + } return 0; } @@ -131,21 +158,36 @@ class ForwardOpenRequest : public Serializable */ virtual size_t getLength() const { - return sizeof(timeout_tick_size) - + sizeof(timeout_ticks) - + sizeof(o_to_t_connection_id) - + sizeof(t_to_o_connection_id) - + sizeof(connection_sn) - + sizeof(originator_vendor_id) - + sizeof(originator_sn) - + sizeof(timeout_multiplyer) - + sizeof(o_to_t_rpi) - + sizeof(o_to_t_conn_params) - + sizeof(t_to_o_rpi) - + sizeof(t_to_o_conn_params) - + sizeof(conn_type) - + 3 // reserved bytes - + path_.getLength(); + size_t ret = sizeof(timeout_tick_size); + ret += sizeof(timeout_ticks); + ret += sizeof(o_to_t_connection_id); + ret += sizeof(t_to_o_connection_id); + ret += sizeof(connection_sn); + ret += sizeof(originator_vendor_id); + ret += sizeof(originator_sn); + ret += sizeof(timeout_multiplyer); + ret += sizeof(o_to_t_rpi); + if (use_legacy_forward_open_request) + { + ret += sizeof(o_to_t_conn_params_legacy); + } + else + { + ret += sizeof(o_to_t_conn_params); + } + ret += sizeof(t_to_o_rpi); + if (use_legacy_forward_open_request) + { + ret += sizeof(t_to_o_conn_params_legacy); + } + else + { + ret += sizeof(t_to_o_conn_params); + } + ret += sizeof(conn_type); + ret += 3; // reserved bytes + ret += path_.getLength(); + return ret; } /** @@ -169,9 +211,23 @@ class ForwardOpenRequest : public Serializable writer.write(reserved); writer.write(reserved); writer.write(o_to_t_rpi); - writer.write(o_to_t_conn_params); + if (use_legacy_forward_open_request) + { + writer.write(o_to_t_conn_params_legacy); + } + else + { + writer.write(o_to_t_conn_params); + } writer.write(t_to_o_rpi); - writer.write(t_to_o_conn_params); + if (use_legacy_forward_open_request) + { + writer.write(t_to_o_conn_params_legacy); + } + else + { + writer.write(t_to_o_conn_params); + } writer.write(conn_type); path_.serialize(writer); return writer; diff --git a/include/odva_ethernetip/path.h b/include/odva_ethernetip/path.h index b51f59a..02ab3f4 100644 --- a/include/odva_ethernetip/path.h +++ b/include/odva_ethernetip/path.h @@ -90,6 +90,18 @@ class Path pad_after_length_ = pad; } + /** + * Add a port segment + * @param link_id ID Number of port's link address id to add to path + */ + void addPort(EIP_USINT link_id); + + /** + * Add a port segment + * @param data data vector to add to path + */ + void addData(std::vector data); + /** * Add a logical class segment * @param class_id ID Number of class to add to path diff --git a/include/odva_ethernetip/serialization/buffer_writer.h b/include/odva_ethernetip/serialization/buffer_writer.h index a173321..2643f27 100644 --- a/include/odva_ethernetip/serialization/buffer_writer.h +++ b/include/odva_ethernetip/serialization/buffer_writer.h @@ -76,7 +76,7 @@ class BufferWriter : public Writer using boost::asio::buffer_size; if (buffer_size(buf_) < buffer_size(b)) { - throw std::length_error("Buffer to small to serialize value"); + throw std::length_error("Buffer too small to serialize value"); } boost::asio::buffer_copy(buf_, b); byte_count_ += buffer_size(b); diff --git a/include/odva_ethernetip/session.h b/include/odva_ethernetip/session.h index 4963c64..71d2390 100644 --- a/include/odva_ethernetip/session.h +++ b/include/odva_ethernetip/session.h @@ -87,6 +87,11 @@ class Session */ void close(); + /** + * Close the session without unregistering the session and just closing the port + */ + void closeWithoutUnregister(); + /** * Get the ID number assigned to this session by the target * @return session ID number @@ -96,6 +101,27 @@ class Session return session_id_; } + /** + * Get data from the given service / extended symbol / path + * @param service Service ID + * @param extended_symbol string for register name + * @param data Command Specific Data to send + * @param result Serializable that will accept the result + * @return RRDataResponse + */ + RRDataResponse getExtendedSymbolSerializable(EIP_USINT service, std::string extended_symbol, + shared_ptr data, Serializable& result); + + /** + * Set data to the given service / extended symbol / path + * @param service Service ID + * @param extended_symbol string for register name + * @param data Command Specific Data to send + * @return RRDataResponse + */ + RRDataResponse setExtendedSymbolSerializable(EIP_USINT service, std::string extended_symbol, + shared_ptr data); + /** * Get a single attribute from the given class / instance / attribute path * @param class_id Class ID for the path to get @@ -153,7 +179,48 @@ class Session * @param t_to_o Target to origin connection info */ int createConnection(const EIP_CONNECTION_INFO_T& o_to_t, - const EIP_CONNECTION_INFO_T& t_to_o); + const EIP_CONNECTION_INFO_T& t_to_o) + { + // Maintains backwards compatability + return createConnection(o_to_t, t_to_o, 0x5B, false); + } + + /** + * Create an Ethernet/IP Connection for sending implicit messages + * @param o_to_t Origin to target connection info + * @param t_to_o Target to origin connection info + * @param service Service code to send + * @param use_legacy_forward_open_request use 16 bit connection parameters instead of news 32 bit parameters + */ + int createConnection(const EIP_CONNECTION_INFO_T& o_to_t, + const EIP_CONNECTION_INFO_T& t_to_o, + EIP_USINT service, + bool use_legacy_forward_open_request) + { + Connection conn(o_to_t, t_to_o); + return createConnection(conn, service, use_legacy_forward_open_request); + } + + /** + * Create an Ethernet/IP Connection for sending implicit messages + * @param conn Connection setup with t_to_o, o_to_t and path + */ + int createConnection(Connection conn) + { + // Maintains backwards compatability + return createConnection(conn, 0x5B, false); + } + + /** + * Create an Ethernet/IP Connection for sending implicit messages + * @param o_to_t Origin to target connection info + * @param t_to_o Target to origin connection info + * @param service Service code to send + * @param use_legacy_forward_open_request use 16 bit connection parameters instead of news 32 bit parameters + */ + int createConnection(Connection conn, + EIP_USINT service, + bool use_legacy_forward_open_request); /** * Close the given connection number diff --git a/src/connection.cpp b/src/connection.cpp index 6884d80..3ea3008 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -34,6 +34,16 @@ using boost::make_shared; namespace eip { Connection::Connection(const EIP_CONNECTION_INFO_T& o_to_t, const EIP_CONNECTION_INFO_T& t_to_o) + : Connection(o_to_t, t_to_o, 1, NULL) +{ +} + +Connection::Connection(const EIP_CONNECTION_INFO_T& o_to_t, const EIP_CONNECTION_INFO_T& t_to_o, EIP_BYTE connection_type) + : Connection(o_to_t, t_to_o, connection_type, NULL) +{ +} + +Connection::Connection(const EIP_CONNECTION_INFO_T& o_to_t, const EIP_CONNECTION_INFO_T& t_to_o, EIP_BYTE connection_type, const Path *path) { o_to_t_rpi = o_to_t.rpi; t_to_o_rpi = t_to_o.rpi; @@ -42,9 +52,45 @@ Connection::Connection(const EIP_CONNECTION_INFO_T& o_to_t, const EIP_CONNECTION timeout_tick_size = 6; timeout_ticks = 80; timeout_multiplyer = 0; + if (const EIP_CONNECTION_INFO_ADVANCED_T* o_to_t_advanced = dynamic_cast(&o_to_t)) + { + o_to_t_variable_size = o_to_t_advanced->variable_size; + o_to_t_priority = o_to_t_advanced->priority; + o_to_t_type = o_to_t_advanced->type; + o_to_t_shared = o_to_t_advanced->shared; + } + else + { + o_to_t_variable_size = false; + o_to_t_priority = CONN_PRIORITY_SCHEDULED; + o_to_t_type = CONN_TYPE_P2P; + o_to_t_shared = false; + } + if (const EIP_CONNECTION_INFO_ADVANCED_T* t_to_o_advanced = dynamic_cast(&t_to_o)) + { + t_to_o_variable_size = t_to_o_advanced->variable_size; + t_to_o_priority = t_to_o_advanced->priority; + t_to_o_type = t_to_o_advanced->type; + t_to_o_shared = t_to_o_advanced->shared; + } + else + { + t_to_o_variable_size = true; + t_to_o_priority = CONN_PRIORITY_SCHEDULED; + t_to_o_type = CONN_TYPE_P2P; + t_to_o_shared = false; + } o_to_t_api = 0; t_to_o_api = 0; - setConnectionPoints(o_to_t.assembly_id, t_to_o.assembly_id); + connection_type_ = connection_type; + if (path == NULL) + { + setConnectionPoints(o_to_t.assembly_id, t_to_o.assembly_id); + } + else + { + path_ = *path; + } } void Connection::setConnectionPoints(EIP_USINT origin, EIP_USINT target) @@ -56,9 +102,9 @@ void Connection::setConnectionPoints(EIP_USINT origin, EIP_USINT target) path_.addLogicalConnectionPoint(target); } -shared_ptr Connection::createForwardOpenRequest() +shared_ptr Connection::createForwardOpenRequest(bool use_legacy_forward_open_request) { - shared_ptr req = make_shared (); + shared_ptr req = make_shared (use_legacy_forward_open_request); req->originator_vendor_id = originator_vendor_id; req->originator_sn = originator_sn; @@ -73,11 +119,10 @@ shared_ptr Connection::createForwardOpenRequest() req->o_to_t_rpi = o_to_t_rpi; req->t_to_o_rpi = t_to_o_rpi; - req->conn_type = 1; + req->conn_type = connection_type_; - // TODO: need connection size info here - req->setOriginToTargetParams(o_to_t_buffer_size, false, CONN_PRIORITY_SCHEDULED, CONN_TYPE_P2P, false); - req->setTargetToOriginParams(t_to_o_buffer_size, true, CONN_PRIORITY_SCHEDULED, CONN_TYPE_P2P, false); + req->setOriginToTargetParams(o_to_t_buffer_size, o_to_t_variable_size, o_to_t_priority, o_to_t_type, o_to_t_shared); + req->setTargetToOriginParams(t_to_o_buffer_size, t_to_o_variable_size, t_to_o_priority, t_to_o_type, t_to_o_shared); req->getPath() = path_; return req; diff --git a/src/path.cpp b/src/path.cpp index 2607aa3..17c68ef 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -56,6 +56,18 @@ void Path::addSegment(EIP_USINT type, EIP_USINT data) path_buf_.push_back(data); } +void Path::addPort(EIP_USINT link_id) +{ + addSegment(0x01, link_id); +} + +void Path::addData(std::vector data) +{ + path_buf_.push_back(0x91); + path_buf_.push_back(data.size()); + path_buf_.insert(path_buf_.end(), data.begin(), data.end()); +} + void Path::addLogicalClass(EIP_USINT class_id) { addSegment(0x20, class_id); diff --git a/src/session.cpp b/src/session.cpp index d1ffd30..6ad60f5 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -158,6 +158,11 @@ void Session::close() CONSOLE_BRIDGE_logInform("Session closed"); + closeWithoutUnregister(); +} + +void Session::closeWithoutUnregister() +{ socket_->close(); io_socket_->close(); session_id_ = 0; @@ -237,6 +242,28 @@ void Session::setSingleAttributeSerializable(EIP_USINT class_id, Path(class_id, instance_id, attribute_id), data); } +RRDataResponse Session::getExtendedSymbolSerializable(EIP_USINT service, std::string extended_symbol, shared_ptr data, Serializable& result) +{ + RRDataResponse resp_data = setExtendedSymbolSerializable(service, extended_symbol, data); + + resp_data.getResponseDataAs(result); + + return resp_data; +} + +RRDataResponse Session::setExtendedSymbolSerializable(EIP_USINT service, std::string extended_symbol, shared_ptr data) +{ + Path path(false); + std::vector extended_symbol_vector; + for (auto c: extended_symbol) + { + extended_symbol_vector.push_back((EIP_USINT) c); + } + path.addData(extended_symbol_vector); + + return sendRRDataCommand(service, path, data); +} + RRDataResponse Session::sendRRDataCommand(EIP_USINT service, const Path& path, shared_ptr data) { @@ -293,18 +320,18 @@ RRDataResponse Session::sendRRDataCommand(EIP_USINT service, const Path& path, return resp_data; } -int Session::createConnection(const EIP_CONNECTION_INFO_T& o_to_t, - const EIP_CONNECTION_INFO_T& t_to_o) +int Session::createConnection(Connection conn, + EIP_USINT service, + bool use_legacy_forward_open_request) { - Connection conn(o_to_t, t_to_o); conn.originator_vendor_id = my_vendor_id_; conn.originator_sn = my_serial_num_; conn.connection_sn = next_connection_sn_++; conn.o_to_t_connection_id = next_connection_id_++; conn.t_to_o_connection_id = next_connection_id_++; - shared_ptr req = conn.createForwardOpenRequest(); - RRDataResponse resp_data = sendRRDataCommand(0x5B, Path(0x06, 1), req); + shared_ptr req = conn.createForwardOpenRequest(use_legacy_forward_open_request); + RRDataResponse resp_data = sendRRDataCommand(service, Path(0x06, 1), req); ForwardOpenSuccess result; resp_data.getResponseDataAs(result); if (!conn.verifyForwardOpenResult(result))