Skip to content

Commit

Permalink
FBD development
Browse files Browse the repository at this point in the history
  • Loading branch information
tnunnink committed Nov 12, 2023
1 parent 002b4d4 commit 90146ed
Show file tree
Hide file tree
Showing 50 changed files with 1,573 additions and 766 deletions.
165 changes: 79 additions & 86 deletions src/.idea/.idea.L5Sharp/.idea/workspace.xml

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/L5Sharp.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ELSE/@EntryIndexedValue">ELSE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=END/@EntryIndexedValue">END</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FBD/@EntryIndexedValue">FBD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ICON/@EntryIndexedValue">ICON</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IF/@EntryIndexedValue">IF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=INT/@EntryIndexedValue">INT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IO/@EntryIndexedValue">IO</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IREF/@EntryIndexedValue">IREF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=JSR/@EntryIndexedValue">JSR</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LEN/@EntryIndexedValue">LEN</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LINT/@EntryIndexedValue">LINT</s:String>
Expand All @@ -31,8 +33,10 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LREAL/@EntryIndexedValue">LREAL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MOD/@EntryIndexedValue">MOD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NOT/@EntryIndexedValue">NOT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OCON/@EntryIndexedValue">OCON</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OF/@EntryIndexedValue">OF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OR/@EntryIndexedValue">OR</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OREF/@EntryIndexedValue">OREF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OTE/@EntryIndexedValue">OTE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PID/@EntryIndexedValue">PID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RAD/@EntryIndexedValue">LOG</s:String>
Expand Down Expand Up @@ -64,12 +68,15 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Acked/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Deadband/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=FFFF/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=IREF/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Linx/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Logix/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=LREAL/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Materializer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=msec/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=OCON/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Oper/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=OREF/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Postscan/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Prescan/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Prog/@EntryIndexedValue">True</s:Boolean>
Expand Down
6 changes: 3 additions & 3 deletions src/L5Sharp/Common/Argument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,14 @@ public static Argument Parse(string? value)
//Empty value - lets not crash on empty or invalid arguments.
if (string.IsNullOrEmpty(value)) return Empty;

//Unknown value - Can be found in TON instructions.
//Unknown value - Can be found in TON instructions and probably others.
if (value == "?") return Unknown;

//Literal string value - We need to intercept this before the Atomic.TryParse method to prevent overflow.
//Literal string value - We need to intercept this before the Atomic.TryParse method to prevent exceptions.
if (value.StartsWith('\'') && value.EndsWith('\'')) return new Argument(value);

//Immediate atomic value
if (Atomic.TryParse(value, out var atomicType) && atomicType is not null) return new Argument(atomicType);
if (Atomic.TryParse(value, out var atomic) && atomic is not null) return new Argument(atomic);

//TagName or Expression otherwise
return TagName.IsTag(value) ? new Argument(new TagName(value)) : new Argument(new NeutralText(value));
Expand Down
24 changes: 0 additions & 24 deletions src/L5Sharp/Common/CrossReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,6 @@ public CrossReference(XElement element, string type, string name)
_element = element ?? throw new ArgumentNullException(nameof(element));
}

/// <summary>
/// Creates a new <see cref="CrossReference"/> with a referencing element, component name and type.
/// </summary>
/// <param name="element">The referencing <see cref="XElement"/> object.</param>
/// <param name="name"></param>
/// <param name="type"></param>
/// <param name="instruction"></param>
/// <exception cref="ArgumentNullException">Any provided parameter is <c>null</c>.</exception>
public CrossReference(XElement element, string type, string name, Instruction instruction)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentException("Name cannot be null or empty.", nameof(name));
if (string.IsNullOrEmpty(type)) throw new ArgumentException("Type cannot be null or empty.", nameof(type));
ComponentType = type;
ComponentName = name;
Instruction = instruction;
_element = element ?? throw new ArgumentNullException(nameof(element));
}

