#include "SoilMoistureIo.h" #include "json.hpp" #include "apps.h" #include #include #include #include enum class Format { PLAIN, JSON, SQL }; void validate(boost::any &v, const std::vector &values, Format *, int) { using namespace boost::program_options; const std::string &s = validators::get_single_string(values); if (s == "plain") { v = boost::any(Format::PLAIN); } else if (s == "json") { v = boost::any(Format::JSON); } else if (s == "sql") { v = boost::any(Format::SQL); } else { throw validation_error(validation_error::invalid_option_value); } } namespace boost { template<> std::string lexical_cast(const Format &arg) { if (arg == Format::PLAIN) return "plain"; else if (arg == Format::JSON) return "json"; else if (arg == Format::SQL) return "sql"; else throw std::runtime_error("Unknown format value: " + lexical_cast(arg)); } } namespace trygvis { namespace apps { using namespace boost::asio; using namespace std; using namespace std::chrono; using namespace trygvis::apps; using namespace trygvis::soil_moisture; namespace po = boost::program_options; using json = nlohmann::json; Format format; string hostname = get_hostname(); class port_handler { public: port_handler(string device, serial_port &serial_port, shared_ptr stream) : device(device), port(serial_port), stream(stream) { } void run() { auto packet = make_shared>(1024); while (port.is_open()) { size_t some = port.read_some(buffer); for (int i = 0; i < some; i++) { uint8_t b = data[i]; if (b == packet_delimiter) { on_packet(packet); packet = make_shared>(1024); } else { packet->emplace_back(b); } } } cerr << "port closed" << endl; } void on_packet(shared_ptr> packet) { auto timestamp = std::chrono::system_clock::now().time_since_epoch().count(); auto s = std::string((char *) packet->data(), packet->size()); cerr << "packet: " << s << endl; static const boost::regex e("([a-zA-Z0-9]+)=([0-9]+)"); std::string::const_iterator start = s.begin(); std::string::const_iterator end = s.end(); boost::match_results what; boost::match_flag_type flags = boost::match_default; while (regex_search(start, end, what, e, flags)) { auto sensor = static_cast(what[1]); auto value = static_cast(what[2]); start = what[0].second; static const string device_type = "serial"; map values; values["hostname"] = hostname; values["device_type"] = device_type; values["device"] = device; values["timestamp"] = to_string(timestamp); values["sensor"] = sensor; values["value"] = value; // cerr << sensor << " => " << value << endl; stream->write(values); flags |= boost::match_prev_avail; flags |= boost::match_not_bob; } } private: static const size_t size = 1024; static const uint8_t packet_delimiter = '\n'; string device; serial_port &port; uint8_t data[size]; mutable_buffers_1 buffer = boost::asio::buffer(data, size); shared_ptr stream; }; class sm_serial_read : public app { public: void add_options(po::options_description_easy_init &options) override { options ("help", "produce help message") ("port", po::value()->required(), "The serial port to read") ("format", po::value(&format)->default_value(Format::PLAIN), "Output format"); } int main(app_execution &execution) override { auto desc = execution.desc; auto vm = execution.vm; uint32_t baud_rate = 115200; auto port_name = vm["port"].as(); io_service io_service; serial_port port(io_service); port.open(port_name); port.set_option(serial_port_base::baud_rate(baud_rate)); port.set_option(serial_port_base::character_size(8)); port.set_option(serial_port_base::parity(serial_port_base::parity::none)); port.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one)); port.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none)); if (port.is_open()) { cerr << "port is open" << endl; } else { cerr << "port is not open" << endl; } auto field_names = vector({ "hostname", "device_type", "device", "timestamp", "sensor", "value" }); shared_ptr sampleStream; if (format == Format::JSON) { sampleStream = make_shared(cout, field_names); } else if (format == Format::SQL) { sampleStream = make_shared(cout, field_names); } else if (format == Format::PLAIN) { sampleStream = make_shared(cout, field_names); } else { cerr << "Unsupported format: " << boost::lexical_cast(format) << endl; return EXIT_FAILURE; } port_handler(port_name, port, sampleStream).run(); return EXIT_SUCCESS; } }; } } using namespace trygvis::apps; int main(int argc, char *argv[]) { sm_serial_read app; return launch_app(argc, argv, app); }