1 // PropertyChangeWebsocket.cxx -- A websocket for propertychangelisteners
3 // Written by Torsten Dreyer, started April 2014.
5 // Copyright (C) 2014 Torsten Dreyer
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "PropertyChangeWebsocket.hxx"
22 #include "PropertyChangeObserver.hxx"
23 #include "jsonprops.hxx"
25 #include <simgear/debug/logstream.hxx>
26 #include <simgear/props/props.hxx>
27 #include <simgear/structure/commands.hxx>
29 #include <simgear/props/props_io.hxx>
30 #include <Main/globals.hxx>
31 #include <Main/fg_props.hxx>
33 #include <3rdparty/cjson/cJSON.h>
35 namespace flightgear {
40 static unsigned nextid = 0;
42 static void setPropertyFromJson(SGPropertyNode_ptr prop, cJSON * json)
45 if ( NULL == json ) return;
46 switch ( json->type ) {
48 prop->setStringValue(json->valuestring);
52 prop->setDoubleValue(json->valuedouble);
56 prop->setBoolValue(true);
60 prop->setBoolValue(false);
68 static void handleSetCommand(const string_list& nodes, cJSON* json, WebsocketWriter &writer)
70 cJSON * value = cJSON_GetObjectItem(json, "value");
71 if ( NULL != value ) {
72 if (nodes.size() > 1) {
73 SG_LOG(SG_NETWORK, SG_WARN, "httpd: WS set: insufficent values for nodes:" << nodes.size());
77 SGPropertyNode_ptr n = fgGetNode(nodes.front());
79 SG_LOG(SG_NETWORK, SG_WARN, "httpd: set '" << nodes.front() << "' not found");
83 setPropertyFromJson(n, value);
87 cJSON * values = cJSON_GetObjectItem(json, "values");
88 if ( ( NULL == values ) || ( static_cast<size_t>(cJSON_GetArraySize(values)) != nodes.size()) ) {
89 SG_LOG(SG_NETWORK, SG_WARN, "httpd: WS set: mismatched nodes/values sizes:" << nodes.size());
93 string_list::const_iterator it;
95 for (it = nodes.begin(); it != nodes.end(); ++it, ++i) {
96 SGPropertyNode_ptr n = fgGetNode(*it);
98 SG_LOG(SG_NETWORK, SG_WARN, "httpd: get '" << *it << "' not found");
102 setPropertyFromJson(n, cJSON_GetArrayItem(values, i));
103 } // of nodes iteration
106 static void handleExecCommand(cJSON* json)
108 cJSON* name = cJSON_GetObjectItem(json, "fgcommand");
109 if ((NULL == name )|| (NULL == name->valuestring)) {
110 SG_LOG(SG_NETWORK, SG_WARN, "httpd: exec: no fgcommand name");
114 SGPropertyNode_ptr arg(new SGPropertyNode);
115 JSON::addChildrenToProp( json, arg );
117 globals->get_commands()->execute(name->valuestring, arg);
120 PropertyChangeWebsocket::PropertyChangeWebsocket(PropertyChangeObserver * propertyChangeObserver)
121 : id(++nextid), _propertyChangeObserver(propertyChangeObserver)
125 PropertyChangeWebsocket::~PropertyChangeWebsocket()
129 void PropertyChangeWebsocket::close()
131 SG_LOG(SG_NETWORK, SG_INFO, "closing PropertyChangeWebsocket #" << id);
132 _watchedNodes.clear();
135 void PropertyChangeWebsocket::handleGetCommand(const string_list& nodes, WebsocketWriter &writer)
137 double t = fgGetDouble("/sim/time/elapsed-sec");
138 string_list::const_iterator it;
139 for (it = nodes.begin(); it != nodes.end(); ++it) {
140 SGPropertyNode_ptr n = fgGetNode(*it);
142 SG_LOG(SG_NETWORK, SG_WARN, "httpd: get '" << *it << "' not found");
146 writer.writeText( JSON::toJsonString( false, n, 0, t ) );
147 } // of nodes iteration
150 void PropertyChangeWebsocket::handleRequest(const HTTPRequest & request, WebsocketWriter &writer)
152 if (request.Content.empty()) return;
157 command : 'addListener',
165 cJSON * json = cJSON_Parse(request.Content.c_str());
168 cJSON * j = cJSON_GetObjectItem(json, "command");
169 if ( NULL != j && NULL != j->valuestring) {
170 command = j->valuestring;
173 // handle a single node name, or an array of them
174 string_list nodeNames;
175 j = cJSON_GetObjectItem(json, "node");
176 if ( NULL != j && NULL != j->valuestring) {
177 nodeNames.push_back(simgear::strutils::strip(string(j->valuestring)));
180 cJSON * nodes = cJSON_GetObjectItem(json, "nodes");
181 if ( NULL != nodes) {
182 for (int i = 0; i < cJSON_GetArraySize(nodes); i++) {
183 cJSON * node = cJSON_GetArrayItem(nodes, i);
184 if ( NULL == node) continue;
185 if ( NULL == node->valuestring) continue;
186 nodeNames.push_back(simgear::strutils::strip(string(node->valuestring)));
190 if (command == "get") {
191 handleGetCommand(nodeNames, writer);
192 } else if (command == "set") {
193 handleSetCommand(nodeNames, json, writer);
194 } else if (command == "exec") {
195 handleExecCommand(json);
197 string_list::const_iterator it;
198 for (it = nodeNames.begin(); it != nodeNames.end(); ++it) {
199 _watchedNodes.handleCommand(command, *it, _propertyChangeObserver);
207 void PropertyChangeWebsocket::poll(WebsocketWriter & writer)
209 for (WatchedNodesList::iterator it = _watchedNodes.begin(); it != _watchedNodes.end(); ++it) {
210 SGPropertyNode_ptr node = *it;
213 if (_propertyChangeObserver->isChangedValue(node)) {
214 string out = JSON::toJsonString( false, node, 0, fgGetDouble("/sim/time/elapsed-sec") );
215 SG_LOG(SG_NETWORK, SG_DEBUG, "PropertyChangeWebsocket::poll() new Value for " << node->getPath(true) << " '" << node->getStringValue() << "' #" << id << ": " << out );
216 writer.writeText( out );
221 void PropertyChangeWebsocket::WatchedNodesList::handleCommand(const string & command, const string & node,
222 PropertyChangeObserver * propertyChangeObserver)
224 if (command == "addListener") {
225 for (iterator it = begin(); it != end(); ++it) {
226 if (node == (*it)->getPath(true)) {
227 SG_LOG(SG_NETWORK, SG_WARN, "httpd: " << command << " '" << node << "' ignored (duplicate)");
231 SGPropertyNode_ptr n = propertyChangeObserver->addObservation(node);
232 if (n.valid()) push_back(n);
233 SG_LOG(SG_NETWORK, SG_INFO, "httpd: " << command << " '" << node << "' success");
235 } else if (command == "removeListener") {
236 for (iterator it = begin(); it != end(); ++it) {
237 if (node == (*it)->getPath(true)) {
239 SG_LOG(SG_NETWORK, SG_INFO, "httpd: " << command << " '" << node << "' success");
243 SG_LOG(SG_NETWORK, SG_WARN, "httpd: " << command << " '" << node << "' ignored (not found)");
248 } // namespace flightgear