Example #1
0
        private async void OnWatchDogTimer(object state)
        {
            try
            {
                OnSendAttempt(null);
                var timeSinceLastAttempt = DateTimeOffset.Now - LastSendAttemptTime;
                if (timeSinceLastAttempt > new TimeSpan(0, 0, WatchDogIdleRecycleIntervalInMinutes, 0))
                {
                    TheBaseAssets.MySYSLOG.WriteToLog(95014, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(strPrometheusExporter, $"WatchDog: No activity since {LastSendAttemptTime}. Disconnecting Sender and waiting 5 seconds to reconnect.", eMsgLevel.l4_Message));
                    Disconnect(true);
                    try
                    {
                        await TheCommonUtils.TaskDelayOneEye(5000, 100);
                    }
                    catch (TaskCanceledException) { }

                    if (TheBaseAssets.MasterSwitch)
                    {
                        TheBaseAssets.MySYSLOG.WriteToLog(95015, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(strPrometheusExporter, $"WatchDog: Reconnecting Sender.", eMsgLevel.l4_Message));
                        Connect();
                    }
                }
            }
            catch (Exception ex)
            {
                TheBaseAssets.MySYSLOG.WriteToLog(95302, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(strPrometheusExporter, $"WatchDog: Internal error.", eMsgLevel.l1_Error, ex.ToString()));
            }
        }
Example #2
0
        async void sinkPinger(object state)
        {
            Ping p = new Ping();

            while (IsConnected && TheBaseAssets.MasterSwitch)
            {
                try
                {
                    if (!IsConnected)
                    {
                        return;
                    }
                    var reply = await SendPingAsync(p, MyBaseThing.Address, PingTimeOut).ConfigureAwait(false);

                    if (!IsConnected)
                    {
                        return;               //Do not change status if disconnected
                    }
                    if (reply.Status != IPStatus.Success)
                    {
                        MyBaseThing.LastMessage = string.Format("{0} at {1}", reply.Status, DateTimeOffset.Now);
                        FailCounter++;
                        MyBaseThing.StatusLevel = FailCounter > FailureLimit ? 3 : 2;
                        RoundTripTime           = PingTimeOut;
                    }
                    else
                    {
                        if (AllowRTT)
                        {
                            RoundTripTime          = reply.RoundtripTime;
                            MyBaseThing.LastUpdate = DateTimeOffset.Now;
                        }
                        MyBaseThing.StatusLevel = 1;
                        FailCounter             = 0;
                    }
                }
                catch (Exception e)
                {
                    if (!IsConnected)
                    {
                        return;
                    }
                    MyBaseThing.LastMessage = string.Format("{0} at {1}", e, DateTimeOffset.Now);
                    MyBaseThing.StatusLevel = 3;
                    RoundTripTime           = PingTimeOut;
                    if (!AutoConnect)
                    {
                        IsConnected = false;
                        return;
                    }
                }
                if (!TheBaseAssets.MasterSwitch)
                {
                    return;
                }
                await TheCommonUtils.TaskDelayOneEye(PingDelay, 100).ConfigureAwait(false);
            }
            MyBaseThing.LastMessage = string.Format("Disconnected at {0}", DateTimeOffset.Now);
            MyBaseThing.LastUpdate  = DateTimeOffset.Now;
        }
Example #3
0
        private async Task ProcessSocketRequest(AspNetWebSocketContext context)
        {
            var socket = context.WebSocket;

            TheCDEKPIs.IncWSTestClients();
            TheCommonUtils.cdeRunAsync("DeadCheck", true, (o) =>
            {
                TheCommonUtils.TaskDelayOneEye(10 * 1000, 100).ContinueWith((t) =>
                {
                    socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "timeout", CancellationToken.None);
                });
            });
            // maintain socket
            while (true)
            {
                var buffer = new ArraySegment <byte>(new byte[100]);
                // async wait for a change in the socket
                var result = await socket.ReceiveAsync(buffer, CancellationToken.None);

                if (socket.State == WebSocketState.Open)
                {
                    await socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                }
                else
                {
                    TheCDEKPIs.DecWSTestClients();
                    break;
                }
            }
        }
