Browse Source

HPCC-15037 Add mechanism to detach ESP from DALI

- Adds ESPControl methods to Attach/detach subscribe/unsubscribe from Dali
- Adds ESPServer and ESPConfig to support above methods
- Changes ESP behavior to prevent shutdown due to missing Dali connection
- Restructures esdl-binding Dali logic
- Adds unsubscribe logic to wsdfu
- Adds esdlconfig detached logic
- Adds ws_packageproecess subscribe/unsubscribe logic
- Remove ws_wuhelper shutdown of ESP if Dali not reachable

- Restructures wswu and wsfs schedule threads to withstand dali issues
- Saves ESP Proc attach state
- Removes commented out lines
- Refactors several WS methods
- Reuses code snippets
- Protects attached state
- Fixes a couple of negation bugs

Signed-off-by: Rodrigo Pastrana <rodrigo.pastrana@lexisnexis.com>
Rodrigo Pastrana 7 năm trước cách đây
mục cha
commit
e359696c2d
30 tập tin đã thay đổi với 1090 bổ sung130 xóa
  1. 21 0
      esp/bindings/SOAP/Platform/soapbind.hpp
  2. 6 1
      esp/bindings/SOAP/Platform/soapservice.hpp
  3. 199 33
      esp/platform/espcfg.cpp
  4. 54 4
      esp/platform/espcfg.ipp
  5. 37 5
      esp/platform/espp.hpp
  6. 15 0
      esp/scm/esp.ecm
  7. 47 1
      esp/scm/ws_espcontrol.ecm
  8. 12 6
      esp/services/esdl_svc_engine/esdl_binding.cpp
  9. 243 3
      esp/services/esdl_svc_engine/esdl_binding.hpp
  10. 1 1
      esp/services/esdl_svc_engine/esdl_store.cpp
  11. 1 0
      esp/services/esdl_svc_engine/esdl_store.hpp
  12. 0 2
      esp/services/esdl_svc_engine/esdl_svc_engine.hpp
  13. 1 0
      esp/services/espcontrol/CMakeLists.txt
  14. 1 1
      esp/services/espcontrol/ws_espcontrolplugin.cpp
  15. 96 0
      esp/services/espcontrol/ws_espcontrolservice.cpp
  16. 23 0
      esp/services/espcontrol/ws_espcontrolservice.hpp
  17. 23 1
      esp/services/ws_dfu/ws_dfuService.hpp
  18. 0 7
      esp/services/ws_ecl/ws_ecl_service.cpp
  19. 30 3
      esp/services/ws_ecl/ws_ecl_service.hpp
  20. 54 0
      esp/services/ws_esdlconfig/ws_esdlconfigservice.cpp
  21. 14 0
      esp/services/ws_esdlconfig/ws_esdlconfigservice.hpp
  22. 12 0
      esp/services/ws_fs/ws_fsBinding.hpp
  23. 27 14
      esp/services/ws_fs/ws_fsService.cpp
  24. 28 0
      esp/services/ws_fs/ws_fsService.hpp
  25. 14 0
      esp/services/ws_loggingservice/loggingservice.hpp
  26. 21 4
      esp/services/ws_packageprocess/ws_packageprocessService.hpp
  27. 51 38
      esp/services/ws_workunits/ws_workunitsHelpers.cpp
  28. 14 0
      esp/services/ws_workunits/ws_workunitsHelpers.hpp
  29. 32 2
      esp/services/ws_workunits/ws_workunitsService.hpp
  30. 13 4
      tools/hidl/hidlcomp.cpp

+ 21 - 0
esp/bindings/SOAP/Platform/soapbind.hpp

@@ -220,6 +220,27 @@ public:
     IEspContainer* queryContainer() { return m_container; }
     virtual int HandleSoapRequest(CHttpRequest* request, CHttpResponse* response);
 
+    virtual bool subscribeBindingToDali()
+    {
+        return this->getService()->subscribeServiceToDali();
+    }
+
+    virtual bool unsubscribeBindingFromDali()
+    {
+        return this->getService()->unsubscribeServiceFromDali();
+    }
+
+    virtual bool detachBindingFromDali()
+    {
+        return this->getService()->detachServiceFromDali();
+    }
+
+    virtual bool attachBindingToDali()
+    {
+        return this->getService()->attachServiceToDali();
+    }
+
+    virtual bool canDetachFromDali() {return true;}
 };
 
 void SetHTTPErrorStatus(int ErrorCode,CHttpResponse* response);

+ 6 - 1
esp/bindings/SOAP/Platform/soapservice.hpp

@@ -60,7 +60,12 @@ public:
    {
       return true;
    }
-   
+
+    bool subscribeServiceToDali() { return false; }
+    bool unsubscribeServiceFromDali() { return false; }
+    bool detachServiceFromDali() { return false; }
+    bool attachServiceToDali() { return false; }
+    bool canDetachFromDali() { return false; }
 };
 
 

+ 199 - 33
esp/platform/espcfg.cpp

