diff --git a/CMakeLists.txt b/CMakeLists.txt index a93274b..624b68d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,11 +3,21 @@ project(keyence_experimental) include_directories(include) +add_definitions(-std=c++11 -Wall -Wextra) + +# Boost is used for threads +FIND_PACKAGE( Boost 1.54 COMPONENTS system thread REQUIRED ) + # Build the underlying implementation library add_library(keyence_impl src/impl/keyence_tcp_client.cpp + # messages src/impl/messages/high_speed_single_profile.cpp - src/impl/ljv7_rawdata.cpp) -target_link_libraries(keyence_impl socket++) + src/impl/ljv7_rawdata.cpp + src/impl/messages/prepare_high_speed.cpp + src/impl/messages/start_high_speed.cpp + src/impl/keyence_high_speed_tcp.cpp) + +target_link_libraries(keyence_impl socket++ ${Boost_LIBRARIES}) # Build test program for changing active program add_executable(keyence_change_program src/utilities/change_program.cpp) @@ -16,3 +26,11 @@ target_link_libraries(keyence_change_program keyence_impl) # Build test program for single keyence profile add_executable(keyence_get_profile src/utilities/get_profile.cpp) target_link_libraries(keyence_get_profile keyence_impl) + +# Build test program for starting high speed mode with raw request calls +add_executable(keyence_highspeed_raw src/utilities/high_speed_raw.cpp) +target_link_libraries(keyence_highspeed_raw keyence_impl) + +# Build test program for starting high speed mode with helper interface +add_executable(keyence_highspeed src/utilities/high_speed.cpp) +target_link_libraries(keyence_highspeed keyence_impl) diff --git a/include/keyence/impl/keyence_high_speed_tcp.h b/include/keyence/impl/keyence_high_speed_tcp.h new file mode 100644 index 0000000..12c8f53 --- /dev/null +++ b/include/keyence/impl/keyence_high_speed_tcp.h @@ -0,0 +1,43 @@ +#ifndef KEYENCE_HIGH_SPEED_TCP_H +#define KEYENCE_HIGH_SPEED_TCP_H + +#include "keyence/impl/keyence_tcp_client.h" +#include "keyence/impl/high_speed_defs.h" + +#include + +namespace keyence +{ + +class HighSpeedTcpClient +{ +public: + // Callback types + typedef boost::function callback_type; + + // Constructors + HighSpeedTcpClient(TcpClient& client, const std::string& port); + + ~HighSpeedTcpClient(); + + const ProfileInformation& profileInfo() const; + + void start(callback_type cb); + + void stop(); + + private: + HighSpeedTcpClient operator=(const HighSpeedTcpClient&); // deleted + HighSpeedTcpClient(const HighSpeedTcpClient&); // deleted + + + TcpClient& client_; + // Pimpl technique + struct Impl; + Impl* impl_; +}; + +} + +#endif // KEYENCE_HIGH_SPEED_TCP_H + diff --git a/include/keyence/impl/keyence_tcp_client.h b/include/keyence/impl/keyence_tcp_client.h index 9e6e06c..674585e 100644 --- a/include/keyence/impl/keyence_tcp_client.h +++ b/include/keyence/impl/keyence_tcp_client.h @@ -20,6 +20,9 @@ class TcpClient : public Client virtual int send(const void* data, std::size_t size); // override virtual int recv(void* data, std::size_t size); // override + std::string hostName() const { return sock_.gethost(); } + std::string hostPort() const { return sock_.getport(); } + protected: libsocket::inet_stream sock_; }; diff --git a/include/keyence/impl/messages/prepare_high_speed.h b/include/keyence/impl/messages/prepare_high_speed.h new file mode 100644 index 0000000..34b75b7 --- /dev/null +++ b/include/keyence/impl/messages/prepare_high_speed.h @@ -0,0 +1,52 @@ +#ifndef PREPARE_HIGH_SPEED_H +#define PREPARE_HIGH_SPEED_H + +#include "../keyence_message.h" +#include "../high_speed_defs.h" + +namespace keyence +{ +namespace command +{ + +class PrepareHighSpeed +{ +public: + // Forward declares + struct Request; + struct Response; + + struct Request + { + // Required definitions + typedef Response response_type; + const static uint32_t size = 4; + const static uint8_t command_code = 0x47; + // Constructor + enum StartPosition { POSITION_PREVIOUS, POSITION_OLDEST, POSITION_NEXT }; + + Request(StartPosition position); + + // Request Data + uint8_t send_position; + + // Required encode function + void encodeInto(MutableBuffer buffer); + }; + + struct Response + { + ProfileInformation profile_info; + uint32_t start_code; + + void decodeFrom(MutableBuffer buffer); + }; + +}; + +} // namespace command +} // namespace keyence + + +#endif // PREPARE_HIGH_SPEED_H + diff --git a/include/keyence/impl/messages/start_high_speed.h b/include/keyence/impl/messages/start_high_speed.h new file mode 100644 index 0000000..51bf1e5 --- /dev/null +++ b/include/keyence/impl/messages/start_high_speed.h @@ -0,0 +1,48 @@ +#ifndef START_HIGH_SPEED_H +#define START_HIGH_SPEED_H + +#include "../keyence_message.h" +#include "../high_speed_defs.h" + +namespace keyence +{ +namespace command +{ + +class StartHighSpeed +{ +public: + // Forward declares + struct Request; + struct Response; + + struct Request + { + // Required definitions + typedef Response response_type; + const static uint32_t size = 8; + const static uint8_t command_code = 0xA0; + + Request(uint32_t code); + + // Request Data + uint32_t start_code; + + // Required encode function + void encodeInto(MutableBuffer buffer); + }; + + struct Response + { + void decodeFrom(MutableBuffer) {} + }; + +}; + +} // namespace command +} // namespace keyence + + + +#endif // START_HIGH_SPEED_H + diff --git a/include/keyence/impl/messages/stop_high_speed.h b/include/keyence/impl/messages/stop_high_speed.h new file mode 100644 index 0000000..a2108f6 --- /dev/null +++ b/include/keyence/impl/messages/stop_high_speed.h @@ -0,0 +1,38 @@ +#ifndef STOP_HIGH_SPEED_H +#define STOP_HIGH_SPEED_H + +namespace keyence +{ +namespace command +{ + +class StopHighSpeed +{ +public: + // Forward declares + struct Request; + struct Response; + + struct Request + { + // Required definitions + typedef Response response_type; + const static uint32_t size = 0; + const static uint8_t command_code = 0x48; + + // Required encode function + void encodeInto(MutableBuffer) {} + }; + + struct Response + { + void decodeFrom(MutableBuffer) {} + }; + +}; + +} // namespace command +} // namespace keyence + +#endif // STOP_HIGH_SPEED_H + diff --git a/src/impl/keyence_high_speed_tcp.cpp b/src/impl/keyence_high_speed_tcp.cpp new file mode 100644 index 0000000..3dbf28b --- /dev/null +++ b/src/impl/keyence_high_speed_tcp.cpp @@ -0,0 +1,197 @@ +#include "keyence/impl/keyence_high_speed_tcp.h" +#include "boost/thread.hpp" +#include "boost/atomic.hpp" + +#include "keyence/impl/messages/prepare_high_speed.h" +#include "keyence/impl/messages/start_high_speed.h" +#include "keyence/impl/messages/stop_high_speed.h" +#include "keyence/impl/keyence_exception.h" +#include "keyence/impl/ljv7_rawdata.h" + +#include "libsocket/epoll.hpp" + +#define KEYENCE_HIGHSPEED_NOTIFY_MESSAGE_SIZE 8 + +namespace keyence +{ + +static void worker_main(boost::atomic_flag& flag, + libsocket::inet_stream& sock, + keyence::HighSpeedTcpClient::callback_type cb, + uint16_t profile_size) +{ + using libsocket::inet_stream; + // prepare buffers + const size_t msg_length = 24 + (profile_size * 5 / 2) + 4; + std::vector byte_buffer (msg_length); + std::vector points_buffer (profile_size); + // keep track of the 'message state' + size_t received = 0; + + libsocket::epollset poll; + poll.add_fd(sock, LIBSOCKET_READ); + + while (flag.test_and_set()) + { + libsocket::epollset::ready_socks ready = poll.wait(100); + if (!ready.first.empty()) + { + // read into buffer + ssize_t n = sock.rcv(byte_buffer.data() + received, msg_length - received); + if (n <= 0) + { + // Socket has been closed + std::cerr << "High speed socket closed\n"; + break; + } + + received += n; + + if (received == msg_length) + { + // if we have enough, process + ljv7_unpack_profile_data(byte_buffer.data(), byte_buffer.size(), + profile_size, points_buffer.data(), + points_buffer.size() * 4); + + cb(points_buffer.data(), points_buffer.size()); + // reset message state + received = 0; + } + else if (received == KEYENCE_HIGHSPEED_NOTIFY_MESSAGE_SIZE) + { + if (byte_buffer[0] == 0xFF && byte_buffer[1] == 0xFF && + byte_buffer[2] == 0xFF && byte_buffer[3] == 0xFF) + { + std::cerr << "High-Speed-Mode Stopped with notify code: " << int(byte_buffer[4]) << std::endl; + break; + } + } + } + } // end main loop + + return; +} + +// Impl details +struct HighSpeedTcpClient::Impl +{ + Impl(const std::string host_ip, + const std::string host_port, + const ProfileInformation& info, + uint32_t code) + : start_code(code) + , info(info) + { + // todo: socket exception safety + sock.connect(host_ip, host_port, LIBSOCKET_IPv4); + } + + ~Impl() + { + stop(); + } + + void start(callback_type cb) + { + flag.test_and_set(); + worker = boost::thread(worker_main, boost::ref(flag), boost::ref(sock), cb, info.num_profiles); + } + + void stop() + { + flag.clear(); + worker.join(); + } + + // Socket details + libsocket::inet_stream sock; + + // Worker details + boost::thread worker; + boost::atomic_flag flag; + + // Data + uint32_t start_code; + ProfileInformation info; +}; + + +HighSpeedTcpClient::HighSpeedTcpClient(TcpClient &client, const std::string &port) + : client_(client) + , impl_(NULL) +{ + using keyence::command::PrepareHighSpeed; + + PrepareHighSpeed::Request prepare (PrepareHighSpeed::Request::POSITION_NEXT); + Client::Response resp = client.sendReceive(prepare); + + if (resp.header.return_code) + { + // Something went wrong + std::ostringstream ss; + ss << "High-Speed-Mode prepare command failed with code: " << int(resp.header.return_code); + throw keyence::KeyenceException(ss.str()); + } + + try + { + impl_ = new Impl(client.hostName(), port, resp.body.profile_info, resp.body.start_code); + } + catch (const libsocket::socket_exception& exception) + { + throw keyence::KeyenceException(exception.mesg); + } +} + +HighSpeedTcpClient::~HighSpeedTcpClient() +{ + stop(); + delete impl_; +} + +const ProfileInformation &HighSpeedTcpClient::profileInfo() const +{ + return impl_->info; +} + +void HighSpeedTcpClient::start(HighSpeedTcpClient::callback_type cb) +{ + using keyence::command::StartHighSpeed; + + StartHighSpeed::Request start (impl_->start_code); + Client::Response resp = client_.sendReceive(start); + + if (resp.header.return_code) + { + std::ostringstream ss; + ss << "High-Speed-Mode start command failed with code: " << int(resp.header.return_code); + throw keyence::KeyenceException(ss.str()); + } + + impl_->start(cb); +} + +void HighSpeedTcpClient::stop() +{ + using keyence::command::StopHighSpeed; + + try + { + StopHighSpeed::Request stop; + Client::Response resp = client_.sendReceive(stop); + + if (resp.header.return_code) + { + std::cerr << "High-Speed-Mode stop command failed with code: " << int(resp.header.return_code); + } + impl_->stop(); + } + catch (keyence::KeyenceException& exc) + { + std::cerr << "An exception occured while trying to stop the high speed connection: " << exc.what(); + } +} + +} // namespace keyence + diff --git a/src/impl/messages/prepare_high_speed.cpp b/src/impl/messages/prepare_high_speed.cpp new file mode 100644 index 0000000..2b5b8b9 --- /dev/null +++ b/src/impl/messages/prepare_high_speed.cpp @@ -0,0 +1,37 @@ +#include "keyence/impl/messages/prepare_high_speed.h" + +keyence::command::PrepareHighSpeed::Request::Request(StartPosition position) +{ + switch (position) + { + case POSITION_PREVIOUS: + send_position = 0; break; + case POSITION_OLDEST: + send_position = 1; break; + case POSITION_NEXT: + send_position = 2; break; + } +} + +void keyence::command::PrepareHighSpeed::Request::encodeInto(MutableBuffer buffer) +{ + using keyence::insert; + + insert(buffer.data, static_cast(send_position), 0); + insert(buffer.data, static_cast(0x0), 1); + insert(buffer.data, static_cast(0x0), 2); + insert(buffer.data, static_cast(0x0), 3); +} + +void keyence::command::PrepareHighSpeed::Response::decodeFrom(MutableBuffer buffer) +{ + using keyence::extract; + + extract(buffer.data, start_code, 4); + + // extract profile info + extract(buffer.data, profile_info.num_profiles, 16); + extract(buffer.data, profile_info.data_unit, 18); + extract(buffer.data, profile_info.x_start, 20); + extract(buffer.data, profile_info.x_increment, 24); +} diff --git a/src/impl/messages/start_high_speed.cpp b/src/impl/messages/start_high_speed.cpp new file mode 100644 index 0000000..ed157a6 --- /dev/null +++ b/src/impl/messages/start_high_speed.cpp @@ -0,0 +1,17 @@ +#include "keyence/impl/messages/start_high_speed.h" + +keyence::command::StartHighSpeed::Request::Request(uint32_t code) + : start_code (code) +{ +} + +void keyence::command::StartHighSpeed::Request::encodeInto(MutableBuffer buffer) +{ + using keyence::insert; + + insert(buffer.data, static_cast(0x47), 0); + insert(buffer.data, static_cast(0x0), 1); + insert(buffer.data, static_cast(0x0), 2); + insert(buffer.data, static_cast(0x0), 3); + insert(buffer.data, static_cast(start_code), 4); +} diff --git a/src/utilities/high_speed.cpp b/src/utilities/high_speed.cpp new file mode 100644 index 0000000..91f530a --- /dev/null +++ b/src/utilities/high_speed.cpp @@ -0,0 +1,62 @@ +#include "keyence/impl/keyence_tcp_client.h" +#include "keyence/impl/keyence_exception.h" +// For given message +#include "keyence/impl/keyence_high_speed_tcp.h" + +#include + +void process(const int32_t* data, uint32_t size) +{ + std::cout << "cb: " << *data << " " << size << '\n'; +} + +struct Handler +{ + keyence::ProfileInformation info; + void handle(const int32_t* data, uint32_t size) + { + std::cout << "struct cb" << *data << " " << size << "\n"; + } +}; + +int main(int argc, char** argv) +{ + using keyence::Client; + + if (argc != 3) + { + std::cerr << "Usage: ./keyence_change_program \n"; + return 1; + } + + try + { + keyence::TcpClient client (argv[1], argv[2]); + + keyence::HighSpeedTcpClient high_speed (client, "24692"); + + high_speed.start(process); + +// An example of how to use member functions with the callback scheme: +// Handler handler; +// handler.info = high_speed.profileInfo(); +// high_speed.start( boost::bind(&Handler::handle, &handler, _1, _2) ); + + ::sleep(5); + std::cout << "Stopping.\n"; + high_speed.stop(); + + ::sleep(2); + std::cout << "Done\n"; + } + catch (const keyence::KeyenceException& exc) + { + std::cerr << exc.what() << std::endl; + return -1; + } + + return 0; +} + + + diff --git a/src/utilities/high_speed_raw.cpp b/src/utilities/high_speed_raw.cpp new file mode 100644 index 0000000..564f629 --- /dev/null +++ b/src/utilities/high_speed_raw.cpp @@ -0,0 +1,55 @@ +#include "keyence/impl/keyence_tcp_client.h" +#include "keyence/impl/keyence_exception.h" +// For given message +#include "keyence/impl/messages/prepare_high_speed.h" +#include "keyence/impl/messages/start_high_speed.h" +#include "keyence/impl/messages/stop_high_speed.h" +#include + +int main(int argc, char** argv) +{ + using keyence::command::PrepareHighSpeed; + using keyence::command::StartHighSpeed; + using keyence::command::StopHighSpeed; + using keyence::Client; + + if (argc != 3) + { + std::cerr << "Usage: ./keyence_change_program \n"; + return 1; + } + + try + { + // regular keyence connection + keyence::TcpClient keyence (argv[1], argv[2]); + // open up parallel high-speed connection + libsocket::inet_stream hs_sock (argv[1], argv[3], LIBSOCKET_IPv4); + + // Prepare for high speed + PrepareHighSpeed::Request prepare (PrepareHighSpeed::Request::POSITION_NEXT); + Client::Response prepare_resp = keyence.sendReceive(prepare); + + std::cout << prepare_resp.header << '\n'; + + // Start + StartHighSpeed::Request start (prepare_resp.body.start_code); + Client::Response start_resp = keyence.sendReceive(start); + + std::cout << start_resp.header << '\n'; + + // Read from the hs socket... + // Check the body of the prepare-response for details about the profile size + ::sleep(10); + + } + catch (const keyence::KeyenceException& exc) + { + std::cerr << exc.what(); + return -1; + } + + return 0; +} + +