public MainWindow()
        {
            InitializeComponent();

            // Start the CDEngine communication channels
            StartCDEngineHost();

            TheBaseEngine.WaitForEnginesStartedAsync().ContinueWith(t =>
            {
                try
                {
                    // Express our interest in the EngineName used for the chat, so that a subscription to the cloud is established even though the engine has not matching plug-in on this node
                    TheCDEngines.RegisterNewMiniRelay(strChatEngine);

                    // Intercept any messages sent to the chat engine
                    var chatEngine = TheThingRegistry.GetBaseEngine(strChatEngine);
                    chatEngine.RegisterEvent(eEngineEvents.IncomingMessage, HandleMessage);

                    // Intercept any messages sent to the ContentService (we will use that as the originating thing, and acknowledge messages will come back to it)
                    TheThingRegistry.GetBaseEngineAsThing(eEngineName.ThingService).RegisterEvent(eThingEvents.IncomingMessage, HandleMessage);
                }
                catch
                {
                }
            });
        }
        public void IncomingMessageEventTest()
        {
            var    contentServiceThing = TheThingRegistry.GetBaseEngineAsThing(eEngineName.ContentService);
            var    contentServiceEng   = contentServiceThing.GetBaseEngine();
            int    numberOfMessages    = 0;
            string txt     = "TEST_TXT";
            string payload = "TestPayload";
            TSM    testMsg = new TSM(eEngineName.ContentService, txt, payload);

            contentServiceEng?.RegisterEvent(eEngineEvents.IncomingMessage, (t, o) =>
            {
                numberOfMessages++;
                if (o is TheProcessMessage msg)
                {
                    Assert.AreEqual(msg.Message.TXT, txt);
                    Assert.AreEqual(msg.Message.PLS, payload);
                    Assert.AreEqual(msg.Message.ENG, contentServiceEng.GetEngineName());
                    testMsg.PLS = "TestPayload2";
                    //Assert.AreNotEqual(msg.Message.PLS, testMsg.PLS); // This fails
                }
                if (t is ICDEThing thing)
                {
                    Assert.AreEqual(thing.GetBaseThing().cdeMID, contentServiceThing.cdeMID);
                }
            });
            TheCommCore.PublishCentral(testMsg, true);
            TheCommonUtils.SleepOneEye(5000, 1000);
            Assert.AreEqual(numberOfMessages, 1);
        }
        private void RemoveTrigger(TheRule tRule, bool DoForce)
        {
            if (TheCDEngines.MyThingEngine == null || !TheBaseAssets.MasterSwitch)
            {
                return;
            }
            if (tRule.IsRuleActive || DoForce)
            {
                if (TheThingRegistry.HasThingsWithFunc(MyBaseEngine.GetEngineName(), s => s.cdeMID != tRule.GetBaseThing().cdeMID&&
                                                       TheThing.GetSafePropertyString(s, "TriggerObject") == tRule.TriggerObject && TheThing.GetSafePropertyBool(s, "IsRuleActive")))
                {
                    return;
                }
                switch (tRule.TriggerObjectType)
                {
                default:
                    TheThing tTriggerThing = TheThingRegistry.GetThingByMID("*", TheCommonUtils.CGuid(tRule.TriggerObject));
                    if (tTriggerThing != null)
                    {
                        cdeP tProp = tTriggerThing.GetProperty(tRule.TriggerProperty);
                        if (tProp != null)
                        {
                            tProp.UnregisterEvent(eThingEvents.PropertyChanged, sinkRuleAction);
                        }
                    }
                    break;

                case "CDE_ENGINE":
                    TheThing tBase = TheThingRegistry.GetBaseEngineAsThing(tRule.TriggerObject);
                    if (tBase != null)
                    {
                        tBase.UnregisterEvent(eEngineEvents.IncomingMessage, sinkRuleIncoming);
                    }
                    break;

                case "CDE_EVENTFIRED":
                    TheThing tBaseE = TheThingRegistry.GetThingByID("*", tRule.TriggerObject);
                    if (tBaseE != null)
                    {
                        tBaseE.UnregisterEvent(tRule.TriggerProperty, sinkRuleThingEvent);
                    }
                    break;
                }
                tRule.IsRuleActive  = false;
                tRule.IsRuleRunning = false;
                tRule.IsRuleWaiting = true;
            }
        }
        internal void CheckOnHealthTimer(long tTimerVal)
        {
            if (mDisableCollection)
            {
                return;
            }
            if ((tTimerVal % HealthUpdateCycle) != 0)
            {
                return;
            }
            if (TheCommonUtils.cdeIsLocked(lockCheckTimer))
            {
                return;
            }
            lock (lockCheckTimer)
            {
                if (!AreCounterInit)
                {
                    return;
                }
                TheBaseAssets.MySYSLOG.WriteToLog(8005, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM("jcHealth", "Enter CheckOnHealthTimer", eMsgLevel.l7_HostDebugMessage));
                TheDiagnostics.SetThreadName("C-MyComputer-HealthTimer", true);
                if (!IsEnergyRegistered)
                {
                    TheThing tThing = TheThingRegistry.GetBaseEngineAsThing("CDMyEnergy.TheCDMyEnergyEngine");
                    if (tThing != null)
                    {
                        IsEnergyRegistered = true;
                        tThing.RegisterEvent(eEngineEvents.MessageProcessed, sinkEnergyFound);
                    }
                }
                SendHealthInfo(Guid.Empty);

                if (MyServiceHealthDataStore != null)
                {
                    TheBaseAssets.MySYSLOG.WriteToLog(8006, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM("jcHealth", "Storing HealthInfo", eMsgLevel.l7_HostDebugMessage, MyHealthData.cdeUptime.ToString()));
                    if (!MyServiceHealthDataStore.IsReady)
                    {
                        MyServiceHealthDataStore.InitializeStore(false);
                        TheBaseAssets.MySYSLOG.WriteToLog(8006, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM("jcHealth", "Storing HealthInfo- Initializing store", eMsgLevel.l4_Message));
                    }
                    MyServiceHealthDataStore.AddAnItem(TheThingStore.CloneFromTheThing(MyHealthData, true));
                }

                TheBaseAssets.MySYSLOG.WriteToLog(8006, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM("jcHealth", "Leave CheckOnHealthTimer", eMsgLevel.l7_HostDebugMessage));
            }
        }
