Exemple #1
0
        private static void PollMessagesToSend(Queue <Message> messagesToSend, IpiSensorNetConfiguration moduleConfiguration, Action <string> logger)
        {
            logger("PollMessagesToSend: Start...");

            using (var context = PiSensorNetDbContext.Connect(moduleConfiguration.ConnectionString))
            {
                var messages = context.Messages
                               .Include(i => i.Function)
                               .Include(i => i.Module)
                               .AsNoTracking()
                               .Where(i => i.State == MessageStateEnum.Queued)
                               .OrderBy(i => i.Created)
                               .ToList();

                if (messages.Count == 0)
                {
                    return;
                }

                foreach (var message in messages)
                {
                    messagesToSend.Enqueue(message);
                }

                logger($"PollMessagesToSend: Enqueued {messages.Count} messages(s)!"); // ~25ms, 1 message
            }
        }
Exemple #2
0
        private static int?SendMessage(Queue <Message> messagesToSend, int?lastMessageSentID, IpiSensorNetConfiguration moduleConfiguration, StringBuilder messageBuilder, Action <string> logger)
        {
            if (messagesToSend.Count == 0 || lastMessageSentID.HasValue)
            {
                return(lastMessageSentID);
            }

            logger("SendMessage: Start...");

            var messageToSend = messagesToSend.Dequeue();
            var text          = AssembleMessage(messageBuilder, messageToSend, moduleConfiguration);

            logger($"SendMessage: Putting '{text}'..."); // <0.5ms

            Functionalities.Serial.Put(text);

            using (var context = PiSensorNetDbContext.Connect(moduleConfiguration.ConnectionString))
            {
                context.EnqueueUpdate <Message>(
                    i => i.State == MessageStateEnum.Sent && i.Sent == DateTime.Now,
                    i => i.ID == messageToSend.ID);

                context.ExecuteRaw();
            }

            logger("SendMessage: Message sent!"); // ~22ms

            return(messageToSend.ID);
        }
Exemple #3
0
        internal static void HandleAbsoluteTimeTriggers(IpiSensorNetConfiguration moduleConfiguration, IReadOnlyDictionary <TriggerSourceTypeEnum, ITriggerSourceHandler> triggerHandlers, IReadOnlyDictionary <int, TriggerDelegate> triggerDelegates, ConcurrentQueue <TriggerSource> absoluteTimeTriggers, Action <string> logger, IReadOnlyDictionary <TriggerDependencyTypeEnum, ITriggerDependencyHandler> triggerDependencyHandlers)
        {
            if (absoluteTimeTriggers.Count == 0)
            {
                return;
            }

            logger("HandleAbsoluteTimeTriggers: Start...");

            using (var context = PiSensorNetDbContext.Connect(moduleConfiguration.ConnectionString))
            {
                var handlerContext = new TriggerSourceHandlerHelperContext(context, triggerHandlers, triggerDelegates, triggerDependencyHandlers, DateTime.Now);

                while (absoluteTimeTriggers.Count > 0)
                {
                    TriggerSource triggerSource;

                    if (absoluteTimeTriggers.TryDequeue(out triggerSource))
                    {
                        continue;
                    }

                    logger($"HandleAbsoluteTimeTriggers: Handling trigger source #{triggerSource.ID}...");

                    TriggerSourceHandlerHelper.Handle(handlerContext, triggerSource);
                }
            }

            logger($"HandleAbsoluteTimeTriggers: Finished handling {absoluteTimeTriggers.Count} trigger source(s)!");
        }
Exemple #4
0
 public FunctionHandlerContext(IpiSensorNetConfiguration moduleConfiguration, PiSensorNetDbContext databaseContext, IReadOnlyDictionary <FunctionTypeEnum, IQueryableFunctionHandler> queryableFunctionHandlers, IReadOnlyMap <FunctionTypeEnum, int> functionTypes, IReadOnlyMap <string, int> functionNames, IReadOnlyDictionary <TriggerSourceTypeEnum, ITriggerSourceHandler> triggerSourceHandlers, IReadOnlyDictionary <int, TriggerDelegate> triggerDelegates, IReadOnlyDictionary <TriggerDependencyTypeEnum, ITriggerDependencyHandler> triggerDependencyHandlers, DateTime triggerDateTime)
     : base(databaseContext, triggerSourceHandlers, triggerDelegates, triggerDependencyHandlers, triggerDateTime)
 {
     ModuleConfiguration       = moduleConfiguration;
     QueryableFunctionHandlers = queryableFunctionHandlers;
     FunctionTypes             = functionTypes;
     FunctionNames             = functionNames;
 }
Exemple #5
0
        public TriggerSourceHandlerHelperContext(PiSensorNetDbContext databaseContext, IReadOnlyDictionary <TriggerSourceTypeEnum, ITriggerSourceHandler> triggerSourceHandlers, IReadOnlyDictionary <int, TriggerDelegate> triggerDelegates, IReadOnlyDictionary <TriggerDependencyTypeEnum, ITriggerDependencyHandler> triggerDependencyHandlers, DateTime triggerDateTime)
            : base(databaseContext)
        {
            TriggerSourceHandlers     = triggerSourceHandlers;
            TriggerDelegates          = triggerDelegates;
            TriggerDependencyHandlers = triggerDependencyHandlers;

            TriggerDateTime = triggerDateTime;
        }
