Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions include/odva_ethernetip/path.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ class Path
*/
Path(EIP_USINT class_id, EIP_USINT instance_id, EIP_USINT attribute_id,
bool pad_after_length = false);
Path(EIP_USINT class_id, EIP_USINT instance_id, EIP_UINT attribute_id,
bool pad_after_length = false);

/**
* Shortcut to construct a path to the given logical class instance
Expand Down Expand Up @@ -107,6 +109,7 @@ class Path
* @param attribute_id ID Number of attribute to add to path
*/
void addLogicalAttribute(EIP_USINT attribute_id);
void addLogicalAttribute(EIP_UINT attribute_id);

/**
* Add a logical connection point segment
Expand Down Expand Up @@ -166,6 +169,11 @@ class Path
throw std::logic_error("Not implemented");
}

/**
* Prints path bytes at std output
*/
void print() const;

private:
bool pad_after_length_;
vector<EIP_USINT> path_buf_;
Expand All @@ -176,6 +184,7 @@ class Path
* @param data Data to add to path
*/
void addSegment(EIP_USINT type, EIP_USINT data);
void addSegment(EIP_USINT type, EIP_UINT data);
};

} // namespace eip
Expand Down
19 changes: 19 additions & 0 deletions include/odva_ethernetip/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ class Session
*/
void getSingleAttributeSerializable(EIP_USINT class_id, EIP_USINT instance_id,
EIP_USINT attribute_id, Serializable& result);
void getSingleAttributeSerializable(EIP_USINT class_id, EIP_USINT instance_id,
EIP_UINT attribute_id, Serializable& result);

/**
* Shortcut to get a single attribute as a primitive type
Expand All @@ -120,6 +122,13 @@ class Session
getSingleAttributeSerializable(class_id, instance_id, attribute_id, data);
return data.data;
}
template <typename T>
T getSingleAttribute(EIP_USINT class_id, EIP_USINT instance_id, EIP_UINT attribute_id, T v)
{
SerializablePrimitive<T> data;
getSingleAttributeSerializable(class_id, instance_id, attribute_id, data);
return data.data;
}

/**
* Set a single attribute from the given class / instance / attribute path
Expand All @@ -130,6 +139,8 @@ class Session
*/
void setSingleAttributeSerializable(EIP_USINT class_id, EIP_USINT instance_id,
EIP_USINT attribute_id, shared_ptr<Serializable> data);
void setSingleAttributeSerializable(EIP_USINT class_id, EIP_USINT instance_id,
EIP_UINT attribute_id, shared_ptr<Serializable> data);

