// // Copyright 2015-2015 Fairwaves LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include "umtrx_impl.hpp" #include #include #include #include using namespace uhd; using namespace uhd::usrp; namespace asio = boost::asio; /*! * Querying sensors using the status monitor server. * Requests are encoded in JSON and end in a newline. * Responses are encoded in JSON and end in a newline. * * Start UmTRX driver with status_port set in args * ./some_application --args="status_port=12345" * * import json * import socket * s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) * s.connect(("localhost", 12345)) * f = s.makefile() #gives us readline() * * #list branches in the property tree * s.send(json.dumps(dict(action='LIST', path='/mboards/0/sensors'))+'\n') * print json.loads(f.readline()) * {u'result': [u'tempA']} * * #check if the specified path exists * s.send(json.dumps(dict(action='HAS', path='/mboards/0/sensors/tempA'))+'\n') * print json.loads(f.readline()) * {u'result': u'true'} * * #get the value of a tree entry, types can be BOOL, INT, DOUBLE, COMPLEX, SENSOR, RANGE * s.send(json.dumps(dict(action='GET', path='/mboards/0/sensors/tempA', type='SENSOR'))+'\n') * print json.loads(f.readline()) * {u'result': {u'unit': u'C', u'name': u'TempA', u'value': u'61.625000'}} * * #set the value of a tree entry, types can be BOOL, INT, DOUBLE, COMPLEX * s.send(json.dumps(dict(action='SET', path='/mboards/0/dboards/A/rx_frontends/0/freq/value', type='DOUBLE', value=1e9))+'\n') * print json.loads(f.readline()) * {} #empty response means no error * * #in case of COMPLEX 'value' is an array of [real, imag] values * s.send(json.dumps(dict(action='SET', path='/mboards/0/rx_frontends/A/dc_offset/value', type='COMPLEX', value=[0.1, 0.0]))+'\n') * print json.loads(f.readline()) * {} #empty response means no error */ void umtrx_impl::status_monitor_start(const uhd::device_addr_t &device_addr) { if (device_addr.has_key("status_port")) { UHD_MSG(status) << "Creating TCP monitor on port " << device_addr.get("status_port") << std::endl; _server_query_tcp_acceptor.reset(new asio::ip::tcp::acceptor( _server_query_io_service, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), device_addr.cast("status_port", 0)))); _server_query_task = task::make(boost::bind(&umtrx_impl::server_query_handler, this)); } _status_monitor_task = task::make(boost::bind(&umtrx_impl::status_monitor_handler, this)); } void umtrx_impl::status_monitor_stop(void) { _status_monitor_task.reset(); _server_query_task.reset(); } static bool wait_read_sockfd(const int sockfd, const size_t timeoutMs) { //setup timeval for timeout timeval tv; tv.tv_sec = 0; tv.tv_usec = timeoutMs*1000; //setup rset for timeout fd_set rset; FD_ZERO(&rset); FD_SET(sockfd, &rset); //call select with timeout on receive socket return ::select(sockfd+1, &rset, NULL, NULL, &tv) > 0; } void umtrx_impl::status_monitor_handler(void) { //TODO read the sensors and react... //read_dc_v, etc... //UHD_MSG(status) << this->read_temp_c("A").to_pp_string() << std::endl; //TODO shutdown frontend when temp > thresh //ctrl->set_rx_enabled(false); //ctrl->set_tx_enabled(false); //this sleep defines the polling time between status checks //when the handler completes, it will be called again asap //if the task is canceled, this sleep in interrupted for exit boost::this_thread::sleep(boost::posix_time::milliseconds(1500)); } void umtrx_impl::server_query_handler(void) { //accept the client socket (timeout is 100 ms, task is called again) if (not wait_read_sockfd(_server_query_tcp_acceptor->native(), 100)) return; boost::shared_ptr socket(new asio::ip::tcp::socket(_server_query_io_service)); _server_query_tcp_acceptor->accept(*socket); //create a new thread to handle the client boost::thread handler(boost::bind(&umtrx_impl::client_query_handle, this, socket)); handler.detach(); } void umtrx_impl::client_query_handle(boost::shared_ptr socket) { while (not boost::this_thread::interruption_requested()) { boost::property_tree::ptree request, response; //receive the request in JSON markup boost::asio::streambuf requestBuff; try { boost::asio::read_until(*socket, requestBuff, "\n"); std::istream is(&requestBuff); boost::property_tree::read_json(is, request); } catch (const std::exception &ex) { if (requestBuff.size() == 0) return; //client ended response.put("error", "request parser error: " + std::string(ex.what())); } //handle the request try { this->client_query_handle1(request, response); } catch (const std::exception &ex) { response.put("error", "failed to handle request: " + std::string(ex.what())); } //send the response boost::asio::streambuf responseBuff; std::ostream os(&responseBuff); try { boost::property_tree::write_json(os, response, false/*not pretty required*/); boost::asio::write(*socket, responseBuff); } catch (const std::exception &ex) { UHD_MSG(error) << "client_query_handle send response failed, exit client thread: " << ex.what() << std::endl; return; } } } void umtrx_impl::client_query_handle1(const boost::property_tree::ptree &request, boost::property_tree::ptree &response) { const std::string action = request.get("action", ""); const std::string path = request.get("path", ""); if (response.count("error") != 0) { //already in error } else if (path.empty()) { response.put("error", "path field not specified"); } else if (action.empty()) { response.put("error", "action field not specified: GET, SET, HAS, LIST"); } else if (action == "GET") { const std::string type = request.get("type", ""); if (type.empty()) response.put("error", "type field not specified: STRING, BOOL, INT, DOUBLE, COMPLEX, SENSOR, RANGE"); else if (type == "STRING") response.put("result", _tree->access(path).get()); else if (type == "BOOL") response.put("result", _tree->access(path).get()); else if (type == "INT") response.put("result", _tree->access(path).get()); else if (type == "DOUBLE") response.put("result", _tree->access(path).get()); else if (type == "COMPLEX") { boost::property_tree::ptree result; boost::property_tree::ptree ptree_i, ptree_q; const std::complex c = _tree->access >(path).get(); ptree_i.put("", c.real()); ptree_q.put("", c.imag()); result.push_back(std::make_pair("", ptree_i)); result.push_back(std::make_pair("", ptree_q)); response.add_child("result", result); } else if (type == "SENSOR") { boost::property_tree::ptree result; const sensor_value_t sensor = _tree->access(path).get(); result.put("name", sensor.name); result.put("value", sensor.value); result.put("unit", sensor.unit); response.add_child("result", result); } else if (type == "RANGE") { boost::property_tree::ptree result; BOOST_FOREACH(const range_t &range, _tree->access(path).get()) { boost::property_tree::ptree rangeData; rangeData.put("start", range.start()); rangeData.put("stop", range.stop()); rangeData.put("step", range.step()); result.push_back(std::make_pair("", rangeData)); } response.add_child("result", result); } else response.put("error", "unknown type: " + type); } else if (action == "SET") { const std::string type = request.get("type", ""); if (type.empty()) response.put("error", "type field not specified: STRING, BOOL, INT, DOUBLE, COMPLEX"); else if (type == "STRING") _tree->access(path).set(request.get("value")); else if (type == "BOOL") _tree->access(path).set(request.get("value")); else if (type == "INT") _tree->access(path).set(request.get("value")); else if (type == "DOUBLE") _tree->access(path).set(request.get("value")); else if (type == "COMPLEX") { boost::property_tree::ptree value = request.get_child("value"); double i = value.front().second.get(""); double q = value.back().second.get(""); _tree->access >(path).set(std::complex(i, q)); } else response.put("error", "unknown type: " + type); } else if (action == "HAS") { response.put("result", _tree->exists(path)); } else if (action == "LIST") { boost::property_tree::ptree result; BOOST_FOREACH(const std::string &branchName, _tree->list(path)) { boost::property_tree::ptree branchData; branchData.put("", branchName); result.push_back(std::make_pair("", branchData)); } response.add_child("result", result); } else { response.put("error", "unknown action: " + action); } }