diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | CMakeLists.txt | 35 | ||||
-rw-r--r-- | LICENSE | 54 | ||||
-rw-r--r-- | README.md | 452 | ||||
-rw-r--r-- | README.md.ja | 452 | ||||
-rwxr-xr-x | buildtest.sh | 12 | ||||
-rw-r--r-- | cluster_ipc.pc.in | 10 | ||||
-rw-r--r-- | include/cluster_ipc.h | 49 | ||||
-rw-r--r-- | include/ipc_protocol.h | 165 | ||||
-rw-r--r-- | ipc_unit_test/CMakeLists.txt | 22 | ||||
-rw-r--r-- | ipc_unit_test/ipc_unit_test_client.c | 171 | ||||
-rw-r--r-- | ipc_unit_test/ipc_unit_test_common.c | 100 | ||||
-rw-r--r-- | ipc_unit_test/ipc_unit_test_common.h | 34 | ||||
-rw-r--r-- | ipc_unit_test/ipc_unit_test_server.c | 218 | ||||
-rw-r--r-- | src/CMakeLists.txt | 37 | ||||
-rw-r--r-- | src/ipc_client.c | 599 | ||||
-rw-r--r-- | src/ipc_internal.c | 74 | ||||
-rw-r--r-- | src/ipc_internal.h | 65 | ||||
-rw-r--r-- | src/ipc_server.c | 560 | ||||
-rw-r--r-- | src/ipc_usage_info_table.c | 84 |
20 files changed, 3194 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6db6abb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) 2021, Nippon Seiki Co., Ltd. +# SPDX-License-Identifier: Apache-2.0 + +project(cluster_ipc C) + +cmake_minimum_required(VERSION 2.8) + +# set gcc flags +set(CMAKE_C_FLAGS "-Wall -O2") + +# Build option for DEBUG +option(DEBUG_BUILD "This is debug build." OFF) +if(DEBUG_BUILD) + add_definitions(-DDEBUG) + set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/installdir/usr CACHE PATH "..." FORCE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") +endif() + +include(GNUInstallDirs) +set(IPC_LIBRARY_VERSION "1.0.0") + +# Name of library API +set(TARGET_NAME cluster_ipc) + +# Name of IC-service server API +#set(SERVER_API_NAME cluster_server_api) + +# Subdirectories +add_subdirectory(src) +add_subdirectory(ipc_unit_test) + +configure_file(cluster_ipc.pc.in cluster_ipc.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cluster_ipc.pc + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/pkgconfig) @@ -0,0 +1,54 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + You must give any other recipients of the Work or Derivative Works a copy of this License; and + You must cause any modified files to carry prominent notices stating that You changed the files; and + You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c68351 --- /dev/null +++ b/README.md @@ -0,0 +1,452 @@ +# IPC Diversion Section + +## Overview + +* It is a general-purpose implementation of Inter-processing communication between the Server and Client (IPC section). +* It consists mainly of the following: + * IPC library implementation source: src, include + * IPC unit test program: ipc_unit_test + +# Building Method + +* Building by following steps: + ```bash + $ mkdir build + $ cd build + $ cmake .. + $ make + ``` + * The above commands can be executed with the following script: + ```bash + $ ./buildtest.sh + ``` + +The installation described next will be installed on the host PC (/usr/local/). To change the installation destination, change the option passed to cmake. + +``` +Example +$ cmake -DCMAKE_INSTALL_PREFIX=./install .. +``` + +# Installing Method + + ```bash + $ cd build + $ sudo make install + ``` + +When succeeded, the following log will be output. + +``` +[100%] Built target ipc +Install the project... +-- Install configuration: "" +-- Installing: /usr/local/lib/pkgconfig/ipc.pc +-- Installing: /usr/local/lib/libipc.so.1.0.0 +-- Installing: /usr/local/lib/libipc.so.1 +-- Installing: /usr/local/lib/libipc.so +-- Installing: /usr/local/include/ipc.h +-- Installing: /usr/local/include/ipc_protocol.h +``` + +# Building Product + +* At last, Building will generate the following: + * \<installdir\>/include/ +(External Public header files) + ```bash + ipc.h + ipc_protocol.h + ``` + * \<installdir\>/lib/ +(Shared library files) + ```bash + libipc.so ( Symbolic link ) + libipc.so.1 ( Symbolic link ) + libipc.so.1.0.0 + ``` + * build/ipc_unit_test/ +(Test program executable file) + ```bash + ipc_unit_test_client + ipc_unit_test_server + ``` +<br> + +# How to use + +* This library contains functions for Server and Client; Each has different usage. + +## Common of Server/Client + +* The user needs to link with the following libraries. + * `libipc.so` +* User includes the library as following. + * #include <ipc.h> + * ipc_protocol.h description is not required; it described later within ipc.h (include <ipc.h>). +* The header files are used as follows. + * ipc.h + * Declare a list of available API functions. + * ipc_protocol.h + * Define IPC usage types and data structures for each usage. +* In libraries, Server and Client communicate using Unix Domain Socket. + * Generate different communication files for each usage type. + * By default, a communication file is generated in the execution hierarchy of the Server. + * For changing the location where generating the communication files, set the environment variable "IPC_DOMAIN_PATH" + ``` + Example) + $ export IPC_DOMAIN_PATH="/tmp" + →Unix Domain Socket communication files will be generated under /tmp. + ``` + +## For IC-Service + +* Using this library for IC-Service, use the following values and structures (see ipc_protocol.h). + * UsageType:IPC_USAGE_TYPE_IC_SERVICE + * Sending data structure:IPC_DATA_IC_SERVICE_S + * Changing type callback notification (enum):IPC_KIND_IC_SERVICE_E + * Unix Domain Socket communication File name:IpcIcService +* For IC-Service, the Cluster API library (libcluster_api.so) is the IPC Client. + * The API for Client is called from libcluster_api.so (described later) + +## Server API + +* Server applied libipc.so can use the following APIs: + * ipcServerStart(IPC_USAGE_TYPE_E usageType); + * Starting the IPC Server for the specified _usageType_. + * ipcSendMessage(IPC_USAGE_TYPE_E usageType, const void* pData, signed int size); + * Sending data to the IPC Client for the specified _usageType_. + * Specifying address and size of the sending data by pData and size arguments. + * Sending data is stored in the Data Pool prepared on the IPC Client side. + * ipcServerStop(IPC_USAGE_TYPE_E usageType); + * Terminate the IPC Server for the specified usageType. + +## Client API + +* The Client applied libipc.so can use the following APIs: + * ipcClientStart(IPC_USAGE_TYPE_E usageType); + * Starting the IPC Client for the specified usageType. + * Connecting with IPC Server for the same usageType. + * ipcReadDataPool(IPC_USAGE_TYPE_E usageType, void* pData, signed int* pSize); + * Reading all data in the Data Pool for the specified usageType. + * The address where storing the read data is specified in pData. Moreover, the size of storing data is specified in pSize. + * The contents of the Data Pool output to pData, and the actual read size output to pSize. + * ipcRegisterCallback(IPC_USAGE_TYPE_E usageType, IPC_CHANGE_NOTIFY_CB changeNotifyCb); + * When receiving data from the IPC Server, register the callback function for the specified usageType, which receiving notification of which data changed to what. + * ipcClientStop(IPC_USAGE_TYPE_E usageType); + * Terminate the IPC Client for the specified usageType. + +# Unit test executing method + +* Limitations + * Currently 2020/12/25, this test program connects Server and Client for IC-Service. + * The Unix domain socket communication files are generated under /tmp/ with the file name ipcIcService. + +* Since Inter processing communication takes place between ipc_unit_test_server and ipc_unit_test_client, so start each one in a separate terminal. + Testing example as bellow (Manually operated): + + 1. **Starting Server and then starting Client** + ```bash + (Terminal 1) + $ ./ipc_unit_test_server + command (h=help, q=quit): + ``` + ```bash + (Terminal 2) + $ ./ipc_unit_test_client + command (h=help, q=quit): + ``` + At this point, the Server and Client connection for IC-Service is completed. + (Executing ipcServerStart () and ipcClientStart ()) + + 2. **Editing and Sending Server data** + ```bash + (Terminal 1) + command (h=help, q=quit):w ←★input w + write command (h=help q=goto main menu):2 1 ←★input 2 1 + write command (h=help q=goto main menu):70 50 ←★input 70 50 + write command (h=help q=goto main menu):l ←★input 1 + ★Sending data list is displayed, the result of input contents as below. + 2: brake(4) = 1 ←★write command 2 1 result + 70: oTempUnitVal(4) = 50 ←★write command 70 50 result + write command (h=help q=goto main menu):q ←★input q + command (h=help, q=quit):s ←★input s (Executing ipcSendMessage()) + ipcSendMessage return:0 + command (h=help, q=quit): + ``` + On the Client side, The callback function should be responding. + ```bash + (Terminal 2) + command (h=help, q=quit):Enter changeNotifyCb ←★callback + kind = 2, size = 4, data=1 ←★Notification of brake value changed to 1 + Leave changeNotifyCb + ``` + ★oFor TempUnitVal change as an IC-Service, there is no callback since it is not monitored. + + 3. **Check Client side receiving.** + ```bash + (Terminal 2) + command (h=help, q=quit):r ←★input r + ★Sending data list is displayed, Sending data contained as bellow. + 2: brake(4) = 1 + 70: oTempUnitVal(4) = 50 + ``` + + 4. **Exit Client then Server.** + ```bash + (Terminal 2) + command (h=help, q=quit):q + bye... + $ + ``` + ```bash + (Terminal 1) + command (h=help, q=quit):q + bye... + $ + ``` + +# Adding/Changing IPC usage type method + +* First, the implementation only for IC-Service, but configured to add data for other services easily. +* Information for each usage type is managed in the following files: + * include/ipc_protocol.h (External Public header) + * src/ipc_usage_info_table.c (IPC Internal Source) +* Adding information for new service or changing information for existed service only by two files above. + * No changes are required other than .c or .h files in ipc. + * However, Regarding the application and test program used IPC, It is necessary to take measures according to the adding/changing of the ipc_protocol.h definition. +* Ideally, code can be generated automatically using tools and else. We do not consider that implementation this time. + +## Sample code for adding/changing the usage type (Sample code difference) + +First, the sample code for adding/changing two files as follow. + +### Example 1: When adding a new usage type + +The following example shows differences when adding the temporary usage type NEW_SERVICE. +(Parameters influenced when adding new usage type within the IPC) + +```patch +diff --git a/include/ipc_protocol.h b/include/ipc_protocol.h +index c0ad861..2bc1115 100644 +--- a/include/ipc_protocol.h ++++ b/include/ipc_protocol.h +@@ -6,6 +6,7 @@ + typedef enum { + IPC_USAGE_TYPE_IC_SERVICE = 0, + IPC_USAGE_TYPE_FOR_TEST, ++ IPC_USAGE_TYPE_NEW_SERVICE, // Adding usage type + IPC_USAGE_TYPE_MAX + } IPC_USAGE_TYPE_E; + +@@ -145,4 +146,17 @@ typedef struct { + signed int test; + } IPC_DATA_FOR_TEST_S; + ++// for IPC_USAGE_TYPE_NEW_SERVICE ++typedef enum { // Preparing only the type which we want to monitor/notify data change ++ IPC_KIND_NS_PARAM1 = 0, ++ IPC_KIND_NS_PARAM2 ++} IPC_KIND_NEW_SERVICE_E; ++ ++typedef struct { // This part for sending and receiving all data ++ int param1; ++ int param2; ++ int param3; ++ int param4; ++} IPC_DATA_NEW_SERVICE_S; ++ + #endif // IPC_PROTOCOL_H +diff --git a/src/ipc_usage_info_table.c b/src/ipc_usage_info_table.c +index 976cc73..51264c6 100644 +--- a/src/ipc_usage_info_table.c ++++ b/src/ipc_usage_info_table.c +@@ -51,16 +51,24 @@ static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeForTest[] = { + DEFINE_OFFSET_SIZE(IPC_DATA_FOR_TEST_S, test, IPC_KIND_TEST_TEST) + }; + ++// for IPC_USAGE_TYPE_FOR_TEST ++static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeNewService[] = { // Describing only the type which we want to monitor/notify data change ++ DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param1, IPC_KIND_NS_PARAM1), ++ DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param2, IPC_KIND_NS_PARAM2) ++}; //This example not monitoring/notifying changes of param3, param4 data ++ + // == usage info table == + // index of [] is IPC_USAGE_TYPE_E + IPC_DOMAIN_INFO_S g_ipcDomainInfoList[] = + { + {sizeof(IPC_DATA_IC_SERVICE_S), "ipcIcService"}, +- {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"} ++ {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"}, ++ {sizeof(IPC_DATA_NEW_SERVICE_S), "ipcNewService"} // add sending/receiving size information for new Service + }; + + IPC_CHECK_CHANGE_INFO_TABLE_S g_ipcCheckChangeInfoTbl[] = { + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeIcService), +- DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest) ++ DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest), ++ DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeNewService) // Registering data change monitoring table for new Service + }; + +``` + +### Example 2: Deleting part of the existing usage type data + +The following example shows differences when deleting the member variable brake from usage sending/receiving data of existing IC-Service. +(Parameters influenced when a member variable is deleted within the IPC) + +```patch +diff --git a/include/ipc_protocol.h b/include/ipc_protocol.h +index c0ad861..7fed8bf 100644 +--- a/include/ipc_protocol.h ++++ b/include/ipc_protocol.h +@@ -13,7 +13,6 @@ typedef enum { + typedef enum { + IPC_KIND_ICS_TURN_R = 0, + IPC_KIND_ICS_TURN_L, +- IPC_KIND_ICS_BRAKE, + IPC_KIND_ICS_SEATBELT, + IPC_KIND_ICS_HIGHBEAM, + IPC_KIND_ICS_DOOR, +@@ -51,7 +50,6 @@ typedef struct { + // Telltale + signed int turnR; + signed int turnL; +- signed int brake; + signed int seatbelt; + signed int frontRightSeatbelt; + signed int frontCenterSeatbelt; +diff --git a/src/ipc_usage_info_table.c b/src/ipc_usage_info_table.c +index 976cc73..40ac8df 100644 +--- a/src/ipc_usage_info_table.c ++++ b/src/ipc_usage_info_table.c +@@ -12,7 +12,6 @@ + static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeIcService[] = { + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, turnR, IPC_KIND_ICS_TURN_R), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, turnL, IPC_KIND_ICS_TURN_L), +- DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, brake, IPC_KIND_ICS_BRAKE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, seatbelt, IPC_KIND_ICS_SEATBELT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, highbeam, IPC_KIND_ICS_HIGHBEAM), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, door, IPC_KIND_ICS_DOOR), +``` + +## Common items regarding new addition of usage type + +* Adding some new enumerations/structures, with addition to existing enumeration/structure. There are no restrictions on the names. + +## Adding Information to include/ipc_protocol.h +* For one usage type, adding the following three items of information. + * Add usage type name. + * For new usage, Define enumeration for the change notification type. + * For new usage, Define sending/receiving data structure. +* Adding usage type name + * Sample code of this part will be as follow: + ```patch + typedef enum { + IPC_USAGE_TYPE_IC_SERVICE = 0, + IPC_USAGE_TYPE_FOR_TEST, + + IPC_USAGE_TYPE_NEW_SERVICE, // Adding usage type + IPC_USAGE_TYPE_MAX + } IPC_USAGE_TYPE_E; + ``` + * Adding a member for the usage type in enum IPC_USAGE_TYPE_E. + * Make sure to add it just before IPC_USAGE_TYPE_MAX (Avoiding effect on existing definitions) + * The value defined here is used to specify the argument usageType such as ipcServerStart() defined in ipc.h. +* For new usage, Define enumeration for the change notification type. + * Sample code of this part will be as follow: + ```patch + +typedef enum { // Preparing only the type which we want to monitor data change + + IPC_KIND_NS_PARAM1 = 0, + + IPC_KIND_NS_PARAM2 + +} IPC_KIND_NEW_SERVICE_E; + ``` + * Adding an enumeration for the data change notification type. Related to the sending/receiving data structures will describe next. + * This value is used to specify the third argument kind of callback function registered by ipcRegisterCallback(). + * There are no restrictions for naming enumeration and member. +* For new usage, Define sending/receiving data structure. + * Sample code of this part will be as follow: + ```patch + +typedef struct { // This part for sending and receiving all data + + int param1; + + int param2; + + int param3; + + int param4; + +} IPC_DATA_NEW_SERVICE_S; + ``` + * For new usage, adding send/receive data structures. + * The IPC Server will send all the data in the defined structure to the IPC Client. + +## Regarding adding src/ipc_usage_info_table.c +* For one usage type, adding the following three items of information. + * Add a type mapping table for data change notification. + * Add communication domain information (Communication size and file name). + * Add mapping table for a relationship between usage and change type. +* Add a type mapping table for data change notification. + * Sample code of this part will be as follow: + ``` + +// for IPC_USAGE_TYPE_FOR_TEST + +static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeNewService[] = { // Preparing only the type which we want to monitor data change + + DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param1, IPC_KIND_NS_PARAM1), + + DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param2, IPC_KIND_NS_PARAM2) + +}; // This example not monitoring/notifying changes of param3, param4 data + ``` + * For new usage, add a structure array of IPC_CHECK_CHANGE_INFO_S. + * Describing a table that maps the definition of change notification type enumeration (defined in ipc_protocol.h) with the data structure members. + * This table is used for Callback notification of the last receiving data type change when the IPC Client received data from the IPC Server. + * In structure array, describing multiple macros which defining matching as shown below. + ```c + DEFINE_OFFSET_SIZE(<Data structure name>, <Structure member name>, Change notification enumeration member name), + ``` + * In the case of the above sample code, g_ipcCheckChangeNewService[] will be as follows. + * If the value of param1 is different from the value of the previous receiving, the callback change type IPC_KIND_NS_PARAM1 is notified to the IPC Client. + * If the value of param2 is different from the value of the previous receiving, the callback change type IPC_KIND_NS_PARAM2 is notified to the IPC Client. + * For param3 and param4 are not described, the callback does not notify even if the value of the previous receiving is different. + +* Add communication domain information (Communication size and file name). + * Sample code of this part will be as follow: + ```patch + IPC_DOMAIN_INFO_S g_ipcDomainInfoList[] = + { + {sizeof(IPC_DATA_IC_SERVICE_S), "ipcIcService"}, + - {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"} + + {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"}, + + {sizeof(IPC_DATA_NEW_SERVICE_S), "ipcNewService"} //add sending/receiving size information for new Service + }; + ``` + * In structure array g_ipcDomainInfoList[], adding the domain information for new usage. + * This addition determines the sending/receiving data size used for the newly added usage type and Domain file name used for Unix Domain Socket communication. + * It is necessary to match the definition order of the enum IPC_USAGE_TYPE_E of ipc_protocol.h, so ensure adding it at the end. + * Add communication data size structure and the domain filename information to the end of g_ipcDomainInfoList[], as follows: + ```c + {sizeof(<communication data structure name>), "domain file name"}, + ``` +* Add mapping table for a relationship between usage and change type. + * Sample code of this part will be as follow: + ```patch + IPC_CHECK_CHANGE_INFO_TABLE_S g_ipcCheckChangeInfoTbl[] = {i + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeIcService), + - DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest) + + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest), + + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeNewService) // Registering data change monitoring table for new service + }; + ``` + * In structure array g_ipcCheckChangeInfoTbl[], adding information about the mapping table for a relationship between new usage and change notification type. + * It is necessary to match the definition order of the enum IPC_USAGE_TYPE_E of ipc_protocol.h, so ensure adding it at the end. + * Describing the above-mentioned "change notification type mapping table structure" in the following macro, then add it to the end of g_ipcCheckChangeInfoTbl[]. + ```c + DEFINE_CHANGE_INFO_TABLE(<change notification type mapping table structure name>), + ``` + +## Changing of sending data for existing usage +* When deleting or renaming a member variable in an existing sending data structure in ipc_protocol.h + * Trying to build each ipc part and application that uses ipc for the service, then fix the part that causing the Compile error. + +* When adding a member variable in an existing sending data structure in ipc_protocol.h + * Refer to [Adding/Changing IPC usage type method](#adding-changing-ipc-usage-type-method), add it to include/ipc_protocol.h and src/ipc_usage_info_table.c. + +## Supplement +* In src/ipc_usage_info_table.c, the information is described in the DEFINE_OFFSET_SIZE() macro, which using offsetof() and sizeof() to get the offset and size of member variables from the head of the related structure. + * In order to make it easier to add usage types, the IPC process does not directly specify variable names in data structures. + * For each usage, by preparing an offset table of the data structure, it becomes possible to know what variables are in which byte of the sending data. + * This structure makes it possible to check data change without directly specifying the member variable name inside the IPC process. + * Adding the usage type according to [Adding/Changing IPC usage type method](#adding-changing-ipc-usage-type-method), the IPC inter processing can process new usage. diff --git a/README.md.ja b/README.md.ja new file mode 100644 index 0000000..a35a80e --- /dev/null +++ b/README.md.ja @@ -0,0 +1,452 @@ +# IPC流用可能部 + +## 概要 + +* ServerとClientの通信処理(IPC)部分を汎用的に実装したものです。 +* 大きく以下で構成されています。 + * IPCライブラリ実装ソース + * IPC単体テスト用プログラム + +# ビルド方法 + +* 以下の手順でビルド可能です。You can build it by following these steps: + ```bash + $ mkdir build + $ cd build + $ cmake .. + $ make + ``` + * 上記のコマンドは以下のスクリプトでも実行可能です。 + ```bash + $ ./buildtest.sh + ``` + +後述するインストールでは、ホストPC(/usr/local/)にインストールされる。インストール先を変更するためには、cmakeに渡すオプションを変更する。 + +``` +例 +$ cmake -DCMAKE_INSTALL_PREFIX=./install .. +``` + +# インストール方法 + + ```bash + $ cd build + $ sudo make install + ``` + +成功すると、下記のログが出力される。 + +``` +[100%] Built target ipc +Install the project... +-- Install configuration: "" +-- Installing: /usr/local/lib/pkgconfig/ipc.pc +-- Installing: /usr/local/lib/libipc.so.1.0.0 +-- Installing: /usr/local/lib/libipc.so.1 +-- Installing: /usr/local/lib/libipc.so +-- Installing: /usr/local/include/ipc.h +-- Installing: /usr/local/include/ipc_protocol.h +``` + +# ビルド生成物 + +* ビルドにより、最終的には以下が生成されます。 + * \<installdir\>/include/ 以下 + 外部公開向けヘッダファイル + ```bash + ipc.h + ipc_protocol.h + ``` + * \<installdir\>/lib/ 以下 + 共有ライブラリファイル + ```bash + libipc.so (シンボリックリンク) + libipc.so.1 (シンボリックリンク) + libipc.so.1.0.0 + ``` + * build/ipc_unit_test/ 以下 + テストプログラム実行ファイル + ```bash + ipc_unit_test_client + ipc_unit_test_server + ``` +<br> + +# 使用方法 + +* 本ライブラリにはServer用とClient用の機能が入っており、それぞれ使用方法の異なる部分があります。 + +## Server/Client共通 + +* 使用者は以下のライブラリとリンクする必要があります。 + * `libipc.so` +* 本ライブラリ使用者は以下をincludeします。 + * #include <ipc.h> + * 後述のipc_protocol.hはipc.h内からincludeされるため、記載不要です。 +* 使用するヘッダファイルは以下の通りです。 + * ipc.h + * 使用可能なAPI関数一覧が宣言されています。 + * ipc_protocol.h + * IPCの用途種別と、各用途に応じたデータ構造体が定義されています。 +* 本ライブラリでは、Unix Domain Socketを用いてServerとClientが通信します。 + * 用途種別ごとに異なる通信用ファイルを生成します。 + * デフォルトではServerの実行階層に通信用ファイルを生成します。 + * 環境変数 "IPC_DOMAIN_PATH" を設定しておくことで、通信用ファイルの生成場所を変更することができます。 + ``` + 例) + $ export IPC_DOMAIN_PATH="/tmp" + →/tmp以下にUnix Domain Socket通信用ファイルが生成されるようになる。 + ``` + +## IC-Service向け + +* 本ライブラリをIC-Service向けに使用する場合、以下の値・構造体を用います(ipc_protocol.h参照)。 + * 用途種別usageType:IPC_USAGE_TYPE_IC_SERVICE + * 送信データ構造体:IPC_DATA_IC_SERVICE_S + * 変化種別コールバック通知用enum:IPC_KIND_IC_SERVICE_E + * Unix Domain Socket通信用ファイル名:IpcIcService +* IC-Serviceの場合、Cluster APIライブラリ(libcluster_api.so)がIPC Clientとなっています。 + * 後述のClient用 APIは、libcluster_api.so内から呼び出されます。 + +## Server用 API + +* libipc.soを用いるServerは以下のAPIを使用できます。 + * ipcServerStart(IPC_USAGE_TYPE_E usageType); + * 指定した用途種別usageType用のIPC Serverを起動します。 + * ipcSendMessage(IPC_USAGE_TYPE_E usageType, const void* pData, signed int size); + * 指定した用途種別usageType用に、IPC Clientへのデータ送信を行います。 + * 送信データのアドレスとサイズを引数pData, sizeで指定します。 + * 送信データは、IPC Client側で用意しているData Poolに格納されます。 + * ipcServerStop(IPC_USAGE_TYPE_E usageType); + * 指定した用途種別usageType用のIPC Serverを終了します。 + +## Client用 API + +* libipc.soを用いるClientは以下のAPIを使用できます。 + * ipcClientStart(IPC_USAGE_TYPE_E usageType); + * 指定した用途種別usageType用のIPC Clientを起動します。 + * 同じusageType用のIPC Serverと接続します。 + * ipcReadDataPool(IPC_USAGE_TYPE_E usageType, void* pData, signed int* pSize); + * 指定したusageType用のData Poolの全データを読み込みます。 + * 読み込みデータ格納先のアドレスはpDataに、格納可能なサイズはpSizeに指定します。 + * Data Poolの内容はpDataに出力され、実際に読み込めたサイズはpSizeに出力されます。 + * ipcRegisterCallback(IPC_USAGE_TYPE_E usageType, IPC_CHANGE_NOTIFY_CB changeNotifyCb); + * IPC Serverからデータを受信した時、どのデータが何に変化したかの通知を受けるためのコールバック関数を、指定したusageType用に登録します。 + * ipcClientStop(IPC_USAGE_TYPE_E usageType); + * 指定した用途種別usageType用のIPC Clientを終了します。 + +# 単体テスト実行方法 + +* 制限 + * 2020/12/25現在、このテストプログラムは必ずIC-Service用としてServerとClientを接続します。 + * unix domain socket通信用ファイルは /tmp/ 以下に ipcIcService というファイル名で生成されます。 + +* ipc_unit_test_server と ipc_unit_test_client 間でプロセス間通信を行いますので、それぞれを別々のターミナルで起動します。 + 操作は手動ですが、以下、テストの一例です。 + + 1. **Server, Clientの順に起動します。** + ```bash + (Terminal 1) + $ ./ipc_unit_test_server + command (h=help, q=quit): + ``` + ```bash + (Terminal 2) + $ ./ipc_unit_test_client + command (h=help, q=quit): + ``` + この時点でServerとClientはIC-Service用として接続済みです。 + (ipcServerStart()とipcClientStart()が実行されています) + + 2. **Server側の送信データを適当に編集して送信してみます。** + ```bash + (Terminal 1) + command (h=help, q=quit):w ←★ w を入力 + write command (h=help q=goto main menu):2 1 ←★2 1を入力 + write command (h=help q=goto main menu):70 50 ←★70 50を入力 + write command (h=help q=goto main menu):l ←★l を入力 + ★送信データ一覧が表示されるが、以下、入力内容が反映されていること。 + 2: brake(4) = 1 ←★write command 2 1 の結果 + 70: oTempUnitVal(4) = 50 ←write command 70 50 の結果 + write command (h=help q=goto main menu):q ←★q を入力 + command (h=help, q=quit):s ←★s を入力(ipcSendMessage()の実行) + ipcSendMessage return:0 + command (h=help, q=quit): + ``` + Client側にコールバック関数の反応が出ているはず。 + ```bash + (Terminal 2) + command (h=help, q=quit):Enter changeNotifyCb ←★コールバック + kind = 2, size = 4, data=1 ←★brakeの値が1に変わったことを通知 + Leave changeNotifyCb + ``` + ★oTempUnitValの変化についてはIC-Serviceとしては監視対象でないのでコールバック無し。 + + 3. **Client側で受信できているか確認します。** + ```bash + (Terminal 2) + command (h=help, q=quit):r ←★r を入力 + ★送信データ一覧が表示されるが、以下、送信されたデータが入っていること。 + 2: brake(4) = 1 + 70: oTempUnitVal(4) = 50 + ``` + + 4. **Client, Serverの順に終了します。** + ```bash + (Terminal 2) + command (h=help, q=quit):q + bye... + $ + ``` + ```bash + (Terminal 1) + command (h=help, q=quit):q + bye... + $ + ``` + +# IPC用途種別の追加・変更方法 + +* まずはIC-Service向けにのみ実装しましたが、別の用途向けのデータを容易に追加することが可能なように構成しています。 +* 各用途種別向けの情報は、以下のファイルにて管理しています。 + * include/ipc_protocol.h (外部公開ヘッダ) + * src/ipc_usage_info_table.c (IPC内部向けソース) +* 新規用途の情報追加、もしくは既存用途への情報変更は上記2つのファイルに対してのみ行うだけで良いようにしています。 + * ipc内の他の.cファイルや.hファイルに対しては変更不要です。 + * ただし、その用途でIPCを用いるアプリやテストプログラムに対しては、ipc_protocol.hへの定義追加・変更に合わせた対応が別途必要になります。 +* 理想ではツール等でコードを自動生成できることが理想ですが、今回はそこまでの実装を考慮しておりません。 + +## 用途種別の追加・変更に関するサンプルコード(差分) + +まずは2つのファイルに対してどのように追加・変更する際のサンプルコードを示します。 + +### 例1:新規の用途種別を追加する場合 + +仮にNEW_SERVICEという用途種別を追加する場合の差分例を示します。 +IPC内における、新規の用途種別追加による影響範囲となります。 + +```patch +diff --git a/include/ipc_protocol.h b/include/ipc_protocol.h +index c0ad861..2bc1115 100644 +--- a/include/ipc_protocol.h ++++ b/include/ipc_protocol.h +@@ -6,6 +6,7 @@ + typedef enum { + IPC_USAGE_TYPE_IC_SERVICE = 0, + IPC_USAGE_TYPE_FOR_TEST, ++ IPC_USAGE_TYPE_NEW_SERVICE, // 用途種別追加 + IPC_USAGE_TYPE_MAX + } IPC_USAGE_TYPE_E; + +@@ -145,4 +146,17 @@ typedef struct { + signed int test; + } IPC_DATA_FOR_TEST_S; + ++// for IPC_USAGE_TYPE_NEW_SERVICE ++typedef enum { // データ変化を監視・通知したい種別のみ用意 ++ IPC_KIND_NS_PARAM1 = 0, ++ IPC_KIND_NS_PARAM2 ++} IPC_KIND_NEW_SERVICE_E; ++ ++typedef struct { // この用途で送受信する全データ ++ int param1; ++ int param2; ++ int param3; ++ int param4; ++} IPC_DATA_NEW_SERVICE_S; ++ + #endif // IPC_PROTOCOL_H +diff --git a/src/ipc_usage_info_table.c b/src/ipc_usage_info_table.c +index 976cc73..51264c6 100644 +--- a/src/ipc_usage_info_table.c ++++ b/src/ipc_usage_info_table.c +@@ -51,16 +51,24 @@ static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeForTest[] = { + DEFINE_OFFSET_SIZE(IPC_DATA_FOR_TEST_S, test, IPC_KIND_TEST_TEST) + }; + ++// for IPC_USAGE_TYPE_FOR_TEST ++static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeNewService[] = { // データ変化を監視・通知したい種別のみ記載 ++ DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param1, IPC_KIND_NS_PARAM1), ++ DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param2, IPC_KIND_NS_PARAM2) ++}; // この例では、param3, param4のデータ変化については監視・通知しない。 ++ + // == usage info table == + // index of [] is IPC_USAGE_TYPE_E + IPC_DOMAIN_INFO_S g_ipcDomainInfoList[] = + { + {sizeof(IPC_DATA_IC_SERVICE_S), "ipcIcService"}, +- {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"} ++ {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"}, ++ {sizeof(IPC_DATA_NEW_SERVICE_S), "ipcNewService"} // 新規用途用の送受信サイズ情報追加 + }; + + IPC_CHECK_CHANGE_INFO_TABLE_S g_ipcCheckChangeInfoTbl[] = { + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeIcService), +- DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest) ++ DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest), ++ DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeNewService) // 新規用途用 データ変化監視テーブルを登録 + }; + +``` + +### 例2:既存の用途種別のデータの一部を削除する場合 + +既存の用途IC-Serviceの送受信データから、メンバ変数brakeを削除する場合の差分例を示します。 +IPC内における、メンバ変数を削除した場合の影響範囲となります。 + +```patch +diff --git a/include/ipc_protocol.h b/include/ipc_protocol.h +index c0ad861..7fed8bf 100644 +--- a/include/ipc_protocol.h ++++ b/include/ipc_protocol.h +@@ -13,7 +13,6 @@ typedef enum { + typedef enum { + IPC_KIND_ICS_TURN_R = 0, + IPC_KIND_ICS_TURN_L, +- IPC_KIND_ICS_BRAKE, + IPC_KIND_ICS_SEATBELT, + IPC_KIND_ICS_HIGHBEAM, + IPC_KIND_ICS_DOOR, +@@ -51,7 +50,6 @@ typedef struct { + // Telltale + signed int turnR; + signed int turnL; +- signed int brake; + signed int seatbelt; + signed int frontRightSeatbelt; + signed int frontCenterSeatbelt; +diff --git a/src/ipc_usage_info_table.c b/src/ipc_usage_info_table.c +index 976cc73..40ac8df 100644 +--- a/src/ipc_usage_info_table.c ++++ b/src/ipc_usage_info_table.c +@@ -12,7 +12,6 @@ + static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeIcService[] = { + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, turnR, IPC_KIND_ICS_TURN_R), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, turnL, IPC_KIND_ICS_TURN_L), +- DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, brake, IPC_KIND_ICS_BRAKE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, seatbelt, IPC_KIND_ICS_SEATBELT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, highbeam, IPC_KIND_ICS_HIGHBEAM), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, door, IPC_KIND_ICS_DOOR), +``` + +## 用途種別の新規追加に関する共通事項 + +* いくつか列挙体・構造体の新規追加、および既存の列挙体・構造体内への追記を伴いますが、いずれも名称については特に制約はありません。 + +## include/ipc_protocol.h へ追記する情報 +* 1つの用途種別に対し、以下の3つの情報を追記します。 + * 用途種別名の追記 + * 新規用途向けの変化通知種別用列挙体の定義 + * 新規用途向けの送受信データ構造体の定義 +* 用途種別名の追記 + * サンプルコードの以下の部分のことになります。 + ```patch + typedef enum { + IPC_USAGE_TYPE_IC_SERVICE = 0, + IPC_USAGE_TYPE_FOR_TEST, + + IPC_USAGE_TYPE_NEW_SERVICE, // 用途種別追加 + IPC_USAGE_TYPE_MAX + } IPC_USAGE_TYPE_E; + ``` + * enum IPC_USAGE_TYPE_E 内に用途種別となるメンバを追加します。 + * IPC_USAGE_TYPE_MAXの1つ手前に追加するようにしてください(既存の定義に影響を及ぼさないようにするため)。 + * ここで定義した値は、ipc.hで定義されているipcServerStart()などの引数usageTypeへの指定用に使用します。 +* 新規用途向けの変化通知種別用列挙体の定義 + * サンプルコードの以下の部分のことになります。 + ```patch + +typedef enum { // データ変化を監視したい種別のみ用意 + + IPC_KIND_NS_PARAM1 = 0, + + IPC_KIND_NS_PARAM2 + +} IPC_KIND_NEW_SERVICE_E; + ``` + * データ変化通知の種別用列挙体を追加します。後述の送受信データ構造体と関連があります。 + * この値は、ipcRegisterCallback()で登録されたコールバック関数の第3引数kindへの指定に使用します。 + * 列挙体名、メンバ名について、特に名称の制約はありません。 +* 新規用途向けのデータ構造体の定義 + * サンプルコードの以下の部分のことになります。 + ```patch + +typedef struct { // この用途で送受信する全データ + + int param1; + + int param2; + + int param3; + + int param4; + +} IPC_DATA_NEW_SERVICE_S; + ``` + * 新規用途で送受信するデータ構造体を追加します。 + * IPC ServerからIPC Clientへは、ここで定義した構造体のデータ全てを送信することになります。 + +## src/ipc_usage_info_table.c に対する追記 +* 1つの用途種別に対し、以下の3つの情報を追記します。 + * データ変化通知用の種別対応テーブルの追加 + * 通信用ドメイン情報追記(通信サイズ、ドメインファイル名) + * 用途と変化種別対応テーブルとの関係追記 +* データ変化通知用の種別対応テーブルの追加 + * サンプルコードの以下の部分のことになります。 + ``` + +// for IPC_USAGE_TYPE_FOR_TEST + +static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeNewService[] = { // データ変化を監視・通知したい種別のみ記載 + + DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param1, IPC_KIND_NS_PARAM1), + + DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param2, IPC_KIND_NS_PARAM2) + +}; // この例では、param3, param4のデータ変化については監視・通知しない。 + ``` + * 新規用途向けに、IPC_CHECK_CHANGE_INFO_Sの構造体配列を追加します。 + * ipc_protocol.hで定義した変化通知種別用列挙体の定義と、通信するデータ構造体メンバを対応付けるテーブルを記載します。 + * このテーブルは、IPC ClientがIPC Serverからデータを受信する時に、前回受信時と変化しているデータ種別をコールバック通知する際に使用します。 + * 構造体配列内には、以下のようなマクロを複数個記載して対応を定義します。 + ```c + DEFINE_OFFSET_SIZE(<データ構造体名>, <構造体メンバ名>, 変化通知列挙体メンバ名), + ``` + * 上記サンプルコード、g_ipcCheckChangeNewService[]の場合は以下のようになります。 + * param1が前回受信時と値が異なる場合、変化種別 IPC_KIND_NS_PARAM1 としてIPC Clientへコールバック通知する。 + * param2が前回受信時と値が異なる場合、変化種別 IPC_KIND_NS_PARAM2 としてIPC Clientへコールバック通知する。 + * 記載していないparam3, param4については、前回受信時と値が異なっていてもコールバック通知はしない。 + +* 通信用ドメイン情報追記(通信サイズ、ドメインファイル名) + * サンプルコードの以下の部分のことになります。 + ```patch + IPC_DOMAIN_INFO_S g_ipcDomainInfoList[] = + { + {sizeof(IPC_DATA_IC_SERVICE_S), "ipcIcService"}, + - {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"} + + {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"}, + + {sizeof(IPC_DATA_NEW_SERVICE_S), "ipcNewService"} // 新規用途用の送受信サイズ情報追加 + }; + ``` + * 構造体配列 g_ipcDomainInfoList[] に、新規用途向けのドメイン情報を追記します。 + * この追記により、新規追加した用途種別で用いる送受信データサイズと、Unix Domain Socket通信で用いるドメインファイル名が決まります。 + * ipc_protocol.hのenum IPC_USAGE_TYPE_Eの定義順と一致させる必要があるので、必ず末尾に追加してください。 + * 以下のように、通信するデータ構造体のサイズと、ドメインファイル名の情報を、g_ipcDomainInfoList[] の末尾に追記します。 + ```c + {sizeof(<通信するデータ構造体名>), "ドメインファイル名"}, + ``` +* 用途と変化種別対応テーブルとの関係追記 + * サンプルコードの以下の部分のことになります。 + ```patch + IPC_CHECK_CHANGE_INFO_TABLE_S g_ipcCheckChangeInfoTbl[] = { + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeIcService), + - DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest) + + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest), + + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeNewService) // 新規用途用 データ変化監視テーブルを登録 + }; + ``` + * 構造体配列 g_ipcCheckChangeInfoTbl[] に、新規用途向けの変化通知種別対応テーブルに関する情報を追記します。 + * ipc_protocol.hのenum IPC_USAGE_TYPE_Eの定義順と一致させる必要があるので、必ず末尾に追加してください。 + * 前述の「変化通知種別対応テーブル構造体」を、以下のようなマクロに記載し、g_ipcCheckChangeInfoTbl[]の末尾に追記します。 + ```c + DEFINE_CHANGE_INFO_TABLE(<変化通知種別対応テーブル構造体名>), + ``` + +## 既存用途向けの送信データを一部変更する場合 +* ipc_protocol.h内の既存の送信データ構造体内のメンバ変数の削除、もしくは名称変更する場合 + * ipc部分、およびipcをその用途で用いるアプリをそれぞれビルドしてみて、コンパイルエラーとなった部分を修正します。 + +* ipc_protocol.h内の既存の送信データ構想体へメンバ変数を追加する場合 + * [IPC用途種別の追加・変更方法](#IPC用途種別の追加・変更方法) を参考に、include/ipc_protocol.hとsrc/ipc_usage_info_table.cへの追記を行います。 + +## 補足 +* src/ipc_usage_info_table.cにて、DEFINE_OFFSET_SIZE()マクロにて情報を記載しているが、これはoffsetof()とsizeof()を使うことで、メンバ変数に関する構造体先頭からオフセットとサイズを取得しています。 + * 用途種別の追加を容易に行えるようにするため、IPC処理内部ではデータ構造体内の変数名を直接指定しないような実装を行っています。 + * 各用途に対して、データ構造体のオフセットテーブルを用意することで、送信されたデータの何バイト目に何の変数があるかがわかるようになります。 + * この仕組みにより、IPC処理内部でメンバ変数名を直接指定しなくとも、データ変化の確認が可能となります。 + * [IPC用途種別の追加・変更方法](#IPC用途種別の追加・変更方法) に従って用途種別を追加することで、IPC内部処理は新たな用途に対する処理ができるようになります。 diff --git a/buildtest.sh b/buildtest.sh new file mode 100755 index 0000000..4c4e2c3 --- /dev/null +++ b/buildtest.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +rm -rf build/ +mkdir build +cd build +cmake .. +make +# make install +#echo "**************** Test for cluster_api ****************" +#./test/cluster_api_test +#echo "*************** Test for cluster_server **************" +#./server_test/cluster_server_test diff --git a/cluster_ipc.pc.in b/cluster_ipc.pc.in new file mode 100644 index 0000000..dac9b70 --- /dev/null +++ b/cluster_ipc.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ + +Name: @TARGET_NAME@ +Description: client and server ipc library by domain socket with defined shared data protocol +Version: @IPC_LIBRARY_VERSION@ + +Cflags: -I${includedir} +Libs: -L${libdir} -lcluster_ipc diff --git a/include/cluster_ipc.h b/include/cluster_ipc.h new file mode 100644 index 0000000..64b731c --- /dev/null +++ b/include/cluster_ipc.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef IPC_H +#define IPC_H + +#include <ipc_protocol.h> + +// Environment Variable for unix-domain-socket file path +#define IPC_ENV_DOMAIN_SOCKET_PATH "IPC_DOMAIN_PATH" + +// return value for API +typedef enum { + IPC_RET_OK = 0, + IPC_ERR_PARAM, + IPC_ERR_SEQUENCE, + IPC_ERR_NO_RESOURCE, + IPC_ERR_OTHER +} IPC_RET_E; + +// format of callback function +typedef void (*IPC_CHANGE_NOTIFY_CB)(void* pData, signed int size, int kind); + +// for Server Function +IPC_RET_E ipcServerStart(IPC_USAGE_TYPE_E usageType); +IPC_RET_E ipcSendMessage(IPC_USAGE_TYPE_E usageType, const void* pData, signed int size); +IPC_RET_E ipcServerStop(IPC_USAGE_TYPE_E usageType); + +// for Client Function +IPC_RET_E ipcClientStart(IPC_USAGE_TYPE_E usageType); +IPC_RET_E ipcReadDataPool(IPC_USAGE_TYPE_E usageType, void* pData, signed int* pSize); +IPC_RET_E ipcRegisterCallback(IPC_USAGE_TYPE_E usageType, IPC_CHANGE_NOTIFY_CB changeNotifyCb); +IPC_RET_E ipcClientStop(IPC_USAGE_TYPE_E usageType); + +#endif // IPC_H diff --git a/include/ipc_protocol.h b/include/ipc_protocol.h new file mode 100644 index 0000000..dc703d0 --- /dev/null +++ b/include/ipc_protocol.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef IPC_PROTOCOL_H +#define IPC_PROTOCOL_H + +// TODO: Write something + +typedef enum { + IPC_USAGE_TYPE_IC_SERVICE = 0, + IPC_USAGE_TYPE_FOR_TEST, + IPC_USAGE_TYPE_MAX +} IPC_USAGE_TYPE_E; + +// for IPC_USAGE_TYPE_IC_SERVICE +typedef enum { + IPC_KIND_ICS_TURN_R = 0, + IPC_KIND_ICS_TURN_L, + IPC_KIND_ICS_BRAKE, + IPC_KIND_ICS_SEATBELT, + IPC_KIND_ICS_HIGHBEAM, + IPC_KIND_ICS_DOOR, + IPC_KIND_ICS_EPS, + IPC_KIND_ICS_SRS_AIRBAG, + IPC_KIND_ICS_ABS, + IPC_KIND_ICS_LOW_BATTERY, + IPC_KIND_ICS_OIL_PRESS, + IPC_KIND_ICS_ENGINE, + IPC_KIND_ICS_FUEL, + IPC_KIND_ICS_IMMOBI, + IPC_KIND_ICS_TM_FAIL, + IPC_KIND_ICS_ESP_ACT, + IPC_KIND_ICS_ESP_OFF, + IPC_KIND_ICS_ADAPTING_LIGHTING, + IPC_KIND_ICS_AUTO_STOP, + IPC_KIND_ICS_AUTO_STOP_FAIL, + IPC_KIND_ICS_PARKING_LIGHTS, + IPC_KIND_ICS_FRONT_FOG, + IPC_KIND_ICS_EXTERIOR_LIGHT_FAULT, + IPC_KIND_ICS_ACC_FAIL, + IPC_KIND_ICS_LDW_OFF, + IPC_KIND_ICS_HILL_DESCENT, + IPC_KIND_ICS_AUTO_HI_BEAM_GREEN, + IPC_KIND_ICS_AUTO_HI_BEAM_AMBER, + IPC_KIND_ICS_LDW_OPERATE, + IPC_KIND_ICS_GENERAL_WARN, + IPC_KIND_ICS_SPORTS_MODE, + IPC_KIND_ICS_DRIVING_POWER_MODE, + IPC_KIND_ICS_HOT_TEMP, + IPC_KIND_ICS_LOW_TEMP +} IPC_KIND_IC_SERVICE_E; + +typedef struct { + // Telltale + signed int turnR; + signed int turnL; + signed int brake; + signed int seatbelt; + signed int frontRightSeatbelt; + signed int frontCenterSeatbelt; + signed int frontLeftSeatbelt; + signed int mid1RightSeatbelt; + signed int mid1CenterSeatbelt; + signed int mid1LeftSeatbelt; + signed int mid2RightSeatbelt; + signed int mid2CenterSeatbelt; + signed int mid2LeftSeatbelt; + signed int rearRightSeatbelt; + signed int rearCenterSeatbelt; + signed int rearLeftSeatbelt; + signed int highbeam; + signed int door; + signed int frontRightDoor; + signed int frontLeftDoor; + signed int rearRightDoor; + signed int rearLeftDoor; + signed int trunkDoor; + signed int hoodDoor; + signed int eps; + signed int srsAirbag; + signed int abs; + signed int lowBattery; + signed int oilPress; + signed int engine; + signed int fuel; + signed int immobi; + signed int tmFail; + signed int espAct; + signed int espOff; + signed int adaptingLighting; + signed int autoStop; + signed int autoStopFail; + signed int parkingLights; + signed int frontFog; + signed int exteriorLightFault; + signed int accFail; + signed int ldwOff; + signed int hillDescent; + signed int autoHiBeamGreen; + signed int autoHiBeamAmber; + signed int sportsMode; + signed int ldwOperate; + signed int generalWarn; + signed int drivingPowerMode; + signed int hotTemp; + signed int lowTemp; + + // ShiftPosition + signed int gearAtVal; + signed int gearMtVal; + + // Speed + unsigned long spAnalogVal; + signed int spAnaDigUnitVal; + + // Tacho + unsigned long taAnalogVal; + + // TripComputer + unsigned long trcomTripAVal; + unsigned long trcomTripBVal; + unsigned long trcomOdoVal; + signed int trcomUnitVal; + unsigned short avgSpeedAVal; + unsigned short avgSpeedBVal; + unsigned short hourAVal; + unsigned short hourBVal; + unsigned char minuteAVal; + unsigned char minuteBVal; + unsigned char secondAVal; + unsigned char secondBVal; + signed short oTempVal; + signed int oTempUnitVal; + unsigned short cruRangeVal; + unsigned short avgFuelAVal; + unsigned short avgFuelBVal; + unsigned short insFuelAVal; + unsigned short insFuelBVal; + signed int fuelEconomyUnitVal; +} IPC_DATA_IC_SERVICE_S; + +// for IPC_USAGE_TYPE_FOR_TEST +typedef enum { + IPC_KIND_TEST_TEST = 0 +} IPC_KIND_FOR_TEST_E; + +typedef struct { + signed int test; +} IPC_DATA_FOR_TEST_S; + +#endif // IPC_PROTOCOL_H diff --git a/ipc_unit_test/CMakeLists.txt b/ipc_unit_test/CMakeLists.txt new file mode 100644 index 0000000..944ad0b --- /dev/null +++ b/ipc_unit_test/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2021, Nippon Seiki Co., Ltd. +# SPDX-License-Identifier: Apache-2.0 + +# Define project Targets +set(TEST_CLIENT_NAME ipc_unit_test_client) +set(TEST_SERVER_NAME ipc_unit_test_server) + +add_executable(${TEST_CLIENT_NAME} ipc_unit_test_client.c ipc_unit_test_common.c) +target_link_libraries(${TEST_CLIENT_NAME} ${TARGET_NAME}) +target_include_directories(${TEST_CLIENT_NAME} PRIVATE + ./ + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> +) + +add_executable(${TEST_SERVER_NAME} ipc_unit_test_server.c ipc_unit_test_common.c) +target_link_libraries(${TEST_SERVER_NAME} ${TARGET_NAME}) +target_include_directories(${TEST_SERVER_NAME} PRIVATE + ./ + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> +) + + diff --git a/ipc_unit_test/ipc_unit_test_client.c b/ipc_unit_test/ipc_unit_test_client.c new file mode 100644 index 0000000..cc09d2a --- /dev/null +++ b/ipc_unit_test/ipc_unit_test_client.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <unistd.h> +#include <cluster_ipc.h> + +#include "ipc_unit_test_common.h" + +IPC_DATA_IC_SERVICE_S g_dataIcService; + +static void changeNotifyCb(void* pData, signed int size, int kind); +static void helpPrint(void); +static void listPrint(void); + +int main(void) +{ + IPC_RET_E ret; + char command[10]; + char dummy[10]; + bool isRunning = true; + signed int size; + char *pRetStr; + + memset(&g_dataIcService, 0, sizeof(g_dataIcService)); + + ret = ipcClientStart(IPC_USAGE_TYPE_IC_SERVICE); + if (ret != IPC_RET_OK) { + printf("ipcClientStart Error:%d\n", ret); + goto end; + } + + ret = ipcRegisterCallback(IPC_USAGE_TYPE_IC_SERVICE, changeNotifyCb); + if (ret != IPC_RET_OK) { + printf("ipcRegisterCallback Error:%d\n", ret); + goto end; + } + + while(isRunning) { + printf("command (h=help, q=quit):"); + pRetStr = fgets(command, 10, stdin); + if (pRetStr == NULL) { + continue; + } + memcpy(dummy, command, 10); + + while(strlen(dummy) == 9 && dummy[8] != '\n') { + pRetStr = fgets(dummy, 10, stdin); + if (pRetStr == NULL) { + break; + } + } + + command[strlen(command)-1] = '\0'; + + switch(command[0]) { + case 'h': + helpPrint(); + break; + case 'q': + isRunning = false; + break; + case 'r': + size = sizeof(g_dataIcService); + ret = ipcReadDataPool(IPC_USAGE_TYPE_IC_SERVICE, &g_dataIcService, &size); + printf("ipcReadDataPool return:%d\n", ret); + listPrint(); + break; + default: + break; + } + } + +end: + ipcClientStop(IPC_USAGE_TYPE_IC_SERVICE); + printf("bye...\n"); + sleep(1); + + return 0; +} + +static void changeNotifyCb(void* pData, signed int size, int kind) +{ + signed long longVal; + signed int intVal; + signed short shortVal; + signed char charVal; + + printf("Enter %s\n", __func__); + + switch(size) { + case 8: + longVal = *((signed long*)pData); + printf("kind = %d, size = %d, data=%ld\n", kind, size, longVal); + break; + case 4: + intVal = *((signed int*)pData); + printf("kind = %d, size = %d, data=%d\n", kind, size, intVal); + break; + case 2: + shortVal = *((signed short *)pData); + printf("kind = %d, size = %d, data=%d\n", kind, size, shortVal); + break; + default: + charVal = *((signed char *)pData); + printf("kind = %d, size = %d, data=%d\n", kind, size, charVal); + break; + } + printf("Leave %s\n", __func__); + return; +} + +static void helpPrint(void) +{ + printf("\n-----------\n"); + printf("'h' : help\n"); + printf("'q' : quit\n"); + printf("'r' : read from Client data pool\n"); + printf("-----------\n\n"); +} + +static void listPrint(void) +{ + int i; + signed long longVal; + signed int intVal; + signed short shortVal; + signed char charVal; + void *pValue; + + for (i = 0; i < IC_SERVICE_LIST_NUM; i++) { + pValue = (void *)&g_dataIcService + IcServiceList[i].offset; + switch(IcServiceList[i].size) { + case 8: + longVal = *((signed long*)pValue); + printf("%3d: %s(%d) = %ld\n", i, IcServiceList[i].name, IcServiceList[i].size, longVal); + break; + case 4: + intVal = *((signed int*)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, intVal); + break; + case 2: + shortVal = *((signed short *)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, shortVal); + break; + default: + charVal = *((signed char *)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, charVal); + break; + } + } +} + diff --git a/ipc_unit_test/ipc_unit_test_common.c b/ipc_unit_test/ipc_unit_test_common.c new file mode 100644 index 0000000..26d4830 --- /dev/null +++ b/ipc_unit_test/ipc_unit_test_common.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 <cluster_ipc.h> +#include "ipc_unit_test_common.h" + +IPC_UNIT_TEST_DATA_LIST IcServiceList[] = { + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, turnR), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, turnL), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, brake), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, seatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontRightSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontCenterSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontLeftSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid1RightSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid1CenterSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid1LeftSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid2RightSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid2CenterSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid2LeftSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, rearRightSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, rearCenterSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, rearLeftSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, highbeam), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, door), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontRightDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontLeftDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, rearRightDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, rearLeftDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, trunkDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, hoodDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, eps), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, srsAirbag), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, abs), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, lowBattery), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, oilPress), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, engine), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, fuel), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, immobi), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, tmFail), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, espAct), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, espOff), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, adaptingLighting), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, autoStop), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, autoStopFail), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, parkingLights), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontFog), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, exteriorLightFault), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, accFail), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, ldwOff), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, hillDescent), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, autoHiBeamGreen), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, autoHiBeamAmber), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, sportsMode), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, ldwOperate), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, generalWarn), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, drivingPowerMode), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, hotTemp), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, lowTemp), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, gearAtVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, gearMtVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, spAnalogVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, spAnaDigUnitVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, taAnalogVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, trcomTripAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, trcomTripBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, trcomOdoVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, trcomUnitVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, avgSpeedAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, avgSpeedBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, hourAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, hourBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, minuteAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, minuteBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, secondAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, secondBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, oTempVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, oTempUnitVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, cruRangeVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, avgFuelAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, avgFuelBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, insFuelAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, insFuelBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, fuelEconomyUnitVal) +}; + diff --git a/ipc_unit_test/ipc_unit_test_common.h b/ipc_unit_test/ipc_unit_test_common.h new file mode 100644 index 0000000..e0a3ed5 --- /dev/null +++ b/ipc_unit_test/ipc_unit_test_common.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef IPC_UNIT_TEST_COMMON_H +#define IPC_UNIT_TEST_COMMON_H +#include <stddef.h> + +#define DEFINE_STRUCT_DATA(struct_name, member) \ + {#member, offsetof(struct_name, member), sizeof(((struct_name *)0)->member)} +#define IC_SERVICE_LIST_NUM (77) + +typedef struct { + const char *name; + int offset; + int size; +} IPC_UNIT_TEST_DATA_LIST; + +extern IPC_UNIT_TEST_DATA_LIST IcServiceList[]; + +#endif // IPC_UNIT_TEST_COMMON_H diff --git a/ipc_unit_test/ipc_unit_test_server.c b/ipc_unit_test/ipc_unit_test_server.c new file mode 100644 index 0000000..807db2c --- /dev/null +++ b/ipc_unit_test/ipc_unit_test_server.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdbool.h> + +#include <cluster_ipc.h> + +#include "ipc_unit_test_common.h" + +static void helpPrint(void); +static void helpWritePrint(void); +static void listPrint(void); +static void writeData(void); +static void writeDataToStruct(int id, long value); + +IPC_DATA_IC_SERVICE_S g_dataIcService; + +int main(void) +{ + IPC_RET_E ret; + char command[10]; + char dummy[10]; + bool isRunning = true; + char *pRetStr; + + memset(&g_dataIcService, 0, sizeof(g_dataIcService)); + + ret = ipcServerStart(IPC_USAGE_TYPE_IC_SERVICE); + if (ret != IPC_RET_OK) { + printf("ipcServerStart Error:%d\n", ret); + goto end; + } + + while(isRunning) { + printf("command (h=help, q=quit):"); + pRetStr = fgets(command, 10, stdin); + if (pRetStr == NULL) { + continue; + } + memcpy(dummy, command, 10); + + while(strlen(dummy) == 9 && dummy[8] != '\n') { + pRetStr = fgets(dummy, 10, stdin); + if (pRetStr == NULL) { + break; + } + } + + command[strlen(command)-1] = '\0'; + + switch(command[0]) { + case 'h': + helpPrint(); + break; + case 'q': + isRunning = false; + break; + case 'l': + listPrint(); + break; + case 'w': + writeData(); + break; + case 's': + ret = ipcSendMessage(IPC_USAGE_TYPE_IC_SERVICE, (const void*)&g_dataIcService, sizeof(g_dataIcService)); + printf("ipcSendMessage return:%d\n", ret); + break; + default: + break; + } + } + +end: + ipcServerStop(IPC_USAGE_TYPE_IC_SERVICE); + printf("bye...\n"); + sleep(1); + + return 0; +} + +static void helpPrint(void) +{ + printf("\n-----------\n"); + printf("'h' : help\n"); + printf("'q' : quit\n"); + printf("'l' : list of Server data\n"); + printf("'w' : write to Server data\n"); + printf("'s' : send data to Client\n"); + printf("-----------\n\n"); +} + +static void listPrint(void) +{ + int i; + signed long longVal; + signed int intVal; + signed short shortVal; + signed char charVal; + void *pValue; + + for (i = 0; i < IC_SERVICE_LIST_NUM; i++) { + pValue = (void *)&g_dataIcService + IcServiceList[i].offset; + switch(IcServiceList[i].size) { + case 8: + longVal = *((signed long*)pValue); + printf("%3d: %s(%d) = %ld\n", i, IcServiceList[i].name, IcServiceList[i].size, longVal); + break; + case 4: + intVal = *((signed int*)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, intVal); + break; + case 2: + shortVal = *((signed short *)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, shortVal); + break; + default: + charVal = *((signed char *)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, charVal); + break; + } + } +} + +static void writeData(void) +{ + char command[40]; + char dummy[40]; + bool isRunning = true; + int i; + + int id; + long value; + + char *pRetStr; + + while(isRunning) { + printf("write command (h=help q=goto main menu):"); + pRetStr = fgets(command, 40, stdin); + if (pRetStr == NULL) { + continue; + } + memcpy(dummy, command, 40); + + while(strlen(dummy) == 39 && dummy[38] != '\n') { + pRetStr = fgets(dummy, 40, stdin); + if (pRetStr == NULL) { + break; + } + } + + command[strlen(command)-1] = '\0'; + switch(command[0]) { + case 'h': + helpWritePrint(); + break; + case 'q': + isRunning = false; + break; + case 'l': + listPrint(); + break; + default: + // write data + id = (int)strtol(command, NULL, 0); + value = 0; + for (i = 0; i < strlen(command); i++) { + if (command[i] == ' ') { + value = (int)strtol(&command[i], NULL, 0); + } + } + writeDataToStruct(id, value); + } + } +} + +static void helpWritePrint(void) +{ + printf("\n-----------\n"); + printf("'h' : help\n"); + printf("'q' : goto main menu\n"); + printf("'l' : list of Server data\n"); + printf("<ID> <value>: write data\n"); + printf(" ex)\n"); + printf(" write command 2 4\n"); + printf(" -> 2: brake = 4\n"); + printf("-----------\n\n"); +} + +static void writeDataToStruct(int id, long value) +{ + void *pValue; + + if (id < 0 || IC_SERVICE_LIST_NUM < id) { + return; + } + + pValue = (void *)&g_dataIcService + IcServiceList[id].offset; + memcpy(pValue, (void *)&value, IcServiceList[id].size); +} + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..dc9366a --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (c) 2021, Nippon Seiki Co., Ltd. +# SPDX-License-Identifier: Apache-2.0 + +# Define project Targets +add_library(${TARGET_NAME} SHARED + ipc_client.c + ipc_server.c + ipc_internal.c + ipc_usage_info_table.c +) + +# Include directories +target_include_directories(${TARGET_NAME} PRIVATE + ./ + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> + $<INSTALL_INTERFACE:/include> +) + +# find thread library +find_package(Threads REQUIRED) +target_link_libraries(${TARGET_NAME} INTERFACE Threads::Threads) +#target_link_libraries(${TARGET_NAME} INTERFACE ${SERVER_API_NAME}) + +set_target_properties(${TARGET_NAME} + PROPERTIES + VERSION ${IPC_LIBRARY_VERSION} + SOVERSION 1 +) + +# make install +# Generate config file +install( + TARGETS ${TARGET_NAME} + LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} +# PUBLIC_HEADER DESTINATION include +) +install(FILES ../include/cluster_ipc.h ../include/ipc_protocol.h DESTINATION include) diff --git a/src/ipc_client.c b/src/ipc_client.c new file mode 100644 index 0000000..1a4a031 --- /dev/null +++ b/src/ipc_client.c @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> +#include <pthread.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/epoll.h> +#include <errno.h> + +#include <cluster_ipc.h> +#include "ipc_internal.h" + +#define IPC_CLIENT_USAGE_MAX_NUM (4) +#define IPC_CLIENT_EPOLL_WAIT_NUM (IPC_CLIENT_USAGE_MAX_NUM + 1) +#define IPC_CLIENT_CONNECT_CHECK_TIME (500) // msec + +// == Internal global values == +static bool g_initedFlag = false; +static pthread_t g_clientThread; +static bool g_threadRunning = false; +static int g_threadCtlPipeFd[2] = {-1, -1}; +static int g_epollFd = -1; + +typedef struct { + IPC_USAGE_TYPE_E usage; + int serverFd; + void *pDataPool; + int poolSize; + IPC_CHANGE_NOTIFY_CB changeNotifyCb; +} IPC_CLIENT_INFO_S; +static IPC_CLIENT_INFO_S g_clientInfo[IPC_CLIENT_USAGE_MAX_NUM]; + +static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; + +// == Prototype declaration +static void *ipcClientThread(void *arg); +static int ipcClientInit(void); +static int ipcClientDeinit(void); +static void ipcClientInfoClear(int index); +static int ipcGetClientInfoIndex(IPC_USAGE_TYPE_E usageType); +static int ipcClientCreateSocket(IPC_USAGE_TYPE_E usageType); +static void ipcCloseConnectFromServer(int eventFd); +static void ipcReceiveDataFromServer(int eventFd, int *pIndex, void *pLocalDataPool); +static void ipcCheckChangeAndCallback(int index, void *pLocalDataPool); +static void ipcWriteToDataPool(int index, void *pLocalDataPool); +static int ipcAddClient(IPC_USAGE_TYPE_E usageType); +static int ipcRemoveClient(IPC_USAGE_TYPE_E usageType); +static int ipcCountClient(void); + +// == Thread function == +static void *ipcClientThread(void *arg) +{ + int fdNum; + struct epoll_event epEvents[IPC_CLIENT_USAGE_MAX_NUM]; + int i; + int index; + IPC_ALL_USAGE_DATA_POOL_U localDataPool; + char dummy; + int rc; + + while(g_threadRunning != false) { + fdNum = epoll_wait(g_epollFd, epEvents, IPC_CLIENT_USAGE_MAX_NUM, -1); + if (g_threadRunning == false) { + break; + } + + pthread_mutex_lock(&g_mutex); + for (i = 0; i < fdNum; i++) { + if (epEvents[i].data.fd == g_threadCtlPipeFd[0]) { + // dummy notify from API function. + rc = read(g_threadCtlPipeFd[0], &dummy, 1); + if (rc < 0) { + printf("[##ERROR##] %s:%s:%d (%s) is false. (%s=%d)\n", __FILE__, __func__, __LINE__, "rc >= 0", "rc", (int)rc); + continue; + } + } + else { + if (epEvents[i].events & EPOLLRDHUP) { + ipcCloseConnectFromServer(epEvents[i].data.fd); + } + else if (epEvents[i].events & EPOLLIN) { + ipcReceiveDataFromServer(epEvents[i].data.fd, &index, (void *)&localDataPool); + if (index >= 0) { + ipcCheckChangeAndCallback(index, &localDataPool); + ipcWriteToDataPool(index, &localDataPool); + } + } + } + } + pthread_mutex_unlock(&g_mutex); + } + + pthread_exit(NULL); + return NULL; +} + +// == Internal function == +static int ipcClientInit(void) +{ + int ret = -1; + int rc; + int i; + struct epoll_event epollEv; + + if (g_initedFlag == false) { + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + ipcClientInfoClear(i); + } + g_threadRunning = false; + rc = pipe(g_threadCtlPipeFd); + IPC_E_CHECK(rc == 0, rc, end); + + g_epollFd = epoll_create(IPC_CLIENT_EPOLL_WAIT_NUM); + IPC_E_CHECK(g_epollFd >= 0, g_epollFd, end); + + epollEv.events = EPOLLIN; + epollEv.data.fd = g_threadCtlPipeFd[0]; + epoll_ctl(g_epollFd, EPOLL_CTL_ADD, epollEv.data.fd, &epollEv); + + g_initedFlag = true; + } + + ret = 0; + +end: + if (ret == -1) { + for (i = 0; i < 2; i++) { + if (g_threadCtlPipeFd[i] >= 0) { + close(g_threadCtlPipeFd[i]); + g_threadCtlPipeFd[i] = -1; + } + } + } + return ret; +} + +static int ipcClientDeinit(void) +{ + int i; + + if (g_initedFlag == true) { + if (g_threadRunning == true) { + g_threadRunning = false; + pthread_cancel(g_clientThread); + pthread_join(g_clientThread, NULL); + } + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + ipcClientInfoClear(i); + } + for (i = 0; i < 2; i++) { + if (g_threadCtlPipeFd[i] >= 0) { + close(g_threadCtlPipeFd[i]); + g_threadCtlPipeFd[i] = -1; + } + } + close(g_epollFd); + g_epollFd = -1; + + g_initedFlag = false; + } + + return 0; +} + +static void ipcClientInfoClear(int index) +{ + IPC_E_CHECK(0 <= index && index < IPC_CLIENT_USAGE_MAX_NUM, index, end); + + g_clientInfo[index].usage = IPC_USAGE_TYPE_MAX; + g_clientInfo[index].serverFd = -1; + g_clientInfo[index].pDataPool = NULL; + g_clientInfo[index].poolSize = 0; + g_clientInfo[index].changeNotifyCb = NULL; + +end: + return; +} + +static int ipcGetClientInfoIndex(IPC_USAGE_TYPE_E usageType) +{ + int index = -1; + int i; + + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + if (g_clientInfo[i].usage == usageType) { + index = i; + break; + } + } + + return index; +} + +static int ipcClientCreateSocket(IPC_USAGE_TYPE_E usageType) +{ + int rc; + int fd = -1; + struct sockaddr_un unixAddr; + int len; + char domainName[IPC_DOMAIN_PATH_MAX] = ""; + int domainLen = IPC_DOMAIN_PATH_MAX; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, err); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + IPC_E_CHECK(fd >= 0, fd, err); + + rc = ipcCreateDomainName(usageType, domainName, &domainLen); + IPC_E_CHECK(rc == 0, rc, err); + + rc = ipcCreateUnixDomainAddr(domainName, &unixAddr, &len); + IPC_E_CHECK(rc == 0, rc, err); + + rc = connect(fd, (struct sockaddr *)&unixAddr, len); + IPC_E_CHECK(rc == 0, rc, err); + + return fd; + +err: + if (fd >= 0) { + shutdown(fd, SHUT_RDWR); + close(fd); + } + return -1; +} + +static void ipcCloseConnectFromServer(int eventFd) +{ + int index; + IPC_CLIENT_INFO_S *pInfo; + struct epoll_event epollEv; + + for (index = 0; index < IPC_CLIENT_USAGE_MAX_NUM; index++) { + pInfo = &(g_clientInfo[index]); + if (pInfo->usage == IPC_USAGE_TYPE_MAX) { + continue; + } + + if (pInfo->serverFd == eventFd) { + shutdown(pInfo->serverFd, SHUT_RDWR); + close(pInfo->serverFd); + if (pInfo->pDataPool != NULL) { + free(pInfo->pDataPool); + pInfo->pDataPool = NULL; + } + memset(&epollEv, 0, sizeof(epollEv)); + epoll_ctl(g_epollFd, EPOLL_CTL_DEL, pInfo->serverFd, &epollEv); + ipcClientInfoClear(index); + } + } +} + +static void ipcReceiveDataFromServer(int eventFd, int *pIndex, void *pLocalDataPool) +{ + int rc; + int i; + IPC_CLIENT_INFO_S *pInfo = NULL; + + *pIndex = -1; + + // check fd + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + if (g_clientInfo[i].serverFd == eventFd) { + pInfo = &(g_clientInfo[i]); + break; + } + } + + IPC_E_CHECK(pInfo != NULL, eventFd, end); + IPC_E_CHECK(pInfo->pDataPool != NULL, i, end); + IPC_E_CHECK(pInfo->poolSize > 0, i, end); + + // receive from server and write to data pool. + rc = recv(eventFd, pLocalDataPool, pInfo->poolSize, 0); + if ((rc == 0) + || (rc >= 0 && errno == ECONNREFUSED)) { + ipcCloseConnectFromServer(eventFd); + } + IPC_E_CHECK(rc >= 0, errno, end); + + *pIndex = i; + +end: + return; +} + +static void ipcCheckChangeAndCallback(int index, void *pLocalDataPool) +{ + IPC_CLIENT_INFO_S *pInfo = NULL; + IPC_CHECK_CHANGE_INFO_TABLE_S *pChangeInfoTbl = NULL; + IPC_CHECK_CHANGE_INFO_S *pChangeInfo = NULL; + int i; + void *pMemCmpData, *pMemCmpLocal; + + pInfo = &(g_clientInfo[index]); + if (pInfo->changeNotifyCb == NULL) { + goto end; + } + + pChangeInfoTbl = &(g_ipcCheckChangeInfoTbl[pInfo->usage]); + + // Check for changes in the data pool. + for (i = 0; i < pChangeInfoTbl->num; i++) { + pChangeInfo = &(pChangeInfoTbl->pInfo[i]); + pMemCmpData = pInfo->pDataPool + pChangeInfo->offset; + pMemCmpLocal = pLocalDataPool + pChangeInfo->offset; + + if (0 != memcmp(pMemCmpData, pMemCmpLocal, pChangeInfo->size)) { + pInfo->changeNotifyCb(pMemCmpLocal, pChangeInfo->size, pChangeInfo->kind); + } + } + +end: + return; +} + +static void ipcWriteToDataPool(int index, void *pLocalDataPool) +{ + IPC_CLIENT_INFO_S *pInfo = NULL; + + pInfo = &(g_clientInfo[index]); + + memcpy(pInfo->pDataPool, pLocalDataPool, pInfo->poolSize); + + return; +} + +static int ipcAddClient(IPC_USAGE_TYPE_E usageType) +{ + int ret = -1; + int index = -1; + int i; + IPC_CLIENT_INFO_S *pInfo; + int fd; + void *pDataPool = NULL; + int dataPoolSize; + struct epoll_event epollEv; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + // check if the usageType is already used + index = ipcGetClientInfoIndex(usageType); + IPC_E_CHECK(index == -1, usageType, end); + + // find empty index + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + pInfo = &(g_clientInfo[i]); + if (pInfo->usage == IPC_USAGE_TYPE_MAX) { + index = i; + break; + } + } + + IPC_E_CHECK(index >= 0, i, end); + pInfo = &(g_clientInfo[index]); + + dataPoolSize = g_ipcDomainInfoList[usageType].size; + pDataPool = malloc(dataPoolSize); + IPC_E_CHECK(pDataPool != NULL, 0, end); + memset(pDataPool, 0, dataPoolSize); + + fd = ipcClientCreateSocket(usageType); + + IPC_E_CHECK(fd >= 0, usageType, end); + + pInfo->usage = usageType; + pInfo->serverFd = fd; + pInfo->pDataPool = pDataPool; + pInfo->poolSize = dataPoolSize; + + memset(&epollEv, 0, sizeof(epollEv)); + epollEv.events = EPOLLIN | EPOLLRDHUP; + epollEv.data.fd = fd; + epoll_ctl(g_epollFd, EPOLL_CTL_ADD, epollEv.data.fd, &epollEv); + + ret = 0; +end: + if (ret != 0 && pDataPool != NULL) { + free(pDataPool); + } + return ret; +} + +static int ipcRemoveClient(IPC_USAGE_TYPE_E usageType) +{ + int ret = -1; + int index; + IPC_CLIENT_INFO_S *pInfo; + struct epoll_event epollEv; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + index = ipcGetClientInfoIndex(usageType); + IPC_E_CHECK(index >= 0, usageType, end); + + pInfo = &(g_clientInfo[index]); + + shutdown(pInfo->serverFd, SHUT_RDWR); + close(pInfo->serverFd); + if (pInfo->pDataPool != NULL) { + free(pInfo->pDataPool); + pInfo->pDataPool = NULL; + } + + memset(&epollEv, 0, sizeof(epollEv)); + epoll_ctl(g_epollFd, EPOLL_CTL_DEL, pInfo->serverFd, &epollEv); + + ipcClientInfoClear(index); + + ret = 0; + +end: + return ret; +} + +static int ipcCountClient(void) +{ + int count = 0; + int i; + + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + if (g_clientInfo[i].usage != IPC_USAGE_TYPE_MAX) { + count++; + } + } + + return count; +} + +// == API function for client == +IPC_RET_E ipcClientStart(IPC_USAGE_TYPE_E usageType) +{ + IPC_RET_E ret; + int rc; + char dummy = 's'; + int index; + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + ret = IPC_ERR_OTHER; + rc = ipcClientInit(); + IPC_E_CHECK(rc == 0, rc, end); + + pthread_mutex_lock(&g_mutex); + rc = ipcAddClient(usageType); + pthread_mutex_unlock(&g_mutex); + + ret = IPC_ERR_NO_RESOURCE; + IPC_E_CHECK(rc == 0, rc, end); + + if (g_threadRunning == false) { + rc = pthread_create(&g_clientThread, NULL, ipcClientThread, NULL); + IPC_E_CHECK(rc == 0, rc, end); + + g_threadRunning = true; + } + + rc = write(g_threadCtlPipeFd[1], &dummy, 1); // for wakeup epoll_wait + IPC_E_CHECK(rc >= 0, rc, end); + + // Check to if the connection is rejected. + usleep(IPC_CLIENT_EPOLL_WAIT_NUM * 1000); + pthread_mutex_lock(&g_mutex); + index = ipcGetClientInfoIndex(usageType); + pthread_mutex_unlock(&g_mutex); + ret = IPC_ERR_NO_RESOURCE; + IPC_E_CHECK(index >= 0, usageType, end); + + ret = IPC_RET_OK; + +end: + if (ret != IPC_RET_OK && g_threadRunning == true) { + pthread_mutex_lock(&g_mutex); + if (ipcCountClient() == 0) { + pthread_mutex_unlock(&g_mutex); + ipcClientDeinit(); + } + else { + pthread_mutex_unlock(&g_mutex); + } + } + return ret; +} + +IPC_RET_E ipcReadDataPool(IPC_USAGE_TYPE_E usageType, void* pData, signed int* pSize) +{ + IPC_RET_E ret; + int index = -1; + IPC_CLIENT_INFO_S *pInfo; + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + IPC_E_CHECK(pData != NULL, 0, end); + IPC_E_CHECK(pSize != NULL, 0, end); + + pthread_mutex_lock(&g_mutex); + index = ipcGetClientInfoIndex(usageType); + + ret = IPC_ERR_SEQUENCE; + IPC_E_CHECK(index >= 0, usageType, end_with_unlock); + pInfo = &(g_clientInfo[index]); + IPC_E_CHECK(pInfo->pDataPool != NULL, usageType, end_with_unlock); + IPC_E_CHECK(*pSize >= pInfo->poolSize, *pSize, end_with_unlock); + + memcpy(pData, pInfo->pDataPool, pInfo->poolSize); + + ret = IPC_RET_OK; + +end_with_unlock: + pthread_mutex_unlock(&g_mutex); + +end: + return ret; +} + +IPC_RET_E ipcRegisterCallback(IPC_USAGE_TYPE_E usageType, IPC_CHANGE_NOTIFY_CB changeNotifyCb) +{ + IPC_RET_E ret; + int index = -1; + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + IPC_E_CHECK(changeNotifyCb != NULL, 0, end); + + pthread_mutex_lock(&g_mutex); + index = ipcGetClientInfoIndex(usageType); + + ret = IPC_ERR_SEQUENCE; + IPC_E_CHECK(index >= 0, usageType, end_with_unlock); + + g_clientInfo[index].changeNotifyCb = changeNotifyCb; + + ret = IPC_RET_OK; + +end_with_unlock: + pthread_mutex_unlock(&g_mutex); + +end: + return ret; +} + +IPC_RET_E ipcClientStop(IPC_USAGE_TYPE_E usageType) +{ + IPC_RET_E ret; + int rc; + char dummy = 'e'; + + ret = IPC_ERR_SEQUENCE; + IPC_E_CHECK(g_initedFlag != false, g_initedFlag, end); + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + pthread_mutex_lock(&g_mutex); + rc = ipcRemoveClient(usageType); + ret = IPC_ERR_PARAM; + IPC_E_CHECK(rc == 0, rc, end_with_unlock); + + rc = write(g_threadCtlPipeFd[1], &dummy, 1); // for wakeup epoll_wait + IPC_E_CHECK(rc >= 0, rc, end_with_unlock); + + if (ipcCountClient() == 0) { + pthread_mutex_unlock(&g_mutex); + ipcClientDeinit(); + } + else { + pthread_mutex_unlock(&g_mutex); + } + + ret = IPC_RET_OK; + +end: + return ret; + +end_with_unlock: + pthread_mutex_unlock(&g_mutex); + return ret; +} + diff --git a/src/ipc_internal.c b/src/ipc_internal.c new file mode 100644 index 0000000..7a37866 --- /dev/null +++ b/src/ipc_internal.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/socket.h> + +#include <cluster_ipc.h> +#include "ipc_internal.h" + +int ipcCreateDomainName(IPC_USAGE_TYPE_E usageType, char *pOutName, int *pSize) +{ + int ret = -1; + int len; + char *ipcDomainPath; // from getenv + char *domainName; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + IPC_E_CHECK(pSize != NULL, 0, end); + IPC_E_CHECK(pOutName != NULL, 0, end); + + domainName = g_ipcDomainInfoList[usageType].domainName; + len = strlen(domainName); + ipcDomainPath = getenv(IPC_ENV_DOMAIN_SOCKET_PATH); + if (ipcDomainPath != NULL) { + len += strlen(ipcDomainPath); + } + + IPC_E_CHECK(*pSize > len, len, end); + + strcpy(pOutName, ""); + if (ipcDomainPath != NULL) { + sprintf(pOutName, "%s/", ipcDomainPath); + } + strcat(pOutName, domainName); + + *pSize = strlen(pOutName) + 1; + + ret = 0; +end: + return ret; +} + +int ipcCreateUnixDomainAddr(const char *domainName, struct sockaddr_un *pOutUnixAddr, int *pOutLen) +{ + int ret = -1; + + IPC_E_CHECK(pOutLen != NULL, 0, end); + IPC_E_CHECK(pOutUnixAddr != NULL, 0, end); + + pOutUnixAddr->sun_family = AF_UNIX; + strcpy(pOutUnixAddr->sun_path, domainName); + *pOutLen = sizeof(pOutUnixAddr->sun_family)+strlen(pOutUnixAddr->sun_path); + + ret = 0; +end: + return ret; +} + diff --git a/src/ipc_internal.h b/src/ipc_internal.h new file mode 100644 index 0000000..486692b --- /dev/null +++ b/src/ipc_internal.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef IPC_INTERNAL_H +#define IPC_INTERNAL_H + +#include <ipc_protocol.h> +#include <stddef.h> +#include <sys/un.h> + +#define IPC_E_CHECK(condition, value, label) \ + do { \ + if (!(condition)) { \ + printf("[##ERROR##] %s:%s:%d (%s) is false. (%s=%d)\n", __FILE__, __func__, __LINE__, #condition, #value, (int)value); \ + goto label; \ + } \ + } while(0) + +#define CHECK_VALID_USAGE(usageType) \ + (0 <= usageType && usageType < IPC_USAGE_TYPE_MAX) + +#define IPC_DOMAIN_PATH_MAX (108) // defined in sun_path[] of sys/un.h + +typedef struct { + signed long size; + char *domainName; +} IPC_DOMAIN_INFO_S; + +typedef struct { + int kind; + int offset; + int size; +} IPC_CHECK_CHANGE_INFO_S; + +typedef struct { + IPC_CHECK_CHANGE_INFO_S* pInfo; + int num; +} IPC_CHECK_CHANGE_INFO_TABLE_S; + +// the union to know the maximum size of the data pool. +typedef union { + IPC_DATA_IC_SERVICE_S icService; +} IPC_ALL_USAGE_DATA_POOL_U; + +extern IPC_DOMAIN_INFO_S g_ipcDomainInfoList[]; +extern IPC_CHECK_CHANGE_INFO_TABLE_S g_ipcCheckChangeInfoTbl[]; + +int ipcCreateDomainName(IPC_USAGE_TYPE_E usageType, char *pOutName, int *pSize); +int ipcCreateUnixDomainAddr(const char *domainName, struct sockaddr_un *pOutUnixAddr, int *pOutLen); + +#endif // IPC_INTERNAL_H diff --git a/src/ipc_server.c b/src/ipc_server.c new file mode 100644 index 0000000..301409c --- /dev/null +++ b/src/ipc_server.c @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/epoll.h> +#include <pthread.h> + +#include <cluster_ipc.h> + +#include "ipc_internal.h" + +#define IPC_SERVER_USAGE_MAX_NUM (1) +#define IPC_LISTEN_CLIENT_NUM (4) +#define IPC_SERVER_EPOLL_WAIT_NUM (IPC_SERVER_USAGE_MAX_NUM * IPC_LISTEN_CLIENT_NUM + 1) + +// == Internal global values == +static bool g_initedFlag = false; +static pthread_t g_serverThread; +static bool g_threadRunning = false; +static int g_threadCtlPipeFd[2] = {-1, -1}; +static int g_epollFd = -1; + +typedef struct { + IPC_USAGE_TYPE_E usage; + int fd; + int clientFd[IPC_LISTEN_CLIENT_NUM]; +} IPC_SERVER_INFO_S; +static IPC_SERVER_INFO_S g_serverInfo[IPC_SERVER_USAGE_MAX_NUM]; + +static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; + +// == Prototype declaration +static void *ipcServerThread(void *arg); +static int ipcServerInit(void); +static int ipcServerDeinit(void); +static void ipcServerInfoClear(int index); +static int ipcGetServerInfoIndex(IPC_USAGE_TYPE_E usageType); +static int ipcServerCreateSocket(IPC_USAGE_TYPE_E usageType); +static void ipcAcceptClient(int eventFd); +static void ipcCloseClient(int eventFd); +static int ipcAddServer(IPC_USAGE_TYPE_E usageType); +static int ipcAddConnectClient(int index, int clientFd); +static int ipcRemoveServer(IPC_USAGE_TYPE_E usageType); +static int ipcCountServer(void); + +// == Thread function == +static void *ipcServerThread(void *arg) +{ + int fdNum; + struct epoll_event epEvents[IPC_SERVER_EPOLL_WAIT_NUM]; + int i; + char dummy; + int rc; + + while(g_threadRunning != false) { + fdNum = epoll_wait(g_epollFd, epEvents, IPC_SERVER_USAGE_MAX_NUM, -1); + if (g_threadRunning == false) { + break; + } + + pthread_mutex_lock(&g_mutex); + for (i = 0; i < fdNum; i++) { + if (epEvents[i].data.fd == g_threadCtlPipeFd[0]) { + // dummy notify from API function. + rc = read(g_threadCtlPipeFd[0], &dummy, 1); + if (rc < 0) { + printf("[##ERROR##] %s:%s:%d (%s) is false. (%s=%d)\n", __FILE__, __func__, __LINE__, "rc < 0", "rc", (int)rc); + continue; + } + } + else { + if (epEvents[i].events & EPOLLRDHUP) { + ipcCloseClient(epEvents[i].data.fd); + } + else if (epEvents[i].events & EPOLLIN) { + ipcAcceptClient(epEvents[i].data.fd); + } + } + } + pthread_mutex_unlock(&g_mutex); + } + + pthread_exit(NULL); + return NULL; +} + +// == Internal function == +static int ipcServerInit(void) +{ + int ret = -1; + int rc; + int i; + struct epoll_event epollEv; + + if (g_initedFlag == false) { + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + ipcServerInfoClear(i); + } + g_threadRunning = false; + rc = pipe(g_threadCtlPipeFd); + IPC_E_CHECK(rc == 0, rc, end); + + g_epollFd = epoll_create(IPC_SERVER_EPOLL_WAIT_NUM); + IPC_E_CHECK(g_epollFd >= 0, g_epollFd, end); + + epollEv.events = EPOLLIN; + epollEv.data.fd = g_threadCtlPipeFd[0]; + epoll_ctl(g_epollFd, EPOLL_CTL_ADD, epollEv.data.fd, &epollEv); + + g_initedFlag = true; + } + + ret = 0; + +end: + if (ret == -1) { + for (i = 0; i < 2; i++) { + if (g_threadCtlPipeFd[i] >= 0) { + close(g_threadCtlPipeFd[i]); + g_threadCtlPipeFd[i] = -1; + } + } + } + return ret; +} + +static int ipcServerDeinit(void) +{ + int i; + + if (g_initedFlag == true) { + if (g_threadRunning == true) { + g_threadRunning = false; + pthread_cancel(g_serverThread); + pthread_join(g_serverThread, NULL); + } + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + ipcServerInfoClear(i); + } + for (i = 0; i < 2; i++) { + if (g_threadCtlPipeFd[i] >= 0) { + close(g_threadCtlPipeFd[i]); + g_threadCtlPipeFd[i] = -1; + } + } + close(g_epollFd); + g_epollFd = -1; + + g_initedFlag = false; + } + + return 0; +} + +static void ipcServerInfoClear(int index) +{ + int i; + + IPC_E_CHECK(0 <= index && index < IPC_SERVER_USAGE_MAX_NUM, index, end); + + g_serverInfo[index].usage = IPC_USAGE_TYPE_MAX; + g_serverInfo[index].fd = -1; + for (i = 0; i < IPC_LISTEN_CLIENT_NUM; i++) { + g_serverInfo[index].clientFd[i] = -1; + } + +end: + return; +} + +static int ipcGetServerInfoIndex(IPC_USAGE_TYPE_E usageType) +{ + int index = -1; + int i; + + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + if (g_serverInfo[i].usage == usageType) { + index = i; + break; + } + } + + return index; +} + +static int ipcServerCreateSocket(IPC_USAGE_TYPE_E usageType) +{ + int rc; + int fd = -1; + struct sockaddr_un unixAddr; + int len; + char domainName[IPC_DOMAIN_PATH_MAX] = ""; + int domainLen = IPC_DOMAIN_PATH_MAX; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, err); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + IPC_E_CHECK(fd >= 0, fd, err); + + rc = ipcCreateDomainName(usageType, domainName, &domainLen); + IPC_E_CHECK(rc == 0, rc, err); + + rc = ipcCreateUnixDomainAddr(domainName, &unixAddr, &len); + IPC_E_CHECK(rc == 0, rc, err); + + rc = bind(fd, (struct sockaddr *)&unixAddr, len); + IPC_E_CHECK(rc == 0, rc, err); + + rc = listen(fd, IPC_LISTEN_CLIENT_NUM); + IPC_E_CHECK(rc == 0, rc, err); + + return fd; + +err: + if (fd >= 0) { + shutdown(fd, SHUT_RDWR); + close(fd); + unlink(domainName); + } + return -1; +} + +static void ipcAcceptClient(int eventFd) +{ + int rc; + int clientFd; + char domainName[IPC_DOMAIN_PATH_MAX] = ""; + int domainLen = IPC_DOMAIN_PATH_MAX; + struct sockaddr_un unixAddr; + int len; + IPC_SERVER_INFO_S *pInfo; + int index = -1; + int i; + struct epoll_event epollEv; + + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + if (eventFd == g_serverInfo[i].fd) { + index = i; + break; + } + } + IPC_E_CHECK(0 <= index && index < IPC_SERVER_USAGE_MAX_NUM, eventFd, end); + pInfo = &(g_serverInfo[index]); + + rc = ipcCreateDomainName(pInfo->usage, domainName, &domainLen); + IPC_E_CHECK(rc == 0, rc, end); + + rc = ipcCreateUnixDomainAddr(domainName, &unixAddr, &len); + IPC_E_CHECK(rc == 0, rc, end); + + // check connect client + clientFd = accept(pInfo->fd, (struct sockaddr*)&unixAddr, (socklen_t *)&len); + if (clientFd >= 0) { + rc = ipcAddConnectClient(index, clientFd); + if (rc == 0) { + memset(&epollEv, 0, sizeof(epollEv)); + epollEv.events = EPOLLRDHUP; + epollEv.data.fd = clientFd; + epoll_ctl(g_epollFd, EPOLL_CTL_ADD, clientFd, &epollEv); + } + else { // The number of connections is already limited. + shutdown(clientFd, SHUT_RDWR); + close(clientFd); + } + } + +end: + return; +} + +static void ipcCloseClient(int eventFd) +{ + int i; + int index; + IPC_SERVER_INFO_S *pInfo; + struct epoll_event epollEv; + + for (index = 0; index < IPC_SERVER_USAGE_MAX_NUM; index++) { + pInfo = &(g_serverInfo[index]); + if (pInfo->usage == IPC_USAGE_TYPE_MAX) { + continue; + } + + for (i = 0; i < IPC_LISTEN_CLIENT_NUM; i++) { + if (pInfo->clientFd[i] == eventFd) { + pInfo->clientFd[i] = -1; + + shutdown(eventFd, SHUT_RDWR); + close(eventFd); + + memset(&epollEv, 0, sizeof(epollEv)); + epoll_ctl(g_epollFd, EPOLL_CTL_DEL, eventFd, &epollEv); + + break; + } + } + } +} + +static int ipcAddServer(IPC_USAGE_TYPE_E usageType) +{ + int ret = -1; + int index = -1; + int i; + int fd; + IPC_SERVER_INFO_S *pInfo; + struct epoll_event epollEv; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + // check if the usageType is used + index = ipcGetServerInfoIndex(usageType); + IPC_E_CHECK(index == -1, usageType, end); + + // find empty index + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + pInfo = &(g_serverInfo[i]); + if (pInfo->usage == IPC_USAGE_TYPE_MAX) { + pInfo->usage = usageType; + index = i; + break; + } + } + IPC_E_CHECK(index >= 0, i, end); + pInfo = &(g_serverInfo[index]); + + fd = ipcServerCreateSocket(usageType); + + IPC_E_CHECK(fd >= 0, usageType, end); + + pInfo->fd = fd; + memset(&epollEv, 0, sizeof(epollEv)); + epollEv.events = EPOLLIN; + epollEv.data.fd = fd; + epoll_ctl(g_epollFd, EPOLL_CTL_ADD, epollEv.data.fd, &epollEv); + + ret = 0; + +end: + if (ret == -1 && index >= 0) { + ipcServerInfoClear(index); + } + return ret; +} + +static int ipcAddConnectClient(int index, int clientFd) +{ + int ret = -1; + int i; + IPC_SERVER_INFO_S *pInfo; + + if (clientFd < 0) { + // do nothing + goto end; + } + + pInfo = &(g_serverInfo[index]); + + // find empty index + for (i = 0; i < IPC_LISTEN_CLIENT_NUM; i++) { + if (pInfo->clientFd[i] == -1) { + pInfo->clientFd[i] = clientFd; + ret = 0; + break; + } + } + +end: + return ret; +} + +static int ipcRemoveServer(IPC_USAGE_TYPE_E usageType) +{ + int ret = -1; + int rc; + int index; + char domainName[IPC_DOMAIN_PATH_MAX] = ""; + int domainLen = IPC_DOMAIN_PATH_MAX; + IPC_SERVER_INFO_S *pInfo; + struct epoll_event epollEv; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + index = ipcGetServerInfoIndex(usageType); + IPC_E_CHECK(index >= 0, usageType, end); + + pInfo = &(g_serverInfo[index]); + + rc = ipcCreateDomainName(pInfo->usage, domainName, &domainLen); + IPC_E_CHECK(rc == 0, rc, end); + + shutdown(pInfo->fd, SHUT_RDWR); + close(pInfo->fd); + unlink(domainName); + + memset(&epollEv, 0, sizeof(epollEv)); + epoll_ctl(g_epollFd, EPOLL_CTL_DEL, pInfo->fd, &epollEv); + + ipcServerInfoClear(index); + + ret = 0; + +end: + return ret; +} + +static int ipcCountServer(void) +{ + int count = 0; + int i; + + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + if (g_serverInfo[i].usage != IPC_USAGE_TYPE_MAX) { + count++; + } + } + + return count; +} + +// == API function for server == +IPC_RET_E ipcServerStart(IPC_USAGE_TYPE_E usageType) +{ + IPC_RET_E ret; + int rc; + char dummy = 's'; + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + ret = IPC_ERR_OTHER; + rc = ipcServerInit(); + IPC_E_CHECK(rc == 0, rc, end); + + pthread_mutex_lock(&g_mutex); + rc = ipcAddServer(usageType); + pthread_mutex_unlock(&g_mutex); + + ret = IPC_ERR_NO_RESOURCE; + IPC_E_CHECK(rc == 0, rc, end); + + if (g_threadRunning == false) { + rc = pthread_create(&g_serverThread, NULL, ipcServerThread, NULL); + IPC_E_CHECK(rc == 0, rc, end); + + g_threadRunning = true; + } + + rc = write(g_threadCtlPipeFd[1], &dummy, 1); // for wakeup epoll_wait + IPC_E_CHECK(rc >= 0, rc, end); + ret = IPC_RET_OK; + +end: + return ret; +} + +IPC_RET_E ipcSendMessage(IPC_USAGE_TYPE_E usageType, const void* pData, signed int size) +{ + IPC_RET_E ret; + int rc; + int index; + IPC_SERVER_INFO_S *pInfo = NULL; + int i; + int clientFd; + + ret = IPC_ERR_SEQUENCE; + IPC_E_CHECK(g_initedFlag != false, g_initedFlag, end); + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + IPC_E_CHECK(pData != NULL, 0, end); + IPC_E_CHECK(g_ipcDomainInfoList[usageType].size >= size, size, end); + + pthread_mutex_lock(&g_mutex); + index = ipcGetServerInfoIndex(usageType); + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(index >= 0, usageType, end_with_unlock); + pInfo = &(g_serverInfo[index]); + + IPC_E_CHECK(pInfo->fd >= 0, usageType, end_with_unlock); + + // Send to All Client + for (i = 0; i < IPC_LISTEN_CLIENT_NUM; i++) { + clientFd = pInfo->clientFd[i]; + if (clientFd == -1) { + continue; + } + rc = write(clientFd, pData, size); + IPC_E_CHECK(rc >= 0, rc, end_with_unlock); + } + + ret = IPC_RET_OK; +end_with_unlock: + pthread_mutex_unlock(&g_mutex); + +end: + return ret; +} + +IPC_RET_E ipcServerStop(IPC_USAGE_TYPE_E usageType) +{ + IPC_RET_E ret; + int rc; + char dummy = 'e'; + + ret = IPC_ERR_SEQUENCE; + IPC_E_CHECK(g_initedFlag != false, g_initedFlag, end); + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + pthread_mutex_lock(&g_mutex); + rc = ipcRemoveServer(usageType); + ret = IPC_ERR_PARAM; + IPC_E_CHECK(rc == 0, rc, end_with_unlock); + + rc = write(g_threadCtlPipeFd[1], &dummy, 1); // for wakeup epoll_wait + IPC_E_CHECK(rc >= 0, rc, end_with_unlock); + + if (ipcCountServer() == 0) { + pthread_mutex_unlock(&g_mutex); + ipcServerDeinit(); + } + else { + pthread_mutex_unlock(&g_mutex); + } + + ret = IPC_RET_OK; + +end: + return ret; + +end_with_unlock: + pthread_mutex_unlock(&g_mutex); + return ret; +} + diff --git a/src/ipc_usage_info_table.c b/src/ipc_usage_info_table.c new file mode 100644 index 0000000..a1f0939 --- /dev/null +++ b/src/ipc_usage_info_table.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021, Nippon Seiki Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 <cluster_ipc.h> +#include "ipc_internal.h" + +#define DEFINE_OFFSET_SIZE(struct_name, member, kind) \ + {kind, offsetof(struct_name, member), sizeof(((struct_name *)0)->member)} + +#define DEFINE_CHANGE_INFO_TABLE(changeInfoName) \ + {changeInfoName, sizeof(changeInfoName) / sizeof(changeInfoName[0])} + +// == check change table == +// for IPC_USAGE_TYPE_IC_SERVICE +static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeIcService[] = { + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, turnR, IPC_KIND_ICS_TURN_R), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, turnL, IPC_KIND_ICS_TURN_L), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, brake, IPC_KIND_ICS_BRAKE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, seatbelt, IPC_KIND_ICS_SEATBELT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, highbeam, IPC_KIND_ICS_HIGHBEAM), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, door, IPC_KIND_ICS_DOOR), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, eps, IPC_KIND_ICS_EPS), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, srsAirbag, IPC_KIND_ICS_SRS_AIRBAG), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, abs, IPC_KIND_ICS_ABS), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, lowBattery, IPC_KIND_ICS_LOW_BATTERY), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, oilPress, IPC_KIND_ICS_OIL_PRESS), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, engine, IPC_KIND_ICS_ENGINE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, fuel, IPC_KIND_ICS_FUEL), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, immobi, IPC_KIND_ICS_IMMOBI), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, tmFail, IPC_KIND_ICS_TM_FAIL), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, espAct, IPC_KIND_ICS_ESP_ACT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, espOff, IPC_KIND_ICS_ESP_OFF), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, adaptingLighting, IPC_KIND_ICS_ADAPTING_LIGHTING), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, autoStop, IPC_KIND_ICS_AUTO_STOP), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, autoStopFail, IPC_KIND_ICS_AUTO_STOP_FAIL), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, parkingLights, IPC_KIND_ICS_PARKING_LIGHTS), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, frontFog, IPC_KIND_ICS_FRONT_FOG), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, exteriorLightFault, IPC_KIND_ICS_EXTERIOR_LIGHT_FAULT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, accFail, IPC_KIND_ICS_ACC_FAIL), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, ldwOff, IPC_KIND_ICS_LDW_OFF), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, hillDescent, IPC_KIND_ICS_HILL_DESCENT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, autoHiBeamGreen, IPC_KIND_ICS_AUTO_HI_BEAM_GREEN), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, autoHiBeamAmber, IPC_KIND_ICS_AUTO_HI_BEAM_AMBER), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, ldwOperate, IPC_KIND_ICS_LDW_OPERATE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, generalWarn, IPC_KIND_ICS_GENERAL_WARN), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, sportsMode, IPC_KIND_ICS_SPORTS_MODE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, drivingPowerMode, IPC_KIND_ICS_DRIVING_POWER_MODE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, hotTemp, IPC_KIND_ICS_HOT_TEMP), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, lowTemp, IPC_KIND_ICS_LOW_TEMP) +}; + +// for IPC_USAGE_TYPE_FOR_TEST +static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeForTest[] = { + DEFINE_OFFSET_SIZE(IPC_DATA_FOR_TEST_S, test, IPC_KIND_TEST_TEST) +}; + +// == usage info table == +// index of [] is IPC_USAGE_TYPE_E +IPC_DOMAIN_INFO_S g_ipcDomainInfoList[] = +{ + {sizeof(IPC_DATA_IC_SERVICE_S), "ipcIcService"}, + {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"} +}; + +IPC_CHECK_CHANGE_INFO_TABLE_S g_ipcCheckChangeInfoTbl[] = { + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeIcService), + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest) +}; + |