FlightGear next
PropertyChangeWebsocket.cxx
Go to the documentation of this file.
1// PropertyChangeWebsocket.cxx -- A websocket for propertychangelisteners
2//
3// Written by Torsten Dreyer, started April 2014.
4//
5// Copyright (C) 2014 Torsten Dreyer
6//
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.
11//
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.
16//
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.
20
23#include "jsonprops.hxx"
24
25#include <simgear/debug/logstream.hxx>
26#include <simgear/props/props.hxx>
27#include <simgear/structure/commands.hxx>
28
29#include <simgear/props/props_io.hxx>
30#include <Main/globals.hxx>
31#include <Main/fg_props.hxx>
32
33#include <cJSON.h>
34
35namespace flightgear {
36namespace http {
37
38using std::string;
39
40static unsigned nextid = 0;
41
42static void setPropertyFromJson(SGPropertyNode_ptr prop, cJSON * json)
43{
44 if (!prop) return;
45 if ( NULL == json ) return;
46 switch ( json->type ) {
47 case cJSON_String:
48 prop->setStringValue(json->valuestring);
49 break;
50
51 case cJSON_Number:
52 prop->setDoubleValue(json->valuedouble);
53 break;
54
55 case cJSON_True:
56 prop->setBoolValue(true);
57 break;
58
59 case cJSON_False:
60 prop->setBoolValue(false);
61 break;
62
63 default:
64 return;
65 }
66}
67
68static void handleSetCommand(const string_list& nodes, cJSON* json, WebsocketWriter &writer)
69{
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());
74 return;
75 }
76
77 SGPropertyNode_ptr n = fgGetNode(nodes.front());
78 if (!n) {
79 SG_LOG(SG_NETWORK, SG_WARN, "httpd: set '" << nodes.front() << "' not found");
80 return;
81 }
82
83 setPropertyFromJson(n, value);
84 return;
85 }
86
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());
90 return;
91 }
92
93 string_list::const_iterator it;
94 int i=0;
95 for (it = nodes.begin(); it != nodes.end(); ++it, ++i) {
96 SGPropertyNode_ptr n = fgGetNode(*it);
97 if (!n) {
98 SG_LOG(SG_NETWORK, SG_WARN, "httpd: get '" << *it << "' not found");
99 return;
100 }
101
102 setPropertyFromJson(n, cJSON_GetArrayItem(values, i));
103 } // of nodes iteration
104}
105
106static void handleExecCommand(cJSON* json)
107{
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");
111 return;
112 }
113
114 SGPropertyNode_ptr arg(new SGPropertyNode);
115 JSON::addChildrenToProp( json, arg );
116
117 globals->get_commands()->execute(name->valuestring, arg, nullptr);
118}
119
121 : id(++nextid),
122 _propertyChangeObserver(propertyChangeObserver),
123 _minTriggerInterval(fgGetDouble("/sim/http/property-websocket/update-interval-secs", 0.05)), // default 20Hz
124 _lastTrigger(-1000)
125{
126}
127
131
133{
134 SG_LOG(SG_NETWORK, SG_INFO, "closing PropertyChangeWebsocket #" << id);
135 _watchedNodes.clear();
136}
137
138void PropertyChangeWebsocket::handleGetCommand(const string_list& nodes, WebsocketWriter &writer)
139{
140 double t = fgGetDouble("/sim/time/elapsed-sec");
141 string_list::const_iterator it;
142 for (it = nodes.begin(); it != nodes.end(); ++it) {
143 SGPropertyNode_ptr n = fgGetNode(*it);
144 if (!n) {
145 SG_LOG(SG_NETWORK, SG_WARN, "httpd: get '" << *it << "' not found");
146 return;
147 }
148
149 writer.writeText( JSON::toJsonString( false, n, 0, t ) );
150 } // of nodes iteration
151}
152
154{
155 if (request.Content.empty()) return;
156
157 /*
158 * allowed JSON is
159 {
160 command : 'addListener',
161 nodes : [
162 '/bar/baz',
163 '/foo/bar'
164 ],
165 node: '/bax/foo'
166 }
167 */
168 cJSON * json = cJSON_Parse(request.Content.c_str());
169 if ( NULL != json) {
170 string command;
171 cJSON * j = cJSON_GetObjectItem(json, "command");
172 if ( NULL != j && NULL != j->valuestring) {
173 command = j->valuestring;
174 }
175
176 // handle a single node name, or an array of them
177 string_list nodeNames;
178 j = cJSON_GetObjectItem(json, "node");
179 if ( NULL != j && NULL != j->valuestring) {
180 nodeNames.push_back(simgear::strutils::strip(string(j->valuestring)));
181 }
182
183 cJSON * nodes = cJSON_GetObjectItem(json, "nodes");
184 if ( NULL != nodes) {
185 for (int i = 0; i < cJSON_GetArraySize(nodes); i++) {
186 cJSON * node = cJSON_GetArrayItem(nodes, i);
187 if ( NULL == node) continue;
188 if ( NULL == node->valuestring) continue;
189 nodeNames.push_back(simgear::strutils::strip(string(node->valuestring)));
190 }
191 }
192
193 if (command == "get") {
194 handleGetCommand(nodeNames, writer);
195 } else if (command == "set") {
196 handleSetCommand(nodeNames, json, writer);
197 } else if (command == "exec") {
198 handleExecCommand(json);
199 } else {
200 string_list::const_iterator it;
201 for (it = nodeNames.begin(); it != nodeNames.end(); ++it) {
202 _watchedNodes.handleCommand(command, *it, _propertyChangeObserver);
203 }
204 }
205
206 cJSON_Delete(json);
207 }
208}
209
211{
212 double now = fgGetDouble("/sim/time/elapsed-sec");
213
214 if( _minTriggerInterval > .0 ) {
215 if( now - _lastTrigger <= _minTriggerInterval )
216 return;
217
218 _lastTrigger = now;
219 }
220
221 for (WatchedNodesList::iterator it = _watchedNodes.begin(); it != _watchedNodes.end(); ++it) {
222 SGPropertyNode_ptr node = *it;
223
224 string newValue;
225 if (_propertyChangeObserver->isChangedValue(node)) {
226 string out = JSON::toJsonString( false, node, 0, now );
227 SG_LOG(SG_NETWORK, SG_DEBUG, "PropertyChangeWebsocket::poll() new Value for " << node->getPath(true) << " '" << node->getStringValue() << "' #" << id << ": " << out );
228 writer.writeText( out );
229 }
230 }
231}
232
233void PropertyChangeWebsocket::WatchedNodesList::handleCommand(const string & command, const string & node,
234 PropertyChangeObserver * propertyChangeObserver)
235{
236 if (command == "addListener") {
237 for (iterator it = begin(); it != end(); ++it) {
238 if (node == (*it)->getPath(true)) {
239 SG_LOG(SG_NETWORK, SG_WARN, "httpd: " << command << " '" << node << "' ignored (duplicate)");
240 return; // dupliate
241 }
242 }
243 SGPropertyNode_ptr n = propertyChangeObserver->addObservation(node);
244 if (n.valid()) push_back(n);
245 SG_LOG(SG_NETWORK, SG_INFO, "httpd: " << command << " '" << node << "' success");
246
247 } else if (command == "removeListener") {
248 for (iterator it = begin(); it != end(); ++it) {
249 if (node == (*it)->getPath(true)) {
250 this->erase(it);
251 SG_LOG(SG_NETWORK, SG_INFO, "httpd: " << command << " '" << node << "' success");
252 return;
253 }
254 }
255 SG_LOG(SG_NETWORK, SG_WARN, "httpd: " << command << " '" << node << "' ignored (not found)");
256 }
257}
258
259} // namespace http
260} // namespace flightgear
#define i(x)
static std::string toJsonString(bool indent, SGPropertyNode_ptr n, int depth, double timestamp=-1.0)
static void addChildrenToProp(cJSON *json, SGPropertyNode_ptr base)
const SGPropertyNode_ptr addObservation(const std::string propertyName)
virtual void handleRequest(const HTTPRequest &request, WebsocketWriter &writer)
PropertyChangeWebsocket(PropertyChangeObserver *propertyChangeObserver)
virtual void poll(WebsocketWriter &writer)
int writeText(const char *data, size_t len)
Definition Websocket.hxx:40
SGCommandMgr::command_t command
FGGlobals * globals
Definition globals.cxx:142
std::vector< std::string > string_list
Definition globals.hxx:36
static void setPropertyFromJson(SGPropertyNode_ptr prop, cJSON *json)
static void handleSetCommand(const string_list &nodes, cJSON *json, WebsocketWriter &writer)
static void handleExecCommand(cJSON *json)
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
const char * name
SGCommandMgr::command_t command
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
Definition proptest.cpp:30
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27