// Copyright (C) 2016-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include #include "../implementation/service_discovery/include/constants.hpp" #include "../implementation/utility/include/byteorder.hpp" #include #include #include #include #include #include #include namespace vsomeip_ctrl { class vsomeip_sender { public: vsomeip_sender(bool _use_tcp, const std::vector& _user_message, vsomeip::instance_t _instance) : use_tcp_(_use_tcp), user_message_(_user_message), instance_(_instance), app_(vsomeip::runtime::get()->create_application("vsomeip_ctrl")), wait_service_available_(true), send_thread_(std::bind(&vsomeip_sender::send, this)), service_id_(0x0), method_id_(0x0), length_(0), client_id_(0x0), interface_version_(0x0), message_type_(vsomeip::message_type_e::MT_UNKNOWN), return_code_(vsomeip::return_code_e::E_UNKNOWN), wait_for_answer_(true) { if (user_message_.size() < VSOMEIP_PAYLOAD_POS) { VSOMEIP_ERROR << "Provided message is to short, min. length " "is 16 Bytes, exiting."; exit(EXIT_FAILURE); } service_id_ = VSOMEIP_BYTES_TO_WORD(user_message_[VSOMEIP_SERVICE_POS_MIN], user_message_[VSOMEIP_SERVICE_POS_MAX]); method_id_ = VSOMEIP_BYTES_TO_WORD(user_message_[VSOMEIP_METHOD_POS_MIN], user_message_[VSOMEIP_METHOD_POS_MAX]); length_ = VSOMEIP_BYTES_TO_LONG(user_message_[VSOMEIP_LENGTH_POS_MIN], user_message_[VSOMEIP_LENGTH_POS_MIN+1], user_message_[VSOMEIP_LENGTH_POS_MIN+2], user_message_[VSOMEIP_LENGTH_POS_MAX]); client_id_ = VSOMEIP_BYTES_TO_WORD(user_message_[VSOMEIP_CLIENT_POS_MIN], user_message_[VSOMEIP_CLIENT_POS_MAX]); interface_version_ = user_message_[VSOMEIP_INTERFACE_VERSION_POS]; message_type_ = static_cast(user_message_[VSOMEIP_MESSAGE_TYPE_POS]); return_code_ = static_cast(user_message_[VSOMEIP_RETURN_CODE_POS]); validate_message(); if (!app_->init()) { VSOMEIP_ERROR << "Couldn't initialize application"; exit(EXIT_FAILURE); } app_->register_state_handler( std::bind(&vsomeip_sender::on_state, this, std::placeholders::_1)); app_->register_message_handler(vsomeip::ANY_SERVICE, vsomeip::ANY_INSTANCE, vsomeip::ANY_METHOD, std::bind(&vsomeip_sender::on_message, this, std::placeholders::_1)); app_->register_availability_handler(service_id_, instance_, std::bind(&vsomeip_sender::on_availability, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); app_->start(); }; void stop(int _exit_code) { app_->clear_all_handler(); app_->release_service(service_id_, instance_); app_->stop(); exit(_exit_code); } ~vsomeip_sender() { send_thread_.join(); } void on_state(vsomeip::state_type_e _state) { VSOMEIP_INFO << "Application " << app_->get_name() << " is " << (_state == vsomeip::state_type_e::ST_REGISTERED ? "registered." : "deregistered."); if (_state == vsomeip::state_type_e::ST_REGISTERED) { app_->request_service(service_id_, instance_); } } void on_availability(vsomeip::service_t _service, vsomeip::instance_t _instance, bool _is_available) { if(_is_available) { VSOMEIP_INFO << "Service [" << std::setw(4) << std::setfill('0') << std::hex << _service << "." << _instance << "] is available."; std::lock_guard its_lock(mutex_); wait_service_available_ = false; condition_.notify_one(); } } void on_message(const std::shared_ptr &_response) { VSOMEIP_INFO << "Received a response from Service [" << std::setw(4) << std::setfill('0') << std::hex << _response->get_service() << "." << std::setw(4) << std::setfill('0') << std::hex << _response->get_instance() << "]:"; VSOMEIP_INFO << "########## begin message"; VSOMEIP_INFO << std::hex << std::setw(4) << std::setfill('0') << _response->get_service() << std::hex << std::setw(4) << std::setfill('0') << _response->get_method() << " # service id / instance id"; VSOMEIP_INFO << std::hex << std::setw(8) << std::setfill('0') << _response->get_length() << " # length"; VSOMEIP_INFO << std::hex << std::setw(4) << std::setfill('0') << _response->get_client() << std::hex << std::setw(4) << std::setfill('0') << _response->get_session() << " # client id / session id"; VSOMEIP_INFO << std::hex << std::setw(2) << std::setfill('0') << static_cast(_response->get_protocol_version()) << std::hex << std::setw(2) << std::setfill('0') << static_cast(_response->get_interface_version()) << std::hex << std::setw(2) << std::setfill('0') << static_cast(_response->get_message_type()) << std::hex << std::setw(2) << std::setfill('0') << static_cast(_response->get_return_code()) << " # protocol version / interface version / " << "message type / return code"; std::stringstream stream; std::string str; for(unsigned int i = 0; i < _response->get_payload()->get_length(); i++) { stream << std::hex << std::setw(2) << std::setfill('0') << static_cast((_response->get_payload()->get_data())[i]); str.append(stream.str()); stream.str(""); stream.clear(); } std::string str2; int k=1; for(unsigned int j = 0; j < str.length(); j+=2, k++) { str2.append(str.substr(j,2)); if(k%4 == 0) { if(k == 4) { VSOMEIP_INFO << str2 << " # payload from here on"; } else { VSOMEIP_INFO << str2; } str2.clear(); } } VSOMEIP_INFO << "########## end message"; VSOMEIP_INFO << "Payload as byte stream: " << str; str.clear(); std::lock_guard its_lock(mutex_); wait_for_answer_ = false; condition_.notify_one(); } void send() { std::unique_lock its_lock(mutex_); while (wait_service_available_) { if(std::cv_status::timeout == condition_.wait_for(its_lock, std::chrono::seconds(6))) { VSOMEIP_INFO << "Service [" << std::setw(4) << std::setfill('0') << std::hex << service_id_ << "." << instance_ << "] isn't available. Exiting"; stop(EXIT_FAILURE); } } std::shared_ptr its_message = vsomeip::runtime::get()->create_message(use_tcp_); its_message->set_method(method_id_); its_message->set_service(service_id_); its_message->set_interface_version(interface_version_); its_message->set_message_type(message_type_); its_message->set_return_code(return_code_); its_message->set_client(app_->get_client()); its_message->set_instance(instance_); std::shared_ptr< vsomeip::payload > its_payload = vsomeip::runtime::get()->create_payload(); its_payload->set_data(&user_message_[VSOMEIP_PAYLOAD_POS], static_cast(user_message_.size() - VSOMEIP_PAYLOAD_POS)); its_message->set_payload(its_payload); VSOMEIP_INFO << "Sending"; app_->send(its_message); while(wait_for_answer_) { if(std::cv_status::timeout == condition_.wait_for(its_lock, std::chrono::seconds(5))) { VSOMEIP_INFO << "Didn't receive answer within 5sec. Shutting down."; stop(EXIT_SUCCESS); break; } } stop(EXIT_SUCCESS); } private: bool validate_message() { if (!check_message_type()) { VSOMEIP_ERROR << "Invalid message type 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(message_type_) << ", exiting."; stop(EXIT_FAILURE); } if(!check_return_code()) { VSOMEIP_ERROR << "Invalid return code 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(return_code_) << ", exiting."; stop(EXIT_FAILURE); } if (service_id_ == vsomeip::sd::service && method_id_ == vsomeip::sd::method) { VSOMEIP_ERROR << "Usage of reserved service id and method id " "of service discovery, exiting."; stop(EXIT_FAILURE); } if (user_message_.size() != length_ + 8) { VSOMEIP_ERROR << "Provided length 0x" << std::setw(8) << std::setfill('0') << std::hex << length_ << " doesn't match message size."; VSOMEIP_ERROR << "Assuming the same payload the length field should" " be set to 0x" << std::setw(8) << std::setfill('0') << std::hex << user_message_.size() - 8 << " , exiting."; stop(EXIT_FAILURE); } if (use_tcp_ && user_message_.size() > VSOMEIP_MAX_TCP_MESSAGE_SIZE) { VSOMEIP_WARNING << "Max allowed message size for TCP is " << std::dec << VSOMEIP_MAX_TCP_MESSAGE_SIZE << ". Provided message size is: " << user_message_.size(); } if (!use_tcp_ && user_message_.size() > VSOMEIP_MAX_UDP_MESSAGE_SIZE) { VSOMEIP_WARNING << "Max allowed message size for UDP is " << std::dec << VSOMEIP_MAX_UDP_MESSAGE_SIZE << ". Provided message size is: " << user_message_.size(); } return true; } bool check_message_type() { switch (message_type_) { case vsomeip::message_type_e::MT_REQUEST: case vsomeip::message_type_e::MT_REQUEST_NO_RETURN: case vsomeip::message_type_e::MT_NOTIFICATION: case vsomeip::message_type_e::MT_RESPONSE: case vsomeip::message_type_e::MT_ERROR: return true; break; case vsomeip::message_type_e::MT_UNKNOWN: case vsomeip::message_type_e::MT_ERROR_ACK: case vsomeip::message_type_e::MT_RESPONSE_ACK: case vsomeip::message_type_e::MT_NOTIFICATION_ACK: case vsomeip::message_type_e::MT_REQUEST_NO_RETURN_ACK: case vsomeip::message_type_e::MT_REQUEST_ACK: default: return false; break; } } bool check_return_code() { if (static_cast(return_code_) > 0x3F) { VSOMEIP_ERROR << "Provided return code 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(return_code_) << " is out of range."; return false; } if (static_cast(return_code_) > static_cast(vsomeip::return_code_e::E_WRONG_MESSAGE_TYPE) && static_cast(return_code_) <= 0x3f) { VSOMEIP_ERROR << "Provided return code 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(return_code_) << "is reserved."; return false; } switch (message_type_) { case vsomeip::message_type_e::MT_REQUEST: case vsomeip::message_type_e::MT_REQUEST_NO_RETURN: case vsomeip::message_type_e::MT_NOTIFICATION: if(return_code_ != vsomeip::return_code_e::E_OK) { VSOMEIP_ERROR << "Provided return code 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(return_code_) << "is invalid in combination with message type 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(message_type_) << " use 0x00 (E_OK)."; return false; } return true; break; default: return true; break; } } private: bool use_tcp_; std::vector user_message_; vsomeip::instance_t instance_; std::shared_ptr app_; std::mutex mutex_; std::condition_variable condition_; bool wait_service_available_; std::thread send_thread_; vsomeip::service_t service_id_; vsomeip::method_t method_id_; std::uint32_t length_; vsomeip::client_t client_id_; vsomeip::interface_version_t interface_version_; vsomeip::message_type_e message_type_; vsomeip::return_code_e return_code_; bool wait_for_answer_; }; } // namespace vsomeip_ctrl static void print_help(char* binary_name) { std::cout << "Usage example:" << std::endl; std::cout << binary_name << " --instance 5678 " << "--message 123480e800000015134300030100000000000009efbbbf576f726c6400\n" << "This will send a message to service with service id 1234 and instance 5678." << std::endl << std::endl; std::cout << "Available options:\n" "--help | -h : print this help\n" "--instance | -i : instance id of target service in hex (required)\n" "--tcp | -t : flag to enable sending over TCP, default off (= UDP)\n" "--message | -m : vSomeIP message to send in hex (required)\n\n" "Please note: the fields client id and session id in the provided message\n" "will be overwritten by the stack with the required values\n" "Please further make sure to use the same configuration file\n" "as the target service, if the system is not using routingmanagerd" << std::endl; } int main(int argc, char** argv) { if (argc < 2) { std::cerr << "To few arguments, please see the help with : " << argv[0] << " --help" << std::endl; exit(EXIT_FAILURE); } std::vector user_message; vsomeip::instance_t instance(vsomeip::ANY_INSTANCE); bool use_tcp(false); for (int i = 1; i < argc; i++) { std::string arg(argv[i]); if (arg == "--help" || arg == "-h") { print_help(argv[0]); exit(EXIT_SUCCESS); break; } else if (arg == "--message" || arg == "-m") { std::string message(argv[i + 1]); for (unsigned int i = 0; i < message.length(); i += 2) { vsomeip::byte_t its_byte; try { std::uint64_t tmp = std::stoul(message.substr(i, 2), 0, 16); tmp = (tmp > (std::numeric_limits::max)()) ? (std::numeric_limits::max)() : tmp; its_byte = static_cast(tmp); } catch (std::invalid_argument &e) { std::cerr << e.what() << ": Couldn't convert '" << message.substr(i, 2) << "' to hex, exiting: " << std::endl; exit(EXIT_FAILURE); } user_message.push_back(its_byte); } } if (arg == "--tcp" || arg == "-t") { use_tcp = true; } else if (arg == "--instance" || arg == "-i") { std::string instance_str(argv[i + 1]); if(instance_str.length() > 4) { std::cerr << "provided instance is to long, exiting." << std::endl; exit(EXIT_FAILURE); } if(instance_str.length() < 4) { while(instance_str.size() != 4) { instance_str = std::string("0") += instance_str; } } vsomeip::byte_t high(0x0); vsomeip::byte_t low(0x0); std::cout << "Instance: " << instance_str << std::endl; for (unsigned int i = 0; i < instance_str.length(); i += 2) { try { std::uint64_t tmp = std::stoul(instance_str.substr(i, 2), 0, 16); tmp = (tmp > (std::numeric_limits::max)()) ? (std::numeric_limits::max)() : tmp; vsomeip::byte_t its_byte = static_cast(tmp); if (i == 0) { high = its_byte; } else { low = its_byte; } } catch (std::invalid_argument &e) { std::cerr << e.what() << ": Couldn't convert '" << instance_str.substr(i, 2) << "' to hex, exiting: " << std::endl; exit(EXIT_FAILURE); } } instance = VSOMEIP_BYTES_TO_WORD(high, low); } } if(instance == vsomeip::ANY_INSTANCE) { std::cerr << "Please provide a target instance (see --help)" << std::endl; exit(EXIT_FAILURE); } if(user_message.empty()) { std::cerr << "Please provide a message to send (see --help)" << std::endl; exit(EXIT_FAILURE); } vsomeip_ctrl::vsomeip_sender sender(use_tcp, user_message, instance); return EXIT_SUCCESS; }