Exemple #5
0
        private TheThing CreateThingForTest(TheEngineDeviceTypeInfo deviceType, out TestInfo testInfo)
        {
            if (!configs.TryGetValue($"{deviceType.EngineName}/{deviceType.DeviceType}", out testInfo))
            {
                WriteLine($"{deviceType}: No config found. Using default.");
                // No test config found: just create the instance without any configuration
                testInfo = new TestInfo
                {
                    MinimumBrowseTags = 0,
                    MaximumBrowseTags = 100,
                    Config            = new TheThing.TheThingConfiguration
                    {
                        ThingIdentity = new TheThingIdentity
                        {
                            EngineName = deviceType.EngineName,
                            DeviceType = deviceType.DeviceType,
                        },
                        ConfigurationValues = new List <TheThing.TheConfigurationProperty>
                        {
                        }
                    }
                };
            }

            var config = testInfo.Config;

            if (string.IsNullOrEmpty(config.ThingIdentity?.EngineName))
            {
                config.ThingIdentity = new TheThingIdentity
                {
                    EngineName = deviceType.EngineName,
                    DeviceType = deviceType.DeviceType,
                };
                var addressValue = config.ConfigurationValues.FirstOrDefault(c => c.Name == "Address");
                if (addressValue != null)
                {
                    config.ThingIdentity.ID = TheCommonUtils.CStr(addressValue);
                }
            }

            var owner     = TheThingRegistry.GetBaseEngineAsThing(eEngineName.ContentService);
            var testThing = TheThing.CreateThingFromConfigurationAsync(owner, config).Result;

            Assert.IsNotNull(testThing, $"Error creating thing for {config.ThingIdentity.EngineName} / {config.ThingIdentity.DeviceType}");
            return(testThing);
        }
        public void IncomingMessage2EventTest()
        {
            var    contentServiceThing = TheThingRegistry.GetBaseEngineAsThing(eEngineName.ContentService);
            var    contentServiceEng   = contentServiceThing.GetBaseEngine();
            int    numberOfMessages    = 0;
            string txt     = "CDE_GET_SERVICEINFO";
            string payload = "CHNL";
            TSM    testMsg = new TSM(eEngineName.ContentService, txt, payload);

            contentServiceThing?.RegisterEvent(eEngineEvents.IncomingEngineMessage, (t, o) =>
            {
                // Two messages should be received here - The first intercepts the CDE_GET_SERVICEINFO
                // The second intercepts the CDE_SERVICEINFO response (since originator Thing was set to ContentService)
                numberOfMessages++;
            });
            testMsg.SetOriginatorThing(contentServiceThing);
            TheCommCore.PublishCentral(testMsg, true);
            TheCommonUtils.SleepOneEye(5000, 1000);
            Assert.AreEqual(numberOfMessages, 2);
        }
        private void SendButton_Click(object sender, RoutedEventArgs e)
        {
            var msgHello = new MsgChatHello
            {
                Message    = MessageText.Text,
                SenderName = UserName.Text,
            };

            var target = new TheMessageAddress
            {
                EngineName = strChatEngine, // Send to this engine
                Node       = Guid.Empty     // Send to all nodes in the mesh
            };

            var response = TheCommRequestResponse.PublishRequestJSonAsync <MsgChatHello, MsgChatHelloResponse>(
                TheThingRegistry.GetBaseEngineAsThing(eEngineName.ThingService), // Since we have no plug-in, use an arbitrary existing thing in the system as the originator
                target,
                msgHello,
                new TimeSpan(0, 0, 10)) // Wait 10 seconds for ackknowledge message
                           .Result;

            if (response == null)
            {
                MessageBox.Show("Error sending message or no acknowlege received.");
            }
            else
            {
                if (response.Acknowledged)
                {
                }
                else
                {
                    MessageBox.Show("Message not acknowledged.");
                }
            }
        }