Exemple #6
0
        public override IReadOnlyDictionary <string, object> Handle(PiSensorNetDbContext context, int?moduleID)
        {
            // TODO KZ: check filtration performance
            var readouts = context.TemperatureReadouts
                           .Where(i => i.TemperatureSensor.ModuleID == moduleID.Value)
                           .GroupBy(i => i.TemperatureSensorID)
                           .Select(i => new
            {
                TemperatureSensorID = i.Key,
                Max = i.Select(ii => ii.Received).Max()
            });

            var sensors = context.TemperatureSensors.Where(i => i.ModuleID == moduleID.Value);

            var readings = context.TemperatureReadouts
                           .Join(readouts,
                                 i => i.TemperatureSensorID,
                                 i => i.TemperatureSensorID,
                                 (l, r) => new { l, r })
                           .Where(i => i.l.Received == i.r.Max)
                           .Join(sensors,
                                 i => i.l.TemperatureSensorID,
                                 i => i.ID,
                                 (l, r) => new { r.ID, r.Address, r.FriendlyName, l.l.Value, l.l.Received })
                           .ToList();

            var byAddress      = new Dictionary <string, decimal>(readings.Count);
            var byFriendlyName = new Dictionary <string, decimal>(readings.Count);

            foreach (var reading in readings)
            {
                byAddress.Add(reading.Address, reading.Value);

                if (reading.FriendlyName != null)
                {
                    byFriendlyName.AddOrReplace(reading.FriendlyName, reading.Value);
                }
            }

            LastTemperatureReadoutsByAddress      = byAddress;
            LastTemperatureReadoutsByFriendlyName = byFriendlyName;

            return(ToProperties());

            //return new Dictionary<string, TypedObject>(properties.Count)
            //       {
            //           {
            //               nameof(LastTemperatureReadoutsByAddress),
            //               byAddress.ToTyped(properties[nameof(LastTemperatureReadoutsByAddress)])
            //           },
            //           {
            //               nameof(LastTemperatureReadoutsByFriendlyName),
            //               byFriendlyName.ToTyped(properties[nameof(LastTemperatureReadoutsByFriendlyName)])
            //           },
            //       };
        }
Exemple #7
0
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc()
            .AddJsonOptions(options => { options.SerializerSettings.Converters.Add(new ExtendedEnumConverter()); });

            services.AddTransient <Func <PiSensorNetDbContext> >(provider =>
                                                                 () => PiSensorNetDbContext.Connect(ConnectionString));

            services.AddSignalR(options => { options.Hubs.EnableDetailedErrors = true; });
        }
Exemple #8
0
        internal static IReadOnlyMap <string, int> CacheModuleAddresses(PiSensorNetDbContext context)
        {
            var modules = context.Modules
                          .AsNoTracking()
                          .Select(i => new
            {
                i.ID,
                i.Address
            })
                          .ToMap(i => i.Address, i => i.ID);

            return(modules);
        }
Exemple #9
0
        protected TestClassBase()
        {
            PiSensorNetDbContext.Initialize(Common.TestsConfiguration.ConnectionString);

            Context = PiSensorNetDbContext.Connect(Common.TestsConfiguration.ConnectionString);

            if (InTransaction)
            {
                Transaction = Context.Database.BeginTransaction(IsolationLevel.ReadUncommitted);
            }

            if (Constants.IsWindows && LogQueries)
            {
                Context.Database.Log = Console.Write;
            }
        }
Exemple #10
0
        public static int Main(string[] args)
        {
            using (var context = PiSensorNetDbContext.Connect(Configuration.ConnectionString)
                                 .WithChangeTracking()
                                 .WithAutoSave())
            {
                //var module = context.Modules.Add(new Module("1izik")
                //{
                //    ModuleFunctions = context.Functions
                //                                                              .AsEnumerable()
                //                                                              .Select(i => new ModuleFunction(0, i.ID))
                //                                                              .ToList()
                //});

                var trigger = new Trigger("Test1", "", null)
                {
                    TriggerSources = new[]
                    {
                        new TriggerSource(0, TriggerSourceTypeEnum.AbsoluteTime)
                        {
                            AbsoluteTime = TimeSpan.Parse("12:13:21")
                        },
                        new TriggerSource(0, TriggerSourceTypeEnum.TemperatureReadout)
                        {
                        }
                    },
                    TriggerDependencies = new[]
                    {
                        new TriggerDependency(0, TriggerDependencyTypeEnum.Communication),
                        new TriggerDependency(0, TriggerDependencyTypeEnum.LastTemperatureReadout)
                    }
                };

                if (context.Triggers.Where(i => i.FriendlyName == trigger.FriendlyName).SingleOrDefault() == null)
                {
                    context.Triggers.Add(trigger);
                }
            }

            EngineMain.Main2(new[] { "standalone" });

            return(0);
        }
