/// <summary><see cref="TorXakisConnector.InputReceived"/></summary> private void Connector_InputReceived(TorXakisAction action) { if (action.Type == ActionType.Input && action.Channel == TorXakisModel.InputChannel) { ModelAction input = ModelAction.Deserialize(action.Data); HandleModelInput(input); } }
/// <summary> /// Deserializes the given string into an object instance. /// </summary> public static ModelAction Deserialize(string str) { string[] parts = str.Split('('); // Map the type name back to a class defined in the models assembly. string typeName = parts[0]; Type type = null; foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { if (assembly.FullName.StartsWith(ModelsAssemblyName)) { type = assembly.GetType(ModelsAssemblyName + "." + typeName); } } // Instantiate an object of this type. ModelAction action = (ModelAction)Activator.CreateInstance(type); // Assign the serialized property values on the new instance. if (parts.Length > 1) { PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); string[] values = parts[1].TrimEnd(')').Split(','); for (int i = 0; i < properties.Length; i++) { PropertyInfo property = properties[i]; string value = values[i]; if (property.PropertyType == typeof(bool)) { property.SetValue(action, bool.Parse(value)); } else if (property.PropertyType == typeof(int)) { property.SetValue(action, int.Parse(value)); } else if (property.PropertyType == typeof(string)) { property.SetValue(action, value); } } } return(action); }
/// <summary> /// Sends the given <see cref="ModelAction"/> output. /// </summary> public void SendModelOutput(ModelAction modelOutput) { lock (locker) { // Pre-filter incompatible types. if (modelOutput is null) { throw new ArgumentNullException(nameof(modelOutput)); } if (!ModelOutputs.Contains(modelOutput.GetType())) { return; } Log.Debug(this, nameof(SendModelOutput) + ": " + modelOutput); string serialized = modelOutput.Serialize(); TorXakisAction output = TorXakisAction.FromOutput(TorXakisModel.OutputChannel, serialized); Connector.SendOutput(output); } }
/// <summary> /// Handles the given <see cref="ModelAction"/> input. /// </summary> public void HandleModelInput(ModelAction modelInput) { lock (locker) { // Pre-filter incompatible types. if (modelInput is null) { throw new ArgumentNullException(nameof(modelInput)); } if (!ModelInputs.Contains(modelInput.GetType())) { return; } Log.Debug(this, nameof(HandleModelInput) + ": " + modelInput); inputs.Enqueue(modelInput); // This must now be called manually from the thread that should perform the operations. //CheckSystems(); } }
/// <summary> /// Checks the <see cref="inputs"/> and <see cref="events"/> queues. /// Determines if the <see cref="CurrentSystem"/> can be advanced. /// Determines if one of the <see cref="Systems"/> can be started. /// </summary> public void CheckSystems() { Log.Debug(this, nameof(CheckSystems) + " inputs: " + inputs.Count + " commands: " + events.Count + "\n" + this); bool transitioned = false; // If proactive transitions are possible, trigger them! if (!transitioned) { HashSet <Tuple <TransitionSystem, ProactiveTransition> > proactives = PossibleProactiveTransitions(); if (proactives.Count > 0) { Log.Debug(this, "Possible proactive transitions: " + string.Join(", ", proactives.Select(x => x.Item1.ModelAction.Name + ": " + x.Item2).ToArray())); Tuple <TransitionSystem, ProactiveTransition> selected = proactives.Random(); Log.Debug(this, "Selected proactive transition: " + selected.Item1.ModelAction.Name + ": " + selected.Item2); IAction generated = ExecuteProactiveTransition(selected); if (generated is ModelAction modelOutput) { SendModelOutput(modelOutput); } else if (generated is ISystemAction systemCommand) { SendSystemCommand(systemCommand); } transitioned = true; } } // If reactive transitions are possible, due to system events, trigger them! if (!transitioned) { if (events.Count > 0) { ISystemAction systemEvent = events.Dequeue(); Log.Debug(this, "Dequeueing system event: " + systemEvent); HashSet <Tuple <TransitionSystem, ReactiveTransition> > reactives = PossibleReactiveTransitions(systemEvent); if (reactives.Count > 0) { Log.Debug(this, "Possible reactive transitions: " + string.Join(", ", reactives.Select(x => x.Item1.ModelAction.Name + ": " + x.Item2).ToArray())); Tuple <TransitionSystem, ReactiveTransition> selected = reactives.Random(); Log.Debug(this, "Selected reactive transition: " + selected.Item1.ModelAction.Name + ": " + selected.Item2); ExecuteReactiveTransition(systemEvent, selected); transitioned = true; } else { // Since all system events are being looped through this, not being able to handle one is not an error per se. (TPE) Log.Warn(this, "No reactive transition possible for system event: " + systemEvent); } } } // If reactive transitions are possible, due to model inputs, trigger them! if (!transitioned) { if (inputs.Count > 0) { ModelAction modelInput = inputs.Dequeue(); Log.Debug(this, "Dequeueing model input: " + modelInput); HashSet <Tuple <TransitionSystem, ReactiveTransition> > reactives = PossibleReactiveTransitions(modelInput); if (reactives.Count > 0) { Log.Debug(this, "Possible reactive transitions: " + string.Join(", ", reactives.Select(x => x.Item1.ModelAction.Name + ": " + x.Item2).ToArray())); Tuple <TransitionSystem, ReactiveTransition> selected = reactives.Random(); Log.Debug(this, "Selected reactive transition: " + selected.Item1.ModelAction.Name + ": " + selected.Item2); ExecuteReactiveTransition(modelInput, selected); transitioned = true; } else { Log.Error(this, "No reactive transition possible for model input: " + modelInput); } } } // If a transition was taken, re-evaluate immediately! if (transitioned) { if (CurrentSystem.CurrentState == CurrentSystem.InitialState) { Log.Debug(this, "System has looped: " + CurrentSystem); CurrentSystem = null; } CheckSystems(); } // If no transition was taken (due to ignored input or event), we still need to check the next input or event. else if (inputs.Count > 0 || events.Count > 0) { CheckSystems(); } }