7 #include <boost/program_options.hpp>
\r
8 #include <boost/filesystem.hpp>
\r
10 #include "openxc/message_set.hpp"
\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
17 template <typename T>
\r
21 std::string line_prefix_;
\r
22 generator(T v, std::string line_prefix = "") : v_{v}, line_prefix_{line_prefix} {}
\r
26 struct generator<openxc::signal>
\r
28 const openxc::signal& v_;
\r
29 std::uint32_t index_;
\r
30 std::string line_prefix_;
\r
31 generator(const openxc::signal& v, std::uint32_t index, std::string line_prefix = "")
\r
32 : v_{v}, index_{index}, line_prefix_{line_prefix}
\r
37 template <typename T>
\r
38 generator<T> gen(const T& v, std::string line_prefix = "") { return generator<T>(v, line_prefix); }
\r
40 generator<openxc::signal> gen(const openxc::signal& v, std::uint32_t index, std::string line_prefix = "")
\r
42 return generator<openxc::signal>(v, index, line_prefix);
\r
45 template <typename T>
\r
46 std::ostream& operator<<(std::ostream& o, const generator<T>& v)
\r
48 o << v.line_prefix_ << v.v_;
\r
53 std::ostream& operator<<(std::ostream& o, const generator<bool>& v)
\r
55 o << v.line_prefix_ << (v.v_ ? "true" : "false");
\r
60 std::ostream& operator<<(std::ostream& o, const generator<float>& v)
\r
62 o << v.line_prefix_ << std::showpoint << v.v_ << "f";
\r
67 std::ostream& operator<<(std::ostream& o, const generator<std::string>& v)
\r
69 o << v.line_prefix_ << '\"' << v.v_ << '\"';
\r
73 template <typename T>
\r
74 std::ostream& operator<<(std::ostream& o, const generator<std::vector<T>>& v)
\r
76 o << v.line_prefix_ << "{\n";
\r
77 auto sz = v.v_.size();
\r
78 for(const T& i : v.v_)
\r
80 o << gen(i, v.line_prefix_ + '\t');
\r
81 if (sz > 1) o << ",";
\r
85 o << v.line_prefix_ << '}';
\r
90 std::ostream& operator<<(std::ostream& o, const generator<openxc::message_set>& v)
\r
95 << gen(v.v_.name()) << ", "
\r
96 << v.v_.buses().size() << ", "
\r
97 << v.v_.messages().size() << ", "
\r
99 std::begin(v.v_.messages()),
\r
100 std::end(v.v_.messages()),
\r
102 [](int sum, const openxc::can_message& p) { return sum + p.signals().size(); }
\r
104 << v.v_.commands().size() << ", "
\r
105 << v.v_.diagnostic_messages().size() << "}";
\r
110 std::ostream& operator<<(std::ostream& o, const generator<openxc::can_message>& v)
\r
112 o << v.line_prefix_
\r
113 << "can_message_definition_t("
\r
115 << gen(v.v_.bus()) << ", "
\r
116 << v.v_.id() << ", "
\r
117 << "can_message_format_t::STANDARD, "
\r
118 << "frequency_clock_t(" << gen(v.v_.max_frequency()) << "), "
\r
119 << gen(v.v_.force_send_changed())
\r
125 std::ostream& operator<<(std::ostream& o, const generator<std::map<std::string, std::vector<std::uint32_t>>>& v)
\r
127 o << v.line_prefix_ << "{\n";
\r
128 std::uint32_t c1 = v.v_.size();
\r
129 for(const auto& state : v.v_)
\r
131 std::uint32_t c2 = state.second.size();
\r
132 for(const auto& i : state.second)
\r
134 o << v.line_prefix_ << "\t" << "{" << i << ", " << gen(state.first) << "}";
\r
135 if (c1 > 1 || c2 > 1) o << ',';
\r
141 o << v.line_prefix_ << "}";
\r
146 std::ostream& operator<<(std::ostream& o, const generator<openxc::signal>& v)
\r
148 o << v.line_prefix_ << "{\n"
\r
149 << v.line_prefix_ << "\t0,\n"
\r
150 << v.line_prefix_ << "\t" << v.index_ << ",\n"
\r
151 << v.line_prefix_ << "\t" << gen(v.v_.generic_name()) << ",\n"
\r
152 << v.line_prefix_ << "\t" << v.v_.bit_position() << ",\n"
\r
153 << v.line_prefix_ << "\t" << v.v_.bit_size() << ",\n"
\r
154 << v.line_prefix_ << "\t" << gen(v.v_.factor()) << ", \n"
\r
155 << v.line_prefix_ << "\t" << v.v_.offset() << ", \n"
\r
156 << v.line_prefix_ << "\t" << "0,\n"
\r
157 << v.line_prefix_ << "\t" << "0,\n"
\r
158 << v.line_prefix_ << "\tfrequency_clock_t(" << gen(v.v_.max_frequency()) << "),\n"
\r
159 << v.line_prefix_ << "\t" << gen(v.v_.send_same()) << ",\n"
\r
160 << v.line_prefix_ << "\t" << gen(v.v_.force_send_changed()) << ",\n"
\r
161 << gen(v.v_.states(), v.line_prefix_ + '\t') << ",\n"
\r
162 << v.line_prefix_ << '\t' << gen(v.v_.writable()) << ",\n"
\r
163 << v.line_prefix_ << '\t' << (v.v_.decoder().size() ? v.v_.decoder() : "nullptr") << ",\n"
\r
164 << v.line_prefix_ << '\t' << (v.v_.encoder().size() ? v.v_.encoder() : "nullptr") << ",\n"
\r
165 << v.line_prefix_ << '\t' << "false\n"
\r
166 << v.line_prefix_ << "}";
\r
171 std::ostream& operator<<(std::ostream& o, const generator<openxc::diagnostic_message>& v)
\r
173 o << v.line_prefix_ << "{\n"
\r
174 << v.line_prefix_ << "\t" << v.v_.pid() << ",\n"
\r
175 << v.line_prefix_ << "\t" << gen(v.v_.name()) << ",\n"
\r
176 << v.line_prefix_ << "\t" << 0 << ",\n"
\r
177 << v.line_prefix_ << "\t" << 0 << ",\n"
\r
178 << v.line_prefix_ << "\t" << "UNIT::INVALID" << ",\n"
\r
179 << v.line_prefix_ << "\t" << gen(v.v_.frequency()) << ",\n"
\r
180 << v.line_prefix_ << "\t" << (v.v_.decoder().size() ? v.v_.decoder() : "nullptr") << ",\n"
\r
181 << v.line_prefix_ << "\t" << (v.v_.callback().size() ? v.v_.callback() : "nullptr") << ",\n"
\r
182 << v.line_prefix_ << "\t" << "true" << "\n"
\r
183 << v.line_prefix_ << "}";
\r
187 /// @brief Generate the configuration code.
\r
188 /// @param[in] header Content to be inserted as a header.
\r
189 /// @param[in] footer Content to be inserted as a footer.
\r
190 /// @param[in] message_set Configuration read from the json file.
\r
191 /// @param[in] out Stream to write on.
\r
192 void generate(const std::string& header, const std::string& footer, const openxc::message_set& message_set, std::ostream& out)
\r
194 out << "#include \"configuration.hpp\"\n"
\r
195 << "#include \"can/can-decoder.hpp\"\n\n";
\r
197 if (header.size()) out << header << "\n";
\r
199 out << "configuration_t::configuration_t()\n"
\r
200 << " : can_message_set_{" << gen(message_set) << "}\n"
\r
201 << " , can_message_definition_\n"
\r
203 << gen(message_set.messages(), "\t\t") << '\n'
\r
205 << " , can_signals_\n"
\r
207 std::uint32_t message_count = message_set.messages().size();
\r
208 std::uint32_t index = 0;
\r
209 for(const openxc::can_message& m : message_set.messages())
\r
212 std::uint32_t signal_count = m.signals().size();
\r
213 for(const openxc::signal& s : m.signals())
\r
215 out << gen(s, index, " ");
\r
216 if (signal_count > 1) out << ',';
\r
221 if (index + 1 < message_count) out << ',';
\r
226 << " , diagnostic_messages_\n"
\r
228 << gen(message_set.diagnostic_messages(), " ") << "\n"
\r
232 << "const std::string configuration_t::get_diagnostic_bus() const\n"
\r
235 std::string active_bus = "";
\r
236 for (const auto& d : message_set.diagnostic_messages())
\r
238 if (d.bus().size() == 0) std::cerr << "ERROR: The bus name should be set for each diagnostic message." << std::endl;
\r
239 if (active_bus.size() == 0) active_bus = d.bus();
\r
240 if (active_bus != d.bus()) std::cerr << "ERROR: The bus name should be the same for each diagnostic message." << std::endl;
\r
243 out << " return " << gen(active_bus) << ";\n"
\r
245 out << footer << std::endl;
\r
248 /// @brief Read whole file content to a string.
\r
249 /// @param[in] file Path to the file.
\r
250 /// @return A std::string which contains the file content. If @c file is an empty string, the return value is also empty.
\r
251 /// @exception std::runtime_error Throw this exception if the specified file is not found or cannot be opened.
\r
252 std::string read_file(const std::string& file)
\r
254 if(file.size() == 0) return std::string();
\r
256 std::string content;
\r
257 std::ifstream stream(file);
\r
260 stream.seekg(0, std::ios::end);
\r
261 content.reserve(stream.tellg());
\r
262 stream.seekg(0, std::ios::beg);
\r
263 content.assign((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
\r
266 std::stringstream ss;
\r
267 ss << "The specified file (" << file << ") is not found!";
\r
268 throw std::runtime_error(ss.str());
\r
271 /// @brief Read whole file content as a json document.
\r
272 /// @param[in] file Path to the file.
\r
273 /// @return A @c nlohmann::json object.
\r
274 /// @exception std::runtime_error Throw this exception if the specified file is not found or cannot be opened.
\r
275 nlohmann::json read_json(const std::string& file)
\r
277 std::ifstream stream(file);
\r
280 nlohmann::json result;
\r
284 std::stringstream ss;
\r
285 ss << "The specified file (" << file << ") is not found!";
\r
286 throw std::runtime_error(ss.str());
\r
289 /// @brief Entry point.
\r
290 /// @param[in] argc Argument's count.
\r
291 /// @param[in] argv Argument's array.
\r
292 /// @return Exit code, zero if success.
\r
293 int main(int argc, char** argv)
\r
295 //std::ios::sync_with_stdio(false);
\r
299 std::string appName = boost::filesystem::basename(argv[0]);
\r
300 std::string message_set_file;
\r
301 std::string output_file;
\r
302 std::string header_file;
\r
303 std::string footer_file;
\r
305 namespace bpo = boost::program_options;
\r
306 bpo::options_description desc("Options");
\r
308 ("help,h", "Display this help.")
\r
309 ("message-set,m", bpo::value<std::string>(&message_set_file)->required(), "The message set definition file.")
\r
310 ("output,o", bpo::value<std::string>(&output_file), "An output file, if not specified stdout is used.")
\r
311 ("header,h", bpo::value<std::string>(&header_file), "A file to copy at the top of the generated output.")
\r
312 ("footer,f", bpo::value<std::string>(&footer_file), "A file to copy at the end of the generated output.");
\r
314 bpo::variables_map vm;
\r
317 bpo::store(bpo::parse_command_line(argc, argv, desc), vm);
\r
319 if (vm.count("help"))
\r
321 std::cout << desc << std::endl;
\r
322 return EXIT_SUCCESS;
\r
327 std::stringstream header;
\r
328 header << read_file(header_file);
\r
330 std::string footer = read_file(footer_file);
\r
331 openxc::message_set message_set;
\r
332 message_set.from_json(read_json(message_set_file));
\r
334 boost::filesystem::path message_set_path(message_set_file);
\r
335 message_set_path.remove_filename();
\r
336 for(const auto& s : message_set.extra_sources())
\r
338 boost::filesystem::path extra_source(s);
\r
339 if (!extra_source.is_complete()) extra_source = message_set_path / extra_source;
\r
340 header << "\n// >>>>> " << s << " >>>>>\n" << read_file(extra_source.string()) << "\n// <<<<< " << s << " <<<<<\n";
\r
344 if (output_file.size())
\r
346 out.open(output_file);
\r
349 std::stringstream ss;
\r
350 ss << "Can't open the ouput file (" << output_file << ") for writing!";
\r
351 throw std::runtime_error(ss.str());
\r
355 generate(header.str(), footer, message_set, output_file.size() ? out : std::cout);
\r
358 catch (bpo::required_option& e)
\r
360 std::cerr << "ERROR: Argument required - " << e.what() << std::endl;
\r
361 std::cout << desc << std::endl;
\r
362 return EXIT_COMMAND_LINE_ERROR;
\r
364 catch (bpo::error& e)
\r
366 std::cerr << "ERROR: Command line error - " << e.what() << std::endl;
\r
367 std::cout << desc << std::endl;
\r
368 return EXIT_COMMAND_LINE_ERROR;
\r
370 catch(std::exception& e)
\r
372 std::cerr << "ERROR: " << e.what() << std::endl;
\r
373 return EXIT_PROGRAM_ERROR;
\r
376 catch (std::exception& e)
\r
378 std::cerr << "ERROR: Unhandled exception - " << e.what() << std::endl;
\r
379 return EXIT_UNKNOWN_ERROR;
\r
382 return EXIT_SUCCESS;
\r