Exemple #11
0
        // ReSharper disable once UnusedMember.Local
        private static void Demo(IpiSensorNetConfiguration moduleConfiguration, IReadOnlyMap <FunctionTypeEnum, int> functionTypes, IReadOnlyMap <string, int> functionNames, IReadOnlyDictionary <FunctionTypeEnum, IFunctionHandler> functionHandlers, IReadOnlyDictionary <FunctionTypeEnum, IQueryableFunctionHandler> queryableFunctionHandlers, IReadOnlyDictionary <TriggerSourceTypeEnum, ITriggerSourceHandler> triggerSourceHandlers, IReadOnlyDictionary <int, TriggerDelegate> triggerDelegates, IReadOnlyDictionary <TriggerDependencyTypeEnum, ITriggerDependencyHandler> triggerDependencyHandlers)
        {
            using (var context = PiSensorNetDbContext.Connect(moduleConfiguration.ConnectionString).WithChangeTracking())
            {
                var functionID   = context.Functions.AsNoTracking().Where(i => i.FunctionType == FunctionTypeEnum.FunctionList).Select(i => i.ID).Single();
                var moduleNumber = context.Modules.AsNoTracking().Count() + 1;
                var address      = $"test{moduleNumber}";

                context.Modules
                .Add(new Module(address)
                {
                    FriendlyName = address,
                    Description  = "Test module",
                }
                     .For(i => { i.ModuleFunctions.Add(context.Functions.Select(f => new ModuleFunction(i, f))); })
                     .For(i =>
                {
                    i.Packets.Add(new[]
                    {
                        new Packet(i, 0, "identify;function_list;voltage;report;ow_list;ow_ds18b20_temperature;ow_ds18b20_temperature_periodical;", DateTime.Now)
                        // "2854280E02000070|25.75;28AC5F2600008030|25.75;"
                        {
                            FunctionID = functionID,
                        }
                    });
                })
                     );

                context.SaveChanges();

                var module = context.Modules.AsNoTracking().Where(i => i.Address == address).Single();
                var packet = context.Packets.Include(i => i.Function).AsNoTracking().Where(i => i.ModuleID == module.ID).OrderByDescending(i => i.Created).First();

                // ReSharper disable once PossibleInvalidOperationException
                var functionHandler = functionHandlers[packet.Function.FunctionType];
                var taskQueue       = new HubMessageQueue();

                functionHandler.Handle(new FunctionHandlerContext(moduleConfiguration, context, queryableFunctionHandlers, functionTypes, functionNames, triggerSourceHandlers, triggerDelegates, triggerDependencyHandlers, DateTime.Now), packet, ref taskQueue);

                context.SaveChanges();
            }
        }
Exemple #12
0
        public Startup()
        {
            ToConsole("Starting...");

            PiSensorNetDbContext.Initialize(ConnectionString);

            ToConsole("Context initialized!");

            var debugProfile = Environment.GetEnvironmentVariable("DEBUG_PROFILE");

            if (debugProfile?.Equals("vs", StringComparison.InvariantCultureIgnoreCase) ?? false)
            {
                var config = new ConfigurationBuilder().AddJsonFile("Properties/launchSettings.json").Build();
                var url    = config[$"profiles:{debugProfile}:launchUrl"];

                Process.Start("chrome", url);

                //LoadDemoData(ConnectionString);
            }
        }
Exemple #13
0
        private static int?HandleArguments(string[] args)
        {
            var recreate      = args.Any(i => String.Equals(i, "recreate", StringComparison.InvariantCultureIgnoreCase));
            var recreateOnly  = args.Any(i => String.Equals(i, "recreateOnly", StringComparison.InvariantCultureIgnoreCase));
            var validateModel = args.Any(i => String.Equals(i, "validateModel", StringComparison.InvariantCultureIgnoreCase));

            var recreateDatabase = (recreate || recreateOnly) && !validateModel;

            PiSensorNetDbContext.Initialize(Configuration.ConnectionString, recreateDatabase);

            //PiSensorNetDbContext.Logger = Console.Write;

            //if (validateModel)
            //{
            //    PiSensorNetDbContext.CheckCompatibility(Configuration.ConnectionString);

            //    Debug("HandleArguments: Model validation finished, exiting!");

            //    return 0;
            //}

            //if (recreateOnly)
            //{
            //    ToConsole("Main: Database recreated, exiting!");

            //    return 0;
            //}

            //if (!recreate && !standalone && args.Length > 0)
            //{
            //    ToConsole($"Main: ERROR: Wrong arguments given: '{args.Join(" ")}'.");

            //    return 1;
            //}

            return(null);
        }
