Import CAN generator from low-level-can-service
[src/low-level-can-generator.git] / src / main.cpp
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644 (file)
index 0000000..d30428e
--- /dev/null
@@ -0,0 +1,395 @@
+/*\r
+ * Copyright (C) 2015, 2016 "IoT.bzh"\r
+ * Author "Loïc Collignon" <loic.collignon@iot.bzh>\r
+ * Author "Romain Forlot" <romain.forlot@iot.bzh>\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+#include <unistd.h>\r
+#include <stdlib.h>\r
+#include <libgen.h>\r
+#include <exception>\r
+#include <fstream>\r
+#include <iostream>\r
+#include <string>\r
+#include <numeric>\r
+#include <iterator>\r
+#include <json.hpp>\r
+#include "openxc/message_set.hpp"\r
+\r
+#define EXIT_SUCCESS                           0\r
+#define EXIT_UNKNOWN_ERROR                     1\r
+#define EXIT_COMMAND_LINE_ERROR                2\r
+#define EXIT_PROGRAM_ERROR                     3\r
+\r
+template <typename T>\r
+struct generator\r
+{\r
+       T v_;\r
+       std::string line_prefix_;\r
+       generator(T v, std::string line_prefix = "") : v_{v}, line_prefix_{line_prefix} {}\r
+};\r
+\r
+template <>\r
+struct generator<openxc::signal>\r
+{\r
+       const openxc::signal& v_;\r
+       std::uint32_t index_;\r
+       std::string line_prefix_;\r
+       generator(const openxc::signal& v, std::uint32_t index, std::string line_prefix = "")\r
+               : v_{v}, index_{index}, line_prefix_{line_prefix}\r
+       {\r
+       }\r
+};\r
+\r
+template <typename T>\r
+generator<T> gen(const T& v, std::string line_prefix = "") { return generator<T>(v, line_prefix); }\r
+\r
+generator<openxc::signal> gen(const openxc::signal& v, std::uint32_t index, std::string line_prefix = "")\r
+{\r
+       return generator<openxc::signal>(v, index, line_prefix);\r
+}\r
+\r
+template <typename T>\r
+std::ostream& operator<<(std::ostream& o, const generator<T>& v)\r
+{\r
+       o << v.line_prefix_ << v.v_;\r
+       return o;\r
+}\r
+\r
+template <>\r
+std::ostream& operator<<(std::ostream& o, const generator<bool>& v)\r
+{\r
+       o << v.line_prefix_ << (v.v_ ? "true" : "false");\r
+       return o;\r
+}\r
+\r
+template <>\r
+std::ostream& operator<<(std::ostream& o, const generator<float>& v)\r
+{\r
+       o << v.line_prefix_ << std::showpoint << v.v_ << "f";\r
+       return o;\r
+}\r
+\r
+template <>\r
+std::ostream& operator<<(std::ostream& o, const generator<std::string>& v)\r
+{\r
+       o << v.line_prefix_ << '\"' << v.v_ << '\"';\r
+       return o;\r
+}\r
+\r
+template <typename T>\r
+std::ostream& operator<<(std::ostream& o, const generator<std::vector<T>>& v)\r
+{\r
+//     o << v.line_prefix_;\r
+       auto sz = v.v_.size();\r
+       for(const T& i : v.v_)\r
+       {\r
+               o << gen(i, v.line_prefix_ + '\t');\r
+               if (sz > 1) o << ",";\r
+               --sz;\r
+//             o << '\n';\r
+       }\r
+//     o << v.line_prefix_;\r
+       return o;\r
+}\r
+\r
+template <>\r
+std::ostream& operator<<(std::ostream& o, const generator<openxc::message_set>& v)\r
+{\r
+       o       << v.line_prefix_\r
+               << "{std::make_shared<can_message_set_t>(can_message_set_t{"\r
+               << "0,"\r
+               << gen(v.v_.name()) << ",\n"\r
+               << "\t\t\t{ // beginning can_message_definition_ vector\n"\r
+               << gen(v.v_.messages(), "\t\t\t")\r
+               << "\n\t\t}, // end can_message_definition vector\n"\r
+               << "\t\t\t{ // beginning diagnostic_messages_ vector\n"\r
+               << gen(v.v_.diagnostic_messages(),"\t\t\t") << "\n"\r
+               << "\t\t\t} // end diagnostic_messages_ vector\n"\r
+               << "\t\t})} // end can_message_set entry\n";\r
+       return o;\r
+}\r
+\r
+template <>\r
+std::ostream& operator<<(std::ostream& o, const generator<std::map<std::string, std::vector<std::uint32_t>>>& v)\r
+{\r
+       o << v.line_prefix_ << "{\n";\r
+       std::uint32_t c1 = (uint32_t)v.v_.size();\r
+       for(const auto& state : v.v_)\r
+       {\r
+               std::uint32_t c2 = (uint32_t)state.second.size();\r
+               for(const auto& i : state.second)\r
+               {\r
+                       o << v.line_prefix_ << "\t" << "{" << i << "," << gen(state.first) << "}";\r
+                       if (c1 > 1 || c2 > 1) o << ',';\r
+                       o << '\n';\r
+                       --c2;\r
+               }\r
+               --c1;\r
+       }\r
+       o << v.line_prefix_ << "}";\r
+       return o;\r
+}\r
+\r
+template <>\r
+std::ostream& operator<<(std::ostream& o, const generator<openxc::signal>& v)\r
+{\r
+       o       << v.line_prefix_ << "{std::make_shared<can_signal_t> (can_signal_t{\n"\r
+               << v.line_prefix_ << "\t" << gen(v.v_.generic_name()) << ",\n"\r
+               << v.line_prefix_ << "\t" << v.v_.bit_position() << ",\n"\r
+               << v.line_prefix_ << "\t" << v.v_.bit_size() << ",\n"\r
+               << v.line_prefix_ << "\t" << gen(v.v_.factor()) << ",\n"\r
+               << v.line_prefix_ << "\t" << v.v_.offset() << ",\n"\r
+               << v.line_prefix_ << "\t" << "0,\n"\r
+               << v.line_prefix_ << "\t" << "0,\n"\r
+               << v.line_prefix_ << "\tfrequency_clock_t(" << gen(v.v_.max_frequency()) << "),\n"\r
+               << v.line_prefix_ << "\t" << gen(v.v_.send_same()) << ",\n"\r
+               << v.line_prefix_ << "\t" << gen(v.v_.force_send_changed()) << ",\n"\r
+               << gen(v.v_.states(), v.line_prefix_ + '\t') << ",\n"\r
+               << v.line_prefix_ << '\t' << gen(v.v_.writable()) << ",\n"\r
+               << v.line_prefix_ << '\t' << (v.v_.decoder().size() ? v.v_.decoder() : "nullptr") << ",\n"\r
+               << v.line_prefix_ << '\t' << (v.v_.encoder().size() ? v.v_.encoder() : "nullptr") << ",\n"\r
+               << v.line_prefix_ << '\t' << "false\n"\r
+               << v.line_prefix_ << "})}";\r
+       return o;\r
+}\r
+\r
+template <>\r
+std::ostream& operator<<(std::ostream& o, const generator<openxc::can_message>& v)\r
+{\r
+       o       << v.line_prefix_\r
+               << "{std::make_shared<can_message_definition_t>(can_message_definition_t{"\r
+               << gen(v.v_.bus()) << ","\r
+               << v.v_.id() << ","\r
+               << "can_message_format_t::STANDARD,"\r
+               << "frequency_clock_t(" << gen(v.v_.max_frequency()) << "),"\r
+               << gen(v.v_.force_send_changed()) << ",\n";\r
+               std::uint32_t index = 0;\r
+       o       << "\t\t\t\t\t{ // beginning can_signals vector\n";\r
+                       std::uint32_t signal_count = (uint32_t)v.v_.signals().size();\r
+                       for(const openxc::signal& s : v.v_.signals())\r
+                       {\r
+       o                       << gen(s, index,"\t\t\t\t\t\t");\r
+                               if (signal_count > 1) o << ',';\r
+                               --signal_count;\r
+       o                       << '\n';\r
+                       }\r
+       o       << "\t\t\t\t\t} // end can_signals vector\n"\r
+               << "\t\t\t\t})} // end can_message_definition entry\n";\r
+       return o;\r
+}\r
+\r
+template <>\r
+std::ostream& operator<<(std::ostream& o, const generator<openxc::diagnostic_message>& v)\r
+{\r
+       o       << v.line_prefix_ << "{std::make_shared<diagnostic_message_t>(diagnostic_message_t{\n"\r
+               << v.line_prefix_ << "\t" << v.v_.pid() << ",\n"\r
+               << v.line_prefix_ << "\t" << gen(v.v_.name()) << ",\n"\r
+               << v.line_prefix_ << "\t" << 0 << ",\n"\r
+               << v.line_prefix_ << "\t" << 0 << ",\n"\r
+               << v.line_prefix_ << "\t" << "UNIT::INVALID" << ",\n"\r
+               << v.line_prefix_ << "\t" << gen(v.v_.frequency()) << ",\n"\r
+               << v.line_prefix_ << "\t" << (v.v_.decoder().size() ? v.v_.decoder() : "nullptr") << ",\n"\r
+               << v.line_prefix_ << "\t" << (v.v_.callback().size() ? v.v_.callback() : "nullptr") << ",\n"\r
+               << v.line_prefix_ << "\t" << "true" << "\n"\r
+               << v.line_prefix_ << "})}\n";\r
+       return o;\r
+}\r
+\r
+/// @brief Generate the application code.\r
+/// @param[in] header Content to be inserted as a header.\r
+/// @param[in] footer Content to be inserted as a footer.\r
+/// @param[in] message_set application read from the json file.\r
+/// @param[in] out Stream to write on.\r
+void generate(const std::string& header, const std::string& footer, const openxc::message_set& message_set, std::ostream& out)\r
+{\r
+       out << "#include \"application.hpp\"\n"\r
+               << "#include \"../can/can-decoder.hpp\"\n\n";\r
+\r
+       if (header.size()) out << header << "\n";\r
+\r
+       out     << "application_t::application_t()\n"\r
+               << "    : can_bus_manager_{utils::config_parser_t{\"/etc/dev-mapping.conf\"}}\n"\r
+               << "    , can_message_set_{\n"\r
+               << gen(message_set, "\t\t")\r
+               << "\t} // end can_message_set vector\n"\r
+               << "{\n"\r
+               << "    for(auto& cms: can_message_set_)\n"\r
+               << "    {\n"\r
+               << "            std::vector<std::shared_ptr<can_message_definition_t> >& can_messages_definition = cms->get_can_message_definition();\n"\r
+               << "            for(auto& cmd : can_messages_definition)\n"\r
+               << "            {\n"\r
+               << "                    cmd->set_parent(cms.get());\n"\r
+               << "                    std::vector<std::shared_ptr<can_signal_t> >& can_signals = cmd->get_can_signals();\n"\r
+               << "                    for(auto& sig: can_signals)\n"\r
+               << "                    {\n"\r
+               << "                            sig->set_parent(cmd.get());\n"\r
+               << "                    }\n"\r
+               << "            }\n\n"\r
+               << "            std::vector<std::shared_ptr<diagnostic_message_t> >& diagnostic_messages = cms->get_diagnostic_messages();\n"\r
+               << "            for(auto& dm : diagnostic_messages)\n"\r
+               << "            {\n"\r
+               << "                    dm->set_parent(cms.get());\n"\r
+               << "            }\n"\r
+               << "    }\n"\r
+               << "            }\n\n"\r
+               << "const std::string application_t::get_diagnostic_bus() const\n"\r
+               << "{\n";\r
+\r
+               std::string active_bus = "";\r
+               for (const auto& d : message_set.diagnostic_messages())\r
+               {\r
+                       if (d.bus().size() == 0) std::cerr << "ERROR: The bus name should be set for each diagnostic message." << std::endl;\r
+                       if (active_bus.size() == 0) active_bus = d.bus();\r
+                       if (active_bus != d.bus()) std::cerr << "ERROR: The bus name should be the same for each diagnostic message." << std::endl;\r
+               }\r
+\r
+       out     << "\treturn " << gen(active_bus) << ";\n"\r
+               << "}\n\n";\r
+       out     << footer << std::endl;\r
+}\r
+\r
+/// @brief Read whole file content to a string.\r
+/// @param[in] file Path to the file.\r
+/// @return A std::string which contains the file content. If @c file is an empty string, the return value is also empty.\r
+/// @exception std::runtime_error Throw this exception if the specified file is not found or cannot be opened.\r
+std::string read_file(const std::string& file)\r
+{\r
+       if(file.size() == 0) return std::string();\r
+       \r
+       std::string content;\r
+       std::ifstream stream(file);\r
+       if (stream)\r
+       {\r
+               stream.seekg(0, std::ios::end);\r
+               content.reserve(stream.tellg());\r
+               stream.seekg(0, std::ios::beg);\r
+               content.assign((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());\r
+               return content;\r
+       }\r
+       std::stringstream ss;\r
+       ss << "The specified file (" << file << ") is not found!";\r
+       throw std::runtime_error(ss.str());\r
+}\r
+\r
+/// @brief Read whole file content as a json document.\r
+/// @param[in] file Path to the file.\r
+/// @return A @c nlohmann::json object.\r
+/// @exception std::runtime_error Throw this exception if the specified file is not found or cannot be opened.\r
+nlohmann::json read_json(const std::string& file)\r
+{\r
+       std::ifstream stream(file);\r
+       if (stream)\r
+       {\r
+               nlohmann::json result;\r
+               stream >> result;\r
+               return result;\r
+       }\r
+       std::stringstream ss;\r
+       ss << "The specified file (" << file << ") is not found!";\r
+       throw std::runtime_error(ss.str());\r
+}\r
+\r
+// function that show the help information\r
+void showhelpinfo(char *s)\r
+{\r
+std::cout<<"Usage:   "<<s<<" <-m inpout.json> [-o application-generated.cpp]"<< std::endl;\r
+std::cout<<"option:  "<<"-m  input.json : JSON file describing CAN messages and signals"<< std::endl;\r
+std::cout<<"         "<<"-h header.cpp : header source file insert at the beginning of generated file"<< std::endl;\r
+std::cout<<"         "<<"-f footer.cpp : footer source file append to generated file."<< std::endl;\r
+std::cout<<"         "<<"-o application-generated.cpp : output source file. Name has to be application-generated.cpp"<< std::endl;\r
+}\r
+\r
+/// @brief Entry point.\r
+/// @param[in] argc Argument's count.\r
+/// @param[in] argv Argument's array.\r
+/// @return Exit code, zero if success.\r
+int main(int argc, char *argv[])\r
+{\r
+       try\r
+       {\r
+               std::string appName = argv[0];\r
+               std::string message_set_file;\r
+               std::string output_file;\r
+               std::string header_file;\r
+               std::string footer_file;\r
+\r
+               char tmp;\r
+               /*if the program is ran witout options ,it will show the usgage and exit*/\r
+               if(argc == 1)\r
+               {\r
+                       showhelpinfo(argv[0]);\r
+                       exit(1);\r
+               }\r
+               /*use function getopt to get the arguments with option."hu:p:s:v" indicate \r
+               that option h,v are the options without arguments while u,p,s are the\r
+               options with arguments*/\r
+               while((tmp=(char)getopt(argc,argv,"m:h:f:o:"))!=-1)\r
+               {\r
+                       switch(tmp)\r
+                       {\r
+                       case 'h':\r
+                               header_file = optarg;\r
+                               break;\r
+                       case 'f':\r
+                               footer_file = optarg;\r
+                               break;\r
+                       case 'm':\r
+                               message_set_file = optarg;\r
+                               break;\r
+                       case 'o':\r
+                               output_file = optarg;\r
+                               break;\r
+                       default:\r
+                               showhelpinfo(argv[0]);\r
+                       break;\r
+                       }\r
+               }\r
+\r
+               std::stringstream header;\r
+               header << read_file(header_file);\r
+\r
+               std::string footer = read_file(footer_file);\r
+               openxc::message_set message_set;\r
+               message_set.from_json(read_json(message_set_file));\r
+\r
+               std::string message_set_path = dirname(strdup(message_set_file.c_str()));\r
+               for(const auto& s : message_set.extra_sources())\r
+               {\r
+                       std::string extra_source = s;\r
+                       extra_source = message_set_path + "/" + extra_source;\r
+                       header << "\n// >>>>> " << s << " >>>>>\n" << read_file(extra_source) << "\n// <<<<< " << s << " <<<<<\n";\r
+               }\r
+\r
+               std::ofstream out;\r
+               if (output_file.size())\r
+               {\r
+                       out.open(output_file);\r
+                       if(!out)\r
+                       {\r
+                               std::stringstream ss;\r
+                               ss << "Can't open the ouput file (" << output_file << ") for writing!";\r
+                               throw std::runtime_error(ss.str());\r
+                       }\r
+               }\r
+               generate(header.str(), footer, message_set, output_file.size() ? out : std::cout); \r
+       }\r
+       catch (std::exception& e)\r
+       {\r
+               std::cerr << "ERROR: Unhandled exception - " << e.what() << std::endl;\r
+               return EXIT_UNKNOWN_ERROR;\r
+       }\r
+       return EXIT_SUCCESS;\r
+}\r