Exemple #8
0
        static public bool StartHost()
        {
            // TODO Figure out how to launch multiple hosts without killing the process
            lock (appStartLock)
            {
                if (Interlocked.Increment(ref activeHosts) != 1)
                {
                    TestContext.Out.WriteLine($"{DateTimeOffset.Now} Not starting host: already running");
                    return(false);
                }
                else
                {
                    TestContext.Out.WriteLine($"{DateTimeOffset.Now} Starting host");
                }

                if (TheBaseAssets.MyApplication != null)
                {
                    TestContext.Out.WriteLine($"{DateTimeOffset.Now} Host already started");
                    return(false);
                }

                //Assert.IsTrue(TheBaseAssets.MyApplication == null, "Not starting test host: BaseApplication already created.");

                TheScopeManager.SetApplicationID("/cVjzPfjlO;{@QMj:jWpW]HKKEmed[llSlNUAtoE`]G?"); //SDK Non-Commercial ID. FOR COMMERCIAL APP GET THIS ID FROM C-LABS!

                ushort port = 8716;
                // MSTest does not stop the Net3.5 host before starting the Net4.5 host, so need to use different ports
                var connections = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
                while (connections.FirstOrDefault(c => c.Port == port) != null)
                {
                    port += 2;
                }


                TheBaseAssets.MyServiceHostInfo = new TheServiceHostInfo(cdeHostType.Application)
                {
                    ApplicationName  = "My-Relay",                      //Friendly Name of Application
                    cdeMID           = Guid.NewGuid(),                  //TODO: Give a Unique ID to this Host Service
                    Title            = "My-Relay (C) C-Labs 2013-2017", //Title of this Host Service
                    ApplicationTitle = "My-Relay Portal",               //Title visible in the NMI Portal
                    CurrentVersion   = 1.0001,                          //Version of this Service, increase this number when you publish a new version that the store displays the correct update icon
                    DebugLevel       = eDEBUG_LEVELS.ESSENTIALS,        //Define a DebugLevel for the SystemLog output.
                    SiteName         = "http://cloud.c-labs.com",       //Link to the main Cloud Node of this host. this is not required and for documentation only

                    ISMMainExecutable = "OPCUAClientUnitTest",          //Name of the executable (without .exe)

                    LocalServiceRoute = "LOCALHOST",                    //Will be replaced by the full DNS name of the host during startup.

                    MyStationPort   = port,                             //Port for REST access to this Host node. If your PC already uses port 80 for another webserver, change this port. We recommend using Port 8700 and higher for UPnP to work properly.
                    MyStationWSPort = (ushort)(port + 1),               //Enables WebSockets on the station port. If UseRandomDeviceID is false, this Value cannot be changed here once the App runs for the first time. On Windows 8 and higher running under "Adminitrator" you can use the same port
                };

                #region Args Parsing
                Dictionary <string, string> ArgList = new Dictionary <string, string>();

                // TODO Get this from the text context?
                //for (int i = 0; i < args.Length; i++)
                //{
                //    string[] tArgs = args[i].Split('=');
                //    if (tArgs.Length == 2)
                //    {
                //        string key = tArgs[0].ToUpper();
                //        ArgList[key] = tArgs[1];
                //    }
                //}
                #endregion

                ArgList.Add("DontVerifyTrust", "True");                                          //NEW: 3.2 If this is NOT set, all plugins have to be signed with the same certificate as the host application or C-DEngine.DLL

                ArgList.Add("UseRandomDeviceID", "true");                                        //ATTENTION: ONLY if you set this to false, some of these parameters will be stored on disk and loaded at a later time. "true" assigns a new node ID everytime the host starts and no configuration data will be cached on disk.
                ArgList.Add("ScopeUserLevel", "255");                                            //Set the Scope Access Level
                ArgList.Add("AROLE", eEngineName.NMIService + ";" + eEngineName.ContentService); //Make NMI and Content Service known to this host
                ArgList.Add("SROLE", eEngineName.NMIService + ";" + eEngineName.ContentService); //Add NMI and Content Service as Service to run on this host. If you omit these entries, this host will become an end-node (not able to relay) and will try to find a proper relay node to talk to.
                string tScope = TheScopeManager.GenerateNewScopeID();                            //TIP: instead of creating a new random ID every time your host starts, you can put a breakpoint in the next line, record the ID and feed it in the "SetScopeIDFromEasyID". Or even set a fixed ScopeID here. (FOR TESTING ONLY!!)
                TestContext.Out.WriteLine("Current Scope:" + tScope);
                TheScopeManager.SetScopeIDFromEasyID(tScope);                                    //Set a ScopeID - the security context of this node. You can replace tScope with any random 8 characters or numbers
                MyBaseApplication = new TheBaseApplication();                                    //Create a new Base (C-DEngine IoT) Application

                //TheBaseAssets.MasterSwitch = false;
                var appStarted = MyBaseApplication.StartBaseApplication(null, ArgList);

                Assert.IsTrue(appStarted, "Failed to StartBaseApplication");         //Start the C-DEngine Application. If a PluginService class is added DIRECTLY to the host project you can instantiate the Service here replacing the null with "new cdePluginService1()"
                                                                                     //If the Application fails to start, quit the app. StartBaseApplication returns very fast as all the C-DEngine code is running asynchronously
                                                                                     //MyBaseApplication.MyCommonDisco.RegisterUPnPUID("*", null);     //Only necessary if UPnP is used to find devices

                bool started           = false;
                var  engineStartedTask = TheBaseEngine.WaitForEnginesStartedAsync();
                if (engineStartedTask.Wait(20000))
                {
                    started = engineStartedTask.Result;
                    Assert.IsTrue(started, "Failed to start engines");
                }
                else
                {
                    ///Assert.IsTrue(started, "Failed to start engines (timeout)");
                    TestContext.Out.WriteLine("Failed to start engines (timeout)");
                }
                AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
            }
            myContentService = TheThingRegistry.GetBaseEngineAsThing(eEngineName.ContentService);
            Assert.IsNotNull(myContentService);
            TestContext.Out.WriteLine("{DateTimeOffset.Now} Host started");
            return(true);
        }
        static readonly TimeSpan defaultRequestTimeout = new TimeSpan(0, 1, 0); // TODO make this configurable?

        /// <summary>
        /// Sends a TSM message to the targetThing and return the matching response message
        /// </summary>
        /// <param name="originator">Thing or engine to use for response messages. If NULL defaults to ContentService.</param>
        /// <param name="target">Thing or engine to which the message is to be sent.</param>
        /// <param name="messageName">Name of the message, as defined by the target thing. The response message will be messageName_RESPONSE.</param>
        /// <param name="txtParameters">Array of simple string parameters, to be attached to the message's TXT field, as defined by the target thing. txtParameters must not contain ":" characters.</param>
        /// <param name="PLS">String payload to be set as the message's PLS field, as defined by the target thing.</param>
        /// <param name="PLB">Binary pauload to be set as the message's PLB field, as defined by the target thing.</param>
        /// <param name="timeout">Time to wait for the response message. Can not exceed TheBaseAsset.MaxMessageResponseTimeout (default: 1 hour).</param>
        /// <returns>The response message as defined by the target thing. The response parameters and correlation token can be parsed using ParseRequestOrResponseMessage, if the target thing used the PublishResponseMessage or an equivalent message format.
        /// If the message times out or an error occured while sending the message, the return value will be null.
        /// </returns>
        public static Task <TSM> PublishRequestAsync(TheMessageAddress originator, TheMessageAddress target, string messageName, TimeSpan timeout, string[] txtParameters = null, string PLS = null, byte[] PLB = null)
        {
            Guid correlationToken = Guid.NewGuid();

            if (originator == null)
            {
                originator = TheThingRegistry.GetBaseEngineAsThing(eEngineName.ContentService);
            }
            var msg = PrepareRequestMessage(originator, target, messageName, correlationToken, txtParameters, PLS, PLB);

            if (msg == null)
            {
                return(null);
            }

            if (timeout > MaxTimeOut)
            {
                timeout = MaxTimeOut;
            }
            if (timeout == TimeSpan.Zero)
            {
                timeout = defaultRequestTimeout;
            }

            var tcsResponse = new TaskCompletionSource <TSM>();

            var callback = new Action <ICDEThing, object>((tSenderThing, responseMsgObj) =>
            {
                var responseMsg = CheckResponseMessage(messageName, correlationToken, responseMsgObj);
                if (responseMsg != null)
                {
                    var bReceived = tcsResponse.TrySetResult(responseMsg);
                    if (bReceived)
                    {
                        TheSystemMessageLog.WriteLog(1000, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM("Thing Registry", $"Processed response message for {messageName}", eMsgLevel.l2_Warning, $"{correlationToken}: {responseMsg?.TXT}"), false);
                    }
                    else
                    {
                        TheSystemMessageLog.WriteLog(1000, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM("Thing Registry", $"Failed to process response message for {messageName}: timed out or double response", eMsgLevel.l2_Warning, $"{correlationToken}: {responseMsg?.TXT}"), false);
                    }
                }
                else
                {
                    responseMsg = (responseMsgObj as TheProcessMessage)?.Message;
                    TheSystemMessageLog.WriteLog(1000, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM("Thing Registry", $"Ignoring response message for {messageName}", eMsgLevel.l2_Warning, $"{correlationToken}: {responseMsg?.TXT}"), false);
                }
            });

            var originatorThingOrEngine = RegisterRequestCallback(originator, callback);

            if (originatorThingOrEngine == null)
            {
                return(null);
            }

            if (target.SendToProvisioningService)
            {
                TheISMManager.SendToProvisioningService(msg);
            }
            else if (target.Node != Guid.Empty)
            {
                TheCommCore.PublishToNode(target.Node, msg);
            }
            else
            {
                TheCommCore.PublishCentral(msg, true);
            }

            TheCommonUtils.TaskWaitTimeout(tcsResponse.Task, timeout).ContinueWith((t) =>
            {
                UnregisterRequestCallback(originatorThingOrEngine, callback);
                var timedOut = tcsResponse.TrySetResult(null); // Will preserve value if actual result has already been set
                if (timedOut)
                {
                    TheSystemMessageLog.WriteLog(1000, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM("Thing Registry", $"Timeout waiting for response message for {messageName}", eMsgLevel.l2_Warning, $"{correlationToken}"), false);
                }
            });
            return(tcsResponse.Task);
        }
        public bool ActivateRules()
        {
            if (TheCDEngines.MyThingEngine == null || TheBaseAssets.MyServiceHostInfo.IsCloudService || TheCommonUtils.cdeIsLocked(LockRules) || !mIsInitialized)
            {
                return(false);
            }

            lock (LockRules)
            {
                if (mIsInitialized)
                {
                    InitRules();
                }
                //List<TheRule> tList = MyRulesStore.MyMirrorCache.GetEntriesByFunc(s => s.IsRuleActive && !s.IsRuleRunning && s.IsRuleWaiting);
                List <TheThing> tList = TheThingRegistry.GetThingsByFunc("*", s =>
                                                                         TheThing.GetSafePropertyString(s, "Parent").Equals(MyBaseThing.ID) && //TheBaseAssets.MyServiceHostInfo.MyDeviceInfo.DeviceID) &&
                                                                         TheThing.GetSafePropertyBool(s, "IsRuleActive") &&
                                                                         !TheThing.GetSafePropertyBool(s, "IsRuleRunning") &&
                                                                         TheThing.GetSafePropertyBool(s, "IsRuleWaiting"));
                if (tList != null && tList.Count > 0)
                {
                    foreach (TheThing tThing in tList)
                    {
                        TheRule tRule = tThing.GetObject() as TheRule;
                        if (tRule == null)
                        {
                            continue;
                        }
                        if (string.IsNullOrEmpty(tRule.TriggerProperty))
                        {
                            tRule.IsIllegal     = true;
                            tRule.IsRuleWaiting = false;
                            continue;
                        }
                        if (tRule.TriggerStartTime > DateTimeOffset.Now)
                        {
                            continue;
                        }
                        if (tRule.TriggerEndTime < DateTimeOffset.Now)
                        {
                            RemoveTrigger(tRule, false);
                            continue;
                        }
                        switch (tRule.TriggerObjectType)
                        {
                        case "CDE_ENGINE":
                            TheThing tBase = TheThingRegistry.GetBaseEngineAsThing(tRule.TriggerObject);
                            if (tBase != null)
                            {
                                tBase.RegisterEvent(eEngineEvents.IncomingMessage, sinkRuleIncoming);
                                tRule.IsRuleWaiting        = false;
                                tRule.IsRuleRunning        = true;
                                tRule.IsTriggerObjectAlive = true;
                                TheSystemMessageLog.WriteLog(4445, TSM.L(eDEBUG_LEVELS.VERBOSE) ? null : new TSM(eKnownDeviceTypes.TheThingRule, $"Rule {tRule.FriendlyName} started with TriggerType: {tRule.TriggerObjectType}"), false);
                            }
                            break;

                        case "CDE_EVENTFIRED":
                            TheThing tBaseE = TheThingRegistry.GetBaseEngineAsThing(tRule.TriggerObject);
                            if (tBaseE != null)
                            {
                                tBaseE.RegisterEvent(tRule.TriggerProperty, sinkRuleThingEvent);
                                tRule.IsRuleWaiting        = false;
                                tRule.IsRuleRunning        = true;
                                tRule.IsTriggerObjectAlive = true;
                                TheSystemMessageLog.WriteLog(4445, TSM.L(eDEBUG_LEVELS.VERBOSE) ? null : new TSM(eKnownDeviceTypes.TheThingRule, $"Rule {tRule.FriendlyName} started with TriggerType: {tRule.TriggerObjectType}"), false);
                            }
                            break;

                        default:
                            TheThing tTriggerThing = TheThingRegistry.GetThingByMID("*", TheCommonUtils.CGuid(tRule.TriggerObject));
                            if (tTriggerThing != null)
                            {
                                //if (tTriggerThing.GetObject() == null) continue;  //TODO: Verify if this can stay removed
                                //if (tTriggerThing.GetProperty("FriendlyName").Value.ToString().Contains("Motion")) continue;
                                cdeP tProp = tTriggerThing.GetProperty(tRule.TriggerProperty, true);
                                if (tProp != null)
                                {
                                    tProp.UnregisterEvent(eThingEvents.PropertyChanged, sinkRuleAction);
                                    tProp.RegisterEvent(eThingEvents.PropertyChanged, sinkRuleAction);
                                }
                                tRule.IsRuleWaiting        = false;
                                tRule.IsRuleRunning        = true;
                                tRule.IsTriggerObjectAlive = true;
                                tRule.RuleTrigger(tProp.ToString(), true);
                                TheSystemMessageLog.WriteLog(4445, TSM.L(eDEBUG_LEVELS.VERBOSE) ? null : new TSM(eKnownDeviceTypes.TheThingRule, $"Rule {tRule.FriendlyName} started with TriggerType: {tRule.TriggerObjectType}"), false);
                            }
                            break;
                        }
                    }
                }
            }
            return(true);
        }
        internal static bool ParseSimplex(string pScopedTopic, TSM pMessage, TheQueuedSender pQSender) //, TheSessionState pSessionState, Action<TSM> pLocalCallback)
        {
            if (pMessage == null)
            {
                return(false);
            }
#if !JC_COMMDEBUG
            try
            {
#endif
            if (pMessage?.TXT?.Equals("CDE_DELETEORPHAN") == true)
            {
                TheQueuedSenderRegistry.RemoveOrphan(TheCommonUtils.CGuid(pMessage.PLS));
                return(false);
            }

            if (pQSender == null)
            {
                TheBaseAssets.MySYSLOG.WriteToLog(291, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM("CoreComm", $"QSender not found! Received from ORG:{TheCommonUtils.GetDeviceIDML(pMessage?.ORG)}", eMsgLevel.l1_Error, pMessage?.PLS));
                return(false);
            }

            //SECURITY-REVIEW: This cannot be permitted without extra tokens and Encryption! otherwise it can be used to change a nodes scope on the fly!
            //if ("CDE_UPDATESCOPE".Equals(pMessage.TXT))
            //{
            //    pQSender.UpdateSubscriptionScope(TheBaseAssets.MyScopeManager.GetRealScopeID(pMessage.SID));     //GRSI: rare
            //    return true;
            //}
            if (pQSender != null && pMessage.ENG?.StartsWith(eEngineName.ContentService) == true && pMessage?.TXT == "CDE_SERVICE_INFO" && pQSender.MyTargetNodeChannel?.RealScopeID == TheBaseAssets.MyScopeManager.GetRealScopeID(pMessage.SID))
            {
                try
                {
                    pQSender.SetNodeInfo(TheCommonUtils.DeserializeJSONStringToObject <TheNodeInfoClone>(pMessage?.PLS));
                }
                catch (Exception e)
                {
                    TheBaseAssets.MySYSLOG.WriteToLog(23056, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM("CoreComm", $"Error decoding SystemInfo {pQSender?.MyTargetNodeChannel?.ToMLString()}", eMsgLevel.l1_Error, e.ToString()));
                }
            }

            if (pMessage.TXT?.Equals("CDE_SUBSCRIBE") == true || pMessage.TXT?.Equals("CDE_INITIALIZE") == true) //9-9-2012 CDEC Did not work right on CDE_INIT
            {
                TheBaseAssets.MySYSLOG.WriteToLog(292, TSM.L(eDEBUG_LEVELS.VERBOSE) ? null : new TSM("CoreComm", $"Parse-Simplex Subscribe from {pQSender?.MyTargetNodeChannel?.ToMLString()} Parsed: {pMessage?.PLS}", eMsgLevel.l7_HostDebugMessage));
                if (pQSender?.MyISBlock != null && !TheBaseAssets.MyServiceHostInfo.IsCloudService)
                {
                    TheBaseAssets.MySYSLOG.WriteToLog(292, TSM.L(eDEBUG_LEVELS.VERBOSE) ? null : new TSM("CoreComm", "Parse-Simplex Subscribe rejected for Custom ISBConnect", eMsgLevel.l7_HostDebugMessage));
                    return(true);
                }
                ParseSubscribe(pMessage.PLS, pQSender);
                if (pMessage.ENG.Equals("CLOUDSYNC"))
                {
                    string[] tTopics = pMessage.PLS.Split(';');
                    foreach (string t in tTopics)
                    {
                        TSM.GetScrambledIDFromTopic(t, out string tEng);
                        if (TheThingRegistry.IsEngineRegistered(tEng))
                        {
                            IBaseEngine tsBase = TheThingRegistry.GetBaseEngine(tEng);
                            tsBase?.GetBaseThing()?.FireEvent(eEngineEvents.NewSubscription, tsBase.GetBaseThing(), pQSender.MyTargetNodeChannel, true);
                        }
                    }
                    return(true);
                }
                else
                {
                    TheThing tBase2 = TheThingRegistry.GetBaseEngineAsThing(pMessage.ENG);
                    if (tBase2 != null)
                    {
                        tBase2.FireEvent(eEngineEvents.NewSubscription, tBase2, pQSender.MyTargetNodeChannel, true);
                    }
                }
                if (pMessage.TXT.Equals("CDE_SUBSCRIBE"))       //NEW:2.06 Make sure Subscribe and Unsubscribe only go to first node
                {
                    return(true);
                }
                else
                {
                    return(false);
                }
            }
            if (pMessage.TXT?.Equals("CDE_UNSUBSCRIBE") == true)
            {
                TheBaseAssets.MySYSLOG.WriteToLog(292, TSM.L(eDEBUG_LEVELS.VERBOSE) ? null : new TSM("CoreComm", $"Parse-Simplex Unsubscribe from {pQSender.MyTargetNodeChannel?.ToMLString()} Parsed: {pMessage.PLS}", eMsgLevel.l7_HostDebugMessage));
                ParseUnsubscribe(pMessage.PLS, pQSender);
                return(true);       //NEW:2.06 Make sure Subscribe and Unsubscribe only go to first node
            }
#if !JC_COMMDEBUG
        }

        catch (Exception ee)
        {
            TheBaseAssets.MySYSLOG.WriteToLog(316, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM("CoreComm", "Parse-Simplex", eMsgLevel.l1_Error, ee.ToString()));
        }
#endif
            return(false);
        }