Exemple #14
0
        private static void LoadDemoData(string connectionString)
        {
            using (var context = PiSensorNetDbContext.Connect(connectionString).WithAutoSave())
            {
                if (context.Modules.FirstOrDefault() != null)
                {
                    return;
                }

                var functions = context.Functions.AsNoTracking().ToDictionary(i => i.FunctionType, i => i.ID);

                var module1 = context.Modules.Add(new Module("1izik")
                {
                    FriendlyName = "kiziu no 1", State = ModuleStateEnum.Identified
                });
                context.Modules.Add(new Module("2izik")
                {
                    FriendlyName = "kiziu no 2"
                });
                context.Modules.Add(new Module("3izik")
                {
                    FriendlyName = "kiziu no 3"
                });

                context.SaveChanges();

                context.ModuleFunctions.AddRange(functions.Select(i => new ModuleFunction(module1.ID, i.Value)));

                context.TemperatureSensors.Add(
                    new TemperatureSensor(module1.ID, "2854280E02000070"),
                    new TemperatureSensor(module1.ID, "28AC5F2600008030")
                {
                    FriendlyName = "Sonda"
                });
            }
        }
Exemple #15
0
        internal static void HandleMessage(string clientID, int?moduleID, FunctionTypeEnum functionType, bool isQuery, string text, IReadOnlyMap <string, int> moduleAddresses, IpiSensorNetConfiguration moduleConfiguration, IReadOnlyMap <FunctionTypeEnum, int> functionTypes, IHubProxy hubProxy, Action <string> logger)
        {
            logger($"HandleMessage: Received message from ${clientID} to @{moduleID?.ToString() ?? "ALL"} - {functionType}{(text == null ? (isQuery ? "?" : String.Empty) : ":" + text)}");

            if (moduleID.HasValue && !moduleAddresses.Reverse.ContainsKey(moduleID.Value))
            {
                hubProxy.Invoke("error", clientID, $"Module #{moduleID.Value} does not exist.");

                logger($"HandleMessage: ERROR: Message not handled, module #{moduleID.Value} does not exist!");

                return;
            }

            int messageID;

            using (var context = PiSensorNetDbContext.Connect(moduleConfiguration.ConnectionString))
            {
                var message = new Message(functionTypes.Forward[functionType], isQuery)
                {
                    ModuleID = moduleID,
                };

                if (!isQuery)
                {
                    message.Text = text;
                }

                context.Messages.Add(message);

                context.SaveChanges();

                messageID = message.ID;
            }

            logger($"HandleMessage: Message handled to #{messageID}!"); // ~38ms
        }
Exemple #16
0
        private static void BuildCache()
        {
            using (var context = PiSensorNetDbContext.Connect(Configuration.ConnectionString))
            {
                ModuleAddresses = CacheModuleAddresses(context);

                var cachedFunctions = CacheFunctions(context);
                FunctionTypes = cachedFunctions.Item1;
                FunctionNames = cachedFunctions.Item2;

                var cachedFunctionHandlers = CacheFunctionHandlers();
                FunctionHandlers          = cachedFunctionHandlers.Item1;
                QueryableFunctionHandlers = cachedFunctionHandlers.Item2;

                TriggerSourceHandlers     = CacheTriggerSourceHandlers();
                TriggerDependencyHandlers = CacheTriggerDependencyHandlers();

                var cachedTriggerSources = CacheTriggerSources(context, TriggerDependencyHandlers);
                TriggerSources   = cachedTriggerSources.Item1;
                TriggerDelegates = cachedTriggerSources.Item2;
            }

            ToConsole("BuildCache: Cache built!");
        }
Exemple #17
0
        public static int Main(string[] args)
        {
            ToConsole("Main: Initializing Serial Monitor..");

            var toDispose       = new DisposalQueue();
            var engineProcessID = FindSerialProcessID(Configuration, ToConsole);

            PiSensorNetDbContext.Initialize(Configuration.ConnectionString);

            //PiSensorNetDbContext.Logger = Console.Write;

            ToConsole("Main: Context initialized!");

            toDispose += Signal.Handle(SignalHandlers);

            Functionalities.Serial.Open();

            Functionalities.Pins.Setup(BroadcomPinNumberEnum.Gpio18, PinModeEnum.Input, PullUpModeEnum.Up);

            toDispose += Functionalities.Interrupts.SetupPolled(BroadcomPinNumberEnum.Gpio18, InterruptModeEnum.FallingEdge, SerialInterruptHandler);

            ToConsole("Main: Started!");

            while (!_doQuit)
            {
                WaitHandle.WaitOne(_readSerial == 0 ? -1 : 3);

                if (_readSerial > 0)
                {
                    --_readSerial;
                    if (ReadSerial(ReceivedMessages, Buffer, ToConsole))
                    {
                        ++_readSerial;
                        continue;
                    }

                    if (_readSerial > 0)
                    {
                        continue;
                    }
                }

                HandleReceivedMessages(engineProcessID, ReceivedMessages, Configuration, ToConsole);

                if (_pollMessagesToSend)
                {
                    _pollMessagesToSend = false;
                    PollMessagesToSend(MessagesToSend, Configuration, ToConsole);
                }

                _lastMessageSentID = SendMessage(MessagesToSend, _lastMessageSentID, Configuration, MessageBuilder, ToConsole);
            }

            ToConsole("Main: Stopping...");

            toDispose.Dispose();

            Functionalities.Interrupts.Remove(BroadcomPinNumberEnum.Gpio18);

            Functionalities.Serial.Flush();
            Functionalities.Serial.Close();

            ToConsole("Main: Stopped!");

            return(0);
        }