@@ -121,28 +121,32 @@ int CSessionCleaner::run()
         int checkSessionTimeoutMillSeconds = checkSessionTimeoutSeconds * 60;
         while(!stopping)
         {
-            Owned<IRemoteConnection> conn = querySDS().connect(espSessionSDSPath.get(), myProcessSession(), RTM_LOCK_WRITE, SESSION_SDS_LOCK_TIMEOUT);
-            if (!conn)
-                throw MakeStringException(-1, "Failed to connect to %s.", PathSessionRoot);
+            if (!m_isDetached)
+            {
+                Owned<IRemoteConnection> conn = querySDS().connect(espSessionSDSPath.get(), myProcessSession(), RTM_LOCK_WRITE, SESSION_SDS_LOCK_TIMEOUT);
+                if (!conn)
+                    throw MakeStringException(-1, "Failed to connect to %s.", PathSessionRoot);
 
-            CDateTime now;
-            now.setNow();
-            time_t timeNow = now.getSimple();
+                CDateTime now;
+                now.setNow();
+                time_t timeNow = now.getSimple();
 
-            Owned<IPropertyTreeIterator> iter1 = conn->queryRoot()->getElements(PathSessionApplication);
-            ForEach(*iter1)
-            {
-                ICopyArrayOf<IPropertyTree> toRemove;
-                Owned<IPropertyTreeIterator> iter2 = iter1->query().getElements(xpath.str());
-                ForEach(*iter2)
-                {
-                    IPropertyTree& item = iter2->query();
-                    if (timeNow >= item.getPropInt64(PropSessionTimeoutAt, 0))
-                        toRemove.append(item);
-                }
-                ForEachItemIn(i, toRemove)
+                Owned<IPropertyTreeIterator> iter1 = conn->queryRoot()->getElements(PathSessionApplication);
+                ForEach(*iter1)
                 {
-                    iter1->query().removeTree(&toRemove.item(i));
+                    ICopyArrayOf<IPropertyTree> toRemove;
+                    Owned<IPropertyTreeIterator> iter2 = iter1->query().getElements(xpath.str());
+                    ForEach(*iter2)
+                    {
+                        IPropertyTree& item = iter2->query();
+                        if (timeNow >= item.getPropInt64(PropSessionTimeoutAt, 0))
+                            toRemove.append(item);
+                    }
+
+                    ForEachItemIn(i, toRemove)
+                    {
+                        iter1->query().removeTree(&toRemove.item(i));
+                    }
                 }
             }
             sem.wait(checkSessionTimeoutMillSeconds);
@@ -242,11 +246,8 @@ CEspConfig::CEspConfig(IProperties* inputs, IPropertyTree* envpt, IPropertyTree*
     hsami_=0;
     serverstatus=NULL;
     useDali=false;
-    
     if(inputs)
         m_inputs.setown(inputs);
-
-
     if(!envpt || !procpt)
         return;
 
@@ -273,22 +274,64 @@ CEspConfig::CEspConfig(IProperties* inputs, IPropertyTree* envpt, IPropertyTree*
     {
         DBGLOG("ESP process name [%s]", m_process.str());
 
-        IPropertyTreeIterator *pt_iter = NULL;
+        setIsDetachedFromDali(false);
+        setIsSubscribedToDali(true);
+        try
+        {
+            StringBuffer procDirectory;
+            m_cfg->getProp("@directory", procDirectory);
+            if (!procDirectory.isEmpty())
+            {
+                m_daliAttachStateFileName.setf("%s%c%s-AttachState.xml",procDirectory.str(), PATHSEPCHAR, m_process.str());
+
+                try
+                {
+                    Owned<IPTree> espProcAttachState = createPTreeFromXMLFile(m_daliAttachStateFileName);
+                    if (espProcAttachState)
+                    {
+                        setIsDetachedFromDali(!(espProcAttachState->getPropBool("@attached", true)));
+                        setIsSubscribedToDali(espProcAttachState->getPropBool("@subscribed", true));
+                    }
+                    else
+                    {
+                        ESPLOG(LogMin, "Could not load DALI Attach state file [%s] for ESP process [%s]", m_daliAttachStateFileName.str(), m_process.str());
+                    }
+                }
+                catch (...)
+                {
+                    ESPLOG(LogMin, "Could not load DALI Attach state file [%s] for ESP process [%s]", m_daliAttachStateFileName.str(), m_process.str());
+                }
+
+                saveAttachState();
+            }
+            else
+                ESPLOG(LogMin, "ESP Process [%s] configuration is missing '@directory' attribute, could not read AttachState", m_process.str());
+        }
+        catch (...)
+        {
+           ESPLOG(LogMin, "Could not load DALI Attach state file [%s] for ESP process [%s]", m_daliAttachStateFileName.str(), m_process.str());
+        }
+
+        if (isDetachedFromDali())
+            WARNLOG("ESP Process [%s] loading in DALI DETACHED state - Some ESP services do not load in detached state!",  m_process.str());
+
         StringBuffer daliservers;
         if (m_cfg->getProp("@daliServers", daliservers))
-            initDali(daliservers.str());
+            initDali(daliservers.str()); //won't init if detached
 
 #ifndef _DEBUG
         startPerformanceMonitor(m_cfg->getPropInt("@perfReportDelay", 60)*1000);
 #endif
 
+        IPropertyTreeIterator *pt_iter = NULL;
+        StringBuffer xpath;
+
         if (m_inputs->hasProp("SingleUserPass"))
         {
             StringBuffer plainesppass;
             StringBuffer encesppass;
             m_inputs->getProp("SingleUserPass", plainesppass);
             encrypt(encesppass, plainesppass.str());
-            StringBuffer xpath;
             xpath.setf("SecurityManagers/SecurityManager[@type=\"SingleUserSecurityManager\"]/SingleUserSecurityManager/");
             pt_iter = m_cfg->getElements(xpath.str());
             if (pt_iter!=NULL)
@@ -320,8 +363,7 @@ CEspConfig::CEspConfig(IProperties* inputs, IPropertyTree* envpt, IPropertyTree*
         m_cfg->getProp("@computer", m_computer);
 
         //get the local computer information:
-        StringBuffer xpath;
-        xpath.appendf("Hardware/Computer[@name=\"%s\"]", m_computer.str());
+        xpath.setf("Hardware/Computer[@name=\"%s\"]", m_computer.str());
 
         IPropertyTree *computer = m_envpt->queryPropTree(xpath.str());
         if (computer)
@@ -336,10 +378,10 @@ CEspConfig::CEspConfig(IProperties* inputs, IPropertyTree* envpt, IPropertyTree*
             }
             m_address.set(address.str(), (unsigned short) port);
         }
-      
+
         xpath.clear();
         xpath.append("EspService");
- 
+
         pt_iter = m_cfg->getElements(xpath.str());
 
         if (pt_iter!=NULL)
@@ -372,7 +414,7 @@ CEspConfig::CEspConfig(IProperties* inputs, IPropertyTree* envpt, IPropertyTree*
 
         xpath.clear();
         xpath.append("EspProtocol");
- 
+
         pt_iter = m_cfg->getElements(xpath.str());
 
         if (pt_iter!=NULL)
@@ -380,7 +422,7 @@ CEspConfig::CEspConfig(IProperties* inputs, IPropertyTree* envpt, IPropertyTree*
             IPropertyTree *ptree = NULL;
 
             pt_iter->first();
-    
+
 
             while(pt_iter->isValid())
             {
@@ -466,7 +508,8 @@ void CEspConfig::sendAlert(int severity, char const * descr, char const * subjec
 
 void CEspConfig::initDali(const char *servers)
 {
-    if (servers!=NULL && *servers!=0 && !daliClientActive())
+    CriticalBlock b(attachcrit);
+    if (servers!=nullptr && *servers!=0 && !daliClientActive() && !isDetachedFromDali())
     {
         DBGLOG("Initializing DALI client [servers = %s]", servers);
 
@@ -829,6 +872,15 @@ void CEspConfig::bindServer(IEspServer &server, IEspContainer &container)
     }
 }
 
+void CEspConfig::saveAttachState()
+{
+    StringBuffer espProcAttachState;
+    espProcAttachState.setf( "<ESPAttachState StateSaveTimems='%d' attached='%s' subscribed='%s'/>", msTick(), isDetachedFromDali() ? "0" : "1", isSubscribedToDali() ? "1" : "0");
+
+    DBGLOG("ESP Process [%s] State to be stored: '%s'", m_process.str(), espProcAttachState.str());
+    Owned<IPropertyTree> serviceESDLDef = createPTreeFromXMLString(espProcAttachState.str(), ipt_caseInsensitive);
+    saveXML(m_daliAttachStateFileName.str(), serviceESDLDef);
+}
 
 void CEspConfig::unloadBindings()
 {
@@ -836,7 +888,7 @@ void CEspConfig::unloadBindings()
     while (iter!=m_bindings.end())
     {
         binding_cfg *bcfg = *iter;
-        
+
         if(bcfg!=NULL)
         {
             bcfg->protocol.clear();
@@ -929,3 +981,117 @@ bool CEspConfig::checkESPCache()
     return espCacheAvailable;
 }
 
+bool CEspConfig::reSubscribeESPToDali()
+{
+    list<binding_cfg*>::iterator iter = m_bindings.begin();
+    while (iter!=m_bindings.end())
+    {
+        binding_cfg& bindingConfig = **iter;
+        if (bindingConfig.bind)
+        {
+            ESPLOG(LogMin, "Requesting binding '%s' to subscribe to DALI notifications", bindingConfig.name.str());
+            bindingConfig.bind->subscribeBindingToDali();
+        }
+        iter++;
+    }
+    setIsSubscribedToDali(true);
+    return true;
+}
+
+bool CEspConfig::unsubscribeESPFromDali()
+{
+    list<binding_cfg*>::iterator iter = m_bindings.begin();
+    while (iter!=m_bindings.end())
+    {
+        binding_cfg& bindingConfig = **iter;
+        if (bindingConfig.bind)
+        {
+            ESPLOG(LogMin, "Requesting binding '%s' to un-subscribe from DALI notifications", bindingConfig.name.str());
+            bindingConfig.bind->unsubscribeBindingFromDali();
+        }
+        iter++;
+    }
+    setIsSubscribedToDali(false);
+    return true;
+}
+
+bool CEspConfig::detachESPFromDali(bool force)
+{
+    CriticalBlock b(attachcrit);
+    if (!isDetachedFromDali())
+    {
+        if(!force)
+        {
+            if (!canAllBindingsDetachFromDali())
+                return false;
+        }
+
+        if (!unsubscribeESPFromDali())
+            return false;
+
+        list<binding_cfg*>::iterator iter = m_bindings.begin();
+        while (iter!=m_bindings.end())
+        {
+            binding_cfg& xcfg = **iter;
+            ESPLOG(LogMin, "Detach ESP From DALI: requesting binding: '%s' to detach...", xcfg.name.str());
+            if (xcfg.bind)
+            {
+                xcfg.bind->detachBindingFromDali();
+            }
+
+            iter++;
+        }
+        setIsDetachedFromDali(true);
+        closedownClientProcess();
+        saveAttachState();
+    }
+    return true;
+}
+
+bool CEspConfig::attachESPToDali()
+{
+    bool success = true;
+
+    CriticalBlock b(attachcrit);
+    if (isDetachedFromDali())
+    {
+        setIsDetachedFromDali(false);
+        StringBuffer daliservers;
+        if (m_cfg->getProp("@daliServers", daliservers))
+            initDali(daliservers.str());
+
+        list<binding_cfg*>::iterator iter = m_bindings.begin();
+        while (iter!=m_bindings.end())
+        {
+            binding_cfg& xcfg = **iter;
+            ESPLOG(LogMin, "Attach ESP to DALI: requesting binding: '%s' to attach...", xcfg.name.str());
+            if (xcfg.bind)
+            {
+                map<string, srv_cfg*>::iterator sit = m_services.find(xcfg.service_name.str());
+                if(sit == m_services.end())
+                    ESPLOG(LogMin, "Warning: Service %s not found for the binding", xcfg.service_name.str());
+                else
+                    ((*sit).second->srv)->attachServiceToDali();
+            }
+            iter++;
+        }
+
+        reSubscribeESPToDali();
+
+        saveAttachState();
+    }
+    return success;
+}
+
+bool CEspConfig::canAllBindingsDetachFromDali()
+{
+    list<binding_cfg*>::iterator iter = m_bindings.begin();
+    while (iter!=m_bindings.end())
+    {
+        binding_cfg& xcfg = **iter;
+        if (!xcfg.bind->canDetachFromDali())
+            return false;
+        iter++;
+    }
+    return true;
+}

+ 54 - 4
esp/platform/espcfg.ipp

@@ -103,15 +103,16 @@ struct esp_option
 
 class CSessionCleaner : public Thread
 {
-    bool       stopping = false;;
+    bool       stopping = false;
     Semaphore  sem;
 
     StringAttr espSessionSDSPath;
     int        checkSessionTimeoutSeconds; //the duration to clean timed out sesssions
+    bool       m_isDetached;
 
 public:
     CSessionCleaner(const char* _espSessionSDSPath, int _checkSessionTimeoutSeconds) : Thread("CSessionCleaner"),
-        espSessionSDSPath(_espSessionSDSPath), checkSessionTimeoutSeconds(_checkSessionTimeoutSeconds) { }
+        espSessionSDSPath(_espSessionSDSPath), checkSessionTimeoutSeconds(_checkSessionTimeoutSeconds) , m_isDetached(false){ }
 
     virtual ~CSessionCleaner()
     {
@@ -120,9 +121,13 @@ public:
         join();
     }
 
+    void setIsDetached(bool isDetached) {m_isDetached = isDetached;}
+
     virtual int run();
 };
 
+static CriticalSection attachcrit;
+
 class CEspConfig : public CInterface, implements IInterface 
 {
 private:
@@ -145,6 +150,9 @@ private:
     HINSTANCE hsami_;
     CSDSServerStatus *serverstatus;
     bool useDali;
+    bool m_detachedFromDali;
+    bool m_subscribedToDali;
+    StringBuffer m_daliAttachStateFileName;
 
 private:
     CEspConfig(CEspConfig &);
@@ -153,7 +161,34 @@ public:
     IMPLEMENT_IINTERFACE;
 
     esp_option m_options;
-    
+
+    void setIsDetachedFromDali(bool isDetached)
+    {
+        CriticalBlock b(attachcrit);
+        if (m_detachedFromDali != isDetached)
+        {
+            m_detachedFromDali = isDetached;
+            if (m_sessionCleaner)
+                m_sessionCleaner->setIsDetached(m_detachedFromDali);
+        }
+    }
+
+    bool isDetachedFromDali() const
+    {
+        CriticalBlock b(attachcrit);
+        return m_detachedFromDali;
+    }
+
+    bool isSubscribedToDali() const
+    {
+        return m_subscribedToDali;
+    }
+
+    void setIsSubscribedToDali(bool issubscribed)
+    {
+        m_subscribedToDali = issubscribed;
+    }
+
     bool usesDali(){return useDali;}
     bool checkDali()
     {
@@ -202,7 +237,16 @@ public:
     void loadAll()
     {
         DBGLOG("loadServices");
-        loadServices();
+        try
+        {
+            loadServices();
+        }
+        catch(IException* ie)
+        {
+            if (isDetachedFromDali())
+                ERRLOG("ERROR: Could not load ESP services while DETACHED FROM DALI! Re-attach ESP process and retry.");
+            throw(ie);
+        }
         loadProtocols();
         loadBindings();
 
@@ -210,6 +254,11 @@ public:
             throw MakeStringException(-1, "Failed in checking ESP cache service using %s", m_cfg->queryProp("@espCacheInitString"));
     }
 
+    bool reSubscribeESPToDali();
+    bool unsubscribeESPFromDali();
+    bool detachESPFromDali(bool force);
+    bool attachESPToDali();
+    bool canAllBindingsDetachFromDali();
     bool checkESPCache();
     IEspPlugin* getPlugin(const char* name);
 
@@ -225,6 +274,7 @@ public:
     void unloadBindings();
     void unloadServices();
     void unloadProtocols();
+    void saveAttachState();
 
     void clear()
     {

+ 37 - 5
esp/platform/espp.hpp

@@ -63,7 +63,7 @@ private:
     Mutex abortMutex;
     bool m_SEHMappingEnabled;
     CEspConfig* m_config;
-    
+
 public:
     IMPLEMENT_IINTERFACE;
 
@@ -104,10 +104,13 @@ public:
                 bool daliOk;
                 {
                     synchronized sync(abortMutex);
-                    daliOk=config.checkDali();
+                    if (!config.isDetachedFromDali())
+                        daliOk=config.checkDali();
+                    //else
+                    //    daliOk=true;
                 }
-                if (daliOk)
-                    m_waitForExit.wait(1000);
+                if (config.isDetachedFromDali() || daliOk)
+                    m_waitForExit.wait(1000); //if detached, should we wait longer?
                 else
                 {
                     DBGLOG("Exiting ESP -- Lost DALI connection!");
@@ -256,8 +259,37 @@ public:
         m_SEHMappingEnabled = mappingEnabled;
     }
     virtual void sendSnmpMessage(const char* msg) { throwUnexpected(); }
-};
 
+    virtual bool reSubscribeESPToDali()
+    {
+        return m_config->reSubscribeESPToDali();
+    }
+
+    virtual bool unsubscribeESPFromDali()
+    {
+        return m_config->unsubscribeESPFromDali();
+    }
+
+    virtual bool detachESPFromDali(bool force)
+    {
+        return m_config->detachESPFromDali(force);
+    }
+
+    virtual bool attachESPToDali()
+    {
+        return m_config->attachESPToDali();
+    }
+
+    virtual bool isAttachedToDali()
+    {
+      return !m_config->isDetachedFromDali();
+    }
+
+    virtual bool isSubscribedToDali()
+    {
+        return m_config->isSubscribedToDali();
+    }
+};
 
 class CEspAbortHandler : public CInterface,
    implements IAbortHandler

+ 15 - 0
esp/scm/esp.ecm

@@ -228,6 +228,12 @@ interface IEspContainer : extends IInterface
     virtual void setFrameTitle(const char* title) = 0;
     virtual const char* getFrameTitle() = 0;
     virtual void sendSnmpMessage(const char* msg) = 0;
+    virtual bool reSubscribeESPToDali() = 0;
+    virtual bool unsubscribeESPFromDali() = 0;
+    virtual bool detachESPFromDali(bool force) = 0;
+    virtual bool attachESPToDali() = 0;
+    virtual bool isAttachedToDali() = 0;
+    virtual bool isSubscribedToDali() = 0;
 };
 
 interface IEspRpcBinding;
@@ -292,6 +298,10 @@ SCMinterface IEspService(IInterface)
     const char * getServiceType();
     bool init(const char *name, const char *type, IPropertyTree *cfg, const char *process);
     void setContainer(IEspContainer *container);
+    bool subscribeServiceToDali();
+    bool unsubscribeServiceFromDali();
+    bool detachServiceFromDali();
+    bool attachServiceToDali();
 };
 
 
@@ -331,6 +341,11 @@ SCMinterface IEspRpcBinding(IInterface)
     void setXslProcessor(IInterface *xslp);
     IEspContainer* queryContainer();
     unsigned getCacheMethodCount();
+    bool subscribeBindingToDali();
+    bool unsubscribeBindingFromDali();
+    bool detachBindingFromDali();
+    bool attachBindingToDali();
+    bool canDetachFromDali();
 };
 
 SCMinterface IEspUriMap(IInterface)

+ 47 - 1
esp/scm/ws_espcontrol.ecm

@@ -93,13 +93,59 @@ ESPresponse [exceptions_inline, nil_remove, http_encode(0)] SetSessionTimeoutRes
     string Message;
 };
 
-ESPservice [auth_feature("NONE"), version("1.01"), default_client_version("1.01"), exceptions_inline("./smc_xslt/exceptions.xslt")] WSESPControl
+ESPrequest [nil_remove] DisableDaliSubscriptionsRequest
+{
+};
+
+ESPresponse [exceptions_inline, nil_remove, http_encode(0)] DisableDaliSubscriptionsResponse
+{
+    int Status;
+    string Message;
+};
+
+ESPrequest [nil_remove] EnableDaliSubscriptionsRequest
+{
+};
+
+ESPresponse [exceptions_inline, nil_remove, http_encode(0)] EnableDaliSubscriptionsResponse
+{
+    int Status;
+    string Message;
+};
+
+ESPrequest [nil_remove] DetachFromDaliRequest
+{
+    bool Force(false);
+};
+
+ESPresponse [exceptions_inline, nil_remove, http_encode(0)] DetachFromDaliResponse
+{
+    int Status;
+    string Message;
+    bool Forced;
+};
+
+ESPrequest [nil_remove] AttachToDaliRequest
+{
+};
+
+ESPresponse [exceptions_inline, nil_remove, http_encode(0)] AttachToDaliResponse
+{
+    int Status;
+    string Message;
+};
+
+ESPservice [auth_feature("NONE"), version("1.02"), default_client_version("1.01"), exceptions_inline("./smc_xslt/exceptions.xslt")] WSESPControl
 {
     ESPmethod SetLogging(SetLoggingRequest, SetLoggingResponse);
     ESPmethod [min_ver("1.01")] SessionQuery(SessionQueryRequest, SessionQueryResponse);
     ESPmethod [min_ver("1.01")] SessionInfo(SessionInfoRequest, SessionInfoResponse);
     ESPmethod [min_ver("1.01")] CleanSession(CleanSessionRequest, CleanSessionResponse);
     ESPmethod [min_ver("1.01")] SetSessionTimeout(SetSessionTimeoutRequest, SetSessionTimeoutResponse);
+    ESPmethod [min_ver("1.02")] DisableDaliSubscriptions(DisableDaliSubscriptionsRequest, DisableDaliSubscriptionsResponse);
+    ESPmethod [min_ver("1.02")] EnableDaliSubscriptions(EnableDaliSubscriptionsRequest, EnableDaliSubscriptionsResponse);
+    ESPmethod [min_ver("1.02")] DetachFromDali(DetachFromDaliRequest, DetachFromDaliResponse);
+    ESPmethod [min_ver("1.02")] AttachToDali(AttachToDaliRequest, AttachToDaliResponse);
 };
 
 SCMexportdef(WSESPControl);

+ 12 - 6
esp/services/esdl_svc_engine/esdl_binding.cpp

@@ -75,7 +75,7 @@ IPropertyTree * fetchESDLBindingFromStateFile(const char *process, const char *b
         if (esdlState)
         {
             const char * restoredBindingTS = esdlState->queryProp("@StateSaveTimems");
-            fprintf(stdout, "ESDL State restored from local state store: %s created epoch: %s", bindingName, restoredBindingTS);
+            ESPLOG(LogNormal, "ESDL State restored from local state store: %s created epoch: %s", bindingName, restoredBindingTS);
             return esdlState->getPropTree("EsdlBinding/Binding");
         }
         else
@@ -929,6 +929,8 @@ EsdlServiceImpl::~EsdlServiceImpl()
 
 EsdlBindingImpl::EsdlBindingImpl()
 {
+        m_pESDLService = nullptr;
+        m_isAttached = true;
 }
 
 EsdlBindingImpl::EsdlBindingImpl(IPropertyTree* cfg, const char *binding,  const char *process) : CHttpSoapBinding(cfg, binding, process)
@@ -937,6 +939,9 @@ EsdlBindingImpl::EsdlBindingImpl(IPropertyTree* cfg, const char *binding,  const
     m_bindingName.set(binding);
     m_processName.set(process);
 
+    m_pESDLService = nullptr;
+    m_isAttached = true;
+
     try
     {
         char currentDirectory[_MAX_DIR];
@@ -986,13 +991,15 @@ EsdlBindingImpl::EsdlBindingImpl(IPropertyTree* cfg, const char *binding,  const
 
 IPropertyTree* EsdlBindingImpl::fetchESDLBinding(const char *process, const char *bindingName, const char * stateFileName)
 {
-    Owned<IPropertyTree> esdlBinding = m_pCentralStore->fetchBinding(process, bindingName);
-    if (!esdlBinding)
+    if(isAttached())
+    {
+        Owned<IPropertyTree> esdlBinding = m_pCentralStore->fetchBinding(process, bindingName);
+        return esdlBinding.getClear();
+    }
+    else
     {
         return fetchESDLBindingFromStateFile(process, bindingName, stateFileName);
     }
-
-    return esdlBinding.getClear();
 }
 
 /* if the target ESDL binding contains an ESDL service definition matching this espServiceName, load it.
@@ -1082,7 +1089,6 @@ void EsdlBindingImpl::saveDESDLState()
 {
     try
     {
-        //rodrigo is this the best way to fetch the ESDL def for a given service???
         Owned<IEsdlDefObjectIterator> it = m_esdl->getDependencies(m_espServiceName.str(), "", 0, NULL, 0);
 
         StringBuffer serviceESXDL;

+ 243 - 3
esp/services/esdl_svc_engine/esdl_binding.hpp

@@ -166,6 +166,11 @@ public:
     virtual void handleFinalRequest(IEspContext &context, Owned<IPropertyTree> &tgtcfg, Owned<IPropertyTree> &tgtctx, IEsdlDefService &srvdef, IEsdlDefMethod &mthdef, const char *ns, StringBuffer& req, StringBuffer &out, bool isroxie, bool isproxy);
     void getSoapBody(StringBuffer& out,StringBuffer& soapresp);
     void getSoapError(StringBuffer& out,StringBuffer& soapresp,const char *,const char *);
+
+    virtual bool unsubscribeServiceFromDali() override {return true;}
+    virtual bool subscribeServiceToDali() override {return false;}
+    virtual bool attachServiceToDali() override {return false;}
+    virtual bool detachServiceFromDali() override {return false;}
 };
 
 #define DEFAULT_ESDLBINDING_URN_BASE "urn:hpccsystems:ws"
@@ -173,6 +178,140 @@ public:
 class EsdlBindingImpl : public CHttpSoapBinding, implements IEsdlListener
 {
 private:
+/*
+
+    //==========================================================================================
+    // the following class implements notification handler for subscription to dali for environment
+    // updates by other clients.
+    //==========================================================================================
+    class CESDLBindingSubscription : public CInterface, implements ISDSSubscription
+    {
+    private :
+        CriticalSection daliSubscriptionCritSec;
+        SubscriptionId sub_id;
+        EsdlBindingImpl * thisBinding;
+
+    public:
+        CESDLBindingSubscription(EsdlBindingImpl * binding)
+        {
+            thisBinding = binding;
+            sub_id = 0;
+            subscribe();
+        }
+
+        virtual ~CESDLBindingSubscription()
+        {
+            unsubscribe();
+        }
+
+        void unsubscribe()
+        {
+            CriticalBlock b(daliSubscriptionCritSec);
+            try
+            {
+                if (sub_id)
+                {
+                    querySDS().unsubscribe(sub_id);
+                    sub_id = 0;
+                }
+            }
+            catch (IException *E)
+            {
+                E->Release();
+            }
+            sub_id = 0;
+        }
+
+        void subscribe()
+        {
+            VStringBuffer fullBindingPath("/ESDL/Bindings/Binding[@id=\'%s.%s\']",thisBinding->m_processName.get(),thisBinding->m_bindingName.get());
+            try
+            {
+                CriticalBlock b(daliSubscriptionCritSec);
+                if (!sub_id)
+                    sub_id = querySDS().subscribe(fullBindingPath.str(), *this, true);
+            }
+            catch (IException *E)
+            {
+                ESPLOG(LogMin, "ESDL Binding %s.%s failed to subscribe to DALI (%s)", thisBinding->m_processName.get(),thisBinding->m_bindingName.get(), fullBindingPath.str());
+                E->Release();
+            }
+        }
+
+        bool isSubscribed()
+        {
+            CriticalBlock b(daliSubscriptionCritSec);
+            return sub_id > 0;
+        }
+
+        IMPLEMENT_IINTERFACE;
+        void notify(SubscriptionId id, const char *xpath, SDSNotifyFlags flags, unsigned valueLen=0, const void *valueData=NULL);
+    };
+
+    class CESDLDefinitionSubscription : public CInterface, implements ISDSSubscription
+    {
+    private :
+        CriticalSection daliSubscriptionCritSec;
+        SubscriptionId sub_id;
+        EsdlBindingImpl * thisBinding;
+
+    public:
+        CESDLDefinitionSubscription(EsdlBindingImpl * binding)
+        {
+            thisBinding = binding;
+            subscribe();
+        }
+
+        virtual ~CESDLDefinitionSubscription()
+        {
+            unsubscribe();
+        }
+
+        void unsubscribe()
+        {
+            CriticalBlock b(daliSubscriptionCritSec);
+            try
+            {
+                if (sub_id)
+                {
+                    ESPLOG(LogNormal,"ESDL Binding %s is UN subscribing from /ESDL/Bindings/Binding dali changes", thisBinding->m_bindingName.get());
+                    querySDS().unsubscribe(sub_id);
+                    sub_id = 0;
+                }
+            }
+            catch (IException *E)
+            {
+                E->Release();
+            }
+            sub_id = 0;
+        }
+
+        void subscribe()
+        {
+            //for some reason subscriptions based on xpaths with attributes don't seem to work correctly
+            //fullBindingPath.set("/ESDL/Bindings/Binding[@EspBinding=\'WsAccurint\'][@EspProcess=\'myesp\']");
+            CriticalBlock b(daliSubscriptionCritSec);
+            try
+            {
+                sub_id = querySDS().subscribe(ESDL_DEF_PATH, *this, true);
+            }
+            catch (IException *E)
+            {
+                E->Release();
+            }
+        }
+
+        bool isSubscribed()
+        {
+            CriticalBlock b(daliSubscriptionCritSec);
+            return sub_id > 0;
+        }
+
+        IMPLEMENT_IINTERFACE;
+        void notify(SubscriptionId id, const char *xpath, SDSNotifyFlags flags, unsigned valueLen=0, const void *valueData=NULL);
+    };
+
+*/
     Owned<IPropertyTree>                    m_bndCfg;
     Owned<IPropertyTree>                    m_esdlBndCfg;
 
@@ -187,9 +326,10 @@ private:
     Owned<IEsdlSubscription>                m_pSubscription;
     Owned<IEsdlStore>                       m_pCentralStore;
     CriticalSection                         configurationLoadCritSec;
+    CriticalSection                         detachCritSec;
     StringBuffer                            m_esdlStateFilesLocation;
-    MapStringTo<SecAccessFlags>             m_accessmap;
     StringBuffer                            m_staticNamespace;
+    bool                                    m_isAttached;
 
     virtual void clearDESDLState()
     {
@@ -200,12 +340,11 @@ private:
         if (m_pESDLService)
             m_pESDLService->clearDESDLState();
         //prob need to un-initesdlservinfo as well.
-        DBGLOG("Warning binding %s.%s is being un-loaded!", m_processName.get(), m_bindingName.get());
+        ESPLOG(LogNormal, "Warning binding %s.%s is being un-loaded!", m_processName.get(), m_bindingName.get());
     }
 
 public:
     EsdlServiceImpl * m_pESDLService;
-    //CIArrayOf<EsdlServiceImpl> m_esdlServices;
     IMPLEMENT_IINTERFACE;
 
     EsdlBindingImpl();
@@ -271,6 +410,107 @@ public:
     virtual unsigned getCacheMethodCount(){return 0;}
     virtual void onNotify(EsdlNotifyData* data);
 
+    virtual bool subscribeBindingToDali() override
+    {
+        ESPLOG(LogNormal, "Binding '%s.%s' is subscribing to Dali for ESDL changes...", m_processName.get(), m_bindingName.get() );
+        if (m_pSubscription)
+        {
+            ESPLOG(LogNormal, "ESDL Binding %s is subscribing to all /ESDL/Bindings/Binding dali changes", this->m_bindingName.get());
+            m_pSubscription->subscribe();
+        }
+
+        //if (m_pDefinitionSubscription)
+        //{
+        //    ESPLOG(LogNormal, "ESDL Binding %s is subscribing to all /ESDL/Bindings/Definition dali changes", this->m_bindingName.get());
+        //    m_pDefinitionSubscription->subscribe();
+        //}
+
+        ESPLOG(LogNormal, "Requesting reload of ESDL %s.%s binding...", m_processName.get(), m_bindingName.get() );
+        reloadBindingFromCentralStore(m_bindingName.get(), m_processName.get());
+
+        setIsAttached(true);
+        return true;
+    }
+
+    virtual bool unsubscribeBindingFromDali() override
+    {
+        if(m_pSubscription)
+            m_pSubscription->unsubscribe();
+        //pSubscription->subscribe/unsubscribe only affects bindings, not definitions
+        // if(m_pDefinitionSubscription)
+        //     m_pDefinitionSubscription->unsubscribe();
+
+         setIsAttached(false);
+         return true;
+    }
+
+    bool detachBindingFromDali() override
+    {
+        unsubscribeBindingFromDali();
+        return false;
+    }
+
+    virtual bool canDetachFromDali() override
+    {
+        return true;
+    }
+
+    virtual bool attach()
+    {
+        if (isAttached())
+        {
+            ESPLOG(LogNormal, "Binding %s.%s is already attached to Dali for ESDL changes...", m_processName.get(), m_bindingName.get() );
+            return true;
+        }
+
+        if (m_pSubscription)
+        {
+            ESPLOG(LogNormal, "Binding %s.%s is subscribing to Dali for ESDL changes...", m_processName.get(), m_bindingName.get() );
+            m_pSubscription->subscribe();
+
+            ESPLOG(LogNormal, "Requesting reload of ESDL %s.%s binding...", m_processName.get(), m_bindingName.get() );
+            reloadBindingFromCentralStore(m_bindingName.get(), m_processName.get());
+            setIsAttached(true);
+
+            /* rodrigo
+
+                    if (m_pDefinitionSubscription)
+                    {
+                        ESPLOG(LogNormal, "ESDL Binding %s is subscribing to all /ESDL/Bindings/Definition dali changes", this->m_bindingName.get());
+                        m_pDefinitionSubscription->subscribe();
+                    }
+            */
+            return true;
+        }
+        else
+            ESPLOG(LogNormal, "Could not subscribe binding '%s.%s' to Dali for ESDL changes...", m_processName.get(), m_bindingName.get() );
+
+        return false;
+    };
+
+    virtual bool detach()
+    {
+        if (m_pSubscription)
+            m_pSubscription->unsubscribe();
+        else
+            ESPLOG(LogNormal, "Could not unsubscribe binding '%s.%s' from Dali for ESDL changes...", m_processName.get(), m_bindingName.get() );
+
+        m_isAttached = false;
+        return true;
+    }
+
+    void setIsAttached(bool isattached)
+    {
+      CriticalBlock b(detachCritSec);
+      m_isAttached = isattached;
+    }
+
+    bool isAttached()
+    {
+        CriticalBlock b(detachCritSec);
+        return m_isAttached;
+    }
+
 private:
     int onGetRoxieBuilder(CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method);
     int onRoxieRequest(CHttpRequest* request, CHttpResponse* response, const char *  method);

+ 1 - 1
esp/services/esdl_svc_engine/esdl_store.cpp

@@ -806,7 +806,6 @@ public:
         mListener->onNotify(dataptr.get());
     }
 
-private:
     void subscribe()
     {
         VStringBuffer fullBindingPath("/ESDL/Bindings/Binding[@id=\'%s.%s\']",mProcessName.str(), mBindingName.str());
@@ -842,6 +841,7 @@ private:
         else
             DBGLOG("Esdl definition subscription already exists.");
     }
+private:
 };
 
 Owned<IEsdlStore> gEsdlCentralStore;

+ 1 - 0
esp/services/esdl_svc_engine/esdl_store.hpp

@@ -70,6 +70,7 @@ interface IEsdlListener
 interface IEsdlSubscription : public IInterface
 {
     virtual void unsubscribe() = 0;
+    virtual void subscribe() = 0;
 };
 
 esdl_engine_decl IEsdlStore* createEsdlCentralStore();

+ 0 - 2
esp/services/esdl_svc_engine/esdl_svc_engine.hpp

@@ -44,8 +44,6 @@ public:
 class CEsdlSvcEngineSoapBindingEx : public EsdlBindingImpl
 {
 public:
-
-public:
     IMPLEMENT_IINTERFACE;
 
     CEsdlSvcEngineSoapBindingEx();

+ 1 - 0
esp/services/espcontrol/CMakeLists.txt

@@ -47,6 +47,7 @@ include_directories (
          ./../../bindings
          ./../../bindings/SOAP/xpp
          ./../../smc/SMCLib
+         ./../../../common/environment
     )
 
 ADD_DEFINITIONS( -D_USRDLL )

+ 1 - 1
esp/services/espcontrol/ws_espcontrolplugin.cpp

@@ -47,7 +47,7 @@ ESP_FACTORY IEspRpcBinding * esp_binding_factory(const char *name, const char* t
 {
     if (strcmp(type, "ws_espcontrolSoapBinding")==0)
     {
-        CWSESPControlSoapBinding* binding = new CWSESPControlSoapBinding(cfg, name, process);
+        CWSESPControlSoapBinding* binding = new CWSESPControlSoapBindingEx(cfg, name, process);
         return binding;
     }
 

+ 96 - 0
esp/services/espcontrol/ws_espcontrolservice.cpp

@@ -75,6 +75,102 @@ void CWSESPControlEx::init(IPropertyTree *cfg, const char *process, const char *
     }
 }
 
+bool CWSESPControlEx::handleDaliAttachmentRequest(bool attach, bool force, StringBuffer & message)
+{
+    bool success = true;
+    CEspServer * thisESPServer = static_cast<CEspServer *>(m_container);
+    if (thisESPServer)
+    {
+        if (attach)
+        {
+            message.setf("Request to ATTACH ESP Process '%s' to Dali has been issued - ", espProcess.get());
+            success = thisESPServer->attachESPToDali();
+        }
+        else
+        {
+            message.setf("Request to DETACH ESP Process '%s' from Dali has been issued - ", espProcess.get());
+            success = thisESPServer->detachESPFromDali(force);
+        }
+
+        if (success)
+            message.append("and success reported.");
+        else
+            message.append("but failure reported.");
+    }
+    else
+    {
+        message.append("ESP/DALI Attachment request could not be issued due to internal error");
+        success = false;
+    }
+
+    return success;
+}
+
+bool CWSESPControlEx::onDetachFromDali(IEspContext& context, IEspDetachFromDaliRequest& req, IEspDetachFromDaliResponse& resp)
+{
+    StringBuffer message;
+    bool status = handleDaliAttachmentRequest(false, req.getForce_isNull() ? false : req.getForce(), message);
+    resp.setMessage(message.str());
+    resp.setStatus(status ? 0 : -1);
+    return status;
+}
+
+bool CWSESPControlEx::onAttachToDali(IEspContext& context, IEspAttachToDaliRequest& req, IEspAttachToDaliResponse& resp)
+{
+    StringBuffer message;
+    bool status = handleDaliAttachmentRequest(true, false, message);
+    resp.setMessage(message.str());
+    resp.setStatus(status ? 0 : -1);
+    return status;
+}
+
+bool CWSESPControlEx::handleDaliSubscriptionRequest(bool enable, StringBuffer & message)
+{
+    bool success = true;
+    CEspServer * thisESPServer = static_cast<CEspServer *>(m_container);
+    if (thisESPServer)
+    {
+        if (enable)
+        {
+            message.setf("Request to enable all DALI subscriptions on ESP Process '%s' issued - ", this->espProcess.get());
+            success = thisESPServer->reSubscribeESPToDali();
+        }
+        else
+        {
+            message.setf("Request to cancel all DALI subscriptions on ESP Process '%s' issued - ", this->espProcess.get());
+            success = thisESPServer->unsubscribeESPFromDali();
+        }
+
+        if (success)
+            message.append("and success reported.");
+        else
+            message.append("but failure reported.");
+    }
+    else
+    {
+        message.append("Dali subscription request could not be issued due to internal error");
+        success = false;
+    }
+
+    return success;
+}
+bool CWSESPControlEx::onDisableDaliSubscriptions(IEspContext& context, IEspDisableDaliSubscriptionsRequest& req, IEspDisableDaliSubscriptionsResponse& resp)
+{
+    StringBuffer message;
+    bool status = handleDaliSubscriptionRequest(false, message);
+    resp.setMessage(message.str());
+    resp.setStatus(status ? 0 : -1);
+    return status;
+}
+
+bool CWSESPControlEx::onEnableDaliSubscriptions(IEspContext& context, IEspEnableDaliSubscriptionsRequest& req, IEspEnableDaliSubscriptionsResponse& resp)
+{
+    StringBuffer message;
+    bool status = handleDaliSubscriptionRequest(true, message);
+    resp.setMessage(message.str());
+    resp.setStatus(status ? 0 : -1);
+    return status;
+}
 
 bool CWSESPControlEx::onSetLogging(IEspContext& context, IEspSetLoggingRequest& req, IEspSetLoggingResponse& resp)
 {

+ 23 - 0
esp/services/espcontrol/ws_espcontrolservice.hpp

@@ -19,6 +19,22 @@
 #define _ESPWIZ_ws_espcontrol_HPP__
 
 #include "ws_espcontrol_esp.ipp"
+#include "espp.hpp"
+#include "environment.hpp"
+
+
+class CWSESPControlSoapBindingEx : public CWSESPControlSoapBinding
+{
+public:
+
+    CWSESPControlSoapBindingEx(http_soap_log_level level=hsl_none) : CWSESPControlSoapBinding(level)
+    {
+    }
+
+    CWSESPControlSoapBindingEx(IPropertyTree* cfg, const char *bindname, const char *procname, http_soap_log_level level=hsl_none) : CWSESPControlSoapBinding(cfg, bindname, procname, level)
+    {
+    }
+};
 
 class CWSESPControlEx : public CWSESPControl
 {
@@ -35,6 +51,9 @@ class CWSESPControlEx : public CWSESPControl
     void cleanSessions(bool allSessions, const char* _id, const char* _userID, const char* _fromIP);
     void setSessionTimeout(int timeoutMinutes, IPropertyTree& session);
 
+    bool handleDaliAttachmentRequest(bool attach, bool force, StringBuffer & message);
+    bool handleDaliSubscriptionRequest(bool enable, StringBuffer & message);
+
 public:
     IMPLEMENT_IINTERFACE;
 
@@ -49,6 +68,10 @@ public:
     virtual bool onSessionInfo(IEspContext& context, IEspSessionInfoRequest& req, IEspSessionInfoResponse& resp);
     virtual bool onCleanSession(IEspContext& context, IEspCleanSessionRequest& req, IEspCleanSessionResponse& resp);
     virtual bool onSetSessionTimeout(IEspContext& context, IEspSetSessionTimeoutRequest& req, IEspSetSessionTimeoutResponse& resp);
+    virtual bool onDisableDaliSubscriptions(IEspContext& context, IEspDisableDaliSubscriptionsRequest& req, IEspDisableDaliSubscriptionsResponse& resp);
+    virtual bool onEnableDaliSubscriptions(IEspContext& context, IEspEnableDaliSubscriptionsRequest& req, IEspEnableDaliSubscriptionsResponse& resp);
+    virtual bool onDetachFromDali(IEspContext& context, IEspDetachFromDaliRequest& req, IEspDetachFromDaliResponse& resp);
+    virtual bool onAttachToDali(IEspContext& context, IEspAttachToDaliRequest& req, IEspAttachToDaliResponse& resp);
 };
 
 #endif //_ESPWIZ_ws_espcontrol_HPP__

+ 23 - 1
esp/services/ws_dfu/ws_dfuService.hpp

@@ -96,12 +96,34 @@ public:
 
 class CWsDfuSoapBindingEx : public CWsDfuSoapBinding
 {
+private:
+    bool m_bIsAttached;
 public:
-    CWsDfuSoapBindingEx(IPropertyTree *cfg, const char *name, const char *process, http_soap_log_level llevel=hsl_none) : CWsDfuSoapBinding(cfg, name, process, llevel){}
+    CWsDfuSoapBindingEx(IPropertyTree *cfg, const char *name, const char *process, http_soap_log_level llevel=hsl_none) : CWsDfuSoapBinding(cfg, name, process, llevel)
+    {
+        m_bIsAttached = true;
+    }
 
     virtual void getNavigationData(IEspContext &context, IPropertyTree & data)
     {
     }
+    virtual bool canDetachFromDali() override
+    {
+      return false;
+    }
+    virtual bool subscribeBindingToDali() override
+    {
+        return true;
+    }
+    virtual bool unsubscribeBindingFromDali() override
+    {
+        return false;
+    }
+
+    bool isAttachedToDali()
+    {
+      return m_bIsAttached;
+    }
 };
 
 

+ 0 - 7
esp/services/ws_ecl/ws_ecl_service.cpp

@@ -331,13 +331,6 @@ void CWsEclBinding::getNavigationData(IEspContext &context, IPropertyTree & data
     ensureNavDynFolder(data, "Targets", "Targets", "root=true", NULL);
 }
 
-IPropertyTree * getQueryRegistries()
-{
-    Owned<IRemoteConnection> conn = querySDS().connect("/QuerySets/", myProcessSession(), RTM_LOCK_READ|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
-    return conn->getRoot();
-}
-
-
 void CWsEclBinding::getRootNavigationFolders(IEspContext &context, IPropertyTree & data)
 {
     DBGLOG("CScrubbedXmlBinding::getNavigationData");

+ 30 - 3
esp/services/ws_ecl/ws_ecl_service.hpp

@@ -115,9 +115,7 @@ public:
 public:
     IMPLEMENT_IINTERFACE;
 
-    CWsEclService()
-    {
-    }
+    CWsEclService() {}
     ~CWsEclService();
 
     virtual const char * getServiceType(){return "ws_ecl";}
@@ -152,6 +150,20 @@ public:
             return callerIdHttpHeader;
         return "HPCC-Caller-Id"; //HPCC default
     }
+
+    bool unsubscribeServiceFromDali() override {return true;}
+    bool subscribeServiceToDali() override {return false;}
+    bool detachServiceFromDali() override
+    {
+        setIsDetached(true);
+        return true;
+    }
+    bool attachServiceToDali() override {return false;}
+
+    void setIsDetached(bool isDetached) { m_isDetached = isDetached;}
+    bool isDetached() { return m_isDetached;}
+private:
+    bool m_isDetached;
 };
 
 class CWsEclBinding : public CHttpSoapBinding
@@ -183,6 +195,21 @@ public:
     virtual int getQualifiedNames(IEspContext& ctx, MethodInfoArray & methods){return 0;}
     virtual unsigned getCacheMethodCount(){return 0;}
 
+    virtual bool canDetachFromDali() override
+    {
+        return false;
+    }
+
+    virtual bool subscribeBindingToDali() override
+    {
+        return true;
+    }
+
+    virtual bool unsubscribeBindingFromDali() override
+    {
+        return false;
+    }
+
     void getNavigationData(IEspContext &context, IPropertyTree & data);
     void getRootNavigationFolders(IEspContext &context, IPropertyTree & data);
     void getDynNavData(IEspContext &context, IProperties *params, IPropertyTree & data);

+ 54 - 0
esp/services/ws_esdlconfig/ws_esdlconfigservice.cpp

@@ -173,6 +173,9 @@ bool CWsESDLConfigEx::onPublishESDLDefinition(IEspContext &context, IEspPublishE
 {
     try
     {
+        if (m_isDetachedFromDali)
+          throw MakeStringException(-1, "Cannot publish ESDL Service definition. ESP is currently detached from DALI.");
+
         if (!context.validateFeatureAccess(FEATURE_URL, SecAccess_Write, false))
             throw MakeStringException(ECLWATCH_ROXIE_QUERY_ACCESS_DENIED, "Failed to Publish ESDL Service definition. Permission denied.");
 
@@ -355,6 +358,9 @@ bool CWsESDLConfigEx::onPublishESDLBinding(IEspContext &context, IEspPublishESDL
 {
     try
     {
+        if (m_isDetachedFromDali)
+            throw MakeStringException(-1, "Cannot publish ESDL Binding. ESP is currently detached from DALI.");
+
         if (!context.validateFeatureAccess(FEATURE_URL, SecAccess_Write, false))
             throw MakeStringException(ECLWATCH_ROXIE_QUERY_ACCESS_DENIED, "Failed to Configure ESDL Service. Permission denied.");
 
@@ -609,6 +615,9 @@ bool CWsESDLConfigEx::onConfigureESDLBindingMethod(IEspContext &context, IEspCon
     int success = 0;
     try
     {
+        if (m_isDetachedFromDali)
+            throw MakeStringException(-1, "Cannot Configure ESDL Binding Method. ESP is currently detached from DALI.");
+
         if (!context.validateFeatureAccess(FEATURE_URL, SecAccess_Write, false))
             throw MakeStringException(ECLWATCH_ROXIE_QUERY_ACCESS_DENIED, "Failed to Configure ESDL Method. Permission denied.");
 
@@ -889,6 +898,9 @@ bool CWsESDLConfigEx::onGetESDLBinding(IEspContext &context, IEspGetESDLBindingR
 {
     try
     {
+        if (m_isDetachedFromDali)
+            throw MakeStringException(-1, "Cannot fetch ESDL Binding. ESP is currently detached from DALI.");
+
         if (!context.validateFeatureAccess(FEATURE_URL, SecAccess_Read, false))
             throw MakeStringException(ECLWATCH_ROXIE_QUERY_ACCESS_DENIED, "Failed to fetch ESDL Service Configuration. Permission denied.");
 
@@ -1101,6 +1113,9 @@ bool CWsESDLConfigEx::onEcho(IEspContext &context, IEspEchoRequest &req, IEspEch
 bool CWsESDLConfigEx::onDeleteESDLDefinition(IEspContext &context, IEspDeleteESDLDefinitionRequest &req, IEspDeleteESDLRegistryEntryResponse &resp)
 {
     resp.updateStatus().setCode(-1);
+    if (m_isDetachedFromDali)
+        throw MakeStringException(-1, "Cannot delete ESDL Definition. ESP is currently detached from DALI.");
+
     if (!context.validateFeatureAccess(FEATURE_URL, SecAccess_Full, false))
         throw MakeStringException(ECLWATCH_ROXIE_QUERY_ACCESS_DENIED, "Failed to DELETE ESDL entry. Permission denied.");
 
@@ -1142,6 +1157,9 @@ bool CWsESDLConfigEx::onDeleteESDLBinding(IEspContext &context, IEspDeleteESDLBi
 {
     resp.updateStatus().setCode(-1);
 
+    if (m_isDetachedFromDali)
+        throw MakeStringException(-1, "Cannot fetch ESDL Binding. ESP is currently detached from DALI.");
+
     if (!context.validateFeatureAccess(FEATURE_URL, SecAccess_Full, false))
         throw MakeStringException(ECLWATCH_ROXIE_QUERY_ACCESS_DENIED, "Failed to DELETE ESDL entry. Permission denied.");
 
@@ -1179,6 +1197,9 @@ bool CWsESDLConfigEx::onDeleteESDLBinding(IEspContext &context, IEspDeleteESDLBi
 
 bool CWsESDLConfigEx::onGetESDLDefinition(IEspContext &context, IEspGetESDLDefinitionRequest&req, IEspGetESDLDefinitionResponse &resp)
 {
+     if (m_isDetachedFromDali)
+         throw MakeStringException(-1, "Cannot fetch ESDL Definition. ESP is currently detached from DALI.");
+
     if (!context.validateFeatureAccess(FEATURE_URL, SecAccess_Read, false))
         throw MakeStringException(ECLWATCH_ROXIE_QUERY_ACCESS_DENIED, "Failed to fetch ESDL definition. Permission denied.");
 
@@ -1302,6 +1323,20 @@ bool CWsESDLConfigEx::onListESDLDefinitions(IEspContext &context, IEspListESDLDe
     Owned<IPropertyTree> esdlDefinitions = m_esdlStore->getDefinitions();
     if(esdlDefinitions.get() == nullptr)
         return false;
+/*RODRIGO
+    if (m_isDetachedFromDali)
+        throw MakeStringException(-1, "Cannot list ESDL Definitions. ESP is currently detached from DALI.");
+
+    Owned<IRemoteConnection> conn = querySDS().connect(ESDL_DEFS_ROOT_PATH, myProcessSession(), RTM_LOCK_READ, SDS_LOCK_TIMEOUT_DESDL);
+    if (!conn)
+       throw MakeStringException(-1, "Unable to connect to ESDL Service definition information in dali '%s'", ESDL_DEFS_ROOT_PATH);
+
+    conn->close(false); //release lock right away
+
+    IPropertyTree * esdlDefinitions = conn->queryRoot();
+    if (!esdlDefinitions)
+       throw MakeStringException(-1, "Unable to open ESDL Service definition information in dali '%s'", ESDL_DEFS_ROOT_PATH);
+*/
     Owned<IPropertyTreeIterator> iter = esdlDefinitions->getElements("Definition");
     IArrayOf<IEspESDLDefinition> list;
     ForEach(*iter)
@@ -1320,6 +1355,9 @@ bool CWsESDLConfigEx::onListESDLDefinitions(IEspContext &context, IEspListESDLDe
 
 bool CWsESDLConfigEx::onListDESDLEspBindings(IEspContext &context, IEspListDESDLEspBindingsReq&req, IEspListDESDLEspBindingsResp &resp)
 {
+    if (m_isDetachedFromDali)
+        throw MakeStringException(-1, "Cannot list ESDL ESP Bindings. ESP is currently detached from DALI.");
+
     bool includeESDLBindings = req.getIncludeESDLBindingInfo();
     IArrayOf<IEspESPServerEx> allESPServers;
     IArrayOf<IEspESPServerEx> desdlESPServers;
@@ -1455,9 +1493,25 @@ bool CWsESDLConfigEx::onListDESDLEspBindings(IEspContext &context, IEspListDESDL
 
 bool CWsESDLConfigEx::onListESDLBindings(IEspContext &context, IEspListESDLBindingsRequest&req, IEspListESDLBindingsResponse &resp)
 {
+
     Owned<IPropertyTree> esdlBindings = m_esdlStore->getBindings();
     if(esdlBindings.get() == nullptr)
         return false;
+/*Rodrigo
+    if (m_isDetachedFromDali)
+        throw MakeStringException(-1, "Cannot list ESDL Bindings. ESP is currently detached from DALI.");
+
+    Owned<IRemoteConnection> conn = querySDS().connect(ESDL_BINDINGS_ROOT_PATH, myProcessSession(), RTM_LOCK_READ, SDS_LOCK_TIMEOUT_DESDL);
+    if (!conn)
+       throw MakeStringException(-1, "Unable to connect to ESDL Service definition information in dali '%s'", ESDL_DEFS_ROOT_PATH);
+
+    conn->close(false); //release lock right away
+
+    IPropertyTree * esdlBindings = conn->queryRoot();
+    if (!esdlBindings)
+       throw MakeStringException(-1, "Unable to open ESDL Service definition information in dali '%s'", ESDL_DEFS_ROOT_PATH);
+
+rodrigo*/
     Owned<IPropertyTreeIterator> iter = esdlBindings->getElements("Binding");
 
     IArrayOf<IEspESDLBinding> list;

+ 14 - 0
esp/services/ws_esdlconfig/ws_esdlconfigservice.hpp

@@ -54,6 +54,20 @@ public:
     bool onListESDLDefinitions(IEspContext &context, IEspListESDLDefinitionsRequest&req, IEspListESDLDefinitionsResponse &resp);
     bool onListESDLBindings(IEspContext &context, IEspListESDLBindingsRequest&req, IEspListESDLBindingsResponse &resp);
     bool onListDESDLEspBindings(IEspContext &context, IEspListDESDLEspBindingsReq&req, IEspListDESDLEspBindingsResp &resp);
+
+    bool attachServiceToDali() override
+    {
+        m_isDetachedFromDali = false;
+        return true;
+    }
+
+    bool detachServiceFromDali() override
+    {
+        m_isDetachedFromDali = true;
+        return true;
+    }
+private:
+    bool m_isDetachedFromDali;
 };
 
 #endif //_ESPWIZ_WsESDLConfig_HPP__

+ 12 - 0
esp/services/ws_fs/ws_fsBinding.hpp

@@ -71,6 +71,18 @@ public:
 
     int onGetInstantQuery(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method);
     int onFinishUpload(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method, StringArray& fileNames, StringArray& files, IMultiException *me);
+    virtual bool canDetachFromDali() override
+    {
+        return false;
+    }
+    virtual bool subscribeBindingToDali() override
+    {
+        return true;
+    }
+    virtual bool unsubscribeBindingFromDali() override
+    {
+        return false;
+    }
 
 private:
     IPropertyTree* createPTreeForXslt(double clientVersion, const char* method, const char* dfuwuid);

+ 27 - 14
esp/services/ws_fs/ws_fsService.cpp

@@ -49,12 +49,20 @@
 void SetResp(StringBuffer &resp, IConstDFUWorkUnit * wu, bool array);
 int Schedule::run()
 {
-    try
+    PROGLOG("DfuWorkunit WUSchedule Thread started.");
+    while(!stopping)
     {
-        PROGLOG("DfuWorkunit WUSchedule Thread started.");
-        while(!stopping)
+        unsigned int waitTimeMillies = 1000*60;
+        if (!detached)
         {
+            try
             {
+                if (waitTimeMillies == (unsigned)-1)
+                {
+                    PROGLOG("WS_FS WUSchedule Thread Re-started.");
+                    waitTimeMillies = 1000*60;
+                }
+
                 Owned<IDFUWorkUnitFactory> factory = getDFUWorkUnitFactory();
                 Owned<IConstDFUWorkUnitIterator> itr = factory->getWorkUnitsByState(DFUstate_scheduled);
                 itr->first();
@@ -82,18 +90,23 @@ int Schedule::run()
                     itr->next();
                 }
             }
-            semSchedule.wait(1000*60);
+            catch(IException *e)
+            {
+                StringBuffer msg;
+                ERRLOG("Exception %d:%s in WS_FS Schedule::run", e->errorCode(), e->errorMessage(msg).str());
+                e->Release();
+            }
+            catch(...)
+            {
+                ERRLOG("Unknown exception in WS_FS Schedule::run");
+            }
         }
-    }
-    catch(IException *e)
-    {
-        StringBuffer msg;
-        ERRLOG("Exception %d:%s in WS_FS Schedule::run", e->errorCode(), e->errorMessage(msg).str());
-        e->Release();
-    }
-    catch(...)
-    {
-        ERRLOG("Unknown exception in WS_FS Schedule::run");
+        else
+        {
+            WARNLOG("Detached from DALI, WS_FS schedule interrupted");
+            waitTimeMillies = (unsigned)-1;
+        }
+        semSchedule.wait(waitTimeMillies);
     }
     return 0;
 }

+ 28 - 0
esp/services/ws_fs/ws_fsService.hpp

@@ -30,10 +30,13 @@ class Schedule : public Thread
     bool stopping;
     Semaphore semSchedule;
     IEspContainer* m_container;
+    bool detached;
 public:
     Schedule()
     {
         stopping = false;
+        detached = false;
+        m_container = nullptr;
     };
     ~Schedule()
     {
@@ -46,7 +49,19 @@ public:
     virtual void setContainer(IEspContainer * container)
     {
         m_container = container;
+        if (m_container)
+          setDetachedState(!m_container->isAttachedToDali());
     }
+
+    void setDetachedState(bool detached_)
+    {
+        if (detached != detached_)
+        {
+            detached = detached_;
+            semSchedule.signal();
+        }
+    }
+
 };
 
 class CFileSprayEx : public CFileSpray
@@ -74,6 +89,19 @@ public:
         return true;
     }
 
+    bool attachServiceToDali() override
+    {
+        m_sched.setDetachedState(false);
+        return true;
+    }
+
+    bool detachServiceFromDali() override
+    {
+        m_sched.setDetachedState(true);
+        return true;
+    }
+
+
     virtual bool onDFUWUSearch(IEspContext &context, IEspDFUWUSearchRequest & req, IEspDFUWUSearchResponse & resp);
     virtual bool onGetDFUWorkunits(IEspContext &context, IEspGetDFUWorkunits &req, IEspGetDFUWorkunitsResponse &resp);
     virtual bool onGetDFUWorkunit(IEspContext &context, IEspGetDFUWorkunit &req, IEspGetDFUWorkunitResponse &resp);

+ 14 - 0
esp/services/ws_loggingservice/loggingservice.hpp

@@ -23,6 +23,20 @@
 #include "logthread.hpp"
 #include "loggingagentbase.hpp"
 
+
+class CWsLoggingServiceSoapBindingEx : public CWsLoggingServiceSoapBinding
+{
+public:
+
+    CWsLoggingServiceSoapBindingEx(http_soap_log_level level=hsl_none) : CWsLoggingServiceSoapBinding(level)
+    {
+    }
+
+    CWsLoggingServiceSoapBindingEx(IPropertyTree* cfg, const char *bindname, const char *procname, http_soap_log_level level=hsl_none) : CWsLoggingServiceSoapBinding(cfg, bindname, procname, level)
+    {
+    }
+};
+
 class CWsLoggingServiceEx : public CWsLoggingService
 {
     typedef std::vector<IUpdateLogThread*> LOGGING_AGENTTHREADS;

+ 21 - 4
esp/services/ws_packageprocess/ws_packageprocessService.hpp

@@ -57,16 +57,21 @@ public:
             dirty |= PMAS_RELOAD_PACKAGE_SET;
     }
 
-    virtual void subscribe()
+    virtual bool subscribe()
     {
         CriticalBlock b(crit);
-        pmChange = querySDS().subscribe("PackageMaps", *this, true);
-        psChange = querySDS().subscribe("PackageSets", *this, true);
+        if (!pmChange)
+            pmChange = querySDS().subscribe("PackageMaps", *this, true);
+        if(!psChange)
+            psChange = querySDS().subscribe("PackageSets", *this, true);
+
+        return pmChange != 0 && psChange != 0;
     }
 
-    virtual void unsubscribe()
+    virtual bool unsubscribe()
     {
         CriticalBlock b(crit);
+        bool success = true;
         try
         {
             if (pmChange)
@@ -77,9 +82,11 @@ public:
         catch (IException *E)
         {
             E->Release();
+            success = false;
         }
         pmChange = 0;
         psChange = 0;
+        return success;
     }
 
     IPropertyTree *getTree()
@@ -165,6 +172,16 @@ public:
     virtual bool onGetPartFromPackageMap(IEspContext &context, IEspGetPartFromPackageMapRequest &req, IEspGetPartFromPackageMapResponse &resp);
     virtual bool onRemovePartFromPackageMap(IEspContext &context, IEspRemovePartFromPackageMapRequest &req, IEspRemovePartFromPackageMapResponse &resp);
 
+    virtual bool unsubscribeServiceFromDali() override
+    {
+        return packageMapAndSet.unsubscribe();
+    }
+
+    virtual bool subscribeServiceToDali() override
+    {
+        return packageMapAndSet.subscribe();
+    }
+
     PackageMapAndSet packageMapAndSet;
 };
 

+ 51 - 38
esp/services/ws_workunits/ws_workunitsHelpers.cpp

@@ -2942,56 +2942,69 @@ bool addToQueryString(StringBuffer &queryString, const char *name, const char *v
 
 int WUSchedule::run()
 {
-    try
+    PROGLOG("ECLWorkunit WUSchedule Thread started.");
+    unsigned int waitTimeMillies = 1000*60;
+    while(!stopping)
     {
-        PROGLOG("ECLWorkunit WUSchedule Thread started.");
-        while(!stopping)
+        if (!detached)
         {
-            Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
-            Owned<IConstWorkUnitIterator> itr = factory->getScheduledWorkUnits();
-            if (itr)
+            try
             {
-                ForEach(*itr)
+                if (waitTimeMillies == (unsigned)-1)
+                {
+                    PROGLOG("ECLWorkunit WUSchedule Thread Re-started.");
+                    waitTimeMillies = 1000*60;
+                }
+
+                Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
+                Owned<IConstWorkUnitIterator> itr = factory->getScheduledWorkUnits();
+                if (itr)
                 {
-                    try
+                    ForEach(*itr)
                     {
-                        IConstWorkUnitInfo & cw = itr->query();
-                        if (factory->isAborting(cw.queryWuid()))
+                        try
                         {
-                            WorkunitUpdate wu(factory->updateWorkUnit(cw.queryWuid()));
-                            wu->setState(WUStateAborted);
-                            continue;
+                            IConstWorkUnitInfo & cw = itr->query();
+                            if (factory->isAborting(cw.queryWuid()))
+                            {
+                                WorkunitUpdate wu(factory->updateWorkUnit(cw.queryWuid()));
+                                wu->setState(WUStateAborted);
+                                continue;
+                            }
+                            WsWuDateTime dt, now;
+                            now.setNow();
+                            cw.getTimeScheduled(dt);
+                            if (now.compare(dt)>=0)
+                                runWorkUnit(cw.queryWuid(), cw.queryClusterName());
+                        }
+                        catch(IException *e)
+                        {
+                            StringBuffer msg;
+                            ERRLOG("Exception %d:%s in WsWorkunits Schedule::run while processing WU", e->errorCode(), e->errorMessage(msg).str());
+                            e->Release();
                         }
-                        WsWuDateTime dt, now;
-                        now.setNow();
-                        cw.getTimeScheduled(dt);
-                        if (now.compare(dt)>=0)
-                            runWorkUnit(cw.queryWuid(), cw.queryClusterName());
-                    }
-                    catch(IException *e)
-                    {
-                        StringBuffer msg;
-                        ERRLOG("Exception %d:%s in WsWorkunits Schedule::run", e->errorCode(), e->errorMessage(msg).str());
-                        e->Release();
                     }
                 }
             }
-            semSchedule.wait(1000*60);
+            catch(IException *e)
+            {
+                StringBuffer msg;
+                ERRLOG("Exception %d:%s in WsWorkunits Schedule::run while fetching scheduled WUs from DALI", e->errorCode(), e->errorMessage(msg).str());
+                e->Release();
+            }
+            catch(...)
+            {
+                ERRLOG("Unknown exception in WsWorkunits Schedule::run while fetching scheduled WUs from DALI");
+            }
+        }
+        else
+        {
+            WARNLOG("Detached from DALI, WSWorkuinits schedule interrupted");
+            waitTimeMillies = (unsigned)-1;
         }
-    }
-    catch(IException *e)
-    {
-        StringBuffer msg;
-        ERRLOG("Exception %d:%s in WsWorkunits Schedule::run", e->errorCode(), e->errorMessage(msg).str());
-        e->Release();
-    }
-    catch(...)
-    {
-        ERRLOG("Unknown exception in WsWorkunits Schedule::run");
-    }
 
-    if (m_container)
-        m_container->exitESP();
+        semSchedule.wait(waitTimeMillies);
+    }
     return 0;
 }
 

+ 14 - 0
esp/services/ws_workunits/ws_workunitsHelpers.hpp

@@ -460,11 +460,14 @@ class WUSchedule : public Thread
     bool stopping;
     Semaphore semSchedule;
     IEspContainer* m_container;
+    bool detached;
 
 public:
     WUSchedule()
     {
         stopping = false;
+        detached = false;
+        m_container = nullptr;
     }
     ~WUSchedule()
     {
@@ -477,6 +480,17 @@ public:
     virtual void setContainer(IEspContainer * container)
     {
         m_container = container;
+        if (m_container)
+            setDetachedState(!m_container->isAttachedToDali());
+    }
+
+    void setDetachedState(bool detached_)
+    {
+        if (detached != detached_)
+        {
+            detached = detached_;
+            semSchedule.signal();
+        }
     }
 };
 

+ 32 - 2
esp/services/ws_workunits/ws_workunitsService.hpp

@@ -91,9 +91,10 @@ public:
             dirty |= UFO_RELOAD_TARGETS_CHANGED_PMID;
         PROGLOG("QueryFilesInUse.notify() called: <%d>", dirty);
     }
-    virtual void subscribe()
+    virtual bool subscribe()
     {
         CriticalBlock b(crit);
+        bool success = true;
         try
         {
             qsChange = querySDS().subscribe("QuerySets", *this, true);
@@ -103,13 +104,16 @@ public:
         }
         catch (IException *E)
         {
+            success = false;
             //TBD failure to subscribe implies dali is down...
             E->Release();
         }
+        return success && qsChange != 0 && pmChange != 0 && psChange != 0;
     }
-    virtual void unsubscribe()
+    virtual bool unsubscribe()
     {
         CriticalBlock b(crit);
+        bool success = true;
         try
         {
             if (qsChange)
@@ -121,12 +125,14 @@ public:
         }
         catch (IException *E)
         {
+            success = false;
             E->Release();
         }
         qsChange = 0;
         pmChange = 0;
         psChange = 0;
         PROGLOG("QueryFilesInUse.unsubscribe() called");
+        return success && qsChange == 0 && pmChange == 0 && psChange == 0;
     }
 
     void abort()
@@ -170,6 +176,7 @@ public:
         filesInUse.abort();
         clusterQueryStatePool.clear();
     };
+
     virtual void init(IPropertyTree *cfg, const char *process, const char *service);
     virtual void setContainer(IEspContainer * container)
     {
@@ -275,6 +282,29 @@ public:
     bool onWUListArchiveFiles(IEspContext &context, IEspWUListArchiveFilesRequest &req, IEspWUListArchiveFilesResponse &resp);
     bool onWUGetArchiveFile(IEspContext &context, IEspWUGetArchiveFileRequest &req, IEspWUGetArchiveFileResponse &resp);
     bool onWUEclDefinitionAction(IEspContext &context, IEspWUEclDefinitionActionRequest &req, IEspWUEclDefinitionActionResponse &resp);
+
+    bool unsubscribeServiceFromDali() override
+    {
+        return filesInUse.unsubscribe();
+    }
+
+    bool subscribeServiceToDali() override
+    {
+        return filesInUse.subscribe();
+    }
+
+    bool attachServiceToDali() override
+    {
+        m_sched.setDetachedState(false);
+        return true;
+    }
+
+    bool detachServiceFromDali() override
+    {
+        m_sched.setDetachedState(true);
+        return true;
+    }
+
 private:
     void addProcessLogfile(Owned<IConstWorkUnit> &cwu, WsWuInfo &winfo, const char * process, const char* path);
     void addThorSlaveLogfile(Owned<IConstWorkUnit> &cwu,WsWuInfo& winfo, const char* path);

+ 13 - 4
tools/hidl/hidlcomp.cpp

@@ -5871,7 +5871,7 @@ void EspServInfo::write_esp_binding()
     }
     outs("\treturn 0;\n");
     outs("}\n");
-    
+
     //method ==> getQualifiedNames
     outf("\nint C%sSoapBinding::getQualifiedNames(IEspContext& ctx, MethodInfoArray & methods)\n", name_);
     outs("{\n");
@@ -6174,7 +6174,6 @@ void EspServInfo::write_esp_binding()
             }
 
             writeAccessMap(servicefeatureurl.str(),name_, 3);
-
             if (bHandleExceptions)
             {
                 outf("\t\t\tsource.setf(\"%s::%%s()\", method);\n", name_);
@@ -6186,6 +6185,7 @@ void EspServInfo::write_esp_binding()
 
                 if (servicefeatureurl.length() != 0)
                     outf("\t\t\t\tif(accessmap.ordinality()>0)\n\t\t\t\t\tonFeaturesAuthorize(context, accessmap, \"%s\", \"%s\");\n", name_, mthi->getName());
+
                 if (mthi->getMetaInt("do_not_log",0))
                     outf("\t\t\t\tcontext.queryRequestParameters()->setProp(\"do_not_log\",1);\n");
                 outf("\t\t\t\tiserv->on%s(context, *esp_request.get(), *resp);\n", mthi->getName());
@@ -6383,14 +6383,21 @@ void EspServInfo::write_esp_service_ipp()
     outs("public:\n");
     outs("\tIMPLEMENT_IINTERFACE;\n\n");
     outf("\tC%s(){}\n\tvirtual ~C%s(){}\n", name_, name_);
-    
+
     outs("\tvirtual void init(IPropertyTree *cfg, const char *process, const char *service)\n\t{\n\t}\n");
     outs("\tvirtual bool init(const char * service, const char * type, IPropertyTree * cfg, const char * process)\n\t{\n\t\treturn true;\n\t}\n");
     outs("\tvirtual void setContainer(IEspContainer *c)\n\t{\n\t\tm_container = c;\n\t}\n");
     outs("\tvirtual IEspContainer *queryContainer()\n\t{\n\t\treturn m_container;\n\t}\n");
-    
+
     outf("\tvirtual const char* getServiceType(){return \"%s\";}\n\n", name_);
 
+    outf("\tvirtual bool unsubscribeServiceFromDali(){return false;}\n\n");
+    outf("\tvirtual bool subscribeServiceToDali(){return false;}\n\n");
+    outf("\tvirtual bool detachServiceFromDali(){return false;}\n\n");
+    outf("\tvirtual bool attachServiceToDali(){return false;}\n\n");
+
+    outf("\tvirtual bool canDetachFromDali(){return false;}\n\n");
+
     EspMethodInfo *mthi;
     for (mthi=methods;mthi!=NULL;mthi=mthi->next)
     {
@@ -6429,12 +6436,14 @@ void EspServInfo::write_esp_client_ipp()
     outs("\tStringBuffer soap_url;\n");
     //dom
     
+
     outs("\tStringBuffer soap_userid;\n");
     outs("\tStringBuffer soap_password;\n");
     outs("\tStringBuffer soap_realm;\n");
     outs("\tStringBuffer soap_action;\n");
     outs("\tlong soap_reqid = 0;\n");
 
+
     outs("\npublic:\n");
     outs("\tIMPLEMENT_IINTERFACE;\n\n");