Example #4
0
 public void sinkStoreReady(StoreEventArgs e)
 {
     if (MyDevice != null)
     {
         if (!string.IsNullOrEmpty(MyDevice.Name))
         {
             MyBaseThing.FriendlyName = MyDevice.Name;
         }
         if (!string.IsNullOrEmpty(MyDevice.IpAddress))
         {
             MyBaseThing.Address = MyDevice.IpAddress;
         }
         if (MyDevice.IpPort == 0)
         {
             MyDevice.IpPort = 502;
         }
         if (ConnectionType == 0)
         {
             ConnectionType = 3;
         }
         TheThing.SetSafePropertyNumber(MyBaseThing, "CustomPort", MyDevice.IpPort);
         TheThing.SetSafePropertyNumber(MyBaseThing, "SlaveAddress", MyDevice.SlaveAddress);
         if (MyDevice.Mapping != null)
         {
             TheThing.SetSafePropertyNumber(MyBaseThing, "Offset", MyDevice.Mapping.Offset);
             //TODO: Create Storage Mirror with Field Mapps
             MyModFieldStore.FlushCache(true);
             foreach (var tFld in MyDevice.Mapping.FieldList)
             {
                 MyModFieldStore.AddAnItem(tFld);
             }
         }
     }
     if (Interval < 100)
     {
         Interval = 100;
     }
     if (AutoConnect)
     {
         TheCommonUtils.cdeRunAsync("ModBusAutoConnect", true, async(o) =>
         {
             while (AutoConnect && TheBaseAssets.MasterSwitch && !IsConnected && !Connect(null))
             {
                 await TheCommonUtils.TaskDelayOneEye((int)Interval, 100);
             }
         });
     }
     mIsInitialized = true;
     FireEvent(eThingEvents.Initialized, this, true, true);
 }
Example #5
0
        public static Task <bool> ParseCSVData(TheThing tThing, string fileName, string csvData, int delayBetweenRows, CSVParserOptions options, long previousErrorCount, Action <long, long, bool> onLineParsed)
        {
            int  lineCount      = 0;
            long lineErrorCount = previousErrorCount;

            var result = ParseCSVData(csvData,
                                      options,
                                      async(dict, sourceTimestamp) =>
            {
                lineCount++;

                if (dict == null || options.NameValuePairs && dict.Count == 0)
                {
                    lineErrorCount++;
                }
                else
                {
                    try
                    {
                        if (!String.IsNullOrEmpty(fileName))
                        {
                            dict["CurrentFile"] = fileName;
                        }
                        tThing.SetProperties(dict, sourceTimestamp);
                    }
                    catch
                    {
                        lineErrorCount++;
                    }

                    if (delayBetweenRows > 0)
                    {
                        await TheCommonUtils.TaskDelayOneEye(delayBetweenRows, 100);
                    }
                }
                onLineParsed?.Invoke(lineCount, lineErrorCount, false);
            });

            onLineParsed?.Invoke(lineCount, lineErrorCount, true);
            return(result);
        }
Example #6
0
            internal void RequestOwnership(string correlationToken, string sourceORG, string ownerORG)
            {
                _pendingAcks.TryAdd(correlationToken, new TaskCompletionSource <bool>(DateTimeOffset.Now + new TimeSpan(0, 1, 0)));

                if (_ackCleanupTask == null)
                {
                    _ackCleanupTask = TheCommonUtils.cdeRunTaskAsync("ackCleanup", async(o) =>
                    {
                        try
                        {
                            while (!_pendingAcks.IsEmpty && TheBaseAssets.MasterSwitch)
                            {
                                try
                                {
                                    await TheCommonUtils.TaskDelayOneEye(61000, 100).ConfigureAwait(false);
                                }
                                catch { }
                                var now = DateTimeOffset.Now;
                                foreach (var ack in _pendingAcks.GetDynamicEnumerable())
                                {
                                    try
                                    {
                                        var expiration = (DateTimeOffset?)ack.Value?.Task?.AsyncState;
                                        if (expiration < now)
                                        {
                                            _pendingAcks.RemoveNoCare(ack.Key);
                                        }
                                    }
                                    catch { }
                                }
                            }
                        }
                        catch { }
                        finally
                        {
                            _ackCleanupTask = null;
                        }
                    }).ContinueWith(c => { var ignored = c.Exception; }, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
                }
            }
Example #7
0
 public async void ReaderThread()
 {
     lock (readerLoopLock)
     {
         if (bReaderLoopRunning)
         {
             return;
         }
         bReaderLoopRunning = true;
     }
     try
     {
         bool bPreviousError = false;
         while (TheBaseAssets.MasterSwitch && IsConnected)
         {
             if (!MyBaseEngine.GetEngineState().IsSimulated)
             {
                 var error = OpenModBus();
                 if (!string.IsNullOrEmpty(error))
                 {
                     MyBaseThing.LastMessage = $"{DateTime.Now} - Modbus Device could not be opened: {error}";
                     if (bPreviousError)
                     {
                         TheBaseAssets.MySYSLOG.WriteToLog(10000, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, MyBaseThing.LastMessage, eMsgLevel.l1_Error));
                         MyBaseThing.StatusLevel = 3;
                     }
                     else
                     {
                         TheBaseAssets.MySYSLOG.WriteToLog(10000, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyBaseThing.EngineName, MyBaseThing.LastMessage, eMsgLevel.l2_Warning));
                         MyBaseThing.StatusLevel = 2;
                     }
                     bPreviousError = true;
                 }
                 else
                 {
                     try
                     {
                         if (bPreviousError)
                         {
                             MyBaseThing.LastMessage = $"{DateTime.Now} - Modbus Device connected after previous error.";
                             TheBaseAssets.MySYSLOG.WriteToLog(10000, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, MyBaseThing.LastMessage, eMsgLevel.l4_Message));
                             bPreviousError = false;
                         }
                         else
                         {
                             if (MyBaseThing.StatusLevel != 1)
                             {
                                 MyBaseThing.LastMessage = $"{DateTime.Now} - Modbus Device connected.";
                                 TheBaseAssets.MySYSLOG.WriteToLog(10000, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, MyBaseThing.LastMessage, eMsgLevel.l4_Message));
                             }
                         }
                         MyBaseThing.StatusLevel = 1;
                         Dictionary <string, object> dict = ReadAll();
                         var timestamp = DateTimeOffset.Now;
                         TheBaseAssets.MySYSLOG.WriteToLog(10000, TSM.L(eDEBUG_LEVELS.VERBOSE) ? null : new TSM(MyBaseThing.EngineName, String.Format("Setting properties for {0}", MyBaseThing.FriendlyName), eMsgLevel.l4_Message, String.Format("{0}: {1}", timestamp, dict.Aggregate("", (s, kv) => s + string.Format("{0}={1};", kv.Key, kv.Value)))));
                         MyBaseThing.SetProperties(dict, timestamp);
                         if (!KeepOpen)
                         {
                             CloseModBus();
                         }
                     }
                     catch (Exception e)
                     {
                         MyBaseThing.StatusLevel = 2;
                         MyBaseThing.LastMessage = $"{DateTime.Now} - Failure during read of modbus properties: {e.Message}";
                         TheBaseAssets.MySYSLOG.WriteToLog(10000, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, MyBaseThing.LastMessage, eMsgLevel.l2_Warning, e.ToString()));
                         CloseModBus(); // Close the connection just in case there's an internal socket error that we didn't detect (yet)
                     }
                 }
             }
             else
             {
                 ExecuteSimulation();
             }
             await TheCommonUtils.TaskDelayOneEye((int)Interval, 100);
         }
     }
     catch (Exception e)
     {
         MyBaseThing.LastMessage = $"{DateTime.Now} - Error during read or processing of modbus properties: {e.Message}";
         TheBaseAssets.MySYSLOG.WriteToLog(10000, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, MyBaseThing.LastMessage, eMsgLevel.l1_Error, e.ToString()));
     }
     finally
     {
         lock (readerLoopLock)
         {
             if (bReaderLoopRunning)
             {
                 bReaderLoopRunning = false;
             }
         }
         Disconnect(null);
     }
 }
