From 28629b65f2141c12cb9c8956bdfa3b3cf8d1921d Mon Sep 17 00:00:00 2001
From: James Turner <zakalawe@mac.com>
Date: Wed, 26 Sep 2018 23:53:46 +0100
Subject: [PATCH] Property mirror: handle request for missing nodes

---
 .../http/MirrorPropertyTreeWebsocket.cxx      | 71 ++++++++++++++-----
 .../http/MirrorPropertyTreeWebsocket.hxx      | 11 +--
 src/Network/http/httpd.cxx                    |  5 +-
 3 files changed, 64 insertions(+), 23 deletions(-)

diff --git a/src/Network/http/MirrorPropertyTreeWebsocket.cxx b/src/Network/http/MirrorPropertyTreeWebsocket.cxx
index 8fd68b60a..1991e7c9a 100644
--- a/src/Network/http/MirrorPropertyTreeWebsocket.cxx
+++ b/src/Network/http/MirrorPropertyTreeWebsocket.cxx
@@ -142,7 +142,7 @@ using std::string;
         {
         }
 
-        virtual void valueChanged(SGPropertyNode* node) override
+        void valueChanged(SGPropertyNode* node) override
         {
             auto it = idHash.find(node);
             if (it == idHash.end()) {
@@ -158,11 +158,16 @@ using std::string;
             }
         }
 
-        virtual void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override
+        void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override
         {
+            SG_UNUSED(parent);
+            recursiveAdd(child);            
+        }
 
-            const auto type = child->getType();
-            const auto& path = child->getPath();
+        void recursiveAdd(SGPropertyNode* node)
+        {
+            const auto type = node->getType();
+            const auto& path = node->getPath();
             auto rrIt = std::find_if(recentlyRemoved.begin(), recentlyRemoved.end(),
                                      [type, &path](const RecentlyRemovedNode& rr)
                                      { return (type == rr.type) && (path == rr.path); });
@@ -170,16 +175,26 @@ using std::string;
                 // recycle nodes which get thrashed from Nasal (deleted + re-created
                 // each time a Nasal timer fires)
                 removedNodes.erase(rrIt->id); // don't remove it!
-                idHash.insert(std::make_pair(child, rrIt->id));
-                changedNodes.insert(child);
+                idHash.insert(std::make_pair(node, rrIt->id));
+                changedNodes.insert(node);
                 recentlyRemoved.erase(rrIt);
+            #if defined (MIRROR_DEBUG)              
+                SG_LOG(SG_NETWORK, SG_INFO, "recycling node:" << path);
+            #endif
                 return;
             }
+        #if defined (MIRROR_DEBUG)    
+            SG_LOG(SG_NETWORK, SG_INFO, "new node:" << path);
+        #endif
+            newNodes.insert(node);
 
-            newNodes.insert(child);
+            int child = 0;
+            for (; child < node->nChildren(); ++child) {
+                recursiveAdd(node->getChild(child));
+            }
         }
 
