initial commit.
[apps/agl-service-can-low-level.git] / src / main.cpp
1 #include <exception>\r
2 #include <fstream>\r
3 #include <iostream>\r
4 #include <string>\r
5 #include <numeric>\r
6 #include <iterator>\r
7 #include <boost/program_options.hpp>\r
8 #include <boost/filesystem.hpp>\r
9 #include <json.hpp>\r
10 #include "openxc/message_set.hpp"\r
11 \r
12 #define EXIT_SUCCESS                            0\r
13 #define EXIT_UNKNOWN_ERROR                      1\r
14 #define EXIT_COMMAND_LINE_ERROR         2\r
15 #define EXIT_PROGRAM_ERROR                      3\r
16 \r
17 /// @brief Generate the configuration code.\r
18 /// @param[in] header Content to be inserted as a header.\r
19 /// @param[in] footer Content to be inserted as a footer.\r
20 /// @param[in] message_set Configuration read from the json file.\r
21 /// @param[in] out Stream to write on.\r
22 void generate(const std::string& header, const std::string& footer, const openxc::message_set& message_set, std::ostream& out)\r
23 {\r
24         if (header.size()) out << header << "\n";\r
25         else out << "#pragma once\n\n";\r
26         \r
27         out << "namespace generated {\n"\r
28                 << "    class configuration_generated\n"\r
29                 << "    {\n"\r
30                 << "    private:\n"\r
31                 << "            can_message_set_t can_message_set_;\n"\r
32                 << "            std::vector<std::vector<can_message_definition_t>> can_message_definition_;\n"\r
33                 << "            std::vector<std::vector<can_signal_t>> can_signals_;\n"\r
34                 << "            std::vector<std::vector<obd2_signal_t>> obd2_signals_;\n"\r
35                 << "    public:\n"\r
36                 << "            configuration_generated()\n"\r
37                 << "                    : message_set_{0, \""\r
38                                                                                         << message_set.name() << "\", "\r
39                                                                                         << message_set.buses().size() << ", "\r
40                                                                                         << message_set.messages().size() << ", "\r
41                                                                                         << std::accumulate(\r
42                                                                                                         std::begin(message_set.messages()),\r
43                                                                                                         std::end(message_set.messages()),\r
44                                                                                                         0,\r
45                                                                                                         [](int sum, const std::map<std::string, openxc::can_message>::value_type& p) { return sum + p.second.signals().size(); }\r
46                                                                                         ) << ", "\r
47                                                                                         << message_set.commands().size() << ", "\r
48                                                                                         << message_set.diagnostic_messages().size() << "}\n"\r
49                 << "                    , can_message_definition_{\n";\r
50 \r
51                 std::uint32_t count = message_set.messages().size();\r
52                 for(const std::map<std::string, openxc::can_message>::value_type& m : message_set.messages())\r
53                 {\r
54                         out << "                                {{\""   << m.second.bus() << "\", "\r
55                                                                                         << m.first << ", "\r
56                                                                                         << "can_message_format_t::STANDARD, frequency_clock_t(), false}}";\r
57                         if (count > 1) out << ",";\r
58                         out << "\n";\r
59                         --count;\r
60                 }\r
61                 out << "                        }\n";\r
62 \r
63                 \r
64                 out << "                        , can_signals_" << "..." << "\n"\r
65                         << "                    , obd2_signals_" << "..." << "\n"\r
66                         << "            {\n"\r
67                         << "            }\n"\r
68                         << "    };\n"\r
69                         << "}\n\n";\r
70         \r
71         out << footer << std::endl;\r
72 }\r
73 \r
74 /// @brief Read whole file content to a string.\r
75 /// @param[in] file Path to the file.\r
76 /// @return A std::string which contains the file content. If @c file is an empty string, the return value is also empty.\r
77 /// @exception std::runtime_error Throw this exception if the specified file is not found or cannot be opened.\r
78 std::string read_file(const std::string& file)\r
79 {\r
80         if(file.size() == 0) return std::string();\r
81         \r
82         std::string content;\r
83         std::ifstream stream(file);\r
84         if (stream)\r
85         {\r
86                 stream.seekg(0, std::ios::end);\r
87                 content.reserve(stream.tellg());\r
88                 stream.seekg(0, std::ios::beg);\r
89                 content.assign((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());\r
90                 return content;\r
91         }\r
92         std::stringstream ss;\r
93         ss << "The specified file (" << file << ") is not found!";\r
94         throw std::runtime_error(ss.str());\r
95 }\r
96 \r
97 /// @brief Read whole file content as a json document.\r
98 /// @param[in] file Path to the file.\r
99 /// @return A @c nlohmann::json object.\r
100 /// @exception std::runtime_error Throw this exception if the specified file is not found or cannot be opened.\r
101 nlohmann::json read_json(const std::string& file)\r
102 {\r
103         std::ifstream stream(file);\r
104         if (stream)\r
105         {\r
106                 nlohmann::json result;\r
107                 stream >> result;\r
108                 return result;\r
109         }\r
110         std::stringstream ss;\r
111         ss << "The specified file (" << file << ") is not found!";\r
112         throw std::runtime_error(ss.str());\r
113 }\r
114 \r
115 /// @brief Entry point.\r
116 /// @param[in] argc Argument's count.\r
117 /// @param[in] argv Argument's array.\r
118 /// @return Exit code, zero if success.\r
119 int main(int argc, char** argv)\r
120 {\r
121         std::ios::sync_with_stdio(false);\r
122         \r
123         try\r
124         {\r
125                 std::string appName = boost::filesystem::basename(argv[0]);\r
126                 std::string message_set_file;\r
127                 std::string output_file;\r
128                 std::string header_file;\r
129                 std::string footer_file;\r
130 \r
131                 namespace bpo = boost::program_options;\r
132                 bpo::options_description desc("Options");\r
133                 desc.add_options()\r
134                         ("help,h", "Display this help.")\r
135                         ("message-set,m", bpo::value<std::string>(&message_set_file)->required(), "The message set definition file.")\r
136                         ("output,o", bpo::value<std::string>(&output_file), "An output file, if not specified stdout is used.")\r
137                         ("header,h", bpo::value<std::string>(&header_file), "A file to copy at the top of the generated output.")\r
138                         ("footer,f", bpo::value<std::string>(&footer_file), "A file to copy at the end of the generated output.");\r
139 \r
140                 bpo::variables_map vm;\r
141                 try\r
142                 {\r
143                         bpo::store(bpo::parse_command_line(argc, argv, desc), vm);\r
144 \r
145                         if (vm.count("help"))\r
146                         {\r
147                                 std::cout << desc << std::endl;\r
148                                 return EXIT_SUCCESS;\r
149                         }\r
150 \r
151                         bpo::notify(vm);\r
152                         \r
153                         std::string header = read_file(header_file);\r
154                         std::string footer = read_file(footer_file);\r
155                         openxc::message_set message_set;\r
156                         message_set.from_json(read_json(message_set_file));\r
157                         std::ofstream out;\r
158                         if (output_file.size())\r
159                         {\r
160                                 out = std::ofstream(output_file);\r
161                                 if(!out)\r
162                                 {\r
163                                         std::stringstream ss;\r
164                                         ss << "Can't open the ouput file (" << output_file << ") for writing!";\r
165                                         throw std::runtime_error(ss.str());\r
166                                 }\r
167                         }\r
168                         \r
169                         generate(header, footer, message_set, output_file.size() ? out : std::cout); \r
170 \r
171                 }\r
172                 catch (bpo::required_option& e)\r
173                 {\r
174                         std::cerr << "ERROR: Argument required - " << e.what() << std::endl;\r
175                         std::cout << desc << std::endl;\r
176                         return EXIT_COMMAND_LINE_ERROR;\r
177                 }\r
178                 catch (bpo::error& e)\r
179                 {\r
180                         std::cerr << "ERROR: Command line error - " << e.what() << std::endl;\r
181                         std::cout << desc << std::endl;\r
182                         return EXIT_COMMAND_LINE_ERROR;\r
183                 }\r
184                 catch(std::exception& e)\r
185                 {\r
186                         std::cerr << "ERROR: " << e.what() << std::endl;\r
187                         return EXIT_PROGRAM_ERROR;\r
188                 }\r
189         }\r
190         catch (std::exception& e)\r
191         {\r
192                 std::cerr << "ERROR: Unhandled exception - " << e.what() << std::endl;\r
193                 return EXIT_UNKNOWN_ERROR;\r
194         }\r
195 \r
196         return EXIT_SUCCESS;\r
197 }\r