Example #8
0
        protected virtual bool InitBase(string friendlyNamePrefix, string deviceType)
        {
            if (TheCommonUtils.CGuid(MyBaseThing.ID) == Guid.Empty)
            {
                MyBaseThing.ID = Guid.NewGuid().ToString();
                if (string.IsNullOrEmpty(MyBaseThing.FriendlyName))
                {
                    MyBaseThing.FriendlyName = $"{friendlyNamePrefix}: {MyBaseThing.ID}";
                }
            }

            MyBaseThing.LastUpdate = DateTimeOffset.Now;

            MyBaseThing.EngineName = MyBaseEngine.GetEngineName();
            MyBaseThing.DeviceType = deviceType;

            TheBaseEngine.WaitForStorageReadiness((pThing, pReady) =>
            {
                if (pReady != null)
                {
                    var receiverThings                      = new TheStorageMirror <TConnectionThing>(TheCDEngines.MyIStorageService);
                    receiverThings.CacheTableName           = nameof(TConnectionThing) + TheThing.GetSafeThingGuid(MyBaseThing, nameof(TConnectionThing));
                    receiverThings.IsRAMStore               = true;
                    receiverThings.CacheStoreInterval       = 1;
                    receiverThings.IsStoreIntervalInSeconds = true;
                    receiverThings.IsCachePersistent        = true;
                    receiverThings.UseSafeSave              = true;
                    receiverThings.RegisterEvent(eStoreEvents.StoreReady, (args) =>
                    {
                        var result = args.Para as TSM;
                        if (result != null && result.LVL == eMsgLevel.l1_Error)
                        {
                            MyBaseThing.SetStatus(3, "Error loading things");
                            TheBaseAssets.MySYSLOG.WriteToLog(98201, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyBaseThing.EngineName, "Initialization", eMsgLevel.l6_Debug, String.Format("Error loading things for connection {0}", this.GetBaseThing().Address)));
                        }
                        else
                        {
                            MyConnectionThings = receiverThings;
                            if (MyConnectionThingsForm != null)
                            {
                                MyConnectionThingsForm.defDataSource = MyConnectionThings.StoreMID.ToString();
                            }
                            TheBaseAssets.MySYSLOG.WriteToLog(95272, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM(MyBaseThing.EngineName, "Initialization", eMsgLevel.l6_Debug, String.Format("Things loaded for connection {0}", this.GetBaseThing().Address)));

                            if (AutoConnect)
                            {
                                Connect();
                                if (!IsConnected)
                                {
                                    TheCommonUtils.cdeRunTaskAsync("receiverAutoConnect", async o =>
                                    {
                                        await TheCommonUtils.TaskDelayOneEye(30000, 100).ConfigureAwait(false);
                                        while (!IsConnected && AutoConnect && TheBaseAssets.MasterSwitch)
                                        {
                                            Connect();
                                            await TheCommonUtils.TaskDelayOneEye(30000, 100).ConfigureAwait(false);
                                        }
                                    }).ContinueWith(t => t.Exception);
                                }
                            }
                            mIsInitialized = true;
                            FireEvent(eThingEvents.Initialized, this, true, true);
                            FireEvent("ServerInit", this, IsConnected.ToString(), true);
                        }
                    }
                                                 );
                    receiverThings.InitializeStore(false, false);
                }
            }, true);
            return(mIsInitialized);
        }