-        virtual void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override
+        void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override
         {
             changedNodes.erase(child); // have to do this here with the pointer valid
             newNodes.erase(child);
@@ -198,6 +213,9 @@ using std::string;
 
         void registerSubtree(SGPropertyNode* node)
         {
+#if defined (MIRROR_DEBUG)              
+            SG_LOG(SG_NETWORK, SG_INFO, "register subtree:" << node->getPath());
+#endif
             valueChanged(node);
 
             // and recurse
@@ -380,13 +398,11 @@ static void handleExecCommand(cJSON* json)
 #endif
 
 MirrorPropertyTreeWebsocket::MirrorPropertyTreeWebsocket(const std::string& path) :
+    _rootPath(path),
     _listener(new MirrorTreeListener),
     _minSendInterval(100)
 {
-    _subtreeRoot = globals->get_props()->getNode(path, true);
-    _subtreeRoot->addChangeListener(_listener.get());
-    _listener->registerSubtree(_subtreeRoot);
-    _lastSendTime = SGTimeStamp::now();
+    checkNodeExists();
 }
 
 MirrorPropertyTreeWebsocket::~MirrorPropertyTreeWebsocket()
@@ -395,16 +411,30 @@ MirrorPropertyTreeWebsocket::~MirrorPropertyTreeWebsocket()
 
 void MirrorPropertyTreeWebsocket::close()
 {
-    _subtreeRoot->removeChangeListener(_listener.get());
+    if (_subtreeRoot) {
+        _subtreeRoot->removeChangeListener(_listener.get());
+    }
+}
 
-  #if 0
-  SG_LOG(SG_NETWORK, SG_INFO, "closing PropertyChangeWebsocket #" << id);
-  _watchedNodes.clear();
-  #endif
+void MirrorPropertyTreeWebsocket::checkNodeExists()
+{
+    _subtreeRoot = globals->get_props()->getNode(_rootPath, false);
+    if (_subtreeRoot) {
+        _subtreeRoot->addChangeListener(_listener.get());
+        _listener->registerSubtree(_subtreeRoot);
+        _lastSendTime = SGTimeStamp::now();
+    }
 }
 
 void MirrorPropertyTreeWebsocket::handleRequest(const HTTPRequest & request, WebsocketWriter &writer)
 {
+    if (!_subtreeRoot) {
+        checkNodeExists();
+        if (!_subtreeRoot) {
+            return; // still no node exists, we can't process this
+        }
+    }
+
   if (request.Content.empty()) return;
 #if 0
   /*
@@ -463,6 +493,13 @@ void MirrorPropertyTreeWebsocket::handleRequest(const HTTPRequest & request, Web
 
 void MirrorPropertyTreeWebsocket::poll(WebsocketWriter & writer)
 {
+    if (!_subtreeRoot) {
+        checkNodeExists();
+        if (!_subtreeRoot) {
+            return;
+        }
+    }
+
     if (!_listener->haveChangesToSend()) {
         return;
     }
diff --git a/src/Network/http/MirrorPropertyTreeWebsocket.hxx b/src/Network/http/MirrorPropertyTreeWebsocket.hxx
index 653280053..8f3e772a6 100644
--- a/src/Network/http/MirrorPropertyTreeWebsocket.hxx
+++ b/src/Network/http/MirrorPropertyTreeWebsocket.hxx
@@ -38,15 +38,18 @@ class MirrorPropertyTreeWebsocket : public Websocket
 {
 public:
     MirrorPropertyTreeWebsocket(const std::string& path);
-  virtual ~MirrorPropertyTreeWebsocket();
+    ~MirrorPropertyTreeWebsocket() override;
 
-  virtual void close();
-  virtual void handleRequest(const HTTPRequest & request, WebsocketWriter & writer);
-  virtual void poll(WebsocketWriter & writer);
+    void close() override;
+    void handleRequest(const HTTPRequest & request, WebsocketWriter & writer) override;
+    void poll(WebsocketWriter & writer) override;
 
 private:
+    void checkNodeExists();
+
     friend class MirrorTreeListener;
 
+    std::string _rootPath;
     SGPropertyNode_ptr _subtreeRoot;
     std::unique_ptr<MirrorTreeListener> _listener;
     int _minSendInterval;
diff --git a/src/Network/http/httpd.cxx b/src/Network/http/httpd.cxx
index 00c59bd44..237170928 100644
--- a/src/Network/http/httpd.cxx
+++ b/src/Network/http/httpd.cxx
@@ -610,8 +610,9 @@ Websocket * MongooseHttpd::newWebsocket(const string & uri)
     SG_LOG(SG_NETWORK, SG_INFO, "new PropertyChangeWebsocket for: " << uri);
     return new PropertyChangeWebsocket(&_propertyChangeObserver);
   } else if (uri.find("/PropertyTreeMirror/") == 0) {
-      SG_LOG(SG_NETWORK, SG_INFO, "new MirrorPropertyTreeWebsocket for: " << uri);
-    return new MirrorPropertyTreeWebsocket(uri.substr(20));
+    const auto path = uri.substr(20);
+    SG_LOG(SG_NETWORK, SG_INFO, "new MirrorPropertyTreeWebsocket for: " << path);
+    return new MirrorPropertyTreeWebsocket(path);
   }
   return NULL;
 }