2 * Copyright (C) 2015, 2016 "IoT.bzh"
\r
3 * Author "Loïc Collignon" <loic.collignon@iot.bzh>
\r
4 * Author "Romain Forlot" <romain.forlot@iot.bzh>
\r
6 * Licensed under the Apache License, Version 2.0 (the "License");
\r
7 * you may not use this file except in compliance with the License.
\r
8 * You may obtain a copy of the License at
\r
10 * http://www.apache.org/licenses/LICENSE-2.0
\r
12 * Unless required by applicable law or agreed to in writing, software
\r
13 * distributed under the License is distributed on an "AS IS" BASIS,
\r
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
15 * See the License for the specific language governing permissions and
\r
16 * limitations under the License.
\r
22 #include <exception>
\r
29 #include "openxc/message_set.hpp"
\r
30 #include "openxc/decoder.hpp"
\r
32 #define EXIT_SUCCESS 0
\r
33 #define EXIT_UNKNOWN_ERROR 1
\r
34 #define EXIT_COMMAND_LINE_ERROR 2
\r
35 #define EXIT_PROGRAM_ERROR 3
\r
41 * Taken from include/low-can/can-message.hpp in agl-service-can-low-level.
\r
42 * The flags values in the generated message_definition_t definitions needs to
\r
43 * match usage of these flags in the binding, e.g. in the various member
\r
44 * functions in low-can-binding/can/message-definition.cpp.
\r
46 * ATM the only flags known to be used are:
\r
48 * CAN_PROTOCOL_WITH_FD_FRAME
\r
51 * BYTE_FRAME_IS_BIG_ENDIAN
\r
53 * Note that for BYTE_FRAME_IS_BIG_ENDIAN, even though it is referenced in
\r
54 * message-definition.cpp, the member function that does so seems currently
\r
55 * unused, so it is not clear what the intended usage actually is.
\r
56 * The JSON parser for CAN messages would likely need an additional field
\r
57 * added to allow setting it, for now that is being left for a future update.
\r
60 #define CAN_PROTOCOL 0x0001
\r
61 #define CAN_PROTOCOL_WITH_FD_FRAME 0x0002
\r
62 #define J1939_ADDR_CLAIM_PROTOCOL 0x0004
\r
63 #define J1939_PROTOCOL 0x0008
\r
64 #define ISOTP_PROTOCOL 0x0010
\r
65 #define ISOTP_SEND 0x0020
\r
66 #define ISOTP_RECEIVE 0x0040
\r
67 #define BYTE_FRAME_IS_BIG_ENDIAN 0x0080
\r
68 #define BIT_POSITION_REVERSED 0x0100
\r
69 #define CONTINENTAL_BIT_POSITION 0x0200
\r
70 #define INVALID_FLAG 0x8000
\r
73 #define VERSION_LOW_CAN "2.0"
\r
76 std::string VERSION_FILE = "1.0";
\r
78 template <typename T>
\r
82 std::string line_prefix_;
\r
83 generator(T v, std::string line_prefix = "") : v_{v}, line_prefix_{line_prefix} {}
\r
87 struct generator<openxc::signal>
\r
89 const openxc::signal& v_;
\r
90 std::uint32_t index_;
\r
91 std::string line_prefix_;
\r
92 generator(const openxc::signal& v, std::uint32_t index, std::string line_prefix = "")
\r
93 : v_{v}, index_{index}, line_prefix_{line_prefix}
\r
98 template <typename T>
\r
99 generator<T> gen(const T& v, std::string line_prefix = "") { return generator<T>(v, line_prefix); }
\r
101 generator<openxc::signal> gen(const openxc::signal& v, std::uint32_t index, std::string line_prefix = "")
\r
103 return generator<openxc::signal>(v, index, line_prefix);
\r
106 template <typename T>
\r
107 std::ostream& operator<<(std::ostream& o, const generator<T>& v)
\r
109 o << v.line_prefix_ << v.v_;
\r
114 std::ostream& operator<<(std::ostream& o, const generator<bool>& v)
\r
116 o << v.line_prefix_ << (v.v_ ? "true" : "false");
\r
121 std::ostream& operator<<(std::ostream& o, const generator<float>& v)
\r
123 o << v.line_prefix_ << std::showpoint << v.v_ << "f";
\r
128 std::ostream& operator<<(std::ostream& o, const generator<std::string>& v)
\r
130 o << v.line_prefix_ << '\"' << v.v_ << '\"';
\r
134 template <typename T>
\r
135 std::ostream& operator<<(std::ostream& o, const generator<std::vector<T>>& v)
\r
137 // o << v.line_prefix_;
\r
138 auto sz = v.v_.size();
\r
139 for(const T& i : v.v_)
\r
141 o << gen(i, v.line_prefix_ + '\t');
\r
142 if (sz > 1) o << ",";
\r
146 // o << v.line_prefix_;
\r
151 std::ostream& operator<<(std::ostream& o, const generator<openxc::message_set>& v)
\r
153 o << "std::shared_ptr<message_set_t> cms = "
\r
154 << "std::make_shared<message_set_t>(message_set_t{"
\r
156 << gen(v.v_.name()) << ",\n"
\r
157 << "\t{ // beginning message_definition_ vector\n"
\r
158 << gen(v.v_.messages(), "\t")
\r
159 << "\t}, // end message_definition vector\n"
\r
160 << "\t{ // beginning diagnostic_messages_ vector\n"
\r
161 << gen(v.v_.diagnostic_messages(),"\t") << "\n"
\r
162 << "\t} // end diagnostic_messages_ vector\n"
\r
163 << "}); // end message_set entry\n";
\r
168 std::ostream& operator<<(std::ostream& o, const generator<std::map<std::string, std::vector<std::uint32_t>>>& v)
\r
170 o << v.line_prefix_ << "{\n";
\r
171 std::uint32_t c1 = (uint32_t)v.v_.size();
\r
172 for(const auto& state : v.v_)
\r
174 std::uint32_t c2 = (uint32_t)state.second.size();
\r
175 for(const auto& i : state.second)
\r
177 o << v.line_prefix_ << "\t" << "{" << i << "," << gen(state.first) << "}";
\r
178 if (c1 > 1 || c2 > 1) o << ',';
\r
184 o << v.line_prefix_ << "}";
\r
189 std::ostream& operator<<(std::ostream& o, const generator<openxc::signal>& v)
\r
191 o << v.line_prefix_ << "{std::make_shared<signal_t> (signal_t{\n"
\r
192 << v.line_prefix_ << "\t" << gen(v.v_.generic_name()) << ",// generic_name\n"
\r
193 << v.line_prefix_ << "\t" << v.v_.bit_position() << ",// bit_position\n"
\r
194 << v.line_prefix_ << "\t" << v.v_.bit_size() << ",// bit_size\n"
\r
195 << v.line_prefix_ << "\t" << gen(v.v_.factor()) << ",// factor\n"
\r
196 << v.line_prefix_ << "\t" << gen(v.v_.offset()) << ",// offset\n"
\r
197 << v.line_prefix_ << "\t" << "0,// min_value\n"
\r
198 << v.line_prefix_ << "\t" << "0,// max_value\n"
\r
199 << v.line_prefix_ << "\tfrequency_clock_t(" << gen(v.v_.max_frequency()) << "),// frequency\n"
\r
200 << v.line_prefix_ << "\t" << gen(v.v_.send_same()) << ",// send_same\n"
\r
201 << v.line_prefix_ << "\t" << gen(v.v_.force_send_changed()) << ",// force_send_changed\n"
\r
202 << gen(v.v_.states(), v.line_prefix_ + '\t') << ",// states\n"
\r
203 << v.line_prefix_ << '\t' << gen(v.v_.writable()) << ",// writable\n"
\r
204 << v.line_prefix_ << '\t' << decoder_t::add_decoder(v.v_.decoder().size() ? v.v_.decoder() : v.v_.states().size() ? "decoder_t::decode_state" : "nullptr"
\r
206 , VERSION_LOW_CAN) << ",// decoder\n"
\r
207 << v.line_prefix_ << '\t' << (v.v_.encoder().size() ? v.v_.encoder() : "nullptr") << ",// encoder\n"
\r
208 << v.line_prefix_ << '\t' << "false,// received\n";
\r
209 std::string multi_first = "";
\r
210 if(v.v_.multiplex().first){
\r
211 multi_first = "true";
\r
213 multi_first = "false";
\r
215 std::string multi = "std::make_pair<bool, int>(" + multi_first + ", " + std::to_string(v.v_.multiplex().second) + ")";
\r
216 o << v.line_prefix_ << '\t' << multi << ",// multiplex\n"
\r
217 << v.line_prefix_ << "\tstatic_cast<sign_t>(" << gen(v.v_.sign()) << "),// signed\n"
\r
218 << v.line_prefix_ << '\t' << gen(v.v_.bit_sign_position()) << ",// bit_sign_position\n"
\r
219 << v.line_prefix_ << "\t" << gen(v.v_.unit()) << "// unit\n"
\r
220 << v.line_prefix_ << "})}";
\r
225 std::ostream& operator<<(std::ostream& o, const generator<openxc::can_message>& v)
\r
227 o << v.line_prefix_
\r
228 << "{std::make_shared<message_definition_t>(message_definition_t{"
\r
229 << gen(v.v_.bus()) << ","
\r
230 << v.v_.id() << ","
\r
231 << "\"" << v.v_.name() << "\","
\r
232 << (v.v_.length() != 0 ? v.v_.length() : 8) << ",";
\r
233 uint32_t flags = 0;
\r
236 flags = flags|CAN_PROTOCOL_WITH_FD_FRAME;
\r
239 if(v.v_.is_j1939())
\r
241 flags = flags|J1939_PROTOCOL;
\r
244 if(v.v_.is_isotp())
\r
246 flags = flags|ISOTP_PROTOCOL;
\r
249 o << gen(flags) << ",";
\r
251 o << "frequency_clock_t(" << gen(v.v_.max_frequency()) << "),"
\r
252 << gen(v.v_.force_send_changed()) << ",";
\r
253 std::uint32_t index = 0;
\r
254 o << "\n\t\t\t{ // beginning signals vector\n";
\r
255 std::uint32_t signal_count = (uint32_t)v.v_.signals().size();
\r
256 for(const openxc::signal& s : v.v_.signals())
\r
258 o << gen(s, index,"\t\t\t\t");
\r
259 if (signal_count > 1) o << ',';
\r
263 o << "\t\t\t} // end signals vector\n"
\r
264 << "\t\t})} // end message_definition entry\n";
\r
269 std::ostream& operator<<(std::ostream& o, const generator<openxc::diagnostic_message>& v)
\r
271 o << v.line_prefix_ << "{std::make_shared<diagnostic_message_t>(diagnostic_message_t{\n"
\r
272 << v.line_prefix_ << "\t" << v.v_.pid() << ",\n"
\r
273 << v.line_prefix_ << "\t" << gen(v.v_.name()) << ",\n"
\r
274 << v.line_prefix_ << "\t" << 0 << ",\n"
\r
275 << v.line_prefix_ << "\t" << 0 << ",\n"
\r
276 << v.line_prefix_ << "\t" << "UNIT::INVALID" << ",\n"
\r
277 << v.line_prefix_ << "\t" << gen(v.v_.frequency()) << ",\n"
\r
278 << v.line_prefix_ << "\t" << decoder_t::add_decoder((v.v_.decoder().size() ? v.v_.decoder() : "nullptr"),VERSION_FILE,VERSION_LOW_CAN) << ",\n"
\r
279 << v.line_prefix_ << "\t" << (v.v_.callback().size() ? v.v_.callback() : "nullptr") << ",\n"
\r
280 << v.line_prefix_ << "\t" << "true" << ",\n"
\r
281 << v.line_prefix_ << "\t" << "false" << "\n"
\r
282 << v.line_prefix_ << "})}\n";
\r
286 /// @brief Generate the application code.
\r
287 /// @param[in] header Content to be inserted as a header.
\r
288 /// @param[in] footer Content to be inserted as a footer.
\r
289 /// @param[in] message_set application read from the json file.
\r
290 /// @param[in] out Stream to write on.
\r
291 void generate(const std::string& header, const std::string& footer, const openxc::message_set& message_set, std::ostream& out)
\r
293 // Derive CAPI name from message set name
\r
294 // (the name is lowercased and spaces are converted to dashes ('-')
\r
295 std::string capi_name(message_set.name());
\r
296 std::transform(capi_name.begin(), capi_name.end(), capi_name.begin(),
\r
297 [](unsigned char c){ return (c == ' ' ? '-' : std::tolower(c)); });
\r
299 out << "#include <binding/application.hpp>\n"
\r
300 << "#include <can/can-decoder.hpp>\n"
\r
301 << "#include <can/can-encoder.hpp>\n\n";
\r
303 if (header.size()) out << header << "\n";
\r
305 out << "extern \"C\" {\n"
\r
306 << "CTLP_CAPI_REGISTER(\"" << capi_name << "\");\n"
\r
308 << gen(message_set, "\t\t")
\r
310 << "CTLP_ONLOAD(plugin, handle) {\n"
\r
311 << "\tafb_api_t api = (afb_api_t) plugin->api;\n"
\r
312 << "\tCtlConfigT* CtlConfig = (CtlConfigT*) afb_api_get_userdata(api);\n"
\r
313 << "\tapplication_t* app = (application_t*) getExternalData(CtlConfig);\n"
\r
315 << "\treturn app->add_message_set(cms);\n"
\r
318 out << decoder_t::apply_patch();
\r
320 out << footer << std::endl;
\r
323 /// @brief Read whole file content to a string.
\r
324 /// @param[in] file Path to the file.
\r
325 /// @return A std::string which contains the file content. If @c file is an empty string, the return value is also empty.
\r
326 /// @exception std::runtime_error Throw this exception if the specified file is not found or cannot be opened.
\r
327 std::string read_file(const std::string& file)
\r
329 if(file.size() == 0) return std::string();
\r
331 std::string content;
\r
332 std::ifstream stream(file);
\r
335 stream.seekg(0, std::ios::end);
\r
336 content.reserve(stream.tellg());
\r
337 stream.seekg(0, std::ios::beg);
\r
338 content.assign((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
\r
341 std::stringstream ss;
\r
342 ss << "The specified file (" << file << ") is not found!";
\r
343 throw std::runtime_error(ss.str());
\r
346 /// @brief Read whole file content as a json document.
\r
347 /// @param[in] file Path to the file.
\r
348 /// @return A @c nlohmann::json object.
\r
349 /// @exception std::runtime_error Throw this exception if the specified file is not found or cannot be opened.
\r
350 nlohmann::json read_json(const std::string& file)
\r
352 std::ifstream stream(file);
\r
355 nlohmann::json result;
\r
359 std::stringstream ss;
\r
360 ss << "The specified file (" << file << ") is not found!";
\r
361 throw std::runtime_error(ss.str());
\r
364 // function that show the help information
\r
365 void showhelpinfo(char *s)
\r
367 std::cout<<"Usage: "<<s<<" <-m inpout.json> [-o application-generated.cpp]"<< std::endl;
\r
368 std::cout<<"option: "<<"-m input.json : JSON file describing CAN messages and signals"<< std::endl;
\r
369 std::cout<<" "<<"-h header.cpp : header source file insert at the beginning of generated file"<< std::endl;
\r
370 std::cout<<" "<<"-f footer.cpp : footer source file append to generated file."<< std::endl;
\r
371 std::cout<<" "<<"-o application-generated.cpp : output source file. Name has to be application-generated.cpp"<< std::endl;
\r
374 /// @brief Entry point.
\r
375 /// @param[in] argc Argument's count.
\r
376 /// @param[in] argv Argument's array.
\r
377 /// @return Exit code, zero if success.
\r
378 int main(int argc, char *argv[])
\r
381 decoder_t::init_decoder();
\r
384 std::string appName = argv[0];
\r
385 std::string message_set_file;
\r
386 std::string output_file;
\r
387 std::string header_file;
\r
388 std::string footer_file;
\r
391 /*if the program is ran witout options ,it will show the usgage and exit*/
\r
394 showhelpinfo(argv[0]);
\r
397 /*use function getopt to get the arguments with option."hu:p:s:v" indicate
\r
398 that option h,v are the options without arguments while u,p,s are the
\r
399 options with arguments*/
\r
400 while((tmp=(char)getopt(argc,argv,"m:h:f:o:"))!=-1)
\r
405 header_file = optarg;
\r
408 footer_file = optarg;
\r
411 message_set_file = optarg;
\r
414 output_file = optarg;
\r
417 showhelpinfo(argv[0]);
\r
422 std::stringstream header;
\r
423 header << read_file(header_file);
\r
425 std::string footer = read_file(footer_file);
\r
426 openxc::message_set message_set;
\r
427 message_set.from_json(read_json(message_set_file));
\r
429 std::string message_set_path = dirname(strdup(message_set_file.c_str()));
\r
430 for(const auto& s : message_set.extra_sources())
\r
432 std::string extra_source = s;
\r
433 extra_source = message_set_path + "/" + extra_source;
\r
434 header << "\n// >>>>> " << s << " >>>>>\n" << read_file(extra_source) << "\n// <<<<< " << s << " <<<<<\n";
\r
438 if (output_file.size())
\r
440 out.open(output_file);
\r
443 std::stringstream ss;
\r
444 ss << "Can't open the ouput file (" << output_file << ") for writing!";
\r
445 throw std::runtime_error(ss.str());
\r
448 VERSION_FILE = message_set.version();
\r
449 generate(header.str(), footer, message_set, output_file.size() ? out : std::cout);
\r
451 catch (std::exception& e)
\r
453 std::cerr << "ERROR: Unhandled exception - " << e.what() << std::endl;
\r
454 return EXIT_UNKNOWN_ERROR;
\r
456 return EXIT_SUCCESS;
\r