Example #9
0
        virtual void HandleMessage(ICDEThing sender, object pIncoming)
        {
            TheProcessMessage pMsg = pIncoming as TheProcessMessage;

            if (pMsg == null || pMsg.Message == null)
            {
                return;
            }

            var cmd = TheCommonUtils.cdeSplit(pMsg.Message.TXT, ":", false, false);

            switch (cmd[0])
            {
            case "RUREADY":
                if (cmd.Length > 1 && cmd[1] == TheCommonUtils.cdeGuidToString(MyBaseThing.cdeMID))
                {
                    TheCommCore.PublishToOriginator(pMsg.Message, new TSM(pMsg.Message.ENG, "IS_READY:" + TheCommonUtils.cdeGuidToString(MyBaseThing.cdeMID), mIsInitialized.ToString())
                    {
                        FLG = 8
                    }, true);
                }
                break;

            case "CONNECT_SERVER":
                Connect();
                break;

            case nameof(MsgConnectDisconnect):
            {
                var request     = TheCommRequestResponse.ParseRequestMessageJSON <MsgConnectDisconnect>(pMsg.Message);
                var responseMsg = new MsgConnectDisconnectResponse();
                if (request == null)
                {
                    responseMsg.Error = "Error parsing request message";
                }
                else
                {
                    try
                    {
                        if (request.Connect.HasValue && request.Reconnect.HasValue)
                        {
                            responseMsg.Error = "Can specify at most one of Connect Reconnect";
                        }
                        else if (!request.Connect.HasValue && !request.Reconnect.HasValue && !request.AutoConnect.HasValue)
                        {
                            responseMsg.Error = "Must specify at least one of Connect Reconnect AutoConnect";
                        }
                        else
                        {
                            if (request.Connect.HasValue)
                            {
                                if (request.Connect == true)
                                {
                                    Connect();
                                }
                                else
                                {
                                    Disconnect(true);
                                }
                            }
                            if (request.Reconnect.HasValue)
                            {
                                Disconnect(true);
                                if (request.WaitTimeBeforeReconnect.HasValue)
                                {
                                    try
                                    {
#if !NET40
                                        await TheCommonUtils.TaskDelayOneEye(request.WaitTimeBeforeReconnect.Value, 100).ConfigureAwait(false);
#else
                                        TheCommonUtils.TaskDelayOneEye(request.WaitTimeBeforeReconnect.Value, 100).Wait();
#endif
                                    }
                                    catch (System.Threading.Tasks.TaskCanceledException) { }
                                }
                                Connect();
                            }
                            if (request.AutoConnect.HasValue)
                            {
                                AutoConnect = request.AutoConnect.Value;
                            }
                            responseMsg.Connected = IsConnected;
                        }
                    }
                    catch (Exception e)
                    {
                        responseMsg.Error = e.Message;
                    }
                }
                TheCommRequestResponse.PublishResponseMessageJson(pMsg.Message, responseMsg);
            }
            break;
            }
        }
