From d4b5c5e463fa124f3dc13b217423af2436c39a60 Mon Sep 17 00:00:00 2001 From: Marcel Vielhaus Date: Thu, 28 Nov 2024 06:46:23 +0100 Subject: [PATCH 1/4] Give structure to current extensions and update comments --- .../Processes/ProcessHolderExtensions.cs | 166 +++++++----------- 1 file changed, 68 insertions(+), 98 deletions(-) diff --git a/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs b/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs index b8dfc61..abd8d03 100644 --- a/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs +++ b/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs @@ -11,121 +11,71 @@ namespace Moryx.ControlSystem.Processes /// public static class ProcessHolderExtensions { + #region Get Position /// - /// Get the position by number + /// Get the position by its /// - public static TPosition GetPositionByIdentifier(this IProcessHolderGroup group, string identifier) - where TPosition : IProcessHolderPosition - { - return GetPosition(group.Positions, t => t.Identifier == identifier); - } + public static TPosition GetPositionByIdentifier(this IProcessHolderGroup group, string identifier) + where TPosition : IProcessHolderPosition => GetPosition(group.Positions, t => t.Identifier == identifier); /// - /// Get the position by number + /// Get the position by its /// public static TPosition GetPositionByIdentifier(this IEnumerable positions, string identifier) - where TPosition : IProcessHolderPosition - { - return GetPosition(positions, t => t.Identifier == identifier); - } + where TPosition : IProcessHolderPosition => GetPosition(positions, t => t.Identifier == identifier); /// - /// Get the position by session + /// Get the position by its /// public static TPosition GetPositionBySession(this IProcessHolderGroup group, Session session) - where TPosition : IProcessHolderPosition - { - return GetPosition(group.Positions, t => t.Session?.Id == session.Id); - } + where TPosition : IProcessHolderPosition => GetPosition(group.Positions, t => t.Session?.Id == session.Id); /// - /// Get the position by session + /// Get the position by its /// public static TPosition GetPositionBySession(this IEnumerable positions, Session session) - where TPosition : IProcessHolderPosition - { - return GetPosition(positions, t => t.Session?.Id == session.Id); - } + where TPosition : IProcessHolderPosition => GetPosition(positions, t => t.Session?.Id == session.Id); /// - /// Get the position by its process id + /// Get the position by its /// public static TPosition GetPositionByProcessId(this IProcessHolderGroup group, long processId) - where TPosition : IProcessHolderPosition - { - return GetPosition(group.Positions, t => t.Process?.Id == processId); - } + where TPosition : IProcessHolderPosition => GetPosition(group.Positions, t => t.Process?.Id == processId); /// - /// Get the position by its process id + /// Get the position by its /// public static TPosition GetPositionByProcessId(this IEnumerable positions, long processId) - where TPosition : IProcessHolderPosition - { - return GetPosition(positions, t => t.Process?.Id == processId); - } + where TPosition : IProcessHolderPosition => GetPosition(positions, t => t.Process?.Id == processId); /// /// Get the position by id of the running activity /// public static TPosition GetPositionByActivityId(this IProcessHolderGroup group, long activityId) - where TPosition : IProcessHolderPosition - { - return GetPosition(group.Positions, t => (t.Session as ActivityStart)?.Activity.Id == activityId); - } - - /// - /// Checks if the group holds a process with a finished activity having the matching result - /// - public static bool HasFinishedActivity(this IProcessHolderGroup group, long activityId, long activityResult) - where TPosition : IProcessHolderPosition - { - return group.Positions.Any(position => position.HasFinishedActivity(activityId, activityResult)); - } - /// - /// Checks if the position holds a process with a finished activity having the matching result. - /// - public static bool HasFinishedActivity(this IProcessHolderPosition holderPosition, long activityId, long activityResult) - { - return holderPosition.Process?.GetActivities(activity => activity.Id == activityId && activity.Result?.Numeric == activityResult).Any() == true; - } + where TPosition : IProcessHolderPosition => GetPosition(group.Positions, t => (t.Session as ActivityStart)?.Activity.Id == activityId); /// /// Get the position by id of the running activity /// public static TPosition GetPositionByActivityId(this IEnumerable positions, long activityId) - where TPosition : IProcessHolderPosition - { - return GetPosition(positions, t => (t.Session as ActivityStart)?.Activity.Id == activityId); - } + where TPosition : IProcessHolderPosition => GetPosition(positions, t => (t.Session as ActivityStart)?.Activity.Id == activityId); private static TPosition GetPosition(IEnumerable positions, Func filter) - where TPosition : IProcessHolderPosition - { - return positions.SingleOrDefault(filter); - } + where TPosition : IProcessHolderPosition => positions.SingleOrDefault(filter); + + #endregion + + #region Get or Create Sessions /// /// Try cast and return the holders session property /// public static TSession ConvertSession(this IProcessHolderPosition holderPosition) - where TSession : Session - { - return holderPosition.Session as TSession; - } + where TSession : Session => holderPosition.Session as TSession; /// - /// Access tracing of the current activity - /// - public static TTracing Tracing(this IProcessHolderPosition position) - where TTracing : Tracing, new() - { - var currentActivity = (position.Session as ActivityStart)?.Activity; - return currentActivity?.TransformTracing(); - } - - /// - /// Create sessions for all positions on a holder group, that have a process + /// Get or create a session for the , if it holds a process. Otherwise returns an empty enumerable. + /// This is usually used when attaching to the control system. /// public static IEnumerable Attach(this ProcessHolderPosition position) { @@ -136,43 +86,63 @@ public static IEnumerable Attach(this ProcessHolderPosition position) } /// - /// Create sessions for all positions on a holder group, that have a process + /// Get or create sessions for all that have a process. + /// This is usually used when attaching to the control system. /// - public static IEnumerable Attach(this IEnumerable positions) - { - return positions.SelectMany(p => p.Attach()); - } + public static IEnumerable Attach(this IEnumerable positions) + => positions.SelectMany(p => p.Attach()); /// - /// Create sessions for all positions on a holder group, that have a process + /// Get a session if has one. Otherwise returns an empty enumerable. + /// This is usually used when detaching from the control system. /// - public static IEnumerable Detach(this ProcessHolderPosition position) - { - return position.Session != null ? new[] { position.Session } : Enumerable.Empty(); - } + public static IEnumerable Detach(this ProcessHolderPosition position) + => position.Session != null ? new[] { position.Session } : Enumerable.Empty(); /// /// Create sessions for all positions on a holder group, that have a process /// - public static IEnumerable Detach(this IEnumerable positions) - { - return positions.Where(p => p.Session != null).Select(p => p.Session); - } + public static IEnumerable Detach(this IEnumerable positions) + => positions.Where(p => p.Session != null).Select(p => p.Session); + + #endregion + + #region Mounting /// - /// Assign process and session to this position + /// Assign a to this position /// - public static void Mount(this IProcessHolderPosition position, IProcess process) - { - position.Mount(new MountInformation(process, null)); - } + public static void Mount(this IProcessHolderPosition position, IProcess process) + => position.Mount(new MountInformation(process, null)); /// - /// Assign process and session to this position + /// Assign and to this position /// - public static void Mount(this IProcessHolderPosition position, IProcess process, Session session) - { - position.Mount(new MountInformation(process, session)); - } + public static void Mount(this IProcessHolderPosition position, IProcess process, Session session) + => position.Mount(new MountInformation(process, session)); + + #endregion + + #region Has + + /// + /// Checks if the group holds a process with a finished activity having the matching result + /// + public static bool HasFinishedActivity(this IProcessHolderGroup group, long activityId, long activityResult) + where TPosition : IProcessHolderPosition => group.Positions.Any(position => position.HasFinishedActivity(activityId, activityResult)); + + /// + /// Checks if the position holds a process with a finished activity having the matching result. + /// + public static bool HasFinishedActivity(this IProcessHolderPosition holderPosition, long activityId, long activityResult) + => holderPosition.Process?.GetActivities(activity => activity.Id == activityId && activity.Result?.Numeric == activityResult).Any() == true; + + #endregion + + /// + /// Access tracing of the current activity + /// + public static TTracing Tracing(this IProcessHolderPosition position) + where TTracing : Tracing, new() => (position.Session as ActivityStart)?.Activity?.TransformTracing(); } } \ No newline at end of file From c516ed408e688e9155911246e3ca239ae46f7620 Mon Sep 17 00:00:00 2001 From: Marcel Vielhaus Date: Fri, 29 Nov 2024 14:28:16 +0100 Subject: [PATCH 2/4] Add extensions for `IProcessHolderPosition`s and `IProcessHolderGroup`s --- .../Processes/ProcessHolderExtensions.cs | 131 +++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs b/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs index abd8d03..bc65de3 100644 --- a/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs +++ b/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs @@ -18,6 +18,12 @@ public static class ProcessHolderExtensions public static TPosition GetPositionByIdentifier(this IProcessHolderGroup group, string identifier) where TPosition : IProcessHolderPosition => GetPosition(group.Positions, t => t.Identifier == identifier); + /// + /// Get the position by its + /// + public static IProcessHolderPosition GetPositionByIdentifier(this IProcessHolderGroup group, string identifier) + => GetPosition(group.Positions, t => t.Identifier == identifier); + /// /// Get the position by its /// @@ -30,6 +36,12 @@ public static TPosition GetPositionByIdentifier(this IEnumerable(this IProcessHolderGroup group, Session session) where TPosition : IProcessHolderPosition => GetPosition(group.Positions, t => t.Session?.Id == session.Id); + /// + /// Get the position by its + /// + public static IProcessHolderPosition GetPositionBySession(this IProcessHolderGroup group, Session session) + => GetPosition(group.Positions, t => t.Session?.Id == session.Id); + /// /// Get the position by its /// @@ -42,6 +54,12 @@ public static TPosition GetPositionBySession(this IEnumerable(this IProcessHolderGroup group, long processId) where TPosition : IProcessHolderPosition => GetPosition(group.Positions, t => t.Process?.Id == processId); + /// + /// Get the position by its + /// + public static IProcessHolderPosition GetPositionByProcessId(this IProcessHolderGroup group, long processId) + => GetPosition(group.Positions, t => t.Process?.Id == processId); + /// /// Get the position by its /// @@ -54,12 +72,40 @@ public static TPosition GetPositionByProcessId(this IEnumerable(this IProcessHolderGroup group, long activityId) where TPosition : IProcessHolderPosition => GetPosition(group.Positions, t => (t.Session as ActivityStart)?.Activity.Id == activityId); + /// + /// Get the position by id of the running activity + /// + public static IProcessHolderPosition GetPositionByActivityId(this IProcessHolderGroup group, long activityId) + => GetPosition(group.Positions, t => (t.Session as ActivityStart)?.Activity.Id == activityId); + /// /// Get the position by id of the running activity /// public static TPosition GetPositionByActivityId(this IEnumerable positions, long activityId) where TPosition : IProcessHolderPosition => GetPosition(positions, t => (t.Session as ActivityStart)?.Activity.Id == activityId); + /// + /// Tries to get an empty from the + /// + /// The group to search + /// When this method returns, contains the first empty + /// , if any is found; otherwise null + /// + /// true if an empty was found; otherwise, false. + /// + public static bool TryGetEmptyPosition(this IProcessHolderGroup group, out IProcessHolderPosition position) + { + foreach (var possible in group.Positions) + { + if (!possible.IsEmpty()) + continue; + position = possible; + return true; + } + position = default; + return false; + } + private static TPosition GetPosition(IEnumerable positions, Func filter) where TPosition : IProcessHolderPosition => positions.SingleOrDefault(filter); @@ -92,6 +138,30 @@ public static IEnumerable Attach(this ProcessHolderPosition position) public static IEnumerable Attach(this IEnumerable positions) => positions.SelectMany(p => p.Attach()); + /// + /// Get or create sessions for all that have a process. + /// This is usually used when attaching to the control system. + /// + public static IEnumerable Attach(this IProcessHolderGroup group) + { + var sessions = new List(); + foreach(var position in group.Positions) + { + if (position.Session is not null) + { + sessions.Add(position.Session); + continue; + } + + if (position.Process is null) + continue; + + if (position is ProcessHolderPosition php) + sessions.Add(php.StartSession()); + } + return sessions; + } + /// /// Get a session if has one. Otherwise returns an empty enumerable. /// This is usually used when detaching from the control system. @@ -105,6 +175,12 @@ public static IEnumerable Detach(this ProcessHolderPosition position) public static IEnumerable Detach(this IEnumerable positions) => positions.Where(p => p.Session != null).Select(p => p.Session); + /// + /// Create sessions for all positions on the , that have a process + /// + public static IEnumerable Detach(this IProcessHolderGroup group) + => group.Positions.Where(p => p.Session != null).Select(p => p.Session); + #endregion #region Mounting @@ -115,6 +191,12 @@ public static IEnumerable Detach(this IEnumerable position.Mount(new MountInformation(process, null)); + /// + /// Assign a to this position + /// + public static void Mount(this IProcessHolderPosition position, Session session) + => position.Mount(new MountInformation(null, session)); + /// /// Assign and to this position /// @@ -123,7 +205,26 @@ public static void Mount(this IProcessHolderPosition position, IProcess process, #endregion - #region Has + #region Status Checks + + /// + /// Checks if the has no empty + /// + public static bool IsFull(this IProcessHolderGroup group) + => group.Positions.All(position => !position.IsEmpty()); + + /// + /// Checks if the has no filled + /// + public static bool IsEmpty(this IProcessHolderGroup group) + => group.Positions.All(position => position.IsEmpty()); + + /// + /// True if the has neither a nor a + /// ; false otherwise. + /// + public static bool IsEmpty(this IProcessHolderPosition position) + => position.Process is null && position.Session is null; /// /// Checks if the group holds a process with a finished activity having the matching result @@ -144,5 +245,33 @@ public static bool HasFinishedActivity(this IProcessHolderPosition holderPositio /// public static TTracing Tracing(this IProcessHolderPosition position) where TTracing : Tracing, new() => (position.Session as ActivityStart)?.Activity?.TransformTracing(); + + /// + /// Updates and by resetting the + /// and remounting the . For s + /// a direct update of the session and process is done on the object. + /// + /// The position to update + /// The updated session on the position + /// Thrown if the given does not match + /// the session on the + public static void Update(this IProcessHolderPosition position, Session session) + { + if (position.Session.Id != session.Id) + throw new InvalidOperationException($"Tried to update the {nameof(Session)} on an " + + $"{nameof(IProcessHolderPosition)} with a different session. Make sure to " + + $"{nameof(IProcessHolderPosition.Reset)} the {nameof(IProcessHolderPosition)} before " + + $"assigning a new {nameof(Session)}"); + + if (position is ProcessHolderPosition explicitPosition) + { + explicitPosition.Session = session; + explicitPosition.AssignProcess(session.Process); + return; + } + + position.Reset(); + position.Mount(session.Process, session); + } } } \ No newline at end of file From e9c686b6f40b80d6c9d3b0b65ce8fa2f5ec2bde9 Mon Sep 17 00:00:00 2001 From: Marcel Vielhaus Date: Tue, 3 Dec 2024 10:34:48 +0100 Subject: [PATCH 3/4] Add extension methods from last application project --- .../Cells/SessionExtensions.cs | 84 +++++++++++++++++++ .../Processes/ProcessHolderExtensions.cs | 6 +- .../VisualInstructorExtensions.cs | 51 +++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/Moryx.ControlSystem/Cells/SessionExtensions.cs diff --git a/src/Moryx.ControlSystem/Cells/SessionExtensions.cs b/src/Moryx.ControlSystem/Cells/SessionExtensions.cs new file mode 100644 index 0000000..125d000 --- /dev/null +++ b/src/Moryx.ControlSystem/Cells/SessionExtensions.cs @@ -0,0 +1,84 @@ +// Copyright (c) 2024, Phoenix Contact GmbH & Co. KG +// Licensed under the Apache License, Version 2.0 + +using Moryx.AbstractionLayer.Recipes; +using Moryx.AbstractionLayer; +using Moryx.AbstractionLayer.Products; +using Moryx.ControlSystem.Recipes; + +namespace Moryx.ControlSystem.Cells +{ + /// + /// Extension methods on the to get product related information, activity details or just provide shortcuts based on the actual session type + /// + public static class SessionExtensions + { + /// + /// Extension method to get the from the of the + /// + /// Type of the that is expected. + /// The sesion to get the from + /// + /// The in the session, if the belongs to a + /// and the holds a ; + /// Otherwise returns null + /// + public static TProductInstance GetProductInstance(this Session session) where TProductInstance : ProductInstance + { + if (session.Process is not ProductionProcess process) return null; + + return process.ProductInstance as TProductInstance; + } + + /// + /// Extension method to get the from the + /// + /// Type of the that is expected. + /// The sesion to get the from + /// + /// The in the session, if the currently handles an + /// Activity of type ; Otherwise returns null + /// + public static TActivityType GetActivity(this Session session) where TActivityType : Activity + { + if (session is ActivityCompleted completed) + return completed.CompletedActivity as TActivityType; + if (session is ActivityStart start) + return start.Activity as TActivityType; + + return null; + } + + /// + /// Extension method to get the last from the + /// + /// Type of the that is expected. + /// The sesion to get the from + /// + /// The last in the session, if the currently handles an + /// Activity of type ; Otherwise returns null + /// + public static TActivityType GetLastActivity(this Session session) where TActivityType : Activity + => session.Process.LastActivity() as TActivityType; + + /// + /// Extension method to get the from the + /// + /// Type of the that is expected. + /// The session to get the from + /// + /// The target in the session, if it belongs to a + /// or holds an with a ; Otherwise returns null. + /// + public static TProductType GetProductType(this Session session) where TProductType : ProductType + { + if (session.Process.Recipe is ISetupRecipe setupRecipe) + return setupRecipe.TargetRecipe.Target as TProductType; + + if (session.Process.Recipe is IProductRecipe prodcutRecipe) + return prodcutRecipe.Target as TProductType; + + return default; + } + } +} diff --git a/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs b/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs index bc65de3..fa08f5f 100644 --- a/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs +++ b/src/Moryx.ControlSystem/Processes/ProcessHolderExtensions.cs @@ -170,13 +170,15 @@ public static IEnumerable Detach(this ProcessHolderPosition position) => position.Session != null ? new[] { position.Session } : Enumerable.Empty(); /// - /// Create sessions for all positions on a holder group, that have a process + /// Gets the session from each in the + /// that holds one. This is usually used when detaching from the control system. /// public static IEnumerable Detach(this IEnumerable positions) => positions.Where(p => p.Session != null).Select(p => p.Session); /// - /// Create sessions for all positions on the , that have a process + /// Gets the session from each in the + /// that holds one. This is usually used when detaching from the control system. /// public static IEnumerable Detach(this IProcessHolderGroup group) => group.Positions.Where(p => p.Session != null).Select(p => p.Session); diff --git a/src/Moryx.ControlSystem/VisualInstructions/VisualInstructorExtensions.cs b/src/Moryx.ControlSystem/VisualInstructions/VisualInstructorExtensions.cs index 278a06c..86bab53 100644 --- a/src/Moryx.ControlSystem/VisualInstructions/VisualInstructorExtensions.cs +++ b/src/Moryx.ControlSystem/VisualInstructions/VisualInstructorExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0 using System; +using System.Linq; using System.Reflection; using Moryx.AbstractionLayer; using Moryx.ControlSystem.Cells; @@ -40,6 +41,56 @@ public static long Display(this IVisualInstructor instructor, string sender, Act return instructor.Display(sender, instructions); } + /// + /// Show a visual instruction text message + /// + /// The instructor to display the message + /// The sender of the message + /// The message to be displayed + /// The id of the instruction + public static long DisplayMessage(this IVisualInstructor instructor, string sender, string message) + { + return instructor.DisplayMessages(sender, new string[] { message }); + } + + /// + /// Show a set of messages as a visual instruction + /// + /// The instructor to display the messages + /// The sender of the message + /// The messages to be displayed + /// The id of the instruction + public static long DisplayMessages(this IVisualInstructor instructor, string sender, string[] messages) + { + var instructions = messages.Select(AsInstruction).ToArray(); + return instructor.Display(sender, instructions); + } + + /// + /// Show a visual instruction text message + /// + /// The instructor to display the message + /// The sender of the message + /// The message to be displayed + /// Time after which the message will be cleared + public static void DisplayMessage(this IVisualInstructor instructor, string sender, string message, int autoClearMs) + { + instructor.DisplayMessages(sender, new string[] { message }, autoClearMs); + } + + /// + /// Show a set of messages as a visual instruction + /// + /// The instructor to display the messages + /// The sender of the message + /// The messages to be displayed + /// Time after which the messages will be cleared + public static void DisplayMessages(this IVisualInstructor instructor, string sender, string[] messages, int autoClearMs) + { + var instructions = messages.Select(AsInstruction).ToArray(); + instructor.Display(sender, instructions, autoClearMs); + } + /// /// Execute these instructions based on the given activity and report the result on completion /// Can (but must not) be cleared with the method From d972a8bb4d12433402b7fcafb0bf118c1346b4f8 Mon Sep 17 00:00:00 2001 From: Marcel Vielhaus Date: Thu, 12 Dec 2024 10:40:55 +0100 Subject: [PATCH 4/4] Add extensions on Sessions, Processes and Jobs --- .../Cells/SessionExtensions.cs | 44 ++++++++++++ src/Moryx.ControlSystem/Jobs/JobExtensions.cs | 51 ++++++++++++++ .../Processes/IProcessExtensions.cs | 70 +++++++++++++++++++ .../Recipes/IRecipeExtensions.cs | 27 +++++++ 4 files changed, 192 insertions(+) create mode 100644 src/Moryx.ControlSystem/Jobs/JobExtensions.cs create mode 100644 src/Moryx.ControlSystem/Processes/IProcessExtensions.cs create mode 100644 src/Moryx.ControlSystem/Recipes/IRecipeExtensions.cs diff --git a/src/Moryx.ControlSystem/Cells/SessionExtensions.cs b/src/Moryx.ControlSystem/Cells/SessionExtensions.cs index 125d000..5221b32 100644 --- a/src/Moryx.ControlSystem/Cells/SessionExtensions.cs +++ b/src/Moryx.ControlSystem/Cells/SessionExtensions.cs @@ -5,6 +5,8 @@ using Moryx.AbstractionLayer; using Moryx.AbstractionLayer.Products; using Moryx.ControlSystem.Recipes; +using System; +using Moryx.ControlSystem.Processes; namespace Moryx.ControlSystem.Cells { @@ -30,6 +32,48 @@ public static TProductInstance GetProductInstance(this Session return process.ProductInstance as TProductInstance; } + /// + /// Modifies the of type + /// on the of the using the given + /// . + /// + /// The expected type of the product instance + /// The sessopm holding the product instance + /// The action to be executed on the product instance + /// + /// + /// ((var instance) => instance.MyProperty = 1) + /// ]]> + /// + /// + /// Thrown if the of the + /// does not hold a product instance of type + /// + /// Thrown if the of the + /// is no + public static TInstance ModifyProductInstance(this Session session, Action setter) + where TInstance : IProductInstance => session.Process.ModifyProductInstance(setter); + + /// + /// Tries to modifies the of type + /// on the of the using the given + /// . Returns false, if the + /// operation could not be executed. + /// + /// The expected type of the product instance + /// The sessopm holding the product instance + /// The action to be executed on the product instance + /// + /// + /// ((var instance) => instance.MyProperty = 1) + /// ]]> + /// + /// + public static bool TryModifyProductInstance(this Session session, Action setter) + where TInstance : IProductInstance => session.Process.TryModifyProductInstance(setter); + /// /// Extension method to get the from the /// diff --git a/src/Moryx.ControlSystem/Jobs/JobExtensions.cs b/src/Moryx.ControlSystem/Jobs/JobExtensions.cs new file mode 100644 index 0000000..9637573 --- /dev/null +++ b/src/Moryx.ControlSystem/Jobs/JobExtensions.cs @@ -0,0 +1,51 @@ +// Copyright (c) 2024, Phoenix Contact GmbH & Co. KG +// Licensed under the Apache License, Version 2.0 + +using Moryx.AbstractionLayer.Recipes; +using Moryx.ControlSystem.Recipes; +using Moryx.ControlSystem.Setups; + +namespace Moryx.ControlSystem.Jobs +{ + /// + /// Extensions methods for s. + /// + public static class JobExtensions + { + /// + /// Checks whether the is an + /// and gets the predicted failure count; otherwise returns 0. + /// + public static int CountPredictedFailures(this Job job) + { + if (job is IPredictiveJob predictiveJob) + return predictiveJob.PredictedFailures.Count; + + return 0; + } + + /// + /// Checks if the references an + /// + public static bool IsProduction(this Job job) => job.Recipe is IProductionRecipe; + + /// + /// Checks if the references an + /// + public static bool IsSetup(this Job job) => job.Recipe is ISetupRecipe; + + /// + /// Checks if the holds an + /// that is set to be executed + /// + public static bool IsPreparingSetup(this Job job) + => job.Recipe is ISetupRecipe setup && setup.Execution == SetupExecution.BeforeProduction; + + /// + /// Checks if the holds an + /// that is set to be executed + /// + public static bool IsCleaningUpSetup(this Job job) + => job.Recipe is ISetupRecipe setup && setup.Execution == SetupExecution.AfterProduction; + } +} diff --git a/src/Moryx.ControlSystem/Processes/IProcessExtensions.cs b/src/Moryx.ControlSystem/Processes/IProcessExtensions.cs new file mode 100644 index 0000000..56d30c8 --- /dev/null +++ b/src/Moryx.ControlSystem/Processes/IProcessExtensions.cs @@ -0,0 +1,70 @@ +using Moryx.AbstractionLayer.Products; +using Moryx.AbstractionLayer; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Moryx.ControlSystem.Processes +{ + /// + /// Extensions on an + /// + public static class IProcessExtensions + { + /// + /// Modifies the of type + /// on the using the given . + /// + /// The expected type of the product instance + /// The process holding the product instance + /// The action to be executed on the product instance + /// + /// + /// ((var instance) => instance.MyProperty = 1) + /// ]]> + /// + /// + /// Thrown if the given does + /// not hold a product instance of type + /// Thrown if the given + /// is no + public static TInstance ModifyProductInstance(this IProcess process, Action setter) + where TInstance : IProductInstance + { + if (process is not ProductionProcess productionProcess) + throw new InvalidOperationException($"Cannot modify an {nameof(IProductInstance)} on a process of type {process.GetType()}"); + if (productionProcess.ProductInstance is not TInstance instance) + throw new InvalidCastException($"Cannot cast {nameof(ProductionProcess.ProductInstance)} of type " + + $"{productionProcess?.ProductInstance?.GetType()} to {typeof(TInstance)}"); + setter.Invoke(instance); + return instance; + } + + /// + /// Tries to modifies the of type + /// on the using the given . Returns false, if the + /// operation could not be executed. + /// + /// The expected type of the product instance + /// The process holding the product instance + /// The action to be executed on the product instance + /// + /// + /// ((var instance) => instance.MyProperty = 1) + /// ]]> + /// + /// + public static bool TryModifyProductInstance(this IProcess process, Action setter) + where TInstance : IProductInstance + { + if (process is not ProductionProcess productionProcess) + return false; + if (productionProcess.ProductInstance is not TInstance instance) + return false; + setter.Invoke(instance); + return true; + } + } +} diff --git a/src/Moryx.ControlSystem/Recipes/IRecipeExtensions.cs b/src/Moryx.ControlSystem/Recipes/IRecipeExtensions.cs new file mode 100644 index 0000000..2611657 --- /dev/null +++ b/src/Moryx.ControlSystem/Recipes/IRecipeExtensions.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2021, Phoenix Contact GmbH & Co. KG +// Licensed under the Apache License, Version 2.0 + +using Moryx.AbstractionLayer.Recipes; +using System; + +namespace Moryx.ControlSystem.Recipes +{ + /// + /// Extensions on s + /// + public static class IRecipeExtensions + { + /// + /// Gets a string concatinating order and operation number from an + /// or and empty string if none could be found. + /// + /// An that implements + /// or an targeting an + /// Separation char between order number and operation number + public static string GetOrderOperationString(this IRecipe recipe, string seperator = "-") + { + var target = (recipe is ISetupRecipe setup ? setup.TargetRecipe : recipe) as IOrderBasedRecipe; + return $"{target?.OrderNumber}{(target is null ? "" : seperator)}{target?.OperationNumber}"; + } + } +}