Exemple #12
0
        public void TestSensorPipeline()
        {
            var sensorThing = new TheThing {
                EngineName = "CDMyVThings.TheVThings", DeviceType = "Memory Tag", ID = "TestSensorThing03"
            };

            TheThingRegistry.RegisterThing(sensorThing);
            //sensorThing = TheThingRegistry.GetThingByMID(sensorThing.cdeMID);

            // Make all providers send properties into the memory thing
            var providers = new List <TheThing>();
            {
                var deviceTypes = TheThingRegistry.GetAllDeviceTypesByCap(nsCDEngine.ViewModels.eThingCaps.SensorProvider, true);
                Assert.Greater(deviceTypes.Count, 0, "No sensor provider device types found");
                foreach (var deviceType in deviceTypes)
                {
                    TestInfo testInfo;
                    var      provider = CreateThingForTest(deviceType, out testInfo);
                    WriteLine($"{deviceType}: Provider created");

                    int subscriptionCount = BrowseAndSubscribeAllSensors(provider, sensorThing, testInfo.MaximumBrowseTags, testInfo.MinimumBrowseTags, out var browseResponse, out var sensorSubscriptionResponse, out var successfullSubscriptions);

                    var getSubscriptionResponse = provider.GetSensorProviderSubscriptionsAsync().Result;
                    Assert.IsNotNull(getSubscriptionResponse, "Timeout or get subscriptions message not implemented by plug-in");
                    Assert.IsTrue(string.IsNullOrEmpty(getSubscriptionResponse.Error), $"Error from GetSensorSubscriptions: {getSubscriptionResponse.Error}");

                    Assert.AreEqual(subscriptionCount, getSubscriptionResponse.Subscriptions.Count);

                    if (sensorSubscriptionResponse.SubscriptionStatus.Count <= 0)
                    {
                        WriteLine($"{deviceType}: No subscriptions found after subscribe request");
                    }
                    providers.Add(provider);
                }
            }

            var consumers = new List <TheThing>();
            {
                var deviceTypes = TheThingRegistry.GetAllDeviceTypesByCap(nsCDEngine.ViewModels.eThingCaps.SensorConsumer, true);
                Assert.Greater(deviceTypes.Count, 0, "No sensor consumer device types found");

                foreach (var deviceType in deviceTypes)
                {
                    TestInfo testInfo;
                    var      consumer = CreateThingForTest(deviceType, out testInfo);
                    WriteLine($"{deviceType}: Consumer created");

                    var thingSubscriptions = new List <TheThing.TheThingSubscription>
                    {
                        new TheThing.TheThingSubscription {
                            ThingReference = sensorThing,
                        },
                    };

                    {
                        var subscribeToThingsResponse = consumer.SubscribeToThingsAsync(thingSubscriptions).Result;

                        Assert.NotNull(subscribeToThingsResponse, $"Timeout or subscribe to things message not implemented by consumer {deviceType}");
                        Assert.IsTrue(string.IsNullOrEmpty(subscribeToThingsResponse.Error), $"Error from consumer {deviceType}: {subscribeToThingsResponse.Error}");
                        Assert.GreaterOrEqual(thingSubscriptions.Count, subscribeToThingsResponse.SubscriptionStatus.Count, $"Not enough status reports for {deviceType}");
                        foreach (var subStatus in subscribeToThingsResponse.SubscriptionStatus)
                        {
                            Assert.IsTrue(string.IsNullOrEmpty(subStatus.Error), $"Error from consumer {deviceType}: {subStatus.Error}");
                            Assert.IsTrue(subStatus.Subscription.SubscriptionId.HasValue && subStatus.Subscription.SubscriptionId != Guid.Empty, $"No subscriptionid from consumer {deviceType}: {subStatus.Subscription.SubscriptionId}");
                        }
                    }
                    // TODO verify that subscriptions are actually getting consumed (at least for some consumer plug-ins)
                    {
                        var getSubscriptionsResponse = consumer.GetThingSubscriptionsAsync().Result;

                        Assert.NotNull(getSubscriptionsResponse, $"Timeout or get things message not implemented by consumer {deviceType}");
                        Assert.IsTrue(string.IsNullOrEmpty(getSubscriptionsResponse.Error), $"Error from consumer {deviceType}: {getSubscriptionsResponse.Error}");
                        Assert.AreEqual(thingSubscriptions.Count, getSubscriptionsResponse.ThingSubscriptions.Count, $"Missing or too many subscriptions for {deviceType}");
                        foreach (var sub in getSubscriptionsResponse.ThingSubscriptions)
                        {
                            Assert.IsTrue(sub.SubscriptionId.HasValue && sub.SubscriptionId != Guid.Empty, $"No subscriptionid from consumer {deviceType}: {sub.SubscriptionId}");
                        }
                    }

                    consumers.Add(consumer);
                }
            }

            var pipelineConfig = sensorThing.GetThingPipelineConfigurationAsync(true).Result;

            foreach (var thing in consumers)
            {
                if (thing.DeviceType != eKnownDeviceTypes.IBaseEngine)
                {
                    TheThingRegistry.DeleteThing(thing);
                }
            }

            foreach (var thing in providers)
            {
                if (thing.DeviceType != eKnownDeviceTypes.IBaseEngine)
                {
                    TheThingRegistry.DeleteThing(thing);
                }
            }
            // TODO Verify that provider properties are gone

            TheThingRegistry.DeleteThing(sensorThing);

            foreach (var thingConfig in pipelineConfig.ThingConfigurations)
            {
                if (configs.TryGetValue($"{thingConfig.ThingIdentity.EngineName}/{thingConfig.ThingIdentity.DeviceType}", out var testInfo))
                {
                    if (testInfo.SpecializationValues != null)
                    {
                        foreach (var propKV in testInfo.SpecializationValues)
                        {
                            thingConfig.ThingSpecializationParameters[propKV.Key] = propKV.Value;
                        }
                    }
                }
            }

            var owner  = TheThingRegistry.GetBaseEngineAsThing(eEngineName.ContentService);
            var things = TheThing.CreateThingPipelineFromConfigurationAsync(owner, pipelineConfig).Result;

            Assert.AreEqual(pipelineConfig.ThingConfigurations.Count, things.Count, "Not all pipeline things were created.");
            Assert.AreEqual(pipelineConfig.ThingConfigurations.Count(cfg => cfg.ThingSpecializationParameters != null), things.Count, "Not all specialization parameters were created.");
            int i = 0;

            foreach (var thing in things)
            {
                Assert.NotNull(thing, $"Thing {i} not created: {pipelineConfig.ThingConfigurations[i].ThingIdentity}");
                i++;
            }
            var memoryThing = things.FirstOrDefault(t => t.EngineName == sensorThing.EngineName);

            Assert.NotNull(memoryThing);
            var minimumTagCount = configs.Sum(c => c.Value.MinimumBrowseTags);

            Assert.Greater(memoryThing.MyPropertyBag.Count, minimumTagCount); // TODO adjust this to actual sensors, not just the OPC UA client subscription count

            foreach (var thing in things)
            {
                if (thing.DeviceType != eKnownDeviceTypes.IBaseEngine)
                {
                    TheThingRegistry.DeleteThing(thing);
                }
            }
        }