Example #10
0
        void sinkTriggerTimeout(object pTime)
        {
            if (!TheBaseAssets.MasterSwitch)
            {
                mTimer?.Dispose();
                mTimer = null;
                return;
            }
            if (IsDisabled)
            {
                if (MyBaseThing.StatusLevel != 0)
                {
                    MyBaseThing.StatusLevel = 0;
                    MyBaseThing.LastMessage = "Countdown disabled";
                }
                return;
            }
            else if (MyBaseThing.StatusLevel == 0)
            {
                MyBaseThing.StatusLevel = 1;
                MyBaseThing.LastMessage = "Countdown enabled";
            }
            if (TheCommonUtils.cdeIsLocked(TriggerLock))
            {
                return;
            }
            lock (TriggerLock)
            {
                int tTIme = (TheCommonUtils.CInt(MyBaseThing.Value) - 1);
                if (tTIme >= 0)
                {
                    MyBaseThing.Value = tTIme.ToString();
                    if (TheCommonUtils.CBool(TheBaseAssets.MySettings.GetSetting("VThing-SimTest")) == true && MyBaseThing.ID == "TESTSIM")
                    {
                        var response = new CDMyMeshManager.Contracts.MsgReportTestStatus
                        {
                            NodeId           = TheBaseAssets.MyServiceHostInfo.MyDeviceInfo.DeviceID,
                            PercentCompleted = 100 - tTIme,
                            SuccessRate      = 100 - tTIme,
                            Status           = CDMyMeshManager.Contracts.eTestStatus.Running,
                            TestRunId        = TheCommonUtils.CGuid(TheBaseAssets.MySettings.GetSetting("TestRunID")),
                            Timestamp        = DateTimeOffset.Now,
                            ResultDetails    = new Dictionary <string, object>
                            {
                                { "SomeKPI", 123 },
                            },
                        }.Publish();
                    }
                }
                if (tTIme <= 0 && mTimer != null)
                {
                    mTimer.Dispose();
                    mTimer   = null;
                    IsActive = false;
                    MyBaseThing.StatusLevel = 0;

                    if (TheCommonUtils.CBool(TheBaseAssets.MySettings.GetSetting("VThing-SimTest")) == true && MyBaseThing.ID == "TESTSIM")
                    {
                        TheCommonUtils.TaskDelayOneEye(2000, 100).ContinueWith(t =>
                        {
                            var response = new CDMyMeshManager.Contracts.MsgReportTestStatus
                            {
                                NodeId           = TheBaseAssets.MyServiceHostInfo.MyDeviceInfo.DeviceID,
                                PercentCompleted = 100,
                                SuccessRate      = 100,
                                Status           = CDMyMeshManager.Contracts.eTestStatus.Success,
                                TestRunId        = TheCommonUtils.CGuid(TheBaseAssets.MySettings.GetSetting("TestRunID")),
                                Timestamp        = DateTimeOffset.Now,
                                ResultDetails    = new Dictionary <string, object>
                                {
                                    { "SomeKPI", 123 },
                                },
                            }.Publish();
                        });
                    }

                    if (Restart)
                    {
                        sinkTriggered(this.GetProperty(nameof(StartValue), false));
                    }
                    else
                    {
                        CountBar?.SetUXProperty(Guid.Empty, string.Format("MaxValue=100"));
                    }
                }
            }
        }
