/* * Copyright (c) 2017 TOYOTA MOTOR CORPORATION * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "wm_connection.hpp" #include #include #include #include #include #include #include "json_helper.hpp" #include "util.hpp" extern "C" { #include #include } /** * namespace wm */ namespace wm { namespace { static const char kPathConnectionConfigFile[] = "/etc/connection.json"; static const char kDefaultIpAddr[] = "192.168.10.10"; static const int kDefaultPort = 4000; static int onIoEventReceive(sd_event_source *src, int fd, uint32_t revents, void * data) { WMConnection *p_wmcon = (WMConnection*)data; json_object *j_out; int ret = p_wmcon->receive(&j_out); if (0 > ret) { return 0; } const char* rq = jh::getStringFromJson(j_out, "req"); const char* id = jh::getStringFromJson(j_out, "appid"); const char* dn = jh::getStringFromJson(j_out, "drawing_name"); const char* da = jh::getStringFromJson(j_out, "drawing_area"); HMI_DEBUG("req:%s appid:%s, drawing_name:%s, drawing_area:%s", rq, id, dn, da); // Callback p_wmcon->callOnReceivedHandler(j_out); return 0; } static int onIoEventAccept(sd_event_source *src, int fd, uint32_t revents, void * data) { struct sockaddr_in addr; WMConnection *p_wmcon = (WMConnection*)data; // Accept connection socklen_t len = sizeof(addr); int my_socket = p_wmcon->getMySocket(); int connected_socket = accept(my_socket, (struct sockaddr *)&addr, &len); if (0 > connected_socket) { HMI_ERROR("Failed to accept connection (%s)", strerror(errno)); return -1; } // Store connected socket p_wmcon->setConnectedSocket(connected_socket); // Register callback to receive int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr, connected_socket, EPOLLIN, onIoEventReceive, p_wmcon); if (0 > ret) { HMI_ERROR("Failed to add I/O event receive(%s)", strerror(-ret)); return -1; } return 0; } } // namespace WMConnection::WMConnection() { // Load connection config file this->loadConnectionConfigFile(); // TODO: ECU name should be decide by config file this->ecu_name = this->mode; } int WMConnection::initialize() { int ret; // Initialize for Master/Slave if (this->isMasterMode()) { ret = this->initializeMaster(); } else { ret = this->initializeSlave(); } return ret; } void WMConnection::registerCallback(ReceivedHandler on_received) { this->onReceived = on_received; } int WMConnection::sendRequest(char const *req, char const *appid, char const *drawing_name, char const *drawing_area) { int ret; json_object *j_obj = json_object_new_object(); json_object_object_add(j_obj, "req", json_object_new_string(req)); json_object_object_add(j_obj, "appid", json_object_new_string(appid)); json_object_object_add(j_obj, "drawing_name", json_object_new_string(drawing_name)); json_object_object_add(j_obj, "drawing_area", json_object_new_string(drawing_area)); ret = this->send(j_obj); json_object_put(j_obj); return ret; } int WMConnection::send(struct json_object* j_in) { // Convert json_object to string to send const char *buf = json_object_to_json_string(j_in); if (nullptr == buf) { HMI_ERROR("Failed to convert json_object to string"); return -1; } int len = strlen(buf); HMI_DEBUG("Send data(len:%d): %s", len, buf); int n = write(this->connected_socket, buf, len); if(0 > n) { HMI_ERROR("Failed to send data (%s)", strerror(errno)); return -1; } return 0; } bool WMConnection::isMasterMode() { if ("master" == this->mode) { return true; } else { return false; } } bool WMConnection::isMasterArea(const char* area) { if (nullptr == area) { return false; } std::string str_area = std::string(area); if ("" == str_area) { return false; } std::vector elements; elements = parseString(str_area, '.'); if ("master" == elements[0]) { return true; } else { return false; } } bool WMConnection::isConnecting() { return (0 > this->connected_socket) ? false : true; } std::string WMConnection::parseMasterArea(const char* area) { std::string ret_area = ""; std::vector elements; elements = parseString(std::string(area), '.'); if ("master" != elements[0]) { return std::string(area); } for (auto itr = (elements.begin() + 1); itr != elements.end(); ++itr) { ret_area += *itr; if ((elements.end() - 1) != itr) { ret_area += "."; } } return ret_area; } bool WMConnection::isSyncDrawingForRemote(const char* appid) { if (std::string(appid) == this->syndDrawingAppId) { return true; } else { return false; } } void WMConnection::startSyncDrawForRemote(const char* appid) { this->syndDrawingAppId = std::string(appid); } void WMConnection::finishSyncDrawForRemote(const char* appid) { if (std::string(appid) == this->syndDrawingAppId) { this->syndDrawingAppId = ""; } } int WMConnection::getMySocket() { return this->my_socket; } int WMConnection::getConnectedSocket() { return this->connected_socket; } void WMConnection::setConnectedSocket(int connected_socket) { this->connected_socket = connected_socket; } std::string WMConnection::getEcuName() { return this->ecu_name; } void WMConnection::callOnReceivedHandler(json_object *j_out) { this->onReceived(j_out); } int WMConnection::initializeMaster() { int ret = 0; struct sockaddr_in addr; // Create socket this->my_socket = socket(AF_INET, SOCK_STREAM, 0); if (0 > this->my_socket) { HMI_ERROR("Failed to create socket (%s)", strerror(errno)); return -1; } // Bind socket addr.sin_family = AF_INET; addr.sin_port = htons(this->port); addr.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(this->my_socket, (struct sockaddr *)&addr, sizeof(addr)); if (0 > ret) { HMI_ERROR("Failed to bind socket (%s)", strerror(errno)); return -1; } // Listen connection ret = listen(this->my_socket, 1); if (0 > ret) { HMI_ERROR("Failed to listen connection (%s)", strerror(errno)); return -1; } // Register callback to accept connection ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr, this->my_socket, EPOLLIN, onIoEventAccept, this); if (0 > ret) { HMI_ERROR("Failed to add I/O event accept(%s)", strerror(-ret)); return -1; } return ret; } int WMConnection::initializeSlave() { // Create socket this->my_socket = socket(AF_INET, SOCK_STREAM, 0); if (0 > this->my_socket) { HMI_ERROR("Failed to create socket (%s)", strerror(errno)); return -1; } return 0; } int WMConnection::connectToMaster() { int ret = 0; struct sockaddr_in addr; // Connect to master addr.sin_family = AF_INET; addr.sin_port = htons(this->port); addr.sin_addr.s_addr = inet_addr(this->ip.c_str()); ret = connect(this->my_socket, (struct sockaddr *)&addr, sizeof(addr)); if (0 > ret) { HMI_ERROR("Failed to connect to master (%s)", strerror(errno)); return ret; } HMI_DEBUG("Connected to master"); // Store connected socket this->connected_socket = this->my_socket; // Register callback to receive ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr, this->connected_socket, EPOLLIN, onIoEventReceive, this); if (0 > ret) { HMI_ERROR("Failed to add I/O event receive(%s)", strerror(-ret)); return -1; } return ret; } int WMConnection::receive(struct json_object** j_out) { char buf[1024]; int n; n = read(this->connected_socket, buf, sizeof(buf)); if(0 > n) { HMI_ERROR("Failed to receive data (%s)", strerror(errno)); return -1; } HMI_DEBUG("Received data length: %d", n); HMI_DEBUG("Received data: %s", buf); // Parse received data struct json_tokener *tokener = json_tokener_new(); *j_out = json_tokener_parse_ex(tokener, buf, n); if (nullptr == *j_out) { HMI_DEBUG("Failed to parse received data"); return -1; } return 0; } int WMConnection::loadConnectionConfigFile() { // Get afm application installed dir char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR"); if (!afm_app_install_dir) { HMI_ERROR("AFM_APP_INSTALL_DIR is not defined"); } std::string path = std::string(afm_app_install_dir) + std::string(kPathConnectionConfigFile); // Load connection config file json_object* json_obj; int ret = jh::inputJsonFilie(path.c_str(), &json_obj); if (0 > ret) { HMI_ERROR("Could not open %s, so use default mode \"slave\"", kPathConnectionConfigFile); this->mode = "slave"; this->ip = kDefaultIpAddr; this->port = kDefaultPort; return 0; } HMI_DEBUG("json_obj dump:%s", json_object_get_string(json_obj)); const char* mode = jh::getStringFromJson(json_obj, "mode"); this->mode = (nullptr != mode) ? mode : "slave"; const char* ip = jh::getStringFromJson(json_obj, "master_ip"); this->ip = (nullptr != ip) ? ip : kDefaultIpAddr; int port = jh::getIntFromJson(json_obj, "master_port"); this->port = (0 != port) ? port : kDefaultPort; // Check HMI_DEBUG("mode:%s master_ip:%s master_port:%d", mode, ip, port); // Release json_object json_object_put(json_obj); return 0; } } // namespace wm