/// <summary>
/// The corresponding <see cref="Common.ComponentKey"/> of the reference, indicating both the component type and name
/// this element is in reference to.
Expand All @@ -69,12 +51,6 @@ public CrossReference(XElement element, string type, string name, Instruction in
/// <value>A <see cref="string"/> indicating the name of the component.</value>
public string ComponentName { get; }

/// <summary>
/// The specific instruction value the element is in reference to.
/// </summary>
/// <value>An <see cref="Common.Instruction"/> object if found; Otherwise, <c>null</c>.</value>
private Instruction? Instruction { get; }

/// <summary>
/// The referencing <see cref="XElement"/> object
/// </summary>
Expand Down
122 changes: 122 additions & 0 deletions src/L5Sharp/Common/Params.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace L5Sharp.Common;

/// <summary>
///
/// </summary>
public class Params : IList<string>
{
private readonly XAttribute _attribute;

/// <summary>
/// Creates a new <see cref="Params"/> collection with a default backing attribute to use as the store for
/// parameter names.
/// </summary>
public Params()
{
_attribute = new XAttribute("Params", string.Empty);
}

/// <summary>
/// Creates a new <see cref="Params"/> collection with the backing attribute having the specified name.
/// </summary>
/// <param name="name">The name or the params attribute to create.</param>
/// <exception cref="ArgumentException"><c>name</c> is null or empty.</exception>
public Params(string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Can not create Params with empty or null name.", nameof(name));

_attribute = new XAttribute(name, string.Empty);
}

/// <summary>
/// Creates a new <see cref="Params"/> collection with the provided backing attribute.
/// </summary>
/// <param name="attribute">The backing attribute to use for storing the parameter collection.</param>
/// <exception cref="ArgumentException"><c>attribute</c> is null.</exception>
public Params(XAttribute attribute)
{
_attribute = attribute ?? throw new ArgumentNullException(nameof(attribute));
}

/// <summary>
///
/// </summary>
/// <param name="parameters"></param>
public Params(IEnumerable<string> parameters)
{
_attribute = new XAttribute(nameof(Params), string.Join(" ", parameters));
}

/// <inheritdoc />
public int Count => GetParams().Count;

/// <inheritdoc />
public string this[int index]
{
get => GetParams()[index];
set
{
var parameters = GetParams();
parameters[index] = value;
SetParams(parameters);
}
}

/// <inheritdoc />
public void Add(string parameter)
{
_attribute.Value = string.Concat(_attribute.Value, " ", parameter).Trim();
}

/// <inheritdoc />
public void Clear() => _attribute.SetValue(string.Empty);

/// <inheritdoc />
public bool Contains(string parameter) => GetParams().Contains(parameter);

/// <inheritdoc />
public void CopyTo(string[] array, int arrayIndex) => GetParams().CopyTo(array, arrayIndex);

/// <inheritdoc />
public bool Remove(string parameter)
{
var parameters = GetParams();
var result = parameters.Remove(parameter);
SetParams(parameters);
return result;
}

/// <inheritdoc />
public int IndexOf(string parameter) => GetParams().IndexOf(parameter);

/// <inheritdoc />
public void Insert(int index, string parameter)
{
var parameters = GetParams();
parameters.Insert(index, parameter);
SetParams(parameters);
}

/// <inheritdoc />
public void RemoveAt(int index) => GetParams().RemoveAt(index);

/// <inheritdoc />
public IEnumerator<string> GetEnumerator() => GetParams().GetEnumerator();

#region Internal

bool ICollection<string>.IsReadOnly => false;

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
private List<string> GetParams() => _attribute.Value.Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList();
private void SetParams(IEnumerable<string> parameters) => _attribute.SetValue(string.Join(" ", parameters));

#endregion
}
79 changes: 79 additions & 0 deletions src/L5Sharp/Components/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using L5Sharp.Enums;
using L5Sharp.Utilities;

namespace L5Sharp.Components;

Expand Down Expand Up @@ -115,4 +118,80 @@ public LogixContainer<Routine> Routines
get => GetContainer<Routine>();
set => SetContainer(value);
}

/// <summary>
/// The collection of program names that are children of this <see cref="Program"/> component.
/// This defines the structure of the program tree in the logical organizer.
/// </summary>
/// <value>A <see cref="IEnumerable{T}"/> containing the string program names.</value>
/// <remarks>
/// <para>
/// This member just returns the read only list of child program names. To modify the list, use the local
/// <see cref="AddChild"/> and <see cref="RemoveChild"/> methods. This list is what is serialized and defines the collection
/// of child programs for a given program in the logical organizer.
/// </para>
/// <para>
/// To get access to the actual child
/// programs, use <see cref="Programs"/>, which is a built in helper that uses the parent <c>L5X</c>
/// to retrieve the child program components.
/// </para>
/// </remarks>
public IEnumerable<string> Children =>
Element.Descendants(L5XName.ChildProgram).Select(e => e.LogixName());