Example #11
0
        private async Task RunScriptAsync(TheScript script, TheThing variables, int stepNumber = 1, bool replay = false)
        {
            TheThing variablesSnapshot;

            try
            {
                for (; stepNumber <= script.Steps.Length; stepNumber++)
                {
                    //Clone thing before step occurs
                    variablesSnapshot = new TheThing();
                    variables.CloneThingAndPropertyMetaData(variablesSnapshot, true);

                    var step = script.Steps[stepNumber - 1];

                    var existingSnapshot = MyScriptTableStorage.MyMirrorCache.GetEntryByFunc(snapshot => snapshot.ScriptName == script.Name && snapshot.ScriptStep == stepNumber);
                    if (existingSnapshot?.Disabled == true)
                    {
                        TheBaseAssets.MySYSLOG.WriteToLog(175002, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Finished script step: skipped step because it was disabled", eMsgLevel.l3_ImportantMessage, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> {
                            { "Script", script.Name },
                            { "Step", stepNumber },
                            { "Message", step.Message.MessageName },
                            { "Target", step.Message.Target },
                        })));

                        UpdateStorageList(script.Name, "Disabled", stepNumber, script, variablesSnapshot, replay);
                        continue;
                    }

                    if (step.Condition != null)
                    {
                        var condition = TheCommonUtils.GenerateFinalStr(step.Condition, variables);
                        if (
                            (condition == "" || condition.ToLowerInvariant() == "false" || condition.Trim() == "0") ||
                            (condition.StartsWith("!") && condition.Length >= 1 && (condition.Substring(1).ToLowerInvariant() == "true") || condition.Substring(1).Trim() == "1"))
                        {
                            TheBaseAssets.MySYSLOG.WriteToLog(175002, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Finished script step: skipped step due to condition not met", eMsgLevel.l3_ImportantMessage, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> {
                                { "Script", script.Name },
                                { "Step", stepNumber },
                                { "Message", step.Message.MessageName },
                                { "Target", step.Message.Target },
                                { "Condition", step.Condition },
                                { "ConditionEvaluated", condition },
                            })));

                            UpdateStorageList(script.Name, "Condition Not Met", stepNumber, script, variablesSnapshot, replay);

                            continue;
                        }
                    }
                    var messageType = TheCommonUtils.GenerateFinalStr(step.Message.MessageName, variables);
                    var txtPayload  = TheCommonUtils.GenerateFinalStr(step.Message.Parameters?.ToString(), variables);
                    {
                        var txtPayload2 = txtPayload?.Replace("\"\\\"", "");
                        var txtPayload3 = txtPayload2?.Replace("\\\"\"", "");
                        txtPayload = txtPayload3;
                    }

                    // TODO Need a simpler and more flexible way to specify thing address in the script JSON
                    var target = step.Message.Target;
                    if (target == null)
                    {
                        if (txtPayload.Contains("EngineName"))
                        {
                            var    payloadDict        = TheCommonUtils.DeserializeJSONStringToObject <Dictionary <string, object> >(txtPayload);
                            object engineNameInferred = null;
                            if (payloadDict?.TryGetValue("EngineName", out engineNameInferred) == true && !string.IsNullOrEmpty(engineNameInferred?.ToString()))
                            {
                                target = new TheMessageAddress {
                                    EngineName = engineNameInferred.ToString()
                                };
                            }
                        }
                    }
                    if (target.EngineName.StartsWith("%") || target.EngineName.StartsWith("{"))
                    {
                        target.EngineName = TheCommonUtils.GenerateFinalStr(target.EngineName, variables);
                        // TODO Clean this up: support a serialized TheMessageAddress in the engine name, so that an output variable can be fed into a method invocation
                        try
                        {
                            var newTarget = TheCommonUtils.DeserializeJSONStringToObject <TheMessageAddress>(target.EngineName);
                            if (newTarget != null)
                            {
                                target = newTarget;
                            }
                        }
                        catch
                        {
                            // parsing error: ignore, will result in other errors downstream
                        }
                    }

                    await TheThingRegistry.WaitForInitializeAsync(target);

                    bool bDoRetry;
                    int  remainingRetryCount = step.RetryCount ?? 0;
                    do
                    {
                        existingSnapshot = MyScriptTableStorage.MyMirrorCache.GetEntryByFunc(snapshot => snapshot.ScriptName == script.Name && snapshot.ScriptStep == stepNumber);
                        if (existingSnapshot?.Disabled == true)
                        {
                            TheBaseAssets.MySYSLOG.WriteToLog(175002, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Finished script step: skipped step because it was disabled", eMsgLevel.l3_ImportantMessage, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> {
                                { "Script", script.Name },
                                { "Step", stepNumber },
                                { "Message", step.Message.MessageName },
                                { "Target", step.Message.Target },
                            })));

                            UpdateStorageList(script.Name, "Disabled", stepNumber, script, variablesSnapshot, replay);
                            break;
                        }

                        bDoRetry = false;
                        var response = await TheCommRequestResponse.PublishRequestAsync(MyBaseThing, target, messageType, new TimeSpan(0, 0, 0, 0, step.Message.timeout), null, txtPayload, null);

                        if (!string.IsNullOrEmpty(response?.PLS))
                        {
                            var outputs = TheCommonUtils.DeserializeJSONStringToObject <Dictionary <string, object> >(response.PLS);
                            if (outputs != null)
                            {
                                if (step.Message.outputs != null)
                                {
                                    foreach (var output in step.Message.outputs)
                                    {
                                        if (output.Key == "*")
                                        {
                                            variables.SetProperty(output.Value, response.PLS);
                                        }
                                        else if (outputs.TryGetValue(output.Key, out var outputValue))
                                        {
                                            variables.SetProperty(output.Value, outputValue);
                                            if (output.Value.Contains("Error") && !string.IsNullOrEmpty(TheCommonUtils.CStr(outputValue)))
                                            {
                                                TheBaseAssets.MySYSLOG.WriteToLog(175004, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Error in script step: output reported error", eMsgLevel.l1_Error, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> {
                                                    { "Script", script.Name },
                                                    { "Step", stepNumber },
                                                    { "Message", messageType },
                                                    { "Target", target },
                                                    { "PLS", txtPayload },
                                                    { "Response", response },
                                                    { "ResponsePLS", response?.PLS },
                                                })));

                                                UpdateStorageList(script.Name, $"Error {outputValue} in output", stepNumber, script, variablesSnapshot, replay);

                                                if (remainingRetryCount < 0 || remainingRetryCount > 0)
                                                {
                                                    remainingRetryCount--;
                                                    bDoRetry = true;
                                                }
                                                string retriesRemaining = bDoRetry ? (remainingRetryCount >= 0 ? $"{remainingRetryCount + 1}" : "infinite") : "none";
                                                MyBaseThing.SetStatus(3, $"Error in script '{script?.Name}', step {stepNumber}: output '{output.Value}' reported error {outputValue}. Retries remaining: {retriesRemaining}");
                                            }
                                        }
                                        else
                                        {
                                            // TODO provide access to sub-elements in the JSON
                                            //var outputParts = output.Key.Split('/');
                                            //dynamic currentNode = outputs;
                                            //foreach (var outputPart in outputParts)
                                            //{
                                            //    if (currentNode.TryGetValue(outputPart, out var nextNode))
                                            //    {
                                            //        currentNode = nextNode;
                                            //    }
                                            //}
                                        }
                                    }
                                }
                                TheBaseAssets.MySYSLOG.WriteToLog(175003, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Finished script step", eMsgLevel.l3_ImportantMessage, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> {
                                    { "Script", script.Name },
                                    { "Step", stepNumber },
                                    { "Message", messageType },
                                    { "Target", target },
                                    { "PLS", txtPayload },
                                    { "ResponsePLS", response.PLS },
                                })));

                                UpdateStorageList(script.Name, "Finished", stepNumber, script, variablesSnapshot, replay);
                            }
                            else
                            {
                                TheBaseAssets.MySYSLOG.WriteToLog(175004, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Error in script step: no outputs found in response", eMsgLevel.l1_Error, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> {
                                    { "Script", script.Name },
                                    { "Step", stepNumber },
                                    { "Message", messageType },
                                    { "Target", target },
                                    { "PLS", txtPayload },
                                    { "Response", response },
                                    { "ResponsePLS", response?.PLS },
                                })));

                                UpdateStorageList(script.Name, "Error: No Output", stepNumber, script, variablesSnapshot, replay);

                                if (step.DontRetryOnEmptyResponse != true && (remainingRetryCount < 0 || remainingRetryCount > 0))
                                {
                                    remainingRetryCount--;
                                    bDoRetry = true;
                                }
                                string retriesRemaining = bDoRetry ? (remainingRetryCount >= 0 ? $"{remainingRetryCount + 1}" : "infinite") : "none";
                                MyBaseThing.SetStatus(3, $"Error in script '{script?.Name}', step {stepNumber}: no outputs found in response. Retries remaining: {retriesRemaining}");
                            }
                        }
                        else
                        {
                            TheBaseAssets.MySYSLOG.WriteToLog(175005, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Error Script step: timeout", eMsgLevel.l1_Error, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> {
                                { "Script", script.Name },
                                { "Step", stepNumber },
                                { "Message", messageType },
                                { "Target", target },
                                { "PLS", txtPayload },
                                { "Response", response },
                            })));

                            UpdateStorageList(script.Name, "Error: Timeout", stepNumber, script, variablesSnapshot, replay);

                            //Retries infinitely unless count is specified
                            if (remainingRetryCount < 0 || remainingRetryCount > 0)
                            {
                                remainingRetryCount--;
                                bDoRetry = true;
                            }
                            string retriesRemaining = bDoRetry ? (remainingRetryCount >= 0 ? $"{remainingRetryCount + 1}" : "infinite") : "none";
                            MyBaseThing.SetStatus(3, $"Error in script '{script?.Name}', step {stepNumber}: timeout. Retries remaining: {retriesRemaining}");
                        }
                        if (bDoRetry)
                        {
                            await TheCommonUtils.TaskDelayOneEye(30000, 100).ConfigureAwait(false);
                        }
                    } while (bDoRetry && TheBaseAssets.MasterSwitch);
                }
            }
            catch (Exception e)
            {
                TheBaseAssets.MySYSLOG.WriteToLog(175006, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Error in script step", eMsgLevel.l1_Error, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> {
                    { "Script", script.Name },
                    { "Exception", e.Message },
                })));
                MyBaseThing.SetStatus(3, $"Error in script '{script?.Name}': {e.Message}");
                //Save variables instead of snapshot in case of error
                UpdateStorageList(script.Name, $"Error: {e.Message}", stepNumber, script, variables, replay);
            }
        }
