/* * 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/hmi-config/connection.json"; static const char kPathTimeoutConfigFile[] = "/etc/timeout.json"; static const char kDefaultIpAddr[] = "192.168.10.10"; static const int kDefaultPort = 4000; static const uint64_t kDefaultTimes = 60000; static const uint64_t kDefaultSleep = 50; static struct sd_event_source *g_limit_timer_src = nullptr; static struct sd_event_source *g_sleep_timer_src = nullptr; static int setTimer(sd_event_source **src, uint64_t usec, void* handler, void* data) { int ret = sd_event_add_time(afb_daemon_get_event_loop(), src, CLOCK_BOOTTIME, usec, 1, (sd_event_time_handler_t)handler, data); if (ret < 0) { HMI_ERROR("Colud't set timer"); } return ret; } static void updateTimer(sd_event_source *src, uint64_t usec) { sd_event_source_set_time(src, usec); sd_event_source_set_enabled(src, SD_EVENT_ONESHOT); } static void stopEvent(sd_event_source *src) { int ret = sd_event_source_set_enabled(src, SD_EVENT_OFF); if (ret < 0) { HMI_ERROR("Not set SD_EVENT_OFF (%s)", strerror(-ret)); } sd_event_source_unref(src); } static int onIoEventReceive(sd_event_source *src, int fd, uint32_t revents, void * data) { WMConnection *p_wmcon = (WMConnection*)data; json_object *j_out[10]; int j_cnt; int ret = p_wmcon->receive(j_out, &j_cnt, fd); if (0 > ret) { return 0; } for(int i = 0; i < j_cnt; i++) { const char* rq = jh::getStringFromJson(j_out[i], "req"); const char* id = jh::getStringFromJson(j_out[i], "appid"); const char* dn = jh::getStringFromJson(j_out[i], "drawing_name"); const char* da = jh::getStringFromJson(j_out[i], "drawing_area"); HMI_DEBUG("req:%s appid:%s, drawing_name:%s, drawing_area:%s", rq, id, dn, da); // Callback p_wmcon->callOnReceivedHandler(j_out[i]); } return 0; } static int onIoEventAccept(sd_event_source *src, int fd, uint32_t revents, void * data) { WMConnection *p_wmcon = (WMConnection*)data; p_wmcon->serverAccept(fd); return 0; } static int onIoEventConnected(sd_event_source *src, int fd, uint32_t revents, void * data) { WMConnection *p_wmcon = (WMConnection*)data; int ret = p_wmcon->connectedToServer(fd); if (ret == 0) { stopEvent(src); ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr, fd, EPOLLIN, onIoEventReceive, p_wmcon); } return ret; } static int sleepTimerHandler(sd_event_source *s, uint64_t usec, void *userdata) { WMConnection *p_wmcon = (WMConnection*)userdata; int ret = p_wmcon->connectToEcu(); return ret; } static void limitTimerHandler(sd_event_source *s, uint64_t usec, void *userdata) { WMConnection *p_wmcon = (WMConnection*)userdata; p_wmcon->connectionTimeLimit(); } } // namespace WMConnection::WMConnection() { this->end_init = false; //Load timeout config file this->loadTimeoutConfigFile(); // 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; uint64_t sleepTime, limitTime; // Initialize for Master/Slave ret = this->initializeServer(); sleepTime = getNextTimerTime(this->sleep); if (sleepTime <= 0) { HMI_ERROR("Cloud't get Next Timer"); goto connection_init_error; } ret = setTimer(&g_sleep_timer_src, sleepTime, (void *)sleepTimerHandler, this); sd_event_source_set_enabled(g_sleep_timer_src, SD_EVENT_OFF); limitTime = getNextTimerTime(this->times); if (limitTime <= 0) { HMI_ERROR("Cloud't get Next Timer"); goto connection_init_error; } ret = setTimer(&g_limit_timer_src, limitTime, (void *)limitTimerHandler, this); this->connectToEcu(); return ret; connection_init_error: HMI_ERROR("Connection Initialize Failed"); return -1; } 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)); std::string ecu_name = getAppIdToEcuName(appid); HMI_DEBUG("send ecu_name %s", ecu_name.c_str()); ret = this->send(j_obj, ecu_name); json_object_put(j_obj); return ret; } int WMConnection::send(struct json_object* j_in, std::string ecu_name) { SocketInfo *socketData = &this->wm_socket[ecu_name]; // 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(socketData->connectedSocket(), buf, len); if(0 > n) { HMI_ERROR("Failed to send data (%s)", strerror(errno)); return -1; } return 0; } bool WMConnection::isRemoteArea(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, '.'); for (auto itr = this->wm_socket.begin(); itr != this->wm_socket.end(); ++itr) { if (itr->first == elements[0]) { if (itr->first != this->ecu_name) { return true; } else { return false; } } } return false; } bool WMConnection::isRemoteEcu(std::string appid) { std::string appToEcuName = this->getAppIdToEcuName(appid); if (appToEcuName == this->getEcuName() || appToEcuName == "") { return false; } else { return true; } } bool WMConnection::isConnecting(std::string ecu_name) { return (0 > this->wm_socket[ecu_name].connectedSocket()) ? false : true; } bool WMConnection::isConnectionMode() { return (Mode_Connection == this->wm_mode) ? true : false; } 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 = ""; } } std::string WMConnection::getAreaToEcuName(const char* area) { std::string result; std::string str_area = std::string(area); if (str_area.find('.') != std::string::npos) { std::vector elements; elements = parseString(str_area, '.'); result = elements[0]; } else { result = str_area; } return result; } std::string WMConnection::getSocketToEcuName(int socket) { for (auto itr = this->wm_socket.begin(); itr != this->wm_socket.end(); ++itr) { if (socket == itr->second.connectedSocket()) { return itr->first; } } return ""; } std::string WMConnection::getMySocketToEcuName(int socket) { for (auto itr = this->wm_socket.begin(); itr != this->wm_socket.end(); ++itr) { if (socket == itr->second.mySocket()) { return itr->first; } } return ""; } std::string WMConnection::getAppIdToEcuName(std::string appid) { if (appid2ecuName.count(appid) == 1) { return this->appid2ecuName[appid]; } else { return ""; } } std::string WMConnection::getIpToEcuName(std::string ip) { for (auto itr = this->wm_socket.begin(); itr != this->wm_socket.end(); ++itr) { if (ip == itr->second.ip()) { return itr->first; } } return ""; } void WMConnection::setAppIdToEcuName(std::string appid, std::string ecu_name) { this->appid2ecuName[appid] = ecu_name; } void WMConnection::setAppIdToReceivedEcuName(std::string appid) { this->appid2ecuName[appid] = received_ecu_name; } std::string WMConnection::getEcuName() { return this->ecu_name; } bool WMConnection::getEndInit() { return this->end_init; } int WMConnection::getSleepTime() { return this->sleep; } void WMConnection::callOnReceivedHandler(json_object *j_out) { this->onReceived(j_out); } int WMConnection::initializeServer() { int ret = 0; struct sockaddr_in addr; SocketInfo *socketData = &this->wm_socket[this->ecu_name]; // Create socket socketData->setMySocket(socket(AF_INET, SOCK_STREAM, 0)); if (0 > socketData->mySocket()) { HMI_ERROR("Failed to create socket (%s)", strerror(errno)); return -1; } socketData->setConnectedSocket(socketData->mySocket()); // Bind socket addr.sin_family = AF_INET; addr.sin_port = htons(socketData->wmPort()); addr.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(socketData->mySocket(), (struct sockaddr *)&addr, sizeof(addr)); if (0 > ret) { HMI_ERROR("Failed to bind socket (%s)", strerror(errno)); return -1; } // Listen connection ret = listen(socketData->mySocket(), 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, socketData->mySocket(), EPOLLIN, onIoEventAccept, this); if (0 > ret) { HMI_ERROR("Failed to add I/O event accept(%s)", strerror(-ret)); return -1; } return ret; } int WMConnection::initializeClient(std::string ecu_name) { SocketInfo *socketData = &this->wm_socket[ecu_name]; if (socketData->mySocket() != -1) { close(socketData->mySocket()); } // Create socket int my_socket = socket(AF_INET, SOCK_STREAM, 0); if (0 > my_socket) { HMI_ERROR("Failed to create socket (%s)", strerror(errno)); return -1; } socketData->setMySocket(my_socket); return 0; } int WMConnection::connectToEcu() { int cnt = 0; HMI_DEBUG("Start Connection"); if (this->wm_socket.empty()) { HMI_DEBUG("Connection destination is not written to connection.json"); stopEvent(g_limit_timer_src); stopEvent(g_sleep_timer_src); this->end_init = true; this->wm_mode = Mode_Standalone; return 0; } for (auto itr = this->wm_socket.begin(); itr != this->wm_socket.end(); ++itr) { std::string ecu_name = itr->first; HMI_DEBUG("ECU Names %s", ecu_name.c_str()); if (itr->second.connectedSocket() != -1 || itr->first == this->ecu_name) { cnt++; HMI_DEBUG("Already Connected"); continue; } if (itr->second.masterMode()) { HMI_DEBUG("Try Connection"); connectToServer(ecu_name); } HMI_DEBUG("Wait Connection"); } if (cnt == (int)this->wm_socket.size()) { HMI_DEBUG("All Connected!!"); stopEvent(g_sleep_timer_src); stopEvent(g_limit_timer_src); this->end_init = true; this->wm_mode = Mode_Connection; return 0; } uint64_t sleepTime = getNextTimerTime(this->sleep); updateTimer(g_sleep_timer_src, sleepTime); return 0; } void WMConnection::connectionTimeLimit() { HMI_DEBUG("Time Limit"); stopEvent(g_sleep_timer_src); stopEvent(g_limit_timer_src); this->wm_socket[this->ecu_name].setConnectedSocket(-1); this->wm_mode = Mode_Standalone; for (auto itr = this->wm_socket.begin(); itr != this->wm_socket.end(); ++itr) { if (itr->second.connectedSocket() != -1) { HMI_DEBUG("Connection Mode"); this->wm_mode = Mode_Connection; } else { close(itr->second.mySocket()); } } if (this->wm_mode == Mode_Standalone) { close(this->wm_socket[this->ecu_name].mySocket()); } this->end_init = true; } int WMConnection::connectToServer(std::string ecu_name) { int ret = 0; struct sockaddr_in addr; this->initializeClient(ecu_name); SocketInfo *socketData = &wm_socket[ecu_name]; // Connect to master addr.sin_family = AF_INET; addr.sin_port = htons(socketData->wmPort()); addr.sin_addr.s_addr = inet_addr(socketData->ip().c_str()); connect(socketData->mySocket(), (struct sockaddr *)&addr, sizeof(addr)); ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr, socketData->mySocket(), EPOLLOUT, onIoEventConnected, this); if (0 > ret) { HMI_ERROR("Failed to add I/O event receive(%s)", strerror(-ret)); return -1; } return ret; } int WMConnection::connectedToServer(int socket) { int ret; struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); std::string ecu_name = getMySocketToEcuName(socket); ret = getpeername(socket, (struct sockaddr*)&addr, &addr_len); if (ret != 0) { HMI_DEBUG("Failed to connect %s (%s)", ecu_name.c_str(), strerror(errno)); return -1; } HMI_DEBUG("Connected to %s", ecu_name.c_str()); SocketInfo *socketData = &this->wm_socket[ecu_name]; // Store connected socket socketData->setConnectedSocket(socket); return 0; } int WMConnection::serverAccept(int socket) { struct sockaddr_in addr; // Accept connection socklen_t len = sizeof(addr); int my_socket = this->wm_socket[this->getEcuName()].mySocket(); 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; } std::string ip = inet_ntoa(addr.sin_addr); std::string ecu_name = getIpToEcuName(ip); SocketInfo *socketData = &this->wm_socket[ecu_name]; // Store connected socket socketData->setConnectedSocket(connected_socket); // Register callback to receive int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr, connected_socket, EPOLLIN, onIoEventReceive, this); if (0 > ret) { HMI_ERROR("Failed to add I/O event receive(%s)", strerror(-ret)); return -1; } return 0; } int WMConnection::receive(json_object** j_out, int *j_cnt, int socket) { char buf[1024] = {}; int n; n = read(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(); int cnt = 0; while (n >= tokener->char_offset) { j_out[cnt] = json_tokener_parse_ex(tokener, &buf[tokener->char_offset], n); if (nullptr == j_out[cnt]) { break; } cnt++; } *j_cnt = cnt; json_tokener_free(tokener); this->received_ecu_name = this->getSocketToEcuName(socket); HMI_DEBUG("received ecu name %s", this->received_ecu_name.c_str()); return 0; } int WMConnection::loadTimeoutConfigFile() { // 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(kPathTimeoutConfigFile); // 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 timeout", path.c_str()); this->times = kDefaultTimes; this->sleep = kDefaultSleep; return 0; } HMI_DEBUG("json_obj dump:%s", json_object_get_string(json_obj)); int times = jh::getIntFromJson(json_obj, "times"); this->times = (0 != times) ? times : kDefaultTimes; int sleep = jh::getIntFromJson(json_obj, "sleep"); this->sleep = (0 != sleep) ? sleep : kDefaultSleep; // Release json_object json_object_put(json_obj); return 0; } int WMConnection::loadConnectionConfigFile() { std::string path = std::string(kPathConnectionConfigFile); // Load connection config file json_object *json_obj, *json_cfg; 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->wm_socket["slave"] = SocketInfo("slave", kDefaultIpAddr, kDefaultPort, false); return 0; } HMI_DEBUG("json_obj dump:%s", json_object_get_string(json_obj)); const char* screen_name = jh::getStringFromJson(json_obj, "screen_name"); this->mode = (nullptr != screen_name) ? screen_name : "slave"; int wm_port = jh::getIntFromJson(json_obj, "wm_port"); this->wm_socket[screen_name] = SocketInfo(screen_name, "", wm_port, true); // Check HMI_DEBUG("screen_name:%s wm_port:%d", screen_name, wm_port); if (!json_object_object_get_ex(json_obj, "connections", &json_cfg)) { HMI_ERROR("connection.json Parse Error!!"); return 0; } int len = json_object_array_length(json_cfg); for (int i = 0; i < len; i++) { json_object *json_conn = json_object_array_get_idx(json_cfg, i); std::string screen_name = jh::getStringFromJson(json_conn, "screen_name"); std::string ip = jh::getStringFromJson(json_conn, "ip"); int wm_port = jh::getIntFromJson(json_conn, "wm_port"); bool master_mode = jh::getBoolFromJson(json_conn, "master_mode"); this->wm_socket[screen_name] = SocketInfo(screen_name, ip, wm_port, master_mode); HMI_DEBUG("screen_name:%s ip:%s port:%d server_mode:%s", screen_name.c_str(), ip.c_str(), wm_port, (master_mode ? "true" : "false")); } // Release json_object json_object_put(json_obj); return 0; } uint64_t WMConnection::getNextTimerTime(uint64_t msec) { struct timespec ts; if (clock_gettime(CLOCK_BOOTTIME, &ts) != 0) { HMI_ERROR("Could't set time (clock_gettime() returns with error"); return -1; } uint64_t timer_nsec = ((ts.tv_sec * 1000000000ULL) + (ts.tv_nsec) + (msec * 1000000)); if (!timer_nsec) { HMI_ERROR("Second is 0"); return -1; } return timer_nsec / 1000; } } // namespace wm