diff --git a/src/.idea/.idea.L5Sharp/.idea/workspace.xml b/src/.idea/.idea.L5Sharp/.idea/workspace.xml
index c0a3d6bb..00c7a035 100644
--- a/src/.idea/.idea.L5Sharp/.idea/workspace.xml
+++ b/src/.idea/.idea.L5Sharp/.idea/workspace.xml
@@ -9,72 +9,55 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
+
+
+
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
+
-
-
-
-
-
@@ -117,7 +100,9 @@
+
+
@@ -126,7 +111,7 @@
-
+
{
"associatedIndex": 4
@@ -139,36 +124,36 @@
- {
+ "keyToString": {
+ "Notification.DisplayName-DoNotAsk-Plugin Error": "Plugins failed to load",
+ "Notification.DoNotAsk-Plugin Error": "true",
+ "RunOnceActivity.OpenProjectViewOnStart": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "TODO_SCOPE": "All Places",
+ "WebServerToolWindowFactoryState": "false",
+ "git-widget-placeholder": "main",
+ "ignore.virus.scanning.warn.message": "true",
+ "last_opened_file_path": "C:/Users/tnunnink/Documents/GitHub/L5Sharp/src/L5Sharp.sln",
+ "node.js.detected.package.eslint": "true",
+ "node.js.detected.package.tslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "settings.editor.selected.configurable": "RiderCSharpLiveTemplatesSettingsId",
+ "vue.rearranger.settings.migration": "true"
},
- "keyToStringList": {
- "com.intellij.ide.scratch.ScratchImplUtil$2/New Scratch File": [
- "C#"
+ "keyToStringList": {
+ "com.intellij.ide.scratch.ScratchImplUtil$2/New Scratch File": [
+ "C#"
],
- "rider.external.source.directories": [
- "C:\\Users\\tnunnink\\AppData\\Roaming\\JetBrains\\Rider2023.2\\resharper-host\\DecompilerCache",
- "C:\\Users\\tnunnink\\AppData\\Roaming\\JetBrains\\Rider2023.2\\resharper-host\\SourcesCache",
- "C:\\Users\\tnunnink\\AppData\\Local\\Symbols\\src"
+ "rider.external.source.directories": [
+ "C:\\Users\\tnunn\\AppData\\Roaming\\JetBrains\\Rider2023.2\\resharper-host\\DecompilerCache",
+ "C:\\Users\\tnunn\\AppData\\Roaming\\JetBrains\\Rider2023.2\\resharper-host\\SourcesCache",
+ "C:\\Users\\tnunn\\AppData\\Local\\Symbols\\src"
]
}
-}]]>
+}
@@ -430,6 +415,10 @@
+
+
+
+ 1677621948966
@@ -859,17 +848,17 @@
- file://$PROJECT_DIR$/../tests/L5Sharp.Tests/Elements/ReferenceBlockTests.cs
- 153
-
+ file://$PROJECT_DIR$/L5Sharp/Elements/DiagramBlock.cs
+ 23
+
-
+
-
+
-
+
@@ -912,4 +901,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/L5Sharp.sln.DotSettings b/src/L5Sharp.sln.DotSettings
index 74525f3d..d11a31b5 100644
--- a/src/L5Sharp.sln.DotSettings
+++ b/src/L5Sharp.sln.DotSettings
@@ -18,11 +18,13 @@
ELSEENDFBD
+ ICONIDIFINTIOIP
+ IREFJSRLENLINT
@@ -31,8 +33,10 @@
LREALMODNOT
+ OCONOFOR
+ OREFOTEPIDLOG
@@ -64,12 +68,15 @@
TrueTrueTrue
+ TrueTrueTrueTrueTrueTrue
+ TrueTrue
+ TrueTrueTrueTrue
diff --git a/src/L5Sharp/Common/Argument.cs b/src/L5Sharp/Common/Argument.cs
index 67b2ce28..0b77d939 100644
--- a/src/L5Sharp/Common/Argument.cs
+++ b/src/L5Sharp/Common/Argument.cs
@@ -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));
diff --git a/src/L5Sharp/Common/CrossReference.cs b/src/L5Sharp/Common/CrossReference.cs
index 52f913c9..4f8b3425 100644
--- a/src/L5Sharp/Common/CrossReference.cs
+++ b/src/L5Sharp/Common/CrossReference.cs
@@ -33,24 +33,6 @@ public CrossReference(XElement element, string type, string name)
_element = element ?? throw new ArgumentNullException(nameof(element));
}
- ///
- /// Creates a new with a referencing element, component name and type.
- ///
- /// The referencing object.
- ///
- ///
- ///
- /// Any provided parameter is null.
- 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));
- }
-
///
/// The corresponding of the reference, indicating both the component type and name
/// this element is in reference to.
@@ -69,12 +51,6 @@ public CrossReference(XElement element, string type, string name, Instruction in
/// A indicating the name of the component.
public string ComponentName { get; }
- ///
- /// The specific instruction value the element is in reference to.
- ///
- /// An object if found; Otherwise, null.
- private Instruction? Instruction { get; }
-
///
/// The referencing object
///
diff --git a/src/L5Sharp/Common/Params.cs b/src/L5Sharp/Common/Params.cs
new file mode 100644
index 00000000..ad3189e1
--- /dev/null
+++ b/src/L5Sharp/Common/Params.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml.Linq;
+
+namespace L5Sharp.Common;
+
+///
+///
+///
+public class Params : IList
+{
+ private readonly XAttribute _attribute;
+
+ ///
+ /// Creates a new collection with a default backing attribute to use as the store for
+ /// parameter names.
+ ///
+ public Params()
+ {
+ _attribute = new XAttribute("Params", string.Empty);
+ }
+
+ ///
+ /// Creates a new collection with the backing attribute having the specified name.
+ ///
+ /// The name or the params attribute to create.
+ /// name is null or empty.
+ 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);
+ }
+
+ ///
+ /// Creates a new collection with the provided backing attribute.
+ ///
+ /// The backing attribute to use for storing the parameter collection.
+ /// attribute is null.
+ public Params(XAttribute attribute)
+ {
+ _attribute = attribute ?? throw new ArgumentNullException(nameof(attribute));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public Params(IEnumerable parameters)
+ {
+ _attribute = new XAttribute(nameof(Params), string.Join(" ", parameters));
+ }
+
+ ///
+ public int Count => GetParams().Count;
+
+ ///
+ public string this[int index]
+ {
+ get => GetParams()[index];
+ set
+ {
+ var parameters = GetParams();
+ parameters[index] = value;
+ SetParams(parameters);
+ }
+ }
+
+ ///
+ public void Add(string parameter)
+ {
+ _attribute.Value = string.Concat(_attribute.Value, " ", parameter).Trim();
+ }
+
+ ///
+ public void Clear() => _attribute.SetValue(string.Empty);
+
+ ///
+ public bool Contains(string parameter) => GetParams().Contains(parameter);
+
+ ///
+ public void CopyTo(string[] array, int arrayIndex) => GetParams().CopyTo(array, arrayIndex);
+
+ ///
+ public bool Remove(string parameter)
+ {
+ var parameters = GetParams();
+ var result = parameters.Remove(parameter);
+ SetParams(parameters);
+ return result;
+ }
+
+ ///
+ public int IndexOf(string parameter) => GetParams().IndexOf(parameter);
+
+ ///
+ public void Insert(int index, string parameter)
+ {
+ var parameters = GetParams();
+ parameters.Insert(index, parameter);
+ SetParams(parameters);
+ }
+
+ ///
+ public void RemoveAt(int index) => GetParams().RemoveAt(index);
+
+ ///
+ public IEnumerator GetEnumerator() => GetParams().GetEnumerator();
+
+ #region Internal
+
+ bool ICollection.IsReadOnly => false;
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ private List GetParams() => _attribute.Value.Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList();
+ private void SetParams(IEnumerable parameters) => _attribute.SetValue(string.Join(" ", parameters));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/L5Sharp/Components/Program.cs b/src/L5Sharp/Components/Program.cs
index b1861c32..30806e05 100644
--- a/src/L5Sharp/Components/Program.cs
+++ b/src/L5Sharp/Components/Program.cs
@@ -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;
@@ -115,4 +118,80 @@ public LogixContainer Routines
get => GetContainer();
set => SetContainer(value);
}
+
+ ///
+ /// The collection of program names that are children of this component.
+ /// This defines the structure of the program tree in the logical organizer.
+ ///
+ /// A containing the string program names.
+ ///
+ ///
+ /// This member just returns the read only list of child program names. To modify the list, use the local
+ /// and methods. This list is what is serialized and defines the collection
+ /// of child programs for a given program in the logical organizer.
+ ///
+ ///
+ /// To get access to the actual child
+ /// programs, use , which is a built in helper that uses the parent L5X
+ /// to retrieve the child program components.
+ ///
+ ///
+ public IEnumerable Children =>
+ Element.Descendants(L5XName.ChildProgram).Select(e => e.LogixName());
+
+ ///
+ /// Gets a collection of Program components that are children of this component.
+ ///
+ /// A of component elements.
+ ///
+ /// This is a helper to retrieve the other program component objects as children of this Program.
+ /// 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 configured, then this will return an empty collection.
+ ///
+ public IEnumerable Programs =>
+ L5X?.Programs.Where(p => Children.Any(c => c == p.Name)) ?? Enumerable.Empty();
+
+ ///
+ /// Finds the in which this is scheduled.
+ ///
+ /// If this component is attached and is scheduled to a defined Task, then the
+ /// component instance, Otherwise, null.
+ ///
+ /// This is a helper for retrieving the parent Task for this program.
+ /// This requires an attached L5X as it traverses the L5X document tree to find the target component(s).
+ ///
+ public Task? Task => L5X?.Tasks.FirstOrDefault(t => t.Scheduled.Any(p => p.IsEquivalent(Name)));
+
+ ///
+ /// Adds the specified program name as a child of this component.
+ ///
+ /// The name of the program to add as a child.
+ /// programName is null or empty.
+ 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);
+ }
+
+ ///
+ /// Removes the program with the specified name from the children collection of this
+ /// component.
+ ///
+ /// The name of the child program to remove.
+ /// programName is null or empty.
+ 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();
+ }
}
\ No newline at end of file
diff --git a/src/L5Sharp/Components/Task.cs b/src/L5Sharp/Components/Task.cs
index ab7dfe46..028da434 100644
--- a/src/L5Sharp/Components/Task.cs
+++ b/src/L5Sharp/Components/Task.cs
@@ -150,11 +150,11 @@ public bool? EnableTimeout
///
/// A containing the string program names.
/// This member just returns the read only list of scheduled programs. To modify the list, use
- public IEnumerable ScheduledPrograms =>
+ public IEnumerable Scheduled =>
Element.Descendants(L5XName.ScheduledProgram).Select(e => e.LogixName());
///
- /// Adds the provided program name to the underlying list of .
+ /// Adds the provided program name to the underlying list of .
///
/// The name of the program to schedule.
public void Schedule(string program)
@@ -168,7 +168,7 @@ public void Schedule(string program)
}
///
- /// Removes the specified program name from the underlying list of
+ /// Removes the specified program name from the underlying list of
///
/// The name of the program to cancel.
public void Cancel(string program)
diff --git a/src/L5Sharp/Elements/AddOnInstructionBlock.cs b/src/L5Sharp/Elements/AOI.cs
similarity index 91%
rename from src/L5Sharp/Elements/AddOnInstructionBlock.cs
rename to src/L5Sharp/Elements/AOI.cs
index e3394c88..0e7bb330 100644
--- a/src/L5Sharp/Elements/AddOnInstructionBlock.cs
+++ b/src/L5Sharp/Elements/AOI.cs
@@ -18,21 +18,21 @@ namespace L5Sharp.Elements;
/// `Logix 5000 Controllers Import/Export` for more information.
///
[L5XType(L5XName.AddOnInstruction, L5XName.Sheet)]
-public class AddOnInstructionBlock : FunctionBlock
+public class AOI : FunctionBlock
{
///
- /// Creates a new with default values.
+ /// Creates a new with default values.
///
- public AddOnInstructionBlock()
+ public AOI()
{
}
///
- /// Creates a new initialized with the provided .
+ /// Creates a new initialized with the provided .
///
/// The to initialize the type with.
/// element is null.
- public AddOnInstructionBlock(XElement element) : base(element)
+ public AOI(XElement element) : base(element)
{
}
diff --git a/src/L5Sharp/Elements/Block.cs b/src/L5Sharp/Elements/Block.cs
index 61e26f0f..4bfc21d6 100644
--- a/src/L5Sharp/Elements/Block.cs
+++ b/src/L5Sharp/Elements/Block.cs
@@ -59,17 +59,17 @@ public TagName? Operand
get => GetValue();
set => SetValue(value);
}
-
+
///
/// Whether or not to hide the description for the DiagramBlock.
///
/// true if the description is hidden; Otherwise; false.
public bool? HideDesc
{
- get => GetValue();
+ get => GetValue();
set => SetValue(value);
}
-
+
///
/// A collection of pin names that are visible for the Block element.
///
@@ -82,30 +82,78 @@ public bool? HideDesc
/// diagram types Block and AddOnInstruction. Invalid configuration of the element may result in a
/// failure to import.
///
- public IEnumerable VisiblePins => throw new NotImplementedException();
+ public IEnumerable VisiblePins
+ {
+ get => Element.Attribute(L5XName.VisiblePins)?.Value.Split(" ").Select(a => new TagName(a)) ??
+ Enumerable.Empty();
+ set => Element.SetAttributeValue(L5XName.VisiblePins, string.Join(" ", value));
+ }
+
+ ///
+ /// Returns a collection of tag name parameters for the Block element.
+ ///
+ /// A containing values.
+ /// This is a helper to get the tag names concatenated with the root
+ /// tag name of the Block.
+ public IEnumerable TagNames => VisiblePins.Select(p => TagName.Concat(Operand ?? TagName.Empty, p));
+ ///
+ /// Adds the provided pin tag name to the collection of .
+ ///
+ /// The of the pin to add.
+ /// This method assists with updating the collection of pins for the underlying Block element
+ /// without having to manually reassign the entire collection property.
public void AddPin(TagName tagName)
{
-
+ var pins = VisiblePins.ToList();
+ pins.Add(tagName);
+ VisiblePins = pins;
}
-
- public void AddPins(IEnumerable tagName)
+
+ ///
+ /// Adds the provided pin tag name collection to the collection of .
+ ///
+ /// The collection pins to add.
+ /// This method assists with updating the collection of pins for the underlying Block element
+ /// without having to manually reassign the entire collection property.
+ public void AddPins(IEnumerable tagNames)
{
-
+ var pins = VisiblePins.ToList();
+ pins.AddRange(tagNames);
+ VisiblePins = pins;
}
-
- public void RemovePin(TagName tagName)
+
+ ///
+ /// Clears the collection of for the element.
+ ///
+ /// This method assists with updating the collection of pins for the underlying Block element
+ /// without having to manually reassign the entire collection property.
+ public void ClearPins()
{
-
+ Element.SetAttributeValue(L5XName.VisiblePins, string.Empty);
}
-
- public void ReplacePin(TagName current, TagName replacement)
+
+ ///
+ /// Removes the specified pin tag name from the collection of .
+ ///
+ /// The of the pin to remove.
+ /// This method assists with updating the collection of pins for the underlying Block element
+ /// without having to manually reassign the entire collection property.
+ public void RemovePin(TagName tagName)
{
-
+ var pins = VisiblePins.ToList();
+ pins.Remove(tagName);
+ VisiblePins = pins;
}
-
- public void ReplacePins(IEnumerable pins)
+
+ ///
+ public override IEnumerable References()
{
+ if (Operand is null) return base.References();
+
+ var references = new List { new(Element, Operand, L5XName.Tag) };
+ references.AddRange(TagNames.Select(t => new CrossReference(Element, t, L5XName.Tag)));
+ return references;
}
}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/Chart.cs b/src/L5Sharp/Elements/Chart.cs
index 9774e4fe..c2b13c56 100644
--- a/src/L5Sharp/Elements/Chart.cs
+++ b/src/L5Sharp/Elements/Chart.cs
@@ -1,11 +1,16 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
-using L5Sharp.Common;
+using L5Sharp.Utilities;
namespace L5Sharp.Elements;
-public class Chart : Diagram
+///
+/// Represents the containing element for Sequential Function Chart (SFC) block elements. This class inherits
+/// the common logic and allows easier manipulation for SFC elements
+/// within a SFC type routine.
+///
+public class Chart : Diagram
{
///
/// Creates a new with default values.
@@ -27,18 +32,19 @@ public Chart(XElement element) : base(element)
public override int Number => 0;
///
- public override IEnumerable References()
- {
- throw new NotImplementedException();
- }
-
protected override IEnumerable Ordering()
{
- throw new NotImplementedException();
- }
-
- public override void Connect(uint fromId, uint toId)
- {
- throw new NotImplementedException();
+ return new List
+ {
+ L5XName.Step,
+ L5XName.Transition,
+ L5XName.Condition,
+ L5XName.SbrRet,
+ L5XName.Stop,
+ L5XName.Branch,
+ L5XName.DirectedLink,
+ L5XName.TextBox,
+ L5XName.Attachment
+ };
}
}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/Connection.cs b/src/L5Sharp/Elements/Connection.cs
index 899fe68c..d8f3e7dc 100644
--- a/src/L5Sharp/Elements/Connection.cs
+++ b/src/L5Sharp/Elements/Connection.cs
@@ -2,6 +2,7 @@
using System.Xml.Linq;
using L5Sharp.Components;
using L5Sharp.Enums;
+using L5Sharp.Utilities;
namespace L5Sharp.Elements;
@@ -16,6 +17,7 @@ public class Connection : LogixElement
public Connection()
{
Name = string.Empty;
+ RPI = 0;
Type = ConnectionType.Unknown;
Priority = ConnectionPriority.Scheduled;
InputConnectionType = TransmissionType.Multicast;
@@ -183,7 +185,7 @@ public string OutputTagSuffix
public Tag? InputTag
{
get => GetComplex();
- set => SetComplex(value);
+ set => SetComplex(value?.Convert(L5XName.InputTag));
}
///
@@ -193,6 +195,6 @@ public Tag? InputTag
public Tag? OutputTag
{
get => GetComplex();
- set => SetComplex(value);
+ set => SetComplex(value?.Convert(L5XName.OutputTag));
}
}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/Diagram.cs b/src/L5Sharp/Elements/Diagram.cs
index 639bc58a..291b63e2 100644
--- a/src/L5Sharp/Elements/Diagram.cs
+++ b/src/L5Sharp/Elements/Diagram.cs
@@ -3,6 +3,7 @@
using System.Linq;
using System.Xml.Linq;
using JetBrains.Annotations;
+using L5Sharp.Common;
using L5Sharp.Utilities;
namespace L5Sharp.Elements;
@@ -13,8 +14,11 @@ namespace L5Sharp.Elements;
/// removing, and accessing child block types from the Diagram.
///
/// The base type this Diagram contains.
+/// The type the Diagram supports.
[PublicAPI]
-public abstract class Diagram : LogixCode where TBlock : DiagramBlock
+public abstract class Diagram : LogixCode
+ where TBlock : DiagramBlock
+ where TConnector : DiagramConnector, new()
{
///
/// The defined order of all child diagram elements. This is required so we can add elements in the correct position.
@@ -23,14 +27,14 @@ public abstract class Diagram : LogixCode where TBlock : DiagramBlock
protected abstract IEnumerable Ordering();
///
- /// Creates a new with default values.
+ /// Creates a new with default values.
///
protected Diagram()
{
}
///
- /// Creates a new initialized with the provided .
+ /// Creates a new initialized with the provided .
///
/// The to initialize the type with.
/// block is null.
@@ -60,7 +64,7 @@ public TBlock this[uint id]
///
/// This will update the block ID to the next available ID if the ID is already used.
/// This will preserve the uniqueness of the ID's and prevent import errors. This will also perform a sort of the
- /// current underlying diagram elements to ensure the order of the element is maintained. This also si required to
+ /// current underlying diagram elements to ensure the order of the element is maintained. This also is required to
/// prevent import errors.
///
public uint Add(TBlock block)
@@ -74,6 +78,20 @@ public uint Add(TBlock block)
return block.ID;
}
+ ///
+ /// Adds the provided DiagramConnector to this Diagram element.
+ ///
+ /// The to add to the diagram.
+ /// connector is null.
+ public void Add(TConnector connector)
+ {
+ if (connector is null)
+ throw new ArgumentNullException(nameof(connector));
+
+ Element.Add(connector.Serialize());
+ SortBlocks();
+ }
+
///
/// Adds the provided TextBox to this Diagram.
///
@@ -82,7 +100,7 @@ public uint Add(TBlock block)
///
/// This will update the block ID to the next available ID if the ID is already used.
/// This will preserve the uniqueness of the ID's and prevent import errors. This will also perform a sort of the
- /// current underlying diagram elements to ensure the order of the element is maintained. This also si required to
+ /// current underlying diagram elements to ensure the order of the element is maintained. This also is required to
/// prevent import errors.
///
public uint Add(TextBox textBox)
@@ -97,11 +115,22 @@ public uint Add(TextBox textBox)
}
///
- ///
+ /// Adds the provided attachment to this Diagram.
///
- ///
- ///
- public abstract void Connect(uint fromId, uint toId);
+ /// The to add to the diagram.
+ /// textBox is null.
+ ///
+ /// This will perform a sort of the current underlying diagram elements to ensure the order of the element is maintained.
+ /// This is required to prevent import errors.
+ ///
+ public void Add(Attachment attachment)
+ {
+ if (attachment is null)
+ throw new ArgumentNullException(nameof(attachment));
+
+ Element.Add(attachment.Serialize());
+ SortBlocks();
+ }
///
/// Finds a block with the specified block id. If not found, returns null.
@@ -131,7 +160,7 @@ public uint Add(TextBox textBox)
}
///
- /// Gets all element contained in the .
+ /// Gets all element contained in the .
///
/// A of elements.
///
@@ -145,7 +174,7 @@ public IEnumerable Blocks()
}
///
- /// Gets all elements of the specified block type contained in the .
+ /// Gets all elements of the specified block type contained in the .
///
/// The diagram block type to return.
/// A containing all found elements.
@@ -159,6 +188,53 @@ public IEnumerable Blocks() where TBlockType : TBlock
return Elements().Where(e => e is TBlockType).Cast();
}
+ ///
+ /// Adds a connector between the specified block IDs.
+ ///
+ /// The ID of the block from which the connector starts.
+ /// The ID of the block to which the connector ends.
+ public void Connect(uint from, uint to)
+ {
+ var connector = new TConnector
+ {
+ FromID = from,
+ ToID = to
+ };
+
+ Element.Add(connector.Serialize());
+ SortBlocks();
+ }
+
+ ///
+ /// Gets all elements in the .
+ ///
+ ///
+ /// An of the diagram specific type objects if any.
+ /// If none, an empty collection.
+ ///
+ public IEnumerable Connectors()
+ {
+ return Elements().Where(e => e is TConnector).Cast();
+ }
+
+ ///
+ /// Finds all other elements that are connected via a to
+ /// the provided block.
+ ///
+ /// The to find connections to/from.
+ ///
+ /// A of elements connected to the provided block.
+ ///
+ ///
+ /// This helps find all the connecting blocks within a sheet, allowing potential traversal of the diagram
+ /// block elements.
+ ///
+ public IEnumerable Connections(TBlock block)
+ {
+ var connections = Connectors().Select(c => c.Connected(block)).Where(id => id.HasValue).ToHashSet();
+ return Blocks().Where(b => connections.Contains(b.ID));
+ }
+
///
/// Gets all child of the current diagram.
///
@@ -168,6 +244,20 @@ public IEnumerable Elements()
return Element.Elements().Select(e => e.Deserialize());
}
+ ///
+ /// Gets all child elements in the
+ ///
+ ///
+ ///
+ public IEnumerable Elements() where TBlockType : DiagramBlock =>
+ Elements().Where(e => e is TBlockType).Cast();
+
+ ///
+ public override IEnumerable References()
+ {
+ return Blocks().Where(b => b is ILogixReferencable).Cast().SelectMany(r => r.References());
+ }
+
///
/// Joins the defined ordered set of element names with the child elements of the diagram, and replaces all current
/// nodes with the same set of order nodes. This maintains the order of the diagram elements base on the derived
diff --git a/src/L5Sharp/Elements/DiagramBlock.cs b/src/L5Sharp/Elements/DiagramBlock.cs
index c791e7f7..fa05934a 100644
--- a/src/L5Sharp/Elements/DiagramBlock.cs
+++ b/src/L5Sharp/Elements/DiagramBlock.cs
@@ -38,6 +38,7 @@ protected DiagramBlock(XElement element) : base(element)
///
/// The unique identifier of the within the containing Diagram.
///
+ /// A zero based representing the block id.
public uint ID
{
get => GetValue();
@@ -74,11 +75,12 @@ public uint Y
/// Gets the cell of the diagram where the is located.
///
/// A containing the cell coordinates (e.g. A1, B2, etc.)
- /// This is determines from the X and Y coordinates of the element.
+ /// This is determines from the X and Y coordinates of the element. Each cell is 200x200
+ /// pixels which can be used to calculate the cell location.
public string Cell => $"{(char)(X / 200 + 'A')}{Y / 200 + 1}";
///
- /// The absolute location of the within the containing .
+ /// The descriptive indication of the location of this within the containing Diagram.
///
/// A indicating the cell and optional sheet or chart number where the block is located.
///
diff --git a/src/L5Sharp/Elements/DiagramConnector.cs b/src/L5Sharp/Elements/DiagramConnector.cs
index 05e40e74..3b57084d 100644
--- a/src/L5Sharp/Elements/DiagramConnector.cs
+++ b/src/L5Sharp/Elements/DiagramConnector.cs
@@ -3,6 +3,11 @@
namespace L5Sharp.Elements;
+///
+/// An abstraction for the different diagram connector elements such as Wire and DirectLink which have
+/// shared to/from id properties. elements will define how
+/// elements are connected within a containing .
+///
public abstract class DiagramConnector : LogixElement
{
///
@@ -20,7 +25,7 @@ protected DiagramConnector()
protected DiagramConnector(XElement element) : base(element)
{
}
-
+
///
/// The ID of the source DiagramBlock this is connected to.
///
@@ -29,7 +34,7 @@ public uint FromID
get => GetValue();
set => SetValue(value);
}
-
+
///
/// The ID of the destination DiagramBlock this is connected to.
///
@@ -38,4 +43,32 @@ public uint ToID
get => GetValue();
set => SetValue(value);
}
+
+ ///
+ /// Determines if this connector connects either to or from the provided DiagramBlock.
+ ///
+ /// The block to determine the connection to/from.
+ /// true if this wire has a ToId or FromId connecting the provided block; Otherwise, false.
+ public bool IsConnected(DiagramBlock block) => ToID == block.ID || FromID == block.ID;
+
+ ///
+ /// Determines if this wire connects to the provided DiagramBlock.
+ ///
+ /// The block to determine the connection to.
+ /// ture if this wire has a ToID connecting the provided block; Otherwise, false.
+ public bool ConnectsTo(DiagramBlock block) => ToID == block.ID;
+
+ ///
+ /// Determines if this wire has a connection from the provided DiagramBlock.
+ ///
+ /// The block to determine the connection to.
+ /// ture if this wire has a ToID connecting the provided block; Otherwise, false.
+ public bool ConnectsFrom(DiagramBlock block) => FromID == block.ID;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public uint? Connected(DiagramBlock block) => ToID == block.ID ? FromID : FromID == block.ID ? ToID : default;
}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/DirectedLink.cs b/src/L5Sharp/Elements/DirectedLink.cs
new file mode 100644
index 00000000..57a89c59
--- /dev/null
+++ b/src/L5Sharp/Elements/DirectedLink.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Xml.Linq;
+using L5Sharp.Utilities;
+
+namespace L5Sharp.Elements;
+
+///
+/// A LogixElement type that defines the properties for a wire connector within a
+/// Function Block Diagram (FBD).
+///
+///
+/// A Connector is not a itself since is does not have the location and ID properties.
+/// It simply maps the connections of pins within a diagram.
+/// Use these guidelines for directed links:
+/// • All directed link blocks must come after all step, transition, stop, and branch blocks.
+/// • A directed link links only one element to one other element.
+///
+///
+[L5XType(L5XName.DirectedLink, L5XName.SFCContent)]
+public class DirectedLink : DiagramConnector
+{
+ ///
+ /// Creates a new with default values.
+ ///
+ public DirectedLink()
+ {
+ }
+
+ ///
+ /// Creates a new initialized with the provided .
+ ///
+ /// The to initialize the type with.
+ /// block is null.
+ public DirectedLink(XElement element) : base(element)
+ {
+ }
+
+ ///
+ /// Indicates whether the link is shown on the SFC Diagram.
+ ///
+ public bool? Show
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
+}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/FunctionBlock.cs b/src/L5Sharp/Elements/FunctionBlock.cs
index e95048f7..d8522124 100644
--- a/src/L5Sharp/Elements/FunctionBlock.cs
+++ b/src/L5Sharp/Elements/FunctionBlock.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Xml.Linq;
namespace L5Sharp.Elements;
@@ -6,7 +8,7 @@ namespace L5Sharp.Elements;
///
/// A abstract derivative of a DiagramBlock type that represent blocks contained within a Function Block Diagram (FBD).
/// This type is primarily to constrain the type of blocks that the caller can add to a Sheet diagram type.
-/// It also add some common properties that we want all FunctionBlock derivatives to contain.
+/// It also adds some common properties that we want all FunctionBlock derivatives to contain.
///
///
public Sheet? Sheet => Element.Parent is not null ? new Sheet(Element.Parent) : default;
+
+ ///
+ /// Finds all other elements that have connections to this block element.
+ ///
+ /// An containing connected element objects.
+ /// This relies on the parent diagram element to find other connecting blocks.
+ /// If this block element is not attached to a Sheet then it will return and empty collection.
+ public IEnumerable Connections() => Sheet?.Connections(this) ?? Enumerable.Empty();
}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/ConnectorBlock.cs b/src/L5Sharp/Elements/ICON.cs
similarity index 66%
rename from src/L5Sharp/Elements/ConnectorBlock.cs
rename to src/L5Sharp/Elements/ICON.cs
index 102e19f6..a3308658 100644
--- a/src/L5Sharp/Elements/ConnectorBlock.cs
+++ b/src/L5Sharp/Elements/ICON.cs
@@ -20,29 +20,30 @@ namespace L5Sharp.Elements;
/// `Logix 5000 Controllers Import/Export` for more information.
///
[L5XType(L5XName.ICon, L5XName.Sheet)]
-[L5XType(L5XName.OCon, L5XName.Sheet)]
-public class ConnectorBlock : FunctionBlock
+public class ICON : FunctionBlock
{
///
- /// Creates a new with default values.
+ /// Creates a new with default values.
///
- public ConnectorBlock()
+ public ICON()
{
+ Name = string.Empty;
}
-
+
///
- /// Creates a new as the specified input or output block type.
+ /// Creates a new with the provided operand value.
///
- public ConnectorBlock(ParameterType parameterType) : base(GenerateElement(parameterType))
+ public ICON(string name)
{
+ Name = name;
}
///
- /// Creates a new initialized with the provided .
+ /// Creates a new initialized with the provided .
///
/// The to initialize the type with.
/// element is null.
- public ConnectorBlock(XElement element) : base(element)
+ public ICON(XElement element) : base(element)
{
}
@@ -54,11 +55,4 @@ public string? Name
get => GetValue();
set => SetValue(value);
}
-
- private static XElement GenerateElement(ParameterType parameterType)
- {
- if (parameterType is null) throw new ArgumentNullException(nameof(parameterType));
- var name = parameterType == ParameterType.Input ? L5XName.ICon : L5XName.OCon;
- return new XElement(name);
- }
}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/IREF.cs b/src/L5Sharp/Elements/IREF.cs
new file mode 100644
index 00000000..73598db6
--- /dev/null
+++ b/src/L5Sharp/Elements/IREF.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Xml.Linq;
+using L5Sharp.Common;
+using L5Sharp.Utilities;
+
+namespace L5Sharp.Elements;
+
+///
+/// A DiagramBlock type that defines the properties for input reference blocks in a Function Block Diagram (FBD).
+///
+///
+/// A OutReference is used to map tags or immediate values to the pin or parameters or a function, block, or AOI
+/// DiagramBlock within a FBD. A will contain both input and output
+/// references which are different types with the same properties.
+///
+///
+[L5XType(L5XName.IRef, L5XName.Sheet)]
+public class IREF : FunctionBlock
+{
+ ///
+ /// Creates a new with default values.
+ ///
+ public IREF()
+ {
+ Operand = string.Empty;
+ HideDesc = false;
+ }
+
+ ///
+ /// Creates a new with the provided operand value.
+ ///
+ public IREF(Argument operand) : this()
+ {
+ Operand = operand;
+ }
+
+ ///
+ /// Creates a new initialized with the provided .
+ ///
+ /// The to initialize the type with.
+ /// element is null.
+ public IREF(XElement element) : base(element)
+ {
+ }
+
+ ///
+ /// The tag name or immediate operand value for the reference element.
+ ///
+ /// A containing the reference if it exists; Otherwise, null.
+ public Argument? Operand
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
+
+ ///
+ /// Whether or not to hide the description for the reference element.
+ ///
+ /// true if the description is hidden; Otherwise; false.
+ public bool? HideDesc
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
+
+ ///
+ public override IEnumerable References()
+ {
+ if (Operand is not null && Operand.IsTag)
+ {
+ yield return new CrossReference(Element, L5XName.Tag, Operand.ToString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/JSR.cs b/src/L5Sharp/Elements/JSR.cs
new file mode 100644
index 00000000..7b2129dd
--- /dev/null
+++ b/src/L5Sharp/Elements/JSR.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Xml.Linq;
+using L5Sharp.Common;
+using L5Sharp.Utilities;
+
+namespace L5Sharp.Elements;
+
+///
+/// A DiagramBlock type that defines the properties for a call to a Routine.
+///
+///
+[L5XType(L5XName.JSR, L5XName.Sheet)]
+public class JSR : FunctionBlock
+{
+ ///
+ /// Creates a new with default values.
+ ///
+ public JSR()
+ {
+ }
+
+ ///
+ /// Creates a new initialized with the provided .
+ ///
+ /// The to initialize the type with.
+ /// element is null.
+ public JSR(XElement element) : base(element)
+ {
+ }
+
+ ///
+ /// The name of the routine to call for the JSR element.
+ ///
+ /// A containing the name of the routine if found; Otherwise, null.
+ public string? Routine
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
+
+ ///
+ /// The collection of input parameters to the routine being called by the FunctionBlock element.
+ ///
+ /// A object wrapping the underlying attribute containing the In parameters
+ /// if exists; Otherwise, null.
+ public Params? In
+ {
+ get => Element.Attribute(L5XName.In) is not null ? new Params(Element.Attribute(L5XName.In)!) : default;
+ set => SetValue(value is not null ? string.Join(" ", value) : null);
+ }
+
+ ///
+ /// The collection of input parameters to the routine being called by the FunctionBlock element.
+ ///
+ /// A object wrapping the underlying attribute containing the In parameters
+ /// if exists; Otherwise, null.
+ public Params? Ret
+ {
+ get => Element.Attribute(L5XName.Ret) is not null ? new Params(Element.Attribute(L5XName.Ret)!) : default;
+ set => SetValue(value is not null ? string.Join(" ", value) : null);
+ }
+
+ ///
+ public override IEnumerable References()
+ {
+ if (Routine is not null)
+ yield return new CrossReference(Element, L5XName.Routine, Routine);
+
+ if (In is not null)
+ {
+ foreach (var parameter in In)
+ yield return new CrossReference(Element, L5XName.Tag, parameter);
+ }
+
+ if (Ret is null) yield break;
+
+ foreach (var parameter in Ret)
+ yield return new CrossReference(Element, L5XName.Tag, parameter);
+ }
+}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/JsrBlock.cs b/src/L5Sharp/Elements/JsrBlock.cs
deleted file mode 100644
index f8795b5c..00000000
--- a/src/L5Sharp/Elements/JsrBlock.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Xml.Linq;
-using L5Sharp.Common;
-using L5Sharp.Utilities;
-
-namespace L5Sharp.Elements;
-
-///
-/// A DiagramBlock type that defines the properties for a call to a Routine.
-///
-///
-[L5XType(L5XName.JSR, L5XName.Sheet)]
-public class JsrBlock : FunctionBlock
-{
- ///
- /// Creates a new with default values.
- ///
- public JsrBlock()
- {
- }
-
- ///
- /// Creates a new initialized with the provided .
- ///
- /// The to initialize the type with.
- /// element is null.
- public JsrBlock(XElement element) : base(element)
- {
- }
-
- ///
- /// The name of the routine to call for the JSR element.
- ///
- /// A containing the name of the routine if found; Otherwise, null.
- public string? Routine
- {
- get => GetValue();
- set => SetValue(value);
- }
-
- ///
- /// A collection of input parameter names for the JSR.
- ///
- /// A containing the names of the parameters if found. If not found then an
- /// empty collection.
- /// To update the property, you must assign a new collection of names.
- public IEnumerable Parameters => throw new NotImplementedException();
-
- ///
- public override IEnumerable References()
- {
- if (Routine is not null)
- yield return new CrossReference(Element, L5XName.Routine, Routine);
-
- foreach (var parameter in Parameters)
- yield return new CrossReference(Element, L5XName.Tag, parameter);
- }
-}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/OCON.cs b/src/L5Sharp/Elements/OCON.cs
new file mode 100644
index 00000000..ccca23c2
--- /dev/null
+++ b/src/L5Sharp/Elements/OCON.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Xml.Linq;
+using L5Sharp.Utilities;
+
+namespace L5Sharp.Elements;
+
+///
+/// A DiagramBlock type that defines the properties for output reference blocks in a Function Block Diagram (FBD).
+///
+///
+/// A OutReference is used to map tags or immediate values to the pin or parameters or a function, block, or AOI
+/// DiagramBlock within a FBD. A will contain both input and output
+/// references which are different types with the same properties.
+///
+///
+[L5XType(L5XName.OCon, L5XName.Sheet)]
+public class OCON : FunctionBlock
+{
+ ///
+ /// Creates a new with default values.
+ ///
+ public OCON()
+ {
+ }
+
+ ///
+ /// Creates a new with the provided operand value.
+ ///
+ public OCON(string name)
+ {
+ Name = name;
+ }
+
+ ///
+ /// Creates a new initialized with the provided .
+ ///
+ /// The to initialize the type with.
+ /// element is null.
+ public OCON(XElement element) : base(element)
+ {
+ }
+
+ ///
+ /// The name identifying the connector element.
+ ///
+ public string? Name
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
+}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/OREF.cs b/src/L5Sharp/Elements/OREF.cs
new file mode 100644
index 00000000..597d7a6b
--- /dev/null
+++ b/src/L5Sharp/Elements/OREF.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Xml.Linq;
+using L5Sharp.Common;
+using L5Sharp.Utilities;
+
+namespace L5Sharp.Elements;
+
+///
+/// A DiagramBlock type that defines the properties for output reference blocks in a Function Block Diagram (FBD).
+///
+///
+/// A OutReference is used to map tags or immediate values to the pin or parameters or a function, block, or AOI
+/// DiagramBlock within a FBD. A will contain both input and output
+/// references which are different types with the same properties.
+///
+///
+[L5XType(L5XName.ORef, L5XName.Sheet)]
+public class OREF : FunctionBlock
+{
+ ///
+ /// Creates a new with default values.
+ ///
+ public OREF()
+ {
+ HideDesc = false;
+ }
+
+ ///
+ /// Creates a new with the provided operand value.
+ ///
+ public OREF(Argument operand)
+ {
+ Operand = operand;
+ HideDesc = false;
+ }
+
+ ///
+ /// Creates a new initialized with the provided .
+ ///
+ /// The to initialize the type with.
+ /// element is null.
+ public OREF(XElement element) : base(element)
+ {
+ }
+
+ ///
+ /// The tag name or immediate operand value for the reference element.
+ ///
+ /// A containing the reference if it exists; Otherwise, null.
+ public Argument? Operand
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
+
+ ///
+ /// Whether or not to hide the description for the reference element.
+ ///
+ /// true if the description is hidden; Otherwise; false.
+ public bool? HideDesc
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
+
+ ///
+ public override IEnumerable References()
+ {
+ if (Operand is not null && Operand.IsTag)
+ {
+ yield return new CrossReference(Element, L5XName.Tag, Operand.ToString());
+ }
+ }
+}
diff --git a/src/L5Sharp/Elements/ReferenceBlock.cs b/src/L5Sharp/Elements/ReferenceBlock.cs
deleted file mode 100644
index 44909e01..00000000
--- a/src/L5Sharp/Elements/ReferenceBlock.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Xml.Linq;
-using L5Sharp.Common;
-using L5Sharp.Enums;
-using L5Sharp.Utilities;
-
-namespace L5Sharp.Elements;
-
-///
-/// A DiagramBlock type that defines the properties for a input or output reference within a
-/// Function Block Diagram (FBD).
-///
-///
-/// A ReferenceBlock can be a tag name or immediate value, which is contained in the
-/// property of the type. A will contain both input and output references.
-///
-///
-[L5XType(L5XName.IRef, L5XName.Sheet)]
-[L5XType(L5XName.ORef, L5XName.Sheet)]
-public class ReferenceBlock : FunctionBlock
-{
- ///
- /// Creates a new with default values.
- ///
- public ReferenceBlock()
- {
- }
-
- ///
- /// Creates a new as the specified input or output block type.
- ///
- public ReferenceBlock(ParameterType parameterType) : base(GenerateElement(parameterType))
- {
- }
-
- ///
- /// Creates a new initialized with the provided .
- ///
- /// The to initialize the type with.
- /// element is null.
- public ReferenceBlock(XElement element) : base(element)
- {
- }
-
- ///
- /// The tag name or immediate value for the ReferenceBlock element.
- ///
- /// A containing the reference name if it exists; Otherwise, null.
- public string? Operand
- {
- get => GetValue();
- set => SetValue(value);
- }
-
- ///
- /// Whether or not to hide the description for the ReferenceBlock element.
- ///
- /// true if the description is hidden; Otherwise; false.
- public bool HideDesc
- {
- get => GetValue();
- set => SetValue(value);
- }
-
- ///
- public override IEnumerable References()
- {
- if (Operand is not null && Operand.IsTag())
- {
- yield return new CrossReference(Element, Operand, L5XName.Tag);
- }
- }
-
- private static XElement GenerateElement(ParameterType parameterType)
- {
- if (parameterType is null) throw new ArgumentNullException(nameof(parameterType));
- var name = parameterType == ParameterType.Input ? L5XName.IRef : L5XName.ORef;
- return new XElement(name);
- }
-}
\ No newline at end of file
diff --git a/src/L5Sharp/Elements/RoutineBlock.cs b/src/L5Sharp/Elements/RoutineBlock.cs
index 1aa05cc3..95deaef4 100644
--- a/src/L5Sharp/Elements/RoutineBlock.cs
+++ b/src/L5Sharp/Elements/RoutineBlock.cs
@@ -24,7 +24,7 @@ protected RoutineBlock()
}
///
- /// Creates a new initialized with the provided .
+ /// Creates a new initialized with the provided .
///
/// The to initialize the type with.
/// element is null.
diff --git a/src/L5Sharp/Elements/Rung.cs b/src/L5Sharp/Elements/Rung.cs
index dd9e93c5..1aec6c0e 100644
--- a/src/L5Sharp/Elements/Rung.cs
+++ b/src/L5Sharp/Elements/Rung.cs
@@ -86,9 +86,9 @@ public override IEnumerable References()
if (instruction.CallsRoutine)
{
var routine = instruction.Arguments.FirstOrDefault()?.ToString() ?? string.Empty;
- references.Add(new CrossReference(Element, L5XName.Routine, routine, instruction));
+ references.Add(new CrossReference(Element, L5XName.Routine, routine));
var parameters = instruction.Arguments.Skip(1).Where(a => a.IsTag).Select(t => t.ToString());
- references.AddRange(parameters.Select(p => new CrossReference(Element, L5XName.Tag, p, instruction)));
+ references.AddRange(parameters.Select(p => new CrossReference(Element, L5XName.Tag, p)));
continue;
}
@@ -100,7 +100,7 @@ public override IEnumerable References()
}
references.AddRange(instruction.Text.Tags()
- .Select(t => new CrossReference(Element, L5XName.Tag, t.ToString(), instruction)));
+ .Select(t => new CrossReference(Element, L5XName.Tag, t.ToString())));
}
return references;
diff --git a/src/L5Sharp/Elements/SbrBlock.cs b/src/L5Sharp/Elements/SBR.cs
similarity index 87%
rename from src/L5Sharp/Elements/SbrBlock.cs
rename to src/L5Sharp/Elements/SBR.cs
index 92e5a7b7..f81db800 100644
--- a/src/L5Sharp/Elements/SbrBlock.cs
+++ b/src/L5Sharp/Elements/SBR.cs
@@ -14,21 +14,21 @@ namespace L5Sharp.Elements;
/// `Logix 5000 Controllers Import/Export` for more information.
///
[L5XType(L5XName.SBR, L5XName.Sheet)]
-public class SbrBlock : DiagramBlock
+public class SBR : DiagramBlock
{
///
- /// Creates a new with default values.
+ /// Creates a new with default values.
///
- public SbrBlock()
+ public SBR()
{
}
///
- /// Creates a new initialized with the provided .
+ /// Creates a new initialized with the provided .
///
/// The to initialize the type with.
/// element is null.
- public SbrBlock(XElement element) : base(element)
+ public SBR(XElement element) : base(element)
{
}
diff --git a/src/L5Sharp/Elements/SequenceBlock.cs b/src/L5Sharp/Elements/SequenceBlock.cs
index deb70c29..ec862184 100644
--- a/src/L5Sharp/Elements/SequenceBlock.cs
+++ b/src/L5Sharp/Elements/SequenceBlock.cs
@@ -35,7 +35,7 @@ protected SequenceBlock()
protected SequenceBlock(XElement element) : base(element)
{
}
-
+
///
/// The parent element that this FunctionBlock is contained within.
///
diff --git a/src/L5Sharp/Elements/Sheet.cs b/src/L5Sharp/Elements/Sheet.cs
index e7e48a30..136aa05a 100644
--- a/src/L5Sharp/Elements/Sheet.cs
+++ b/src/L5Sharp/Elements/Sheet.cs
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Xml.Linq;
-using L5Sharp.Common;
using L5Sharp.Utilities;
namespace L5Sharp.Elements;
@@ -31,7 +29,7 @@ namespace L5Sharp.Elements;
///
///
[L5XType(L5XName.Sheet, L5XName.FBDContent)]
-public class Sheet : Diagram
+public class Sheet : Diagram
{
///
/// Creates a new with default values.
@@ -59,22 +57,6 @@ public string? Description
set => SetDescription(value);
}
- ///
- public override IEnumerable References()
- {
- var references = new List();
- references.AddRange(Blocks().SelectMany(r => r.References()));
- references.AddRange(Blocks().SelectMany(r => r.References()));
- references.AddRange(Blocks().SelectMany(r => r.References()));
- references.AddRange(Blocks().SelectMany(r => r.References()));
- return references;
- }
-
- public override void Connect(uint fromId, uint toId)
- {
- throw new NotImplementedException();
- }
-
///
protected override IEnumerable Ordering()
{
diff --git a/src/L5Sharp/Elements/Step.cs b/src/L5Sharp/Elements/Step.cs
index 63d6b04e..24818927 100644
--- a/src/L5Sharp/Elements/Step.cs
+++ b/src/L5Sharp/Elements/Step.cs
@@ -50,9 +50,9 @@ public string? Operand
/// Whether or not to hide the description for the Step.
///
/// true if the description is hidden; Otherwise; false.
- public bool HideDesc
+ public bool? HideDesc
{
- get => GetValue();
+ get => GetValue();
set => SetValue(value);
}
diff --git a/src/L5Sharp/Elements/Wire.cs b/src/L5Sharp/Elements/Wire.cs
index bc08cab2..b5ee9fdd 100644
--- a/src/L5Sharp/Elements/Wire.cs
+++ b/src/L5Sharp/Elements/Wire.cs
@@ -1,6 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Xml.Linq;
using L5Sharp.Utilities;
@@ -19,7 +17,7 @@ namespace L5Sharp.Elements;
/// `Logix 5000 Controllers Import/Export` for more information.
///
[L5XType(L5XName.Wire, L5XName.Sheet)]
-public class Wire : LogixElement
+public class Wire : DiagramConnector
{
///
/// Creates a new with default values.
@@ -37,15 +35,6 @@ public Wire(XElement element) : base(element)
{
}
- ///
- /// The ID of the source DiagramBlock this wire is connected to.
- ///
- public uint FromID
- {
- get => GetValue();
- set => SetValue(value);
- }
-
///
/// The parameter name of source DiagramBlock pin this wire is connected to.
///
@@ -55,15 +44,6 @@ public string? FromParam
set => SetValue(value);
}
- ///
- /// The ID of the destination DiagramBlock this wire is connected to.
- ///
- public uint ToID
- {
- get => GetValue();
- set => SetValue(value);
- }
-
///
/// The parameter name of destination DiagramBlock pin this wire is connected to.
///
@@ -76,21 +56,13 @@ public string? ToParam
///
/// Determines if this connector connects either to or from the provided DiagramBlock.
///
- /// The block to determine the connection to/from.
+ /// The id of the source block to find the connection to.
+ /// The parameter name of the source block to find the connection to.
/// true if this wire has a ToId or FromId connecting the provided block; Otherwise, false.
- public bool Connects(DiagramBlock block) => ToID == block.ID || FromID == block.ID;
-
- ///
- /// Determines if this wire connects to the provided DiagramBlock.
- ///
- /// The block to determine the connection to.
- /// ture if this wire has a ToID connecting the provided block; Otherwise, false.
- public bool ConnectsTo(DiagramBlock block) => ToID == block.ID;
-
- ///
- /// Determines if this wire has a connection from the provided DiagramBlock.
- ///
- /// The block to determine the connection to.
- /// ture if this wire has a ToID connecting the provided block; Otherwise, false.
- public bool ConnectsFrom(DiagramBlock block) => FromID == block.ID;
+ public Tuple? Connection(uint id, string param)
+ {
+ return ToID == id && ToParam?.IsEquivalent(param) == true ? new Tuple(FromID, FromParam)
+ : FromID == id && FromParam?.IsEquivalent(param) == true ? new Tuple(ToID, ToParam)
+ : default;
+ }
}
\ No newline at end of file
diff --git a/src/L5Sharp/L5Sharp.csproj b/src/L5Sharp/L5Sharp.csproj
index b903e231..2d4e7597 100644
--- a/src/L5Sharp/L5Sharp.csproj
+++ b/src/L5Sharp/L5Sharp.csproj
@@ -25,17 +25,12 @@
-
-
-
-
-
diff --git a/src/L5Sharp/L5X.cs b/src/L5Sharp/L5X.cs
index 0666cbca..77ee0096 100644
--- a/src/L5Sharp/L5X.cs
+++ b/src/L5Sharp/L5X.cs
@@ -171,6 +171,7 @@ public static L5X New(TComponent component, Revision? revision = nul
/// Gets the collection of components found in the L5X file.
///
/// A of components.
+ [PublicAPI]
public LogixContainer Modules => new(GetContainer(L5XName.Modules));
///
@@ -454,8 +455,8 @@ public IEnumerable FindComponents(string name) where TCo
/// Finds all tags with the specified name in the L5X using internal component index.
///
/// The of the tag to find in the L5X file.
- /// The optional scope of the tag to find. If not provided, this will return the first found
- /// object with the provided tag name.
+ /// The optional scoped container of the tag to find. If not provided, this will return
+ /// the first found object with the provided tag name.
/// A with the specified tag name and scope if found; Otherwise, null.
/// tagName is null.
///
@@ -465,7 +466,7 @@ public IEnumerable FindComponents(string name) where TCo
/// By default this will return the first found tag with the specified name. You can also specify a scope
/// name to find a tag within a specific program.
///
- public Tag? FindTag(TagName tagName, string? scope = null)
+ public Tag? FindTag(TagName tagName, string? container = null)
{
if (tagName is null) throw new ArgumentNullException(nameof(tagName));
@@ -474,8 +475,8 @@ public IEnumerable FindComponents(string name) where TCo
if (!_componentIndex.TryGetValue(key, out var components))
return default;
- if (scope is not null)
- return components.TryGetValue(scope, out var element) ? new Tag(element).Member(tagName.Path) : default;
+ if (container is not null)
+ return components.TryGetValue(container, out var element) ? new Tag(element).Member(tagName.Path) : default;
return components.Values.FirstOrDefault()?.Deserialize().Member(tagName.Path);
}
@@ -566,7 +567,7 @@ public TComponent GetComponent(string name) where TComponent : Logix
throw new KeyNotFoundException($"FindComponent not found in L5X: {key}");
var component = components.Values.SingleOrDefault();
-
+
return component is not null
? component.Deserialize()
: throw new KeyNotFoundException($"FindComponent not found in L5X: {key}");
@@ -597,7 +598,7 @@ public TComponent GetComponent(string name, string scope) where TCom
var key = new ComponentKey(typeof(TComponent).L5XType(), name);
if (_componentIndex.TryGetValue(key, out var components) && components.TryGetValue(scope, out var element))
- LogixSerializer.Deserialize(element);
+ element.Deserialize();
throw new KeyNotFoundException($"FindComponent not found in L5X: {key}");
}
diff --git a/src/L5Sharp/LogixCode.cs b/src/L5Sharp/LogixCode.cs
index 3f2e171b..40df8882 100644
--- a/src/L5Sharp/LogixCode.cs
+++ b/src/L5Sharp/LogixCode.cs
@@ -4,7 +4,6 @@
using System.Xml.Linq;
using L5Sharp.Common;
using L5Sharp.Components;
-using L5Sharp.Enums;
using L5Sharp.Utilities;
namespace L5Sharp;
diff --git a/src/L5Sharp/LogixElement.cs b/src/L5Sharp/LogixElement.cs
index 1fdad07b..213c5d28 100644
--- a/src/L5Sharp/LogixElement.cs
+++ b/src/L5Sharp/LogixElement.cs
@@ -30,6 +30,8 @@ protected LogixElement()
///
/// The L5X to initialize the entity with.
/// element is null.
+ /// element name does not match any configured L5XType for this class
+ /// type. These are defined via the on the derived classes.
protected LogixElement(XElement element)
{
Element = element ?? throw new ArgumentNullException(nameof(element));
@@ -147,7 +149,7 @@ public void AddAfter(LogixElement element)
{
element = element.Convert(L5XType);
}
-
+
Element.AddAfterSelf(element.Serialize());
}
@@ -180,7 +182,7 @@ public void AddBefore(LogixElement element)
{
element = element.Convert(L5XType);
}
-
+
Element.AddBeforeSelf(element.Serialize());
}
@@ -203,7 +205,7 @@ public void AddBefore(LogixElement element)
/// The deserialized type can not be cast to the specified generic type parameter.
/// This method will simply deserialize a new instance using the current underlying element data.
public TElement Clone() where TElement : LogixElement => new XElement(Element).Deserialize();
-
+
///
/// Converts this element to the specified element type name.
///
@@ -231,7 +233,7 @@ public LogixElement Convert(string typeName)
Element.Name = typeName;
return Element.Deserialize();
}
-
+
///
/// Converts this element to the specified element type name.
///
@@ -306,7 +308,7 @@ public void Replace(LogixElement element)
{
element = element.Convert(L5XType);
}
-
+
Element.AddAfterSelf(element.Serialize());
}
diff --git a/src/L5Sharp/Utilities/L5XParser.cs b/src/L5Sharp/Utilities/L5XParser.cs
index d790f051..d794b5ba 100644
--- a/src/L5Sharp/Utilities/L5XParser.cs
+++ b/src/L5Sharp/Utilities/L5XParser.cs
@@ -68,6 +68,7 @@ public static class L5XParser
{typeof(TriggerType), TriggerType.FromValue},
{typeof(Use), s => Use.FromName(s)},
//Common
+ {typeof(Argument), Argument.Parse},
{typeof(Address), s => new Address(s)},
{typeof(Dimensions), Dimensions.Parse},
{typeof(NeutralText), s => new NeutralText(s)},
diff --git a/src/L5Sharp/Utilities/TagDictionary.cs b/src/L5Sharp/Utilities/TagDictionary.cs
deleted file mode 100644
index c86245c4..00000000
--- a/src/L5Sharp/Utilities/TagDictionary.cs
+++ /dev/null
@@ -1,295 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Xml.Linq;
-using L5Sharp.Common;
-using L5Sharp.Components;
-
-namespace L5Sharp.Utilities;
-
-///
-/// Provides performant and concise access to tag objects within an L5X file.
-///
-///
-///
-/// This class is optimized to quickly retrieve tag objects by name using nested dictionaries.
-/// This is important simply for the fact that a single L5X file could reasonably contain millions of tag objects
-/// when considering that even atomic members can have up to 64 member tags.
-///
-///
-/// This class is quick to create and quick to retrieve, and should probably be used in place of
-/// .NET ToLookup() of ToDictionary() as they will not be as performant upon creation,
-/// especially with larger collections.
-///
-///
-public class TagDictionary : IEnumerable
-{
- private static readonly List TagElements = new()
- {
- L5XName.Tag,
- L5XName.ConfigTag,
- L5XName.InputTag,
- L5XName.OutputTag
- };
-
- private readonly Dictionary> _tags = new()
- {
- {L5XName.Controller, new Dictionary()}
- };
-
- ///
- /// Creates a new instance with the provided L5X content file.
- ///
- /// The to initialize the dictionary with.
- /// content is null
- /// contains tag elements with
- /// no name property -or- contains elements with the same tag name.
- public TagDictionary(LogixContent content)
- {
- if (content is null) throw new ArgumentNullException(nameof(content));
-
- var elements = content.L5X.Descendants().Where(d => TagElements.Any(e => e == d.Name));
-
- foreach (var element in elements)
- {
- var scope = GetScope(element);
- var tagName = GetTagName(element);
-
- if (!_tags.TryAdd(scope, new Dictionary {{tagName, element}}))
- _tags[scope].Add(tagName, element);
- }
- }
-
- ///
- /// Returns the total number of tag objets in both controller and program scopes found in the L5X content file.
- ///
- /// An representing the number of tags in the L5X.
- public int Count => _tags.Select(x => x.Value).SelectMany(t => t.Values).Count();
-
- ///
- /// Gets a with the specified tag name value.
- ///
- /// The identifying the tag to retrieve.
- /// For program tags, you must prefix with Program:{ProgramName}. Otherwise, the tag will be
- /// considered a controller scoped tag.
- ///
- /// tagName is null -or- no tag exists with the specified value.
- ///
- ///
- /// This performs a quick lookup of the tag object using a unique tag name. Given that program scoped tag names are
- /// not unique, tagName must be prefixed with the program name specifier in the format Program:{ProgramName},
- /// where ProgramName is the name of the program the tag is contained in.
- ///
- ///
- public Tag this[TagName tagName]
- {
- get
- {
- if (tagName is null) throw new ArgumentNullException(nameof(tagName));
-
- var scope = GetScopeName(tagName);
- var local = GetLocalName(tagName);
-
- var element = _tags[scope][local.Root];
- var tag = new Tag(element);
- return tagName.Depth == 0 ? tag : tag[local.Path];
- }
- }
-
- ///
- /// Determines if the specified tag name exists in the L5X content file.
- ///
- /// The value to search for.
- /// true if a tag with the specified name exists; Otherwise, false.
- ///
- /// If no program specifier is prepended to the tag name, then this will search all controller and
- /// program scoped tags for the specified tag name. If a program specifier is prepended, then this will
- /// narrow the search to the specific program.
- public bool Contains(TagName tagName)
- {
- if (tagName is null) throw new ArgumentNullException(nameof(tagName));
-
- var local = GetLocalName(tagName);
-
- if (tagName.Root.StartsWith("Program:"))
- {
- var scope = GetScopeName(tagName);
- return _tags.ContainsKey(scope) && _tags[scope].TryGetValue(local.Root, out var element) &&
- (tagName.Depth == 0 || new Tag(element).Member(local.Path) is not null);
- }
-
- foreach (var container in _tags)
- {
- if (container.Value.TryGetValue(tagName.Root, out var element))
- return tagName.Depth == 0 || new Tag(element).Member(local.Path) is not null;
- }
-
- return false;
- }
-
- ///
- /// Finds a with the specified tag name value.
- ///
- /// The identifying the tag to retrieve.
- /// For program tags, you must prefix with Program:{ProgramName}. Otherwise, the tag will be
- /// considered a controller scoped tag.
- ///
- /// A with the specified name if it exists; Otherwise, null.
- /// tagName is null.
- ///
- ///
- /// This performs a quick lookup of the tag object using a unique tag name. Given that program scoped tag names are
- /// not unique, tagName must be prefixed with the program name specifier in the format Program:{ProgramName},
- /// where ProgramName is the name of the program the tag is contained in.
- ///
- ///
- public Tag? Find(TagName tagName)
- {
- if (tagName is null) throw new ArgumentNullException(nameof(tagName));
-
- var scope = GetScopeName(tagName);
- var local = GetLocalName(tagName);
-
- if (!_tags.TryGetValue(scope, out var container)) return default;
- if (!container.TryGetValue(local.Root, out var element)) return default;
-
- var tag = new Tag(element);
- return tagName.Depth == 0 ? tag : tag.Member(local.Path);
- }
-
-
-
- ///
- /// Finds the ir
- ///
- ///
- ///
- ///
- ///
- public Tag? FindFirst(TagName tagName, string program)
- {
- if (tagName is null) throw new ArgumentNullException(nameof(tagName));
-
- if (_tags.TryGetValue(program, out var container) && container.TryGetValue(tagName, out var programElement))
- return new Tag(programElement);
-
- return _tags[L5XName.Controller].TryGetValue(tagName, out var controllerElement)
- ? new Tag(controllerElement)
- : default;
- }
-
- ///
- /// Finds all tags with the specified tag name value.
- ///
- /// The tag name to search.
- /// A containing all objects found in the L5X.
- ///
- /// tagName does not need to be prefixed with the program specifier for this method to
- /// return results. It will iterate each container and search for the specified tag name. Therefore, this returns
- /// both controller scoped and program scoped tags.
- ///
- public IEnumerable FindAll(TagName tagName)
- {
- if (tagName is null) throw new ArgumentNullException(nameof(tagName));
-
- var results = new List();
-
- foreach (var container in _tags)
- {
- if (container.Value.TryGetValue(tagName.Root, out var element))
- results.Add(new Tag(element));
- }
-
- return results;
- }
-
- ///
- /// Finds a tag in a single program with the specified tag name value.
- ///
- /// The name of the program to search.
- /// The name of the tag to find.
- /// A with the specified name if found; Otherwise, null.
- /// tagName is null.
- public Tag? In(string program, TagName tagName)
- {
- if (tagName is null) throw new ArgumentNullException(nameof(tagName));
-
- if (!_tags.TryGetValue(program, out var container)) return default;
- if (!container.TryGetValue(tagName, out var element)) return default;
-
- var tag = new Tag(element);
- return tagName.Depth == 0 ? tag : tag.Member(tagName.Path);
- }
-
- ///
- /// Returns all tags in a specified program container.
- ///
- /// The name of the program to search.
- /// A of contained in the program.
- public IEnumerable In(string program) =>
- _tags.TryGetValue(program, out var container)
- ? container.Values.Select(e => new Tag(e))
- : Enumerable.Empty();
-
- ///
- public IEnumerator GetEnumerator() =>
- _tags.Select(x => x.Value).SelectMany(x => x.Values).Select(e => new Tag(e)).GetEnumerator();
-
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- #region Internal
-
- private static string GetScopeName(TagName tagName) =>
- tagName.Root.StartsWith("Program:") ? tagName.Root.Replace("Program:", string.Empty) : L5XName.Controller;
-
- private static TagName GetLocalName(TagName tagName) =>
- tagName.Root.StartsWith("Program:") ? new TagName(tagName.Path) : tagName;
-
- private static string GetScope(XNode element) =>
- element.Ancestors(L5XName.Program).FirstOrDefault()?.LogixName() ?? L5XName.Controller;
-
- private static string GetTagName(XElement element)
- {
- if (element.Name == L5XName.ConfigTag || element.Name == L5XName.InputTag || element.Name == L5XName.OutputTag)
- return GetModuleTagName(element);
-
- return element.Attribute(L5XName.Name)?.Value ??
- throw new ArgumentException($"No name attribute exists for element '{element.Name}'.");
- }
-
- private static string GetModuleTagName(XElement element)
- {
- var suffix = DetermineModuleSuffix(element);
-
- var moduleName = element.Ancestors(L5XName.Module)
- .FirstOrDefault()?.Attribute(L5XName.Name)?.Value;
-
- var parentName = element.Ancestors(L5XName.Module)
- .FirstOrDefault()?.Attribute(L5XName.ParentModule)?.Value;
-
- var slot = element
- .Ancestors(L5XName.Module)
- .Descendants(L5XName.Port)
- .Where(p => bool.TryParse(p.Attribute(L5XName.Upstream)?.Value, out var upstream) && upstream
- && p.Attribute(L5XName.Type)?.Value != "Ethernet"
- && int.TryParse(p.Attribute(L5XName.Address)?.Value, out _))
- .Select(p => p.Attribute(L5XName.Address)?.Value)
- .FirstOrDefault();
-
- return slot is not null ? $"{parentName}:{slot}:{suffix}" : $"{moduleName}:{suffix}";
- }
-
- private static string DetermineModuleSuffix(XElement element)
- {
- if (element.Name == L5XName.InputTag)
- return element.Parent?.Attribute(L5XName.InputTagSuffix)?.Value ?? "I";
-
- if (element.Name == L5XName.OutputTag)
- return element.Parent?.Attribute(L5XName.OutputTagSuffix)?.Value ?? "O";
-
- return "C";
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Common/ArgumentTests.cs b/tests/L5Sharp.Tests/Common/ArgumentTests.cs
index baa9932b..38618e46 100644
--- a/tests/L5Sharp.Tests/Common/ArgumentTests.cs
+++ b/tests/L5Sharp.Tests/Common/ArgumentTests.cs
@@ -1,5 +1,6 @@
using FluentAssertions;
using L5Sharp.Common;
+using NUnit.Framework.Internal;
namespace L5Sharp.Tests.Common;
@@ -13,7 +14,7 @@ public void Empty_WhenCalled_ShouldHaveExpectedValue()
argument.Should().Be(string.Empty);
}
-
+
[Test]
public void Unknown_WhenCalled_ShouldHaveExpectedValue()
{
diff --git a/tests/L5Sharp.Tests/Common/CrossReferenceTests.cs b/tests/L5Sharp.Tests/Common/CrossReferenceTests.cs
new file mode 100644
index 00000000..7954af67
--- /dev/null
+++ b/tests/L5Sharp.Tests/Common/CrossReferenceTests.cs
@@ -0,0 +1,13 @@
+using L5Sharp.Common;
+
+namespace L5Sharp.Tests.Common;
+
+[TestFixture]
+public class CrossReferenceTests
+{
+ [Test]
+ public void New_ValidElement_ShouldNotBeNull()
+ {
+ /*var reference = new CrossReference()*/
+ }
+}
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Common/ParamsTests.cs b/tests/L5Sharp.Tests/Common/ParamsTests.cs
new file mode 100644
index 00000000..a7996b96
--- /dev/null
+++ b/tests/L5Sharp.Tests/Common/ParamsTests.cs
@@ -0,0 +1,153 @@
+using System.Xml.Linq;
+using FluentAssertions;
+using L5Sharp.Common;
+
+namespace L5Sharp.Tests.Common;
+
+[TestFixture]
+public class ParamsTests
+{
+ [Test]
+ public void New_Default_ShouldNotBeNull()
+ {
+ // ReSharper disable once CollectionNeverUpdated.Local
+ var test = new Params();
+
+ test.Should().NotBeNull();
+ }
+
+ [Test]
+ public void New_Default_ShouldBeEmpty()
+ {
+ // ReSharper disable once CollectionNeverUpdated.Local
+ var test = new Params();
+
+ test.Should().BeEmpty();
+ }
+
+ [Test]
+ public void New_ValidName_ShouldNotBeNull()
+ {
+ var pins = new Params("Pins");
+
+ pins.Should().NotBeNull();
+ }
+
+ [Test]
+ public void New_NullName_ShouldThrowArgumentException()
+ {
+ FluentActions.Invoking(() => new Params(((string)null)!)).Should().Throw();
+ }
+
+ [Test]
+ public void New_EmptyName_ShouldThrowArgumentException()
+ {
+ FluentActions.Invoking(() => new Params(string.Empty)).Should().Throw();
+ }
+
+ [Test]
+ public void Add_Scenario_Expected()
+ {
+ }
+
+ [Test]
+ public void Add_ValidString_ShouldHaveExpectedCount()
+ {
+ // ReSharper disable once UseObjectOrCollectionInitializer
+ var test = new Params("Test");
+
+ test.Add("Parameter1");
+
+ test.Should().HaveCount(1);
+ }
+
+ [Test]
+ public void Clear_Default_ShouldBeEmpty()
+ {
+ var test = new Params("Test");
+
+ test.Clear();
+
+ test.Should().HaveCount(0);
+ }
+
+ [Test]
+ public void Clear_HasItems_ShouldBeEmpty()
+ {
+ var attribute = new XAttribute("Test", "Pin1 Pin2 Pin3");
+ // ReSharper disable once CollectionNeverUpdated.Local
+ var test = new Params(attribute);
+
+ test.Clear();
+
+ test.Should().HaveCount(0);
+ }
+
+ [Test]
+ public void Count_WithNoItems_ShouldBeZero()
+ {
+ var test = new Params("Test");
+
+ var count = test.Count;
+
+ count.Should().Be(0);
+ }
+
+ [Test]
+ public void Contains_HasItem_ShouldBeTrue()
+ {
+ var test = new Params { "Test" };
+
+ var result = test.Contains("Test");
+
+ result.Should().BeTrue();
+ }
+
+ [Test]
+ public void Contains_DoesntHaveItem_ShouldBeFalse()
+ {
+ var test = new Params { "Test" };
+
+ var result = test.Contains("Fake");
+
+ result.Should().BeFalse();
+ }
+
+ [Test]
+ public void Remove_NoItem_ShouldBeFalse()
+ {
+ var test = new Params { "Test" };
+
+ var result = test.Remove("Item");
+
+ result.Should().BeFalse();
+ }
+
+ [Test]
+ public void Remove_HasItem_ShouldBeTrue()
+ {
+ var test = new Params { "Test" };
+
+ var result = test.Remove("Test");
+
+ result.Should().BeTrue();
+ }
+
+ [Test]
+ public void Insert_ValidIndexAndItem_ShouldHaveExpectedCount()
+ {
+ var test = new Params { "Test" };
+
+ test.Insert(0, "Test");
+
+ test.Should().HaveCount(2);
+ }
+
+ [Test]
+ public void Insert_InvalidIndex_ShouldThrowOutOfRangeException()
+ {
+ var test = new Params();
+
+ test.Insert(2, "Test");
+ }
+}
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Components/TaskTests.cs b/tests/L5Sharp.Tests/Components/TaskTests.cs
index a36a8260..456a850d 100644
--- a/tests/L5Sharp.Tests/Components/TaskTests.cs
+++ b/tests/L5Sharp.Tests/Components/TaskTests.cs
@@ -29,7 +29,7 @@ public void New_Default_ShouldHaveDefaults()
task.Watchdog.Should().Be(new Watchdog(500));
task.InhibitTask.Should().BeFalse();
task.DisableUpdateOutputs.Should().BeFalse();
- task.ScheduledPrograms.Should().BeEmpty();
+ task.Scheduled.Should().BeEmpty();
}
[Test]
@@ -55,7 +55,7 @@ public void New_WithValues_ShouldHaveExpectedValues()
task.Watchdog.Should().Be(new Watchdog(501));
task.InhibitTask.Should().BeTrue();
task.DisableUpdateOutputs.Should().BeTrue();
- task.ScheduledPrograms.Should().BeEmpty();
+ task.Scheduled.Should().BeEmpty();
}
[Test]
@@ -83,7 +83,7 @@ public void Schedule_ValidName_ShouldHaveExpectedPrograms()
task.Schedule("Test");
- task.ScheduledPrograms.Should().HaveCount(1);
+ task.Scheduled.Should().HaveCount(1);
}
[Test]
@@ -92,10 +92,10 @@ public void Cancel_Existing_ShouldHaveExpectedPrograms()
var task = new Task();
task.Schedule("Test");
- task.ScheduledPrograms.Should().HaveCount(1);
+ task.Scheduled.Should().HaveCount(1);
task.Cancel("Test");
- task.ScheduledPrograms.Should().BeEmpty();
+ task.Scheduled.Should().BeEmpty();
}
[Test]
diff --git a/tests/L5Sharp.Tests/Elements/BlockTests.New_Overriden_ShouldBeVerified.verified.txt b/tests/L5Sharp.Tests/Elements/BlockTests.New_Overriden_ShouldBeVerified.verified.txt
new file mode 100644
index 00000000..9190a7bd
--- /dev/null
+++ b/tests/L5Sharp.Tests/Elements/BlockTests.New_Overriden_ShouldBeVerified.verified.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Elements/BlockTests.cs b/tests/L5Sharp.Tests/Elements/BlockTests.cs
new file mode 100644
index 00000000..47ecd30a
--- /dev/null
+++ b/tests/L5Sharp.Tests/Elements/BlockTests.cs
@@ -0,0 +1,121 @@
+using System.Xml.Linq;
+using FluentAssertions;
+using L5Sharp.Common;
+using L5Sharp.Elements;
+using L5Sharp.Enums;
+using L5Sharp.Utilities;
+
+namespace L5Sharp.Tests.Elements;
+
+[TestFixture]
+public class BlockTests
+{
+ [Test]
+ public void New_Default_ShouldNotBeNull()
+ {
+ var block = new Block();
+
+ block.Should().NotBeNull();
+ }
+
+ [Test]
+ public void New_Default_ShouldHaveDefaultValues()
+ {
+ var block = new Block();
+
+ block.ID.Should().Be(0);
+ block.X.Should().Be(0);
+ block.Y.Should().Be(0);
+ block.Operand.Should().BeNull();
+ block.Type.Should().BeNull();
+ block.VisiblePins.Should().BeEmpty();
+ block.HideDesc.Should().BeNull();
+ block.IsAttached.Should().BeFalse();
+ block.L5X.Should().BeNull();
+ block.L5XType.Should().Be(L5XName.Block);
+ block.Cell.Should().Be("A1");
+ block.Container.Should().BeEmpty();
+ block.Scope.Should().Be(Scope.Null);
+ }
+
+ [Test]
+ public void New_Element_ShouldNotBeNull()
+ {
+ var block = new Block(new XElement(L5XName.Block));
+
+ block.Should().NotBeNull();
+ }
+
+ [Test]
+ public void New_Overriden_ShouldBeExpected()
+ {
+ var block = new Block()
+ {
+ ID = 1, X = 100, Y = 100, Operand = "TestBlock", Type = "SCL",
+ VisiblePins = new List()
+ {
+ "Source", "Destination"
+ }
+ };
+
+ block.ID.Should().Be(1);
+ block.X.Should().Be(100);
+ block.Y.Should().Be(100);
+ block.VisiblePins.Should().HaveCount(2);
+ }
+
+
+ [Test]
+ public Task New_Overriden_ShouldBeVerified()
+ {
+ var block = new Block
+ {
+ ID = 1, X = 100, Y = 100, Operand = "TestBlock", Type = "SCL",
+ VisiblePins = new List
+ {
+ "Source", "Destination"
+ }
+ };
+
+ return Verify(block.Serialize().ToString());
+ }
+
+ [Test]
+ public void AddPin_ValidTagName_ShouldHaveExpectedCount()
+ {
+ var block = new Block();
+
+ block.AddPin("MyPinName");
+
+ block.VisiblePins.Should().HaveCount(1);
+ }
+
+ [Test]
+ public void AddPins_ValidCollection_ShouldHaveExpectedCount()
+ {
+ var block = new Block();
+ var pins = new List()
+ {
+ "Pin1", "Pin2", "Pin3"
+ };
+
+ block.AddPins(pins);
+
+ block.VisiblePins.Should().HaveCount(3);
+ }
+
+ [Test]
+ public void References_WhenCalled_ShouldHaveExpectedCount()
+ {
+ var block = new Block
+ {
+ ID = 1, X = 100, Y = 100,
+ Operand = "MyTagName", Type = "SCL",
+ VisiblePins = new List { "Source", "Destination" }
+ };
+
+ var references = block.References().ToList();
+
+ references.Should().HaveCount(3);
+ }
+}
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Elements/ConnectionTests.cs b/tests/L5Sharp.Tests/Elements/ConnectionTests.cs
new file mode 100644
index 00000000..9e8ddf54
--- /dev/null
+++ b/tests/L5Sharp.Tests/Elements/ConnectionTests.cs
@@ -0,0 +1,117 @@
+using FluentAssertions;
+using L5Sharp.Components;
+using L5Sharp.Elements;
+using L5Sharp.Enums;
+using L5Sharp.Types;
+using L5Sharp.Types.Atomics;
+using Task = System.Threading.Tasks.Task;
+
+namespace L5Sharp.Tests.Elements;
+
+[TestFixture]
+public class ConnectionTests
+{
+ [Test]
+ public void New_Default_ShouldNotBeNull()
+ {
+ var connection = new Connection();
+
+ connection.Should().NotBeNull();
+ }
+
+ [Test]
+ public void New_Default_ShouldHaveExpectedDefaults()
+ {
+ var connection = new Connection();
+
+ connection.Name.Should().BeEmpty();
+ connection.RPI.Should().Be(0);
+ connection.InputCxnPoint.Should().Be(0);
+ connection.InputSize.Should().Be(0);
+ connection.OutputCxnPoint.Should().Be(0);
+ connection.OutputSize.Should().Be(0);
+ connection.Type.Should().BeNull();
+ connection.Priority.Should().Be(ConnectionPriority.Scheduled);
+ connection.InputConnectionType.Should().Be(TransmissionType.Multicast);
+ connection.InputProductionTrigger.Should().Be(ProductionTrigger.Cyclic);
+ connection.OutputRedundantOwner.Should().BeFalse();
+ connection.Unicast.Should().BeFalse();
+ connection.EventId.Should().Be(0);
+ connection.InputTagSuffix.Should().Be("I");
+ connection.InputTagSuffix.Should().Be("O");
+ connection.InputTag.Should().BeNull();
+ connection.OutputTag.Should().BeNull();
+ }
+
+ [Test]
+ public void New_Overridden_ShouldHaveExpectedValues()
+ {
+ var connection = new Connection
+ {
+ Name = "Test",
+ RPI = 4000,
+ InputCxnPoint = 100,
+ InputSize = 10,
+ OutputCxnPoint = 100,
+ OutputSize = 10,
+ Type = ConnectionType.Input,
+ Priority = ConnectionPriority.High,
+ InputConnectionType = TransmissionType.Unicast,
+ InputProductionTrigger = ProductionTrigger.Application,
+ OutputRedundantOwner = true,
+ Unicast = true,
+ EventId = 1,
+ InputTagSuffix = "II",
+ OutputTagSuffix = "OO",
+ InputTag = new Tag { Value = ArrayType.New(100) },
+ OutputTag = new Tag { Value = ArrayType.New(100) }
+ };
+
+ connection.Should().NotBeNull();
+ connection.Name.Should().Be("Test");
+ connection.RPI.Should().Be(4000);
+ connection.InputCxnPoint.Should().Be(100);
+ connection.InputSize.Should().Be(10);
+ connection.OutputCxnPoint.Should().Be(100);
+ connection.OutputSize.Should().Be(10);
+ connection.Type.Should().Be(ConnectionType.Input);
+ connection.Priority.Should().Be(ConnectionPriority.High);
+ connection.InputConnectionType.Should().Be(TransmissionType.Unicast);
+ connection.InputProductionTrigger.Should().Be(ProductionTrigger.Application);
+ connection.OutputRedundantOwner.Should().BeTrue();
+ connection.Unicast.Should().BeTrue();
+ connection.EventId.Should().Be(1);
+ connection.InputTagSuffix.Should().Be("II");
+ connection.OutputTagSuffix.Should().Be("OO");
+ connection.InputTag.Should().NotBeNull();
+ connection.OutputTag.Should().NotBeNull();
+ }
+
+ [Test]
+ public Task New_Overridden_ShouldBeVerified()
+ {
+ var connection = new Connection
+ {
+ Name = "Test",
+ RPI = 4000,
+ InputCxnPoint = 100,
+ InputSize = 10,
+ OutputCxnPoint = 100,
+ OutputSize = 10,
+ Type = ConnectionType.Input,
+ Priority = ConnectionPriority.High,
+ InputConnectionType = TransmissionType.Unicast,
+ InputProductionTrigger = ProductionTrigger.Application,
+ OutputRedundantOwner = true,
+ Unicast = true,
+ EventId = 1,
+ InputTagSuffix = "II",
+ OutputTagSuffix = "OO",
+ InputTag = new Tag { Value = ArrayType.New(100) },
+ OutputTag = new Tag { Value = ArrayType.New(100) }
+ };
+
+ return Verify(connection.Serialize().ToString());
+ }
+
+}
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Elements/FunctionTests.Serialize_Default_ShouldBeVerified.verified.txt b/tests/L5Sharp.Tests/Elements/FunctionTests.Serialize_Default_ShouldBeVerified.verified.txt
new file mode 100644
index 00000000..d6ebf955
--- /dev/null
+++ b/tests/L5Sharp.Tests/Elements/FunctionTests.Serialize_Default_ShouldBeVerified.verified.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Elements/FunctionTests.Serialize_Overloaded_ShouldBeVerified.verified.txt b/tests/L5Sharp.Tests/Elements/FunctionTests.Serialize_Overloaded_ShouldBeVerified.verified.txt
new file mode 100644
index 00000000..78b4d3a1
--- /dev/null
+++ b/tests/L5Sharp.Tests/Elements/FunctionTests.Serialize_Overloaded_ShouldBeVerified.verified.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Elements/FunctionTests.cs b/tests/L5Sharp.Tests/Elements/FunctionTests.cs
new file mode 100644
index 00000000..a9838598
--- /dev/null
+++ b/tests/L5Sharp.Tests/Elements/FunctionTests.cs
@@ -0,0 +1,63 @@
+using FluentAssertions;
+using L5Sharp.Elements;
+
+namespace L5Sharp.Tests.Elements;
+
+[TestFixture]
+public class FunctionTests
+{
+ [Test]
+ public void New_Default_ShouldNotBeNull()
+ {
+ var function = new Function();
+
+ function.Should().NotBeNull();
+ }
+
+ [Test]
+ public void New_Default_ShouldHaveDefaultValues()
+ {
+ var function = new Function();
+
+ function.ID.Should().Be(0);
+ function.X.Should().Be(0);
+ function.Y.Should().Be(0);
+ function.Type.Should().BeNull();
+ function.Sheet.Should().BeNull();
+ }
+
+ [Test]
+ public void New_Overloaded_ShouldHaveExpectedValues()
+ {
+ var function = new Function
+ {
+ ID = 1, X = 123, Y = 123, Type = "ADD"
+ };
+
+ function.Should().NotBeNull();
+ function.ID.Should().Be(1);
+ function.X.Should().Be(123);
+ function.Y.Should().Be(123);
+ function.Type.Should().Be("ADD");
+ }
+
+ [Test]
+ public Task Serialize_Default_ShouldBeVerified()
+ {
+ var function = new Function();
+
+ return Verify(function.Serialize().ToString());
+ }
+
+
+ [Test]
+ public Task Serialize_Overloaded_ShouldBeVerified()
+ {
+ var function = new Function
+ {
+ ID = 1, X = 123, Y = 123, Type = "SUB"
+ };
+
+ return Verify(function.Serialize().ToString());
+ }
+}
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Elements/ReferenceBlockTests.cs b/tests/L5Sharp.Tests/Elements/IREFTests.cs
similarity index 73%
rename from tests/L5Sharp.Tests/Elements/ReferenceBlockTests.cs
rename to tests/L5Sharp.Tests/Elements/IREFTests.cs
index 3cfc46c1..4c9116a7 100644
--- a/tests/L5Sharp.Tests/Elements/ReferenceBlockTests.cs
+++ b/tests/L5Sharp.Tests/Elements/IREFTests.cs
@@ -8,12 +8,12 @@
namespace L5Sharp.Tests.Elements;
[TestFixture]
-public class ReferenceBlockTests
+public class IREFTests
{
[Test]
public void New_Default_ShouldNotBeNull()
{
- var element = new ReferenceBlock();
+ var element = new IREF();
element.Should().NotBeNull();
}
@@ -21,7 +21,7 @@ public void New_Default_ShouldNotBeNull()
[Test]
public void New_Default_ShouldHaveExpectedDefaults()
{
- var element = new ReferenceBlock();
+ var element = new IREF();
element.ID.Should().Be(0);
element.X.Should().Be(0);
@@ -39,24 +39,9 @@ public void New_Default_ShouldHaveExpectedDefaults()
}
[Test]
- public Task New_IRefOverloaded_ShouldBeVerified()
+ public Task New_Overloaded_ShouldBeVerified()
{
- var element = new ReferenceBlock(ParameterType.Input)
- {
- ID = 1,
- X = 100,
- Y = 100,
- Operand = "TestTag",
- HideDesc = true
- };
-
- return Verify(element.Serialize().ToString());
- }
-
- [Test]
- public Task New_ORefOverloaded_ShouldBeVerified()
- {
- var element = new ReferenceBlock(ParameterType.Output)
+ var element = new IREF
{
ID = 1,
X = 100,
@@ -73,7 +58,7 @@ public void New_ValidElementNoAttributesIRef_ShouldNotBeNull()
{
var element = new XElement(L5XName.IRef);
- var reference = new ReferenceBlock(element);
+ var reference = new IREF(element);
reference.Should().NotBeNull();
}
@@ -83,7 +68,7 @@ public void New_ValidElementNoAttributesORef_ShouldNotBeNull()
{
var element = new XElement(L5XName.ORef);
- var reference = new ReferenceBlock(element);
+ var reference = new IREF(element);
reference.Should().NotBeNull();
}
@@ -98,7 +83,7 @@ public void New_ValidElement_ShouldHaveExpectedValues()
element.SetAttributeValue(L5XName.Operand, "TestTag");
element.SetAttributeValue(L5XName.HideDesc, true);
- var reference = new ReferenceBlock(element);
+ var reference = new IREF(element);
reference.ID.Should().Be(1);
reference.X.Should().Be(100);
@@ -116,21 +101,11 @@ public void Deserialize_IRefElement_ShouldNotBeNull()
reference.Should().NotBeNull();
}
-
- [Test]
- public void Deserialize_ORefElement_ShouldNotBeNull()
- {
- var element = new XElement(L5XName.ORef);
-
- var reference = element.Deserialize();
-
- reference.Should().NotBeNull();
- }
[Test]
public void Clone_WhenCalled_ShouldNotBeSame()
{
- var element = new ReferenceBlock();
+ var element = new IREF();
var clone = element.Clone();
@@ -140,7 +115,7 @@ public void Clone_WhenCalled_ShouldNotBeSame()
[Test]
public void References_WhenCalled_ShouldReturnExpected()
{
- var element = new ReferenceBlock(ParameterType.Output)
+ var element = new IREF
{
ID = 1,
X = 100,
diff --git a/tests/L5Sharp.Tests/Elements/SheetTests.Add_BlocksOutOfOrder_ShouldBeVerified.verified.txt b/tests/L5Sharp.Tests/Elements/SheetTests.Add_BlocksOutOfOrder_ShouldBeVerified.verified.txt
index 2bdbb927..eb5f262c 100644
--- a/tests/L5Sharp.Tests/Elements/SheetTests.Add_BlocksOutOfOrder_ShouldBeVerified.verified.txt
+++ b/tests/L5Sharp.Tests/Elements/SheetTests.Add_BlocksOutOfOrder_ShouldBeVerified.verified.txt
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Elements/SheetTests.cs b/tests/L5Sharp.Tests/Elements/SheetTests.cs
index 4717f5d1..f866d2fd 100644
--- a/tests/L5Sharp.Tests/Elements/SheetTests.cs
+++ b/tests/L5Sharp.Tests/Elements/SheetTests.cs
@@ -1,6 +1,5 @@
using FluentAssertions;
using L5Sharp.Elements;
-using L5Sharp.Enums;
namespace L5Sharp.Tests.Elements;
@@ -36,23 +35,46 @@ public void New_Default_ShouldHaveExpectedValues()
}
[Test]
- public void Add_IRefBlock_ShouldHaveSingleBlock()
+ public void Add_IRef_ShouldHaveExpectedCountAndId()
{
var sheet = new Sheet();
- sheet.Add(new ReferenceBlock { Operand = "MyTagName", X = 100, Y = 300 });
+ var id = sheet.Add(new IREF("MyTagName"));
+ id.Should().Be(0);
sheet.Blocks().Should().HaveCount(1);
}
-
+
+ [Test]
+ public void Add_ORef_ShouldHaveExpectedCountAndId()
+ {
+ var sheet = new Sheet();
+
+ var id = sheet.Add(new IREF("MyTagName"));
+
+ id.Should().Be(0);
+ sheet.Blocks().Should().HaveCount(1);
+ }
+
+ [Test]
+ public void Add_Block_ShouldHaveExpectedCountAndId()
+ {
+ var sheet = new Sheet();
+
+ var id = sheet.Add(new Block { Type = "SCL", Operand = "MyTag" });
+
+ id.Should().Be(0);
+ sheet.Blocks().Should().HaveCount(1);
+ }
+
[Test]
public void Add_MultipleBlocks_ShouldGetExpectedIds()
{
var sheet = new Sheet();
- var zero = sheet.Add(new ReferenceBlock { Operand = "MyTagName", X = 100, Y = 300 });
- var one = sheet.Add(new ReferenceBlock { Operand = "MyTagName", X = 100, Y = 300 });
- var two = sheet.Add(new ReferenceBlock { Operand = "MyTagName", X = 100, Y = 300 });
+ var zero = sheet.Add(new IREF { Operand = "MyTagName", X = 100, Y = 300 });
+ var one = sheet.Add(new IREF { Operand = "MyTagName", X = 100, Y = 300 });
+ var two = sheet.Add(new IREF { Operand = "MyTagName", X = 100, Y = 300 });
zero.Should().Be(0);
one.Should().Be(1);
@@ -64,9 +86,9 @@ public Task Add_BlocksOutOfOrder_ShouldBeVerified()
{
var sheet = new Sheet();
- sheet.Add(new ReferenceBlock { Operand = "InputReference", X = 100, Y = 300 });
+ sheet.Add(new IREF { Operand = "InputReference", X = 100, Y = 300 });
sheet.Add(new Block { Operand = "MyBlockTag", X = 100, Y = 300 });
- sheet.Add(new ReferenceBlock(ParameterType.Output) { Operand = "OutputReference", X = 100, Y = 300 });
+ sheet.Add(new IREF { Operand = "OutputReference", X = 100, Y = 300 });
return Verify(sheet.Serialize().ToString());
}
@@ -76,9 +98,123 @@ public void Add_BlockAndText_ShouldHaveExpectedCount()
{
var sheet = new Sheet();
- sheet.Add(new ReferenceBlock { Operand = "InputReference", X = 100, Y = 300 });
+ sheet.Add(new IREF { Operand = "InputReference", X = 100, Y = 300 });
+ sheet.Add(new TextBox { Text = "MyBlockTag", X = 100, Y = 300 });
+
+ sheet.Blocks().Should().HaveCount(1);
+ }
+
+ [Test]
+ public void Block_ValidId_ShouldNotBeNull()
+ {
+ var sheet = new Sheet();
+ sheet.Add(new IREF { Operand = "InputReference", X = 100, Y = 300 });
+ sheet.Add(new Block { Operand = "MyBlockTag", X = 100, Y = 300 });
+ sheet.Add(new OREF { Operand = "OutputReference", X = 100, Y = 300 });
+
+ var block = sheet.Block(2);
+
+ block.Should().NotBeNull();
+ block.Should().BeOfType();
+ }
+
+ [Test]
+ public void Block_InvalidId_ShouldBeNull()
+ {
+ var sheet = new Sheet();
+ sheet.Add(new IREF { Operand = "InputReference", X = 100, Y = 300 });
+ sheet.Add(new Block { Operand = "MyBlockTag", X = 100, Y = 300 });
+ sheet.Add(new OREF { Operand = "OutputReference", X = 100, Y = 300 });
+
+ var block = sheet.Block(4);
+
+ block.Should().BeNull();
+ }
+
+ [Test]
+ public void BlockOfType_Exists_ShouldNotBeNull()
+ {
+ var sheet = new Sheet();
+ sheet.Add(new IREF { Operand = "InputReference", X = 100, Y = 300 });
+ sheet.Add(new Block { Operand = "MyBlockTag", X = 100, Y = 300 });
+ sheet.Add(new OREF { Operand = "OutputReference", X = 100, Y = 300 });
+
+ var block = sheet.Block(2);
+
+ block.Should().NotBeNull();
+ }
+
+ [Test]
+ public void Blocks_Default_ShouldBeEmpty()
+ {
+ var sheet = new Sheet();
+
+ var blocks = sheet.Blocks().ToList();
+
+ blocks.Should().BeEmpty();
+ }
+
+ [Test]
+ public void Blocks_HasCollection_ShouldHaveExpectedCount()
+ {
+ var sheet = new Sheet();
+ sheet.Add(new IREF() { Operand = "InputReference", X = 100, Y = 300 });
sheet.Add(new Block { Operand = "MyBlockTag", X = 100, Y = 300 });
+ sheet.Add(new OREF() { Operand = "OutputReference", X = 100, Y = 300 });
+
+ var blocks = sheet.Blocks();
+
+ blocks.Should().HaveCount(3);
+ }
+
+ [Test]
+ public void Connect_ValidIds_ShouldHaveExpectedConnectorsCount()
+ {
+ var sheet = new Sheet();
+
+ sheet.Connect(0, 1);
+ sheet.Connect(1, 2);
+ sheet.Connect(0, 2);
+
+ sheet.Connectors().Should().HaveCount(3);
+ }
+
+ [Test]
+ public void Connectors_Default_ShouldBeEmpty()
+ {
+ var sheet = new Sheet();
+
+ var wires = sheet.Connectors().ToList();
+
+ wires.Should().BeEmpty();
+ }
+
+ [Test]
+ public void Connectors_HasWired_ShouldHaveExpectedCount()
+ {
+ var sheet = new Sheet();
+ sheet.Add(new Wire { FromID = 0, ToID = 1 });
+ sheet.Add(new Wire { FromID = 0, ToID = 2 });
+ sheet.Add(new Wire { FromID = 1, ToID = 3 });
+
+ var wires = sheet.Connectors().ToList();
+
+ wires.Should().HaveCount(3);
+ }
+
+ [Test]
+ public void Connections_BlockWithConnections_ShouldHaveExpectedCount()
+ {
+ var block = new Block { Operand = "ScaleTag", Type = "SCL" };
+ var sheet = new Sheet();
+ sheet.Add(new IREF("MyTag"));
+ sheet.Add(block);
+ sheet.Add(new OREF("ResultTag"));
+ sheet.Add(new Wire { FromID = 0, ToID = 1 });
+ sheet.Add(new Wire { FromID = 1, ToID = 2 });
+
+ var connections = sheet.Connections(block).ToList();
- sheet.Blocks().Should().HaveCount(2);
+ connections.Should().HaveCount(2);
}
}
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/L5Sharp.Tests.csproj b/tests/L5Sharp.Tests/L5Sharp.Tests.csproj
index fd6a93e6..2d07586c 100644
--- a/tests/L5Sharp.Tests/L5Sharp.Tests.csproj
+++ b/tests/L5Sharp.Tests/L5Sharp.Tests.csproj
@@ -13,7 +13,7 @@
-
+
@@ -244,7 +244,16 @@
SheetTests.cs
- ReferenceBlockTests.cs
+ InRefTests.cs
+
+
+ BlockTests.cs
+
+
+ FunctionTests.cs
+
+
+ InRefTests.cs