/**
* Shortcut to set a single attribute from a primitive type
Expand All @@ -146,6 +157,14 @@ class Session
make_shared< SerializablePrimitive<T> > (v);
setSingleAttributeSerializable(class_id, instance_id, attribute_id, data);
}
template <typename T>
void setSingleAttribute(EIP_USINT class_id, EIP_USINT instance_id,
EIP_UINT attribute_id, T v)
{
shared_ptr< SerializablePrimitive<T> > data =
make_shared< SerializablePrimitive<T> > (v);
setSingleAttributeSerializable(class_id, instance_id, attribute_id, data);
}

/**
* Create an Ethernet/IP Connection for sending implicit messages
Expand Down
40 changes: 40 additions & 0 deletions src/path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI

#include <boost/asio.hpp>

#include <iostream>
#include <unistd.h>
#include <iomanip>

namespace eip {

Path::Path(bool pad_after_length) : pad_after_length_(pad_after_length)
Expand All @@ -41,13 +45,26 @@ Path::Path(EIP_USINT class_id, EIP_USINT instance_id, EIP_USINT attribute_id,
addLogicalClass(class_id);
addLogicalInstance(instance_id);
addLogicalAttribute(attribute_id);
//this->print();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove (here and elsewhere).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, they can be removed. They were used for debugging.

}

Path::Path(EIP_USINT class_id, EIP_USINT instance_id, EIP_UINT attribute_id,
bool pad_after_length) : pad_after_length_(pad_after_length)
{
path_buf_.reserve(7);
addLogicalClass(class_id);
addLogicalInstance(instance_id);
addLogicalAttribute(attribute_id);
//this->print();
}


Path::Path(EIP_USINT class_id, EIP_USINT instance_id) : pad_after_length_(false)
{
path_buf_.reserve(4);
addLogicalClass(class_id);
addLogicalInstance(instance_id);
//this->print();
}

void Path::addSegment(EIP_USINT type, EIP_USINT data)
Expand All @@ -56,6 +73,13 @@ void Path::addSegment(EIP_USINT type, EIP_USINT data)
path_buf_.push_back(data);
}

void Path::addSegment(EIP_USINT type, EIP_UINT data)
{
path_buf_.push_back(type);
path_buf_.push_back( (EIP_USINT)(data&0xff) );
path_buf_.push_back( (EIP_USINT)((data&0xff00)>>8) );
}

void Path::addLogicalClass(EIP_USINT class_id)
{
addSegment(0x20, class_id);
Expand All @@ -71,6 +95,11 @@ void Path::addLogicalAttribute(EIP_USINT attribute_id)
addSegment(0x30, attribute_id);
}

void Path::addLogicalAttribute(EIP_UINT attribute_id)
{
addSegment(0x31, attribute_id);
}

void Path::addLogicalConnectionPoint(EIP_USINT connection_id)
{
addSegment(0x2C, connection_id);
Expand All @@ -94,4 +123,15 @@ Writer& Path::serialize(Writer& writer, bool pad_after_length) const
return writer;
}

void Path::print() const
{
std::cout << "PATH: ";
for (unsigned int ii=0; ii< path_buf_.size(); ii++)
{
std::cout << std::hex << std::setfill('0') << std::setw(2) << (unsigned short int)path_buf_.at(ii);
if ( (ii+1)%2 == 0 ) std::cout << " ";
}
std::cout << std::dec << std::endl;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the pattern thus far in this code was to write to cout and avoid an explicit ros dependency, but I'd rather just pull in ros/console.h and use the established logging framework— then everything can be left on as debug-level outputs and simply enabled at runtime as required.

Note also that rather than printing here, you can implement a stream operator, which allows the printing code to decide on logging level rather than hard-coding it in the print function. Eg:

std::ostream& operator<<(std::ostream& os, const Path& p)
{
  os << "PATH ";
  // for ...
  return os;
}

And then later:

ROS_DEBUG_STREAM("Processing " << my_path);

Thoughts on this as an alternative approach?

Copy link
Author

@andreucm andreucm Feb 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea to overload the operator "<<"
Doing so, as far as I understand we keep the no-dependency on ros/console at this level of the library, but it can be used with ROS_xx_STREAM()
thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another possibility if you want to log while avoiding an explicit ros/control.h include is to use console_bridge:

http://wiki.ros.org/console_bridge


} // namespace eip
34 changes: 25 additions & 9 deletions src/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ Session::Session(shared_ptr<Socket> socket, shared_ptr<Socket> io_socket,
boost::random::uniform_int_distribution<> dist(0, 0xFFFF);
next_connection_id_ = gen();
next_connection_sn_ = dist(gen);
cout << "Generated starting connection ID " << next_connection_id_
<< " and SN " << next_connection_sn_ << endl;;
//cout << "Generated starting connection ID " << next_connection_id_ << " and SN " << next_connection_sn_ << endl;;
}

Session::~Session()
Expand All @@ -81,12 +80,12 @@ Session::~Session()

void Session::open(string hostname, string port, string io_port)
{
cout << "Resolving hostname and connecting socket" << endl;
//cout << "Resolving hostname and connecting socket" << endl;
socket_->open(hostname, port);
io_socket_->open(hostname, io_port);

// create the registration message
cout << "Creating and sending the registration message" << endl;
//cout << "Creating and sending the registration message" << endl;
shared_ptr<RegisterSessionData> reg_data = make_shared<RegisterSessionData>();
EncapPacket reg_msg(EIP_CMD_REGISTER_SESSION, 0, reg_data);

Expand Down Expand Up @@ -149,7 +148,7 @@ void Session::open(string hostname, string port, string io_port)
}

session_id_ = response.getHeader().session_handle;
cout << "Successfully opened session ID " << session_id_ << endl;
//cout << "Successfully opened session ID " << session_id_ << endl;
}

void Session::close()
Expand All @@ -170,12 +169,12 @@ void Session::close()

EncapPacket Session::sendCommand(EncapPacket& req)
{
cout << "Sending Command" << endl;
//cout << "Sending Command" << endl;
socket_->send(req);

cout << "Waiting for response" << endl;
//cout << "Waiting for response" << endl;
size_t n = socket_->receive(buffer(recv_buffer_));
cout << "Received response of " << n << " bytes" << endl;
//cout << "Received response of " << n << " bytes" << endl;

BufferReader reader(buffer(recv_buffer_, n));
EncapPacket result;
Expand Down Expand Up @@ -237,17 +236,34 @@ void Session::getSingleAttributeSerializable(EIP_USINT class_id, EIP_USINT insta
resp_data.getResponseDataAs(result);
}

void Session::getSingleAttributeSerializable(EIP_USINT class_id, EIP_USINT instance_id,
EIP_UINT attribute_id, Serializable& result)
{
shared_ptr<Serializable> no_data;
RRDataResponse resp_data = sendRRDataCommand(0x0E,
Path(class_id, instance_id, attribute_id), no_data);

resp_data.getResponseDataAs(result);
}


void Session::setSingleAttributeSerializable(EIP_USINT class_id,
EIP_USINT instance_id, EIP_USINT attribute_id, shared_ptr<Serializable> data)
{
RRDataResponse resp_data = sendRRDataCommand(0x10,
Path(class_id, instance_id, attribute_id), data);
}
void Session::setSingleAttributeSerializable(EIP_USINT class_id,
EIP_USINT instance_id, EIP_UINT attribute_id, shared_ptr<Serializable> data)
{
RRDataResponse resp_data = sendRRDataCommand(0x10,
Path(class_id, instance_id, attribute_id), data);
}

RRDataResponse Session::sendRRDataCommand(EIP_USINT service, const Path& path,
shared_ptr<Serializable> data)
{
cout << "Creating RR Data Request" << endl;
//cout << "Creating RR Data Request" << endl;
shared_ptr<RRDataRequest> req_data =
make_shared<RRDataRequest> (service, path, data);
EncapPacket encap_pkt(EIP_CMD_SEND_RR_DATA, session_id_, req_data);
Expand Down