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 } }
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); }
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)!"); }
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; }
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; }
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)]) // }, // }; }
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; }); }
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); }
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; } }
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); }
// 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(); } }
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); } }
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); }
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" }); } }
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 }
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!"); }
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); }
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())); }
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); } }
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())); }
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; }
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); }
public override IReadOnlyDictionary <string, object> Handle(PiSensorNetDbContext context, int?moduleID) { throw new NotImplementedException(); }
public abstract IReadOnlyDictionary <string, object> Handle(PiSensorNetDbContext context, int?moduleID);
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); }