Example #12
0
        private async Task PlaybackLoop(TheThing tThingOverride, CancellationToken cancelToken, IEnumerable <object> updatesToPlay, TimeSpan startupDelayRange, bool bFromAutoStart)
        {
            var lastItemTime       = DateTimeOffset.MaxValue;
            var previousUpdateTime = DateTimeOffset.Now;

            if (bFromAutoStart && AutoStartDelay > 0 && tThingOverride.FriendlyName != "ignored") // hack to delay only for real things
            {
                TheBaseAssets.MySYSLOG.WriteToLog(700, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyBaseThing.EngineName, $"Playback loop for {tThingOverride?.FriendlyName} holding for {AutoStartDelay} ms.", eMsgLevel.l6_Debug));
                await TheCommonUtils.TaskDelayOneEye(AutoStartDelay, 100);
            }
            TheBaseAssets.MySYSLOG.WriteToLog(700, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyBaseThing.EngineName, $"Playback loop started for {tThingOverride?.FriendlyName}", eMsgLevel.l6_Debug));

            if (startupDelayRange > TimeSpan.Zero)
            {
                var startupDelayInMs = (uint)startupDelayRange.TotalMilliseconds;
                var randomDelay      = TheCommonUtils.GetRandomUInt(0, startupDelayInMs);
                await TheCommonUtils.TaskDelayOneEye((int)randomDelay, 100, cancelToken).ConfigureAwait(false);
            }

            var eventconverter = TheEventConverters.GetEventConverter("JSON Things", true) as JSonThingEventConverter;

            // These get set in the callback from ProcessEventData so we can process them afterwards
            List <Tuple <TheThing, TheThingStore> > updatesProcessed = new List <Tuple <TheThing, TheThingStore> >();

            eventconverter.ApplyUpdateCallback = (thing, update) =>
            {
                updatesProcessed.Add(Tuple.Create(thing, update));
                return(false);
            };
            eventconverter.eventDecoder = (o => o.ToString());

            do
            {
                foreach (var updateObj in updatesToPlay)
                {
                    updatesProcessed.Clear();
                    eventconverter.ProcessEventData(tThingOverride, updateObj, DateTimeOffset.Now);
                    if (IsDisabled)
                    {
                        //How can a cacnel token be set?
                        break;
                    }
                    if (cancelToken.IsCancellationRequested)
                    {
                        break;
                    }
                    foreach (var updateAndThing in updatesProcessed)
                    {
                        TimeSpan timeToWait = TimeSpan.Zero;

                        var tThingProcessed       = updateAndThing.Item1;
                        var tThingUpdateProcessed = updateAndThing.Item2;
                        if (PlaybackSpeedFactor > 0)
                        {
                            var now = DateTimeOffset.Now;
                            var timeSinceLastUpdate = now - previousUpdateTime;
                            var timeToNextItem      = tThingUpdateProcessed.cdeCTIM - lastItemTime;
                            if (timeToNextItem > TimeSpan.Zero && timeSinceLastUpdate < timeToNextItem)
                            {
                                timeToWait = timeToNextItem - timeSinceLastUpdate;
                                if (timeToWait > TimeSpan.Zero)
                                {
                                    if (PlaybackSpeedFactor != 1)
                                    {
                                        timeToWait = new TimeSpan(0, 0, 0, 0, (int)(timeToWait.TotalMilliseconds / PlaybackSpeedFactor));
                                    }
                                    if (MaxItemDelay > 0 && timeToWait.TotalMilliseconds > MaxItemDelay)
                                    {
                                        timeToWait = new TimeSpan(0, 0, 0, 0, MaxItemDelay);
                                    }
                                }
                                else
                                {
                                    // falling behind!
                                }
                            }
                        }
                        else if (PlaybackItemDelay > 0)
                        {
                            timeToWait = new TimeSpan(0, 0, 0, 0, PlaybackItemDelay);
                        }

                        if (timeToWait > TimeSpan.Zero)
                        {
                            await TheCommonUtils.TaskDelayOneEye((int)timeToWait.TotalMilliseconds, 100, cancelToken).ConfigureAwait(false);
                        }

                        if (cancelToken.IsCancellationRequested)
                        {
                            break;
                        }

                        lastItemTime = tThingUpdateProcessed.cdeCTIM;
                        {
                            var now = DateTimeOffset.Now;

                            previousUpdateTime = now;

                            var timeToSend = AdjustTimestamps ? now : tThingUpdateProcessed.cdeCTIM;
                            if (tThingOverride != null)
                            {
                                if (tThingUpdateProcessed.PB.ContainsKey("FriendlyName"))
                                {
                                    tThingUpdateProcessed.PB["FriendlyName"] = tThingOverride.FriendlyName;
                                }
                                if (tThingUpdateProcessed.PB.ContainsKey("ID"))
                                {
                                    tThingUpdateProcessed.PB["ID"] = tThingOverride.FriendlyName;
                                }
                                if (tThingUpdateProcessed.PB.ContainsKey("EngineName"))
                                {
                                    tThingUpdateProcessed.PB["EngineName"] = tThingOverride.EngineName;
                                }
                                if (tThingUpdateProcessed.PB.ContainsKey("DeviceType"))
                                {
                                    tThingUpdateProcessed.PB["DeviceType"] = tThingOverride.DeviceType;
                                }
                                if (tThingUpdateProcessed.PB.ContainsKey("DeviceType"))
                                {
                                    tThingUpdateProcessed.PB["DeviceType"] = tThingOverride.DeviceType;
                                }
                            }
                            tThingProcessed.SetProperties(tThingUpdateProcessed.PB, timeToSend);
                            Interlocked.Add(ref _propertyCounter, tThingUpdateProcessed.PB.Count);
                            _lastSendTime = timeToSend;
                        }
                    }
                }
            } while (RestartPlayback && !cancelToken.IsCancellationRequested && !IsDisabled);
            TheBaseAssets.MySYSLOG.WriteToLog(700, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyBaseThing.EngineName, $"Playback loop stopped for {tThingOverride?.FriendlyName}", eMsgLevel.l6_Debug));
        }