Exemple #18
0
        internal static Tuple <IReadOnlyMap <FunctionTypeEnum, int>, IReadOnlyMap <string, int> > CacheFunctions(PiSensorNetDbContext context)
        {
            var functions = context.Functions
                            .AsNoTracking()
                            .Select(i => new
            {
                i.FunctionType,
                i.ID,
                i.Name,
            })
                            .ToList();

            var functionTypes = functions.ToMap(i => i.FunctionType, i => i.ID);
            var functionames  = functions.ToMap(i => i.Name, i => i.ID);

            return(Tuple.Create(functionTypes.ReadOnly(), functionames.ReadOnly()));
        }
Exemple #19
0
        internal static void HandleReceivedMessages(int?engineProcessID, ConcurrentQueue <string> receivedMessages, IpiSensorNetConfiguration moduleConfiguration, Action <string> logger)
        {
            if (receivedMessages.Count == 0)
            {
                return;
            }

            logger("HandleReceivedMessages: Start...");

            var arePacketsProcessed = false;

            using (var context = PiSensorNetDbContext.Connect(moduleConfiguration.ConnectionString))
            {
                while (receivedMessages.Count > 0)
                {
                    logger("HandleReceivedMessages: Dequeing...");

                    string text;
                    if (!receivedMessages.TryDequeue(out text))
                    {
                        continue;
                    }

                    logger($"HandleReceivedMessages: Processing '{text}'...");

                    var isFailed = text.StartsWith("FAIL ", StringComparison.InvariantCultureIgnoreCase);
                    var isOk     = text.StartsWith("OK ", StringComparison.InvariantCultureIgnoreCase);

                    if (isFailed || isOk)
                    {
                        if (!_lastMessageSentID.HasValue)
                        {
                            logger($"HandleReceivedMessages: ERROR: Message '{text} was not handled due to lack of {nameof(_lastMessageSentID)}!");
                            continue;
                        }

                        var state = isOk ? MessageStateEnum.Completed : MessageStateEnum.Failed;
                        var error = isFailed ? text.Substring("FAIL ".Length) : null;

                        //context.EnqueueRaw(Message.GenerateUpdate(context,
                        //    new Dictionary<Expression<Func<Message, object>>, string>
                        //    {
                        //        {i => i.State, state.ToSql()},
                        //        {i => i.ResponseReceived, DateTime.Now.ToSql()},
                        //        {i => i.Error, error.ToSql()},
                        //    },
                        //    new Tuple<Expression<Func<Message, object>>, string, string>(i => i.ID, "=", _lastMessageSentID.Value.ToSql())));

                        context.EnqueueUpdate <Message>(
                            i => i.State == state && i.ResponseReceived == DateTime.Now && i.Error == error,
                            i => i.ID == _lastMessageSentID.Value);

                        logger($"HandleReceivedMessages: Updated message #{_lastMessageSentID.Value} to '{state}'!");

                        _lastMessageSentID = null;
                    }
                    else
                    {
                        var partialPacket = new PartialPacket(
                            String.Format(moduleConfiguration.AddressPattern, text.SubstringBetween("@", "#")),
                            Byte.Parse(text.SubstringBetween("#", "(")),
                            Byte.Parse(text.SubstringBetween("(", "/")),
                            Byte.Parse(text.SubstringBetween("/", ")")),
                            text.SubstringAfter("):"),
                            DateTime.Now);

                        context.PartialPackets.Add(partialPacket);

                        arePacketsProcessed = true;

                        logger("HandleReceivedMessages: Processed message to new partial packet!");
                    }
                }

                context.SaveChanges();

                logger("HandleReceivedMessages: Done!"); // ~64ms, 4 messagess
            }

            if (arePacketsProcessed && engineProcessID.HasValue)
            {
                Signal.Send(engineProcessID.Value, SignalTypeEnum.User1);
            }
        }