Exemple #13
0
        //internal static Task<ApplyConfigFilesResult> ApplyPipelineConfigJsonAsync(string pipelineConfigJson, IEnumerable<string> answerFilesJson, string configFileNameForLog, string[] answerFileNamesForLog)
        //{
        //    var pipeLineTasks = ApplyPipelineConfigJsonInternal(pipelineConfigJson, answerFilesJson, configFileNameForLog, answerFileNamesForLog);
        //    if (pipeLineTasks == null)
        //    {
        //        return TheCommonUtils.TaskFromResult(new ApplyConfigFilesResult
        //        {
        //            Success = false,
        //        });
        //    }

        //    return TheCommonUtils.TaskWhenAll(pipeLineTasks.Select(t => (Task)t))
        //        .ContinueWith(t =>
        //        {
        //            bool bSuccess = true;
        //            int failedFileCount = 0;
        //            foreach (var task in pipeLineTasks)
        //            {
        //                if (task.IsFaulted)
        //                {
        //                    failedFileCount++;
        //                    bSuccess = false;
        //                }
        //            }
        //            return new ApplyConfigFilesResult { Success = bSuccess, NumberOfFiles = 1, NumberOfFailedFiles = failedFileCount };
        //        });
        //}

        static List <Task <List <TheThing> > > ApplyPipelineConfigJsonInternal(ThePipelineConfiguration pipelineConfig, IEnumerable <ThePipelineConfiguration> answerConfigs, string configFileNameForLog, string[] answerFileNamesForLog)
        {
            List <Task <List <TheThing> > > pipeLineTasks = new List <Task <List <TheThing> > >();

            try
            {
                if (configFileNameForLog == null)
                {
                    configFileNameForLog = pipelineConfig?.FriendlyName ?? "Not specified";
                }
                if (pipelineConfig != null && pipelineConfig.ThingConfigurations != null)
                {
                    var pipelines = new List <ThePipelineConfiguration>();

                    if (!pipelineConfig.ThingConfigurations.Any(tc => tc.ThingSpecializationParameters == null)) // If all thing instances have specializations, add the pipeline as a config
                    {
                        pipelines.Add(pipelineConfig);
                    }

                    try
                    {
                        if (!answerConfigs?.Any() == true && !pipelines.Contains(pipelineConfig))
                        {
                            TheBaseAssets.MySYSLOG.WriteToLog(7721, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(eEngineName.ThingService, "Found no answer file for generalized config. Attempting to install the generalized config.", eMsgLevel.l2_Warning, $"File: {configFileNameForLog}. Config {pipelineConfig}"));
                            pipelines.Add(pipelineConfig);
                        }
                        int answerFileIndex = 0;
                        foreach (var answerConfig in answerConfigs)
                        {
                            var answerFileNameForLog = answerFileIndex < answerFileNamesForLog?.Length ? answerFileNamesForLog[answerFileIndex] : answerConfig.FriendlyName ?? $"Answer Config {answerFileIndex}";
                            answerFileIndex++;
                            try
                            {
                                var pipelineInstance = TheCommonUtils.DeserializeJSONStringToObject <ThePipelineConfiguration>(TheCommonUtils.SerializeObjectToJSONString(pipelineConfig)); // TODO implement Clone to avoid reparsing

                                int thingConfigIndex = 0;
                                foreach (var answerThingConfig in answerConfig.ThingConfigurations)
                                {
                                    // verify that the rest matches (or doesn't exist)
                                    if (thingConfigIndex < pipelineInstance.ThingConfigurations.Count)
                                    {
                                        if (answerThingConfig.ThingSpecializationParameters != null)
                                        {
                                            pipelineInstance.ThingConfigurations[thingConfigIndex].ThingSpecializationParameters = answerThingConfig.ThingSpecializationParameters;
                                        }
                                        else
                                        {
                                            TheBaseAssets.MySYSLOG.WriteToLog(7721, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(eEngineName.ThingService, "Error processing answer file: no specialization parameters", eMsgLevel.l1_Error, $"File: {configFileNameForLog}. Answer File: {answerFileNameForLog}. Config {pipelineInstance.ThingConfigurations[thingConfigIndex]}"));
                                        }

                                        if (answerThingConfig.ThingSubscriptions != null)
                                        {
                                            var generalizedSubscriptions = pipelineInstance.ThingConfigurations[thingConfigIndex].ThingSubscriptions;
                                            int subIndex         = 0;
                                            var newSubscriptions = new List <TheThingSubscription>();

                                            foreach (var answerSub in answerThingConfig.ThingSubscriptions)
                                            {
                                                if (answerSub != null)
                                                {
                                                    if (answerSub.ExtensionData.ContainsKey("Add"))
                                                    {
                                                        // Add a new subscription: remember for now, add later to not confused the subIndex logic
                                                        newSubscriptions.Add(answerSub);
                                                    }
                                                    else if (answerSub.ExtensionData.ContainsKey("Remove"))
                                                    {
                                                        // remove this subscription
                                                        generalizedSubscriptions[subIndex] = null; // Just set to null here, so the subIndex logic doesn't get confused. Will remove the null's later.
                                                        subIndex++;
                                                    }
                                                    else
                                                    {
                                                        // Update this subscription
                                                        var generalizedSub = generalizedSubscriptions[subIndex];
                                                        TheThingSubscription.SpecializeThingSubscription(answerSub, generalizedSub);
                                                        subIndex++;
                                                    }
                                                }
                                            }
                                            generalizedSubscriptions.AddRange(newSubscriptions);
                                            generalizedSubscriptions.RemoveAll(sub => sub == null);
                                        }
                                        if (answerThingConfig.SensorSubscriptions != null)
                                        {
                                            var generalizedSubscriptions = pipelineInstance.ThingConfigurations[thingConfigIndex].SensorSubscriptions;
                                            int subIndex         = 0;
                                            var newSubscriptions = new List <TheSensorSubscription>();

                                            foreach (var answerSub in answerThingConfig.SensorSubscriptions)
                                            {
                                                if (answerSub != null)
                                                {
                                                    if (answerSub.ExtensionData.ContainsKey("Add"))
                                                    {
                                                        // Add a new subscription: remember for now, add later to not confused the index
                                                        newSubscriptions.Add(answerSub);
                                                    }
                                                    else if (answerSub.ExtensionData.ContainsKey("Remove"))
                                                    {
                                                        // remove this subscription
                                                        generalizedSubscriptions[subIndex] = null; // Just set to null here, so the index doesn't get confused. Will remove the null's later.
                                                        subIndex++;
                                                    }
                                                    else
                                                    {
                                                        // Update this subscription
                                                        var generalizedSub = generalizedSubscriptions[subIndex];
                                                        TheSensorSubscription.SpecializeSensorSubscription(answerSub, generalizedSub);

                                                        subIndex++;
                                                    }
                                                }
                                            }
                                            generalizedSubscriptions.AddRange(newSubscriptions);
                                            generalizedSubscriptions.RemoveAll(sub => sub == null);
                                        }
                                    }
                                    else
                                    {
                                        TheBaseAssets.MySYSLOG.WriteToLog(7721, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(eEngineName.ThingService, "Error processing answer file: too many specialization parameters. Ignoring.", eMsgLevel.l1_Error, $"File: {configFileNameForLog}. Answer File: {answerFileNameForLog}. Config# {thingConfigIndex}"));
                                    }
                                    thingConfigIndex++;
                                }
                                if (answerConfig.ThingConfigurations.Count < pipelineInstance.ThingConfigurations.Count)
                                {
                                    TheBaseAssets.MySYSLOG.WriteToLog(7721, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(eEngineName.ThingService, "Error processing answer file: not enough specialization parameters.", eMsgLevel.l1_Error, $"File: {configFileNameForLog}. Answer File: {answerFileNameForLog}. Params {answerConfig.ThingConfigurations.Count} Expected: {pipelineInstance.ThingConfigurations.Count}"));
                                }
                                pipelines.Add(pipelineInstance);
                            }
                            catch (Exception e)
                            {
                                TheBaseAssets.MySYSLOG.WriteToLog(7721, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(eEngineName.ThingService, "Error processing answer file", eMsgLevel.l1_Error, $"File: {configFileNameForLog}. Answer File: {answerFileNameForLog}. {e.Message}"));
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        TheBaseAssets.MySYSLOG.WriteToLog(7721, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(eEngineName.ThingService, "Error processing answer files", eMsgLevel.l1_Error, $"File: {configFileNameForLog}. {e.Message}"));
                    }

                    // TODO use the application host thing or specify the host thing in the pipeline config/answer file?
                    var owner = TheThingRegistry.GetBaseEngineAsThing(eEngineName.ThingService);
                    if (!owner.IsInit())
                    {
                        TheBaseAssets.MySYSLOG.WriteToLog(7720, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(eEngineName.ThingService, $"ApplyConfig Files: error getting Thing engine", eMsgLevel.l1_Error, $"{pipelineConfig}"));
                    }

                    foreach (var pipeline in pipelines)
                    {
                        var thingReferenceMap = new Dictionary <string, string>();
                        foreach (var thingConfig in pipeline.ThingConfigurations)
                        {
                            var specializedThingIdentity = thingConfig.GetSpecializedThingIdentity(thingReferenceMap);

                            // First synchronously apply the configuration properties, so the plug-in does not start up with the old values
                            var tThing = new TheThingReference(specializedThingIdentity).GetMatchingThings().FirstOrDefault();
                            if (tThing != null)
                            {
                                if (!tThing.ApplyConfigurationPropertiesInternal(thingConfig, thingReferenceMap))
                                {
                                    TheBaseAssets.MySYSLOG.WriteToLog(7719, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(eEngineName.ThingService, $"Error applying config properties", eMsgLevel.l3_ImportantMessage, $"{thingConfig}"));
                                }
                            }
                        }

                        var thingsTask = CreateThingPipelineFromConfigurationAsync(owner, pipeline);
                        pipeLineTasks.Add(thingsTask);
                    }
                }
                else
                {
                    throw new Exception($"Error parsing config file {configFileNameForLog}");
                }
            }
            catch (Exception e)
            {
                TheBaseAssets.MySYSLOG.WriteToLog(7721, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(eEngineName.ThingService, "Error processing config file", eMsgLevel.l1_Error, $"File: {configFileNameForLog}. {e.Message}"));
                pipeLineTasks = null;
            }

            return(pipeLineTasks);
        }