/// <summary>
/// Gets a collection of <c>Program</c> components that are children of this <see cref="Program"/> component.
/// </summary>
/// <value>A <see cref="IEnumerable{T}"/> of <see cref="Program"/> component elements.</value>
/// <remarks>
/// This is a helper to retrieve the other program component objects as children of this <c>Program</c>.
/// This allows the caller to travers down the logical hierarchy of programs. This requires an attached L5X as
/// it reaches back up the document tree and back down to down to find the child programs. If this component is not
/// attached to an L5X or as no <see cref="Children"/> configured, then this will return an empty collection.
/// </remarks>
public IEnumerable<Program> Programs =>
L5X?.Programs.Where(p => Children.Any(c => c == p.Name)) ?? Enumerable.Empty<Program>();

/// <summary>
/// Finds the <see cref="Components.Task"/> in which this <see cref="Program"/> is scheduled.
/// </summary>
/// <value>If this component is attached and is scheduled to a defined <c>Task</c>, then the
/// <see cref="Components.Task"/> component instance, Otherwise, null.</value>
/// <remarks>
/// This is a helper for retrieving the parent <c>Task</c> for this program.
/// This requires an attached L5X as it traverses the L5X document tree to find the target component(s).
/// </remarks>
public Task? Task => L5X?.Tasks.FirstOrDefault(t => t.Scheduled.Any(p => p.IsEquivalent(Name)));

/// <summary>
/// Adds the specified program name as a child of this <see cref="Program"/> component.
/// </summary>
/// <param name="programName">The name of the program to add as a child.</param>
/// <exception cref="ArgumentException"><c>programName</c> is null or empty.</exception>
public void AddChild(string programName)
{
if (string.IsNullOrEmpty(programName))
throw new ArgumentException("Can not remove program with null or empty name.", nameof(programName));

var element = new XElement(L5XName.ChildProgram, new XAttribute(L5XName.Name, programName));

if (Element.Element(L5XName.ChildProgram) is null)
Element.Add(new XElement(L5XName.ChildProgram));

Element.Element(L5XName.ChildProgram)!.Add(element);
}

/// <summary>
/// Removes the program with the specified name from the children collection of this <see cref="Program"/>
/// component.
/// </summary>
/// <param name="programName">The name of the child program to remove.</param>
/// <exception cref="ArgumentException"><c>programName</c> is null or empty.</exception>
public void RemoveChild(string programName)
{
if (string.IsNullOrEmpty(programName))
throw new ArgumentException("Can not remove program with null or empty name.", nameof(programName));

Element.Element(L5XName.ChildPrograms)?.Elements().Where(e => e.LogixName().IsEquivalent(programName)).Remove();
}
}
6 changes: 3 additions & 3 deletions src/L5Sharp/Components/Task.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,11 @@ public bool? EnableTimeout
/// </summary>
/// <value>A <see cref="IEnumerable{T}"/> containing the string program names.</value>
/// <remarks>This member just returns the read only list of scheduled programs. To modify the list, use </remarks>
public IEnumerable<string> ScheduledPrograms =>
public IEnumerable<string> Scheduled =>
Element.Descendants(L5XName.ScheduledProgram).Select(e => e.LogixName());

/// <summary>
/// Adds the provided program name to the underlying list of <see cref="ScheduledPrograms"/>.
/// Adds the provided program name to the underlying list of <see cref="Scheduled"/>.
/// </summary>
/// <param name="program">The name of the program to schedule.</param>
public void Schedule(string program)
Expand All @@ -168,7 +168,7 @@ public void Schedule(string program)
}

/// <summary>
/// Removes the specified program name from the underlying list of <see cref="ScheduledPrograms"/>
/// Removes the specified program name from the underlying list of <see cref="Scheduled"/>
/// </summary>
/// <param name="program">The name of the program to cancel.</param>
public void Cancel(string program)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ namespace L5Sharp.Elements;
/// `Logix 5000 Controllers Import/Export`</a> for more information.
/// </footer>
[L5XType(L5XName.AddOnInstruction, L5XName.Sheet)]
public class AddOnInstructionBlock : FunctionBlock
public class AOI : FunctionBlock
{
/// <summary>
/// Creates a new <see cref="AddOnInstructionBlock"/> with default values.
/// Creates a new <see cref="AOI"/> with default values.
/// </summary>
public AddOnInstructionBlock()
public AOI()
{
}

/// <summary>
/// Creates a new <see cref="AddOnInstructionBlock"/> initialized with the provided <see cref="XElement"/>.
/// Creates a new <see cref="AOI"/> initialized with the provided <see cref="XElement"/>.
/// </summary>
/// <param name="element">The <see cref="XElement"/> to initialize the type with.</param>
/// <exception cref="ArgumentNullException"><c>element</c> is null.</exception>
public AddOnInstructionBlock(XElement element) : base(element)
public AOI(XElement element) : base(element)
{
}

Expand Down
Loading

0 comments on commit 90146ed

Please sign in to comment.