Exemple #20
0
        internal static Tuple <IReadOnlyDictionary <TriggerSourceTypeEnum, IReadOnlyCollection <TriggerSource> >, IReadOnlyDictionary <int, TriggerDelegate> > CacheTriggerSources(PiSensorNetDbContext context, IReadOnlyDictionary <TriggerDependencyTypeEnum, ITriggerDependencyHandler> triggerDependencyHandlers)
        {
            // TODO KZ: test
            var triggerSources = context.TriggerSources
                                 .Include(i => i.Trigger)
                                 .AsNoTracking()
                                 .ToList();

            var groupedTriggerSources = triggerSources.GroupBy(i => i.TriggerID)
                                        .ToDictionary();

            var triggerDependencies = context.TriggerDependencies
                                      .AsNoTracking()
                                      .AsEnumerable()
                                      .GroupBy(i => i.TriggerID)
                                      .ToDictionary();

            triggerSources.Each(i =>
            {
                var trigger = i.Trigger;

                if (trigger.TriggerSources.Count == 0)
                {
                    trigger.TriggerSources.Add(groupedTriggerSources[trigger.ID]);
                }

                if (trigger.TriggerDependencies.Count == 0)
                {
                    trigger.TriggerDependencies.Add(triggerDependencies[trigger.ID]);
                }
            });

            var typedTriggerSources = triggerSources.GroupBy(i => i.Type)
                                      .ToDictionary(i => i.ReadOnly());

            var properties = new Dictionary <string, IReadOnlyDictionary <string, Type> >(1)
            {
                {
                    nameof(TriggerDelegateContext.Properties),
                    triggerDependencyHandlers.SelectMany(i => i.Value.Properties)
                    .ToDictionary()
                }
            };

            var triggerDelegates = new Dictionary <int, TriggerDelegate>();

            foreach (var triggerSource in triggerSources)
            {
                var trigger = triggerSource.Trigger;
                if (triggerDelegates.ContainsKey(trigger.ID))
                {
                    continue;
                }

                var methodCompilationResult = TriggerDelegateCompilerHelper.Compile(properties, trigger.Content);
                if (!methodCompilationResult.IsSuccessful)
                {
                    // ReSharper disable once AssignNullToNotNullAttribute
                    var errors = methodCompilationResult.CompilerErrors.Select(i => $"({i.Line}, {i.Column}) [{i.ErrorNumber}] {i.ErrorText}").Join(Environment.NewLine);

                    throw new Exception($"Compilation of {nameof(Trigger)} #{trigger} failed.{Environment.NewLine}{errors}");
                }

                triggerDelegates.Add(trigger.ID, methodCompilationResult.Method);
            }

            return(Tuple.Create(typedTriggerSources.ReadOnly(), triggerDelegates.ReadOnly()));
        }
Exemple #21
0
        internal static bool MergePackets(PiSensorNetDbContext context, IpiSensorNetConfiguration moduleConfiguration, IReadOnlyMap <FunctionTypeEnum, int> functionTypes, IReadOnlyMap <string, int> functionNames, IReadOnlyMap <string, int> moduleAddresses, CacheModuleAddressesDelegate cacheModuleAddresses, Action <string> logger)
        {
            logger("MergePackets: Start...");

            var partialPackets = context.PartialPackets
                                 .AsNoTracking()
                                 .Where(i => i.State == PartialPacketStateEnum.New)
                                 .ToList();

            //logger("MergePackets: Done selecting..."); // ~14ms

            var groupedPackets = partialPackets.GroupBy(i => new
            {
                i.Address,
                i.Number,
                i.Total
            })
                                 .ToList();

            var newModules = new Dictionary <string, Module>();

            foreach (var packetGroup in groupedPackets)
            {
                var address = packetGroup.Key.Address;

                if (moduleAddresses.Forward.ContainsKey(address) || newModules.ContainsKey(address))
                {
                    continue;
                }

                var module = new Module(address);

                context.Modules.Add(module);

                newModules.Add(address, module);
            }

            if (newModules.Count > 0)
            {
                logger("MergePackets: Done finding modules...");

                context.SaveChanges();

                logger("MergePackets: Done saving modules...");

                moduleAddresses = cacheModuleAddresses(context);
            }

            logger($"MergePackets: Done creating new modules, found {newModules.Count}!"); // 700us, no modules

            var packetGroupWithPacket = new List <Tuple <Packet, IEnumerable <PartialPacket> > >(groupedPackets.Count);

            foreach (var packetGroup in groupedPackets)
            {
                if (packetGroup.Count() != packetGroup.Key.Total)
                {
                    context.EnqueueUpdate <PartialPacket>(
                        i => i.State == PartialPacketStateEnum.Fragmented,
                        i => packetGroup.Select(ii => ii.ID).Contains(i.ID));

                    continue;
                }

                var moduleID  = moduleAddresses.Forward[packetGroup.Key.Address];
                int?messageID = null;
                var text      = packetGroup.OrderBy(i => i.Current).Select(i => i.Message).Concat();
                var received  = packetGroup.Max(i => i.Received);

                var messageIDDelimiterIndex = text.IndexOf(moduleConfiguration.MessageIDDelimiter);
                if (messageIDDelimiterIndex > 0)
                {
                    messageID = text.Substring(0, messageIDDelimiterIndex).FromBase36();
                    text      = text.Substring(messageIDDelimiterIndex + 1);
                }

                var functionName = text.SubstringBefore(moduleConfiguration.FunctionResultNameDelimiter).ToLowerInvariant();
                var functionID   = functionNames.Forward.GetValueOrNullable(functionName);

                if (functionID.HasValue)
                {
                    text = text.Substring(functionName.Length + 1);
                }

                var packet = new Packet(moduleID, packetGroup.Key.Number, text, received)
                {
                    MessageID  = messageID,
                    FunctionID = functionID,
                };

                context.Packets.Add(packet);

                packetGroupWithPacket.Add(Tuple.Create(packet, (IEnumerable <PartialPacket>)packetGroup));
            }

            if (packetGroupWithPacket.Count > 0)
            {
                //logger("MergePackets: Done parsing packet groups!"); // ~3ms

                context.SaveChanges();

                logger("MergePackets: Saved changes!"); // ~23ms

                packetGroupWithPacket.Each(p =>
                                           context.EnqueueUpdate <PartialPacket>(
                                               i => i.PacketID == p.Item1.ID && i.State == PartialPacketStateEnum.Merged,
                                               i => p.Item2.Select(ii => ii.ID).Contains(i.ID)));

                context.ExecuteRaw();

                logger("MergePackets: Updated partial packets!"); // ~15ms, 1 packet
            }

            logger($"MergePackets: Done, created {packetGroupWithPacket.Count} packet(s)!");

            return(packetGroupWithPacket.Count > 0);
        }
 public TriggerSourceHandlerContext(PiSensorNetDbContext databaseContext)
 {
     DatabaseContext = databaseContext;
 }
Exemple #23
0
        internal static bool HandlePackets(PiSensorNetDbContext context, IpiSensorNetConfiguration moduleConfiguration, IReadOnlyMap <FunctionTypeEnum, int> functionTypes, IReadOnlyMap <string, int> functionNames, IReadOnlyDictionary <FunctionTypeEnum, IFunctionHandler> functionHandlers, IReadOnlyDictionary <FunctionTypeEnum, IQueryableFunctionHandler> queryableFunctionHandlers, IReadOnlyDictionary <TriggerSourceTypeEnum, ITriggerSourceHandler> triggerSourceHandlers, IReadOnlyDictionary <int, TriggerDelegate> triggerDelegates, IReadOnlyDictionary <TriggerDependencyTypeEnum, ITriggerDependencyHandler> triggerDependencyHandlers, int?serialProcessID, IHubProxy hubProxy, Action <string> logger)
        {
            logger("HandlePackets: Start...");

            var packets = context.Packets
                          .Include(i => i.Module)
                          .Include(i => i.Function)
                          .Where(i => i.State == PacketStateEnum.New)
                          .Where(i => i.FunctionID.HasValue)
                          .OrderBy(i => i.Received)
                          .ToList();

            logger("HandlePackets: Done selecting..."); // ~21ms

            if (packets.Count == 0)
            {
                return(false);
            }

            var handlerContext = new FunctionHandlerContext(moduleConfiguration, context, queryableFunctionHandlers, functionTypes, functionNames, triggerSourceHandlers, triggerDelegates, triggerDependencyHandlers, DateTime.Now);

            var handleAgain     = false;
            var newMesagesAdded = false;
            var hubTasksQueue   = new HubMessageQueue();

            foreach (var packet in packets)
            {
                // ReSharper disable once PossibleInvalidOperationException
                var handler = functionHandlers.GetValueOrDefault(packet.Function.FunctionType);
                if (handler == null)
                {
                    context.EnqueueUpdate <Packet>(
                        i => i.State == PacketStateEnum.Unhandled,
                        i => i.ID == packet.ID);

                    logger($"HandlePackets: Packet #{packet.ID} (function '{packet.Function.FunctionType}') could not be handled, no handler found!");

                    continue;
                }

                var functionHandlerResult = handler.IsModuleIdentityRequired && packet.Module.State != ModuleStateEnum.Identified
                    ? PacketStateEnum.Skipped
                    : handler.Handle(handlerContext, packet, ref hubTasksQueue);

                handleAgain     = handleAgain || functionHandlerResult.ShouldHandlePacketsAgain;
                newMesagesAdded = newMesagesAdded || functionHandlerResult.NewMessagesAdded;

                context.EnqueueUpdate <Packet>(
                    i => i.State == functionHandlerResult.PacketState && i.Processed == DateTime.Now,
                    i => i.ID == packet.ID);

                if (handler.TriggerSourceType.HasValue && functionHandlerResult.PacketState == PacketStateEnum.Handled)
                {
                    TriggerSources[handler.TriggerSourceType.Value].Each(i =>
                                                                         TriggerSourceHandlerHelper.Handle(handlerContext, i, packet.Module.ID));
                }

                logger($"HandlePackets: Packet #{packet.ID} processed to '{functionHandlerResult.PacketState}'" +
                       $"{(functionHandlerResult.NewMessagesAdded ? ", new messaged were added" : String.Empty)}" +
                       $"{(functionHandlerResult.ShouldHandlePacketsAgain ? ", requested another handling" : String.Empty)}" +
                       "!");
            }

            logger($"HandlePackets: Done handling packets, processed {packets.Count}!"); // ~51ms

            context.ExecuteRaw();

            logger("HandlePackets: Queries executed!"); // ~13ms

            if (newMesagesAdded && serialProcessID.HasValue)
            {
                Signal.Send(serialProcessID.Value, SignalTypeEnum.User1);
            }

            while (hubTasksQueue.Count > 0)
            {
                hubProxy.SafeInvoke(hubTasksQueue.Dequeue());
            }

            logger($"HandlePackets: Hub message(s) sent{(newMesagesAdded ? ", Serial signaled about new message(s)" : String.Empty)}{(handleAgain ? ", packet(s) will be handled again" : String.Empty)}!"); // ~10ms

            return(handleAgain);
        }
Exemple #24
0
 public override IReadOnlyDictionary <string, object> Handle(PiSensorNetDbContext context, int?moduleID)
 {
     throw new NotImplementedException();
 }
Exemple #25
0
 public abstract IReadOnlyDictionary <string, object> Handle(PiSensorNetDbContext context, int?moduleID);
Exemple #26
0
        public static int Main2(string[] args)
        {
            ToConsole("Main: Initializing Engine...");

            var recreate      = args.Any(i => String.Equals(i, "recreate", StringComparison.InvariantCultureIgnoreCase));
            var recreateOnly  = args.Any(i => String.Equals(i, "recreateOnly", StringComparison.InvariantCultureIgnoreCase));
            var standalone    = args.Any(i => String.Equals(i, "standalone", StringComparison.InvariantCultureIgnoreCase));
            var validateModel = args.Any(i => String.Equals(i, "validateModel", StringComparison.InvariantCultureIgnoreCase));

            standalone = standalone || recreate || recreateOnly || validateModel;

            int?serialProcessID = null;

            if (!standalone)
            {
                serialProcessID = FindSerialProcessID(Configuration, ToConsole);
            }

            var recreateDatabase = (recreate || recreateOnly) && !validateModel;

            PiSensorNetDbContext.Initialize(Configuration.ConnectionString, recreateDatabase);

            //PiSensorNetDbContext.Logger = Console.Write;

            if (validateModel)
            {
                PiSensorNetDbContext.CheckCompatibility(Configuration.ConnectionString);

                ToConsole("Main: Model validation finished, exiting!");

                return(0);
            }

            if (recreateOnly)
            {
                ToConsole("Main: Database recreated, exiting!");

                return(0);
            }

            if (!recreate && !standalone && args.Length > 0)
            {
                ToConsole($"Main: ERROR: Wrong arguments given: '{args.Join(" ")}'.");

                return(1);
            }

            ToConsole("Main: Context initialized!");

            BuildCache();

            //Demo(ModuleConfiguration, FunctionTypes, FunctionNames, FunctionHandlers, QueryableFunctionHandlers, TriggerSourceHandlers, TriggerDelegates, TriggerDependencyHandlers);
            //return 666;

            var toDispose = new DisposalQueue();
            var hubProxy  = InitializeHubConnection(Configuration, Configuration, InternalHandleMessage, ModuleAddresses, FunctionTypes, serialProcessID, toDispose, ToConsole);

            toDispose += Signal.Handle(SignalHandlers);

            var timer = new Timer(1000);

            timer.Elapsed += HandleTimerTick;

            timer.Start();

            ToConsole("Main: Started!");

            while (!_doQuit)
            {
                if (Constants.IsWindows)
                {
                    WaitHandle.WaitOne(1000);

                    using (var context = PiSensorNetDbContext.Connect(Configuration.ConnectionString))
                    {
                        _pollPackets = context.Packets
                                       .Where(i => i.State == PacketStateEnum.New)
                                       .Where(i => i.FunctionID.HasValue)
                                       .Any();

                        _pollPartialPackets = _pollPackets ||
                                              context.PartialPackets
                                              .AsNoTracking()
                                              .Where(i => i.State == PartialPacketStateEnum.New)
                                              .Any();
                    }
                }
                else
                {
                    WaitHandle.WaitOne();
                }

                if (_pollPartialPackets)
                {
                    _pollPartialPackets = false;

                    using (var context = PiSensorNetDbContext.Connect(Configuration.ConnectionString))
                    {
                        _pollPackets = MergePackets(context, Configuration, FunctionTypes, FunctionNames, ModuleAddresses, CacheModuleAddresses, ToConsole);

                        if (_pollPackets)
                        {
                            _pollPackets = false;

                            while (HandlePackets(context, Configuration, FunctionTypes, FunctionNames, FunctionHandlers, QueryableFunctionHandlers, TriggerSourceHandlers, TriggerDelegates, TriggerDependencyHandlers, serialProcessID, hubProxy, ToConsole))
                            {
                            }
                        }
                    }
                }

                HandleAbsoluteTimeTriggers(Configuration, TriggerSourceHandlers, TriggerDelegates, AbsoluteTimeTriggers, ToConsole, TriggerDependencyHandlers);
            }

            ToConsole("Main: Stopping...");

            timer.Stop();
            toDispose.Dispose();

            ToConsole("Main: Stopped!");

            return(0);
        }