diff --git a/src/.idea/.idea.L5Sharp/.idea/workspace.xml b/src/.idea/.idea.L5Sharp/.idea/workspace.xml
index 7d550f34..113b4f08 100644
--- a/src/.idea/.idea.L5Sharp/.idea/workspace.xml
+++ b/src/.idea/.idea.L5Sharp/.idea/workspace.xml
@@ -8,13 +8,69 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -57,8 +113,10 @@
+
+
@@ -372,7 +430,7 @@
-
+ 1677621948966
@@ -790,17 +848,17 @@
- file://$PROJECT_DIR$/../tests/L5Sharp.Tests/Enums/LogixEnumTests.cs
- 84
-
+ file://$PROJECT_DIR$/L5Sharp.Core/LogixParser.cs
+ 98
+
-
+
-
+
-
+
diff --git a/src/L5Sharp.Core/Common/Address.cs b/src/L5Sharp.Core/Common/Address.cs
index 1bfa1a10..97612f83 100644
--- a/src/L5Sharp.Core/Common/Address.cs
+++ b/src/L5Sharp.Core/Common/Address.cs
@@ -8,10 +8,13 @@ namespace L5Sharp.Core;
///
/// Provides a wrapper around the string port address value to indicate what type of address the value is.
///
-public class Address
+public partial class Address : ILogixParsable
{
private readonly string _value;
+ [GeneratedRegex("^[A-Za-z][A-Za-z0-9.-]{1,63}$")]
+ private static partial Regex HostNamePattern();
+
///
/// Creates a new with the provided string value.
///
@@ -21,7 +24,7 @@ public Address(string address)
{
_value = address ?? throw new ArgumentNullException(nameof(address));
}
-
+
///
/// Indicates whether the current is a slot number address.
///
@@ -42,7 +45,7 @@ public Address(string address)
/// A host name must start with a letter, contain only alpha-numeric characters or special characters '.' and '-',
/// and have a maximum length of 64 characters.
///
- public bool IsHostName => Regex.IsMatch(_value, "^[A-Za-z][A-Za-z0-9.-]{1,63}$");
+ public bool IsHostName => HostNamePattern().IsMatch(_value);
///
/// Indicates that the address value is an empty string.
@@ -53,87 +56,48 @@ public Address(string address)
/// Represents no address value, or an empty string.
///
public static Address None => new(string.Empty);
-
- ///
- /// Converts the current to a value.
- ///
- /// The value to convert.
- /// A representing the address value.
- public static implicit operator Address(string address) => new(address);
-
- ///
- /// Converts the current to a value.
- ///
- /// The value to convert.
- /// A representing the address value.
- public static implicit operator string(Address address) => address.ToString();
///
- /// Converts the current to a value.
- ///
- /// The value to convert.
- /// A representing the address value.
- public static implicit operator Address(byte address) => new(address.ToString());
-
- ///
- /// Converts the current to a value.
- ///
- /// The value to convert.
- /// A representing the address value.
- public static implicit operator byte(Address address) => byte.Parse(address._value);
-
- ///
- /// Creates a new instance from the provided object.
- ///
- /// The value that represents the port address.
- /// A new value from the provided IP.
- /// ipAddress is null.
- public static Address FromIP(IPAddress ipAddress)
- {
- if (ipAddress is null) throw new ArgumentNullException(nameof(ipAddress));
- return new Address(ipAddress.ToString());
- }
-
- ///
- /// Creates a new instance from the provided byte slot number value.
+ /// Creates a new with the optional IP address value.
///
- /// the byte number value that represents the port address.
- /// A new value from the provided slot number.
- public static Address FromSlot(byte slot) => new(slot.ToString());
+ /// A with the default IP value.
+ public static Address IP(string? ip = default) => ip is not null ? new Address(ip) : new Address("192.168.0.1");
///
- /// Creates a new with the common default IP of 192.168.0.1.
+ /// Creates a new with the optional slot number.
///
- /// A with the default IP value.
- public static Address DefaultIP() => new("192.168.0.1");
-
+ /// The slot number to create. If not provided will default to 0.
+ /// A representing a slot number within a chassis.
+ public static Address Slot(byte slot = 0) => new($"{slot}");
+
///
- /// Creates a new with the default slot 0.
+ /// Parses a string into a value.
///
- /// A with the default slot value.
- public static Address DefaultSlot() => new("0");
+ /// The string to parse.
+ /// The representing the parsed value.
+ public static Address Parse(string value) => new(value);
///
- /// Creates a new with the specified slot number.
+ /// Tries to parse a string into a value.
///
- /// The slot number to create.
- ///
- public static Address Slot(byte slot) => new($"{slot}");
-
+ /// The string to parse.
+ /// An representing the parsed value if successful; Otherwise, .
+ public static Address TryParse(string? value) => string.IsNullOrEmpty(value) ? None : new Address(value);
+
///
- ///
+ /// Converts the address value to an IPAddress object.
///
- ///
- ///
+ /// An object representing the address.
+ /// Thrown when the address value is not a valid IP address.
public IPAddress ToIPAddress() => IsIPv4
? IPAddress.Parse(_value)
: throw new InvalidOperationException($"The current address '{_value}' is not a valid IP address");
///
- ///
+ /// Converts the current address to a slot number.
///
- ///
- ///
+ /// The slot number of the address.
+ /// Thrown if the address is not a valid slot number.
public byte ToSlot() => IsSlot
? byte.Parse(_value)
: throw new InvalidOperationException($"The current address '{_value}' is not a valid slot number");
@@ -172,4 +136,32 @@ public override bool Equals(object? obj)
/// An object to compare.
/// true if the provided objects are not equal; otherwise, false.
public static bool operator !=(Address? left, Address? right) => !Equals(left, right);
+
+ ///
+ /// Converts the current to a value.
+ ///
+ /// The value to convert.
+ /// A representing the address value.
+ public static implicit operator Address(string address) => new(address);
+
+ ///
+ /// Converts the current to a value.
+ ///
+ /// The value to convert.
+ /// A representing the address value.
+ public static implicit operator string(Address address) => address.ToString();
+
+ ///
+ /// Converts the current to a value.
+ ///
+ /// The value to convert.
+ /// A representing the address value.
+ public static implicit operator Address(byte address) => new(address.ToString());
+
+ ///
+ /// Converts the current to a value.
+ ///
+ /// The value to convert.
+ /// A representing the address value.
+ public static implicit operator byte(Address address) => byte.Parse(address._value);
}
\ No newline at end of file
diff --git a/src/L5Sharp.Core/Common/Argument.cs b/src/L5Sharp.Core/Common/Argument.cs
index 4194bd2c..ecab4fbe 100644
--- a/src/L5Sharp.Core/Common/Argument.cs
+++ b/src/L5Sharp.Core/Common/Argument.cs
@@ -7,7 +7,7 @@ namespace L5Sharp.Core;
///
/// Represents an argument to an instruction, which could be a tag name reference or an immediate atomic value.
///
-public class Argument
+public class Argument : ILogixParsable
{
private readonly object _value;
@@ -87,28 +87,48 @@ private Argument(object value)
public static Argument Empty => new(string.Empty);
///
- /// Parses the string input into a valid object.
+ /// Parses the provided string into a value.
///
- /// Teh string value to parse.
- /// A representing the string input.
+ /// Teh string to parse.
+ /// An representing the parsed value.
/// value is null or empty
- /// This parse method expects the string to be an immediate value,
- /// a single value, or an expression that can be represented as
- /// type.
- ///
public static Argument Parse(string? value)
{
- //Empty value - lets not crash on empty or invalid arguments.
- if (string.IsNullOrEmpty(value)) return Empty;
+ if (string.IsNullOrEmpty(value))
+ throw new ArgumentException("Value can not be null or empty to parse.");
+
+ //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 overflows.
+ if (value.StartsWith('\'') && value.EndsWith('\'')) return new Argument(value);
+
+ //Immediate atomic value
+ var atomic = AtomicType.TryParse(value);
+ if (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));
+ }
+
+ ///
+ /// Parses the provided string into a value.
+ ///
+ /// Teh string to parse.
+ /// An representing the parsed value if successful; Otherwise, .
+ public static Argument TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value)) return Empty;
+
//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 exceptions.
+ //Literal string value - We need to intercept this before the Atomic.TryParse method to prevent overflows.
if (value.StartsWith('\'') && value.EndsWith('\'')) return new Argument(value);
//Immediate atomic value
- if (Atomic.TryParse(value, out var atomic) && atomic is not null) return new Argument(atomic);
+ var atomic = AtomicType.TryParse(value);
+ if (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));
@@ -233,7 +253,7 @@ public static Argument Parse(string? value)
public override int GetHashCode() => _value.GetHashCode();
///
- public override string ToString() => _value.ToString();
+ public override string ToString() => _value.ToString()!;
///
/// Determines whether two Argument objects are equal.
diff --git a/src/L5Sharp.Core/Common/Dimensions.cs b/src/L5Sharp.Core/Common/Dimensions.cs
index 41253bca..b381b346 100644
--- a/src/L5Sharp.Core/Common/Dimensions.cs
+++ b/src/L5Sharp.Core/Common/Dimensions.cs
@@ -13,7 +13,7 @@ namespace L5Sharp.Core;
/// These dimensions are represented by the properties X, Y, and Z.
/// This class also provides helpful methods and properties for working with dimensions of an array.
///
-public sealed class Dimensions : IEquatable
+public sealed class Dimensions : IEquatable, ILogixParsable
{
private Dimensions()
{
@@ -191,15 +191,15 @@ public IEnumerable Indices()
}
///
- /// Parses the provided string to a object.
+ /// Parses the provided string into a value.
///
- /// The value to parse.
+ /// The string to parse.
///
- /// A new object that represents parsed dimensional value.
+ /// A object that represents parsed dimensional value.
/// If value empty or 0; returns .
///
- /// value is null
- /// value contains invalid characters.
+ /// is null.
+ /// contains invalid characters.
///
/// Valid dimensions must have only numbers and special characters "[ ,]". If more than 3 numbers are
/// found in the provided value, or the numbers are not parsable to a ,
@@ -223,47 +223,34 @@ public static Dimensions Parse(string value)
3 => new Dimensions(numbers[0], numbers[1], numbers[2]),
2 => new Dimensions(numbers[0], numbers[1]),
1 => new Dimensions(numbers[0]),
- _ => throw new ArgumentOutOfRangeException(nameof(numbers.Count),
+ _ => throw new ArgumentOutOfRangeException(nameof(value),
$"Value '{value}' has a invalid number of arguments. Expecting between 1 and 3 arguments.")
};
}
///
- /// Attempts to parse the provided string to a object.
+ /// Tries to parse the provided string into a value.
///
- /// The value to parse.
- /// When the method returns, the value of the parsed dimensions if successful;
- /// otherwise, null
- /// true if the parse was successful; otherwise, false.
- /// value is null
- ///
- /// Valid dimensions must have only numbers and special characters "[ ,]". If more than 3 numbers are
- /// found in the provided value, or the numbers are not parsable to a ,
- /// then exception will be thrown.
- ///
+ /// The string to parse.
+ /// A representing the parsed value if successful; Otherwise, null.
/// 1 2 3 -or- [1,2,3]
///
- public static bool TryParse(string value, out Dimensions? dimensions)
+ public static Dimensions? TryParse(string? value)
{
if (string.IsNullOrEmpty(value))
- {
- dimensions = null;
- return false;
- }
+ return null;
var numbers = Regex.Matches(value, @"\d+")
.Select(m => ushort.Parse(m.Value))
.ToList();
- dimensions = numbers.Count switch
+ return numbers.Count switch
{
3 => new Dimensions(numbers[0], numbers[1], numbers[2]),
2 => new Dimensions(numbers[0], numbers[1]),
1 => new Dimensions(numbers[0]),
_ => null
};
-
- return dimensions is not null;
}
///
diff --git a/src/L5Sharp.Core/Common/Instruction.cs b/src/L5Sharp.Core/Common/Instruction.cs
index 42e81a6d..48f3468e 100644
--- a/src/L5Sharp.Core/Common/Instruction.cs
+++ b/src/L5Sharp.Core/Common/Instruction.cs
@@ -185,7 +185,7 @@ public static Instruction Parse(string text)
var key = text[..text.IndexOf('(')];
var signature = Regex.Match(text, SignaturePattern).Value[1..^1];
- var arguments = Regex.Split(signature, ArgumentSplitPattern).Select(Argument.Parse).ToArray();
+ var arguments = Regex.Split(signature, ArgumentSplitPattern).Select(Argument.TryParse).ToArray();
return _known.TryGetValue(key, out var create)
? create().Of(arguments)
diff --git a/src/L5Sharp.Core/Common/NeutralText.cs b/src/L5Sharp.Core/Common/NeutralText.cs
index a3c56a31..cf36ddb9 100644
--- a/src/L5Sharp.Core/Common/NeutralText.cs
+++ b/src/L5Sharp.Core/Common/NeutralText.cs
@@ -19,7 +19,7 @@ namespace L5Sharp.Core;
///
///
///
-public sealed class NeutralText
+public sealed class NeutralText : ILogixParsable
{
private readonly string _text;
@@ -51,6 +51,20 @@ public NeutralText(string text)
///
/// An empty object.
public static NeutralText Empty => new(string.Empty);
+
+ ///
+ /// Parses the provided string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value.
+ public static NeutralText Parse(string value) => new(value);
+
+ ///
+ /// Tries to parse the provided string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value if successful; Otherwise, null.
+ public static NeutralText? TryParse(string? value) => value is not null ? new NeutralText(value) : null;
///
/// Returns a value indicating whether a specified instruction key occurs within this neutral text.
@@ -157,7 +171,7 @@ public override bool Equals(object? obj)
/// A NeutralText that represents the value of the string.
public static implicit operator NeutralText(string text) => new(text);
- private bool TextIsBalanced(string value, char opening, char closing)
+ private static bool TextIsBalanced(string value, char opening, char closing)
{
var characters = new Stack();
diff --git a/src/L5Sharp.Core/Common/ProductType.cs b/src/L5Sharp.Core/Common/ProductType.cs
index f0da89e5..443ff992 100644
--- a/src/L5Sharp.Core/Common/ProductType.cs
+++ b/src/L5Sharp.Core/Common/ProductType.cs
@@ -11,7 +11,7 @@ namespace L5Sharp.Core;
/// Some known/common types that are available as static members of this class
/// include , , , and .
///
-public class ProductType
+public class ProductType : ILogixParsable
{
///
/// Creates a new entity with the provided id and name.
@@ -74,12 +74,20 @@ public ProductType(ushort id, string? name = null)
public static implicit operator ProductType(ushort productTypeId) => new(productTypeId);
///
- /// Creates a new using the provided product Id.
+ /// Parses a string into a value.
///
- /// The unique valid that identifies the Product.
- /// A new object with the provided Id.
- public static ProductType Parse(string productId) =>
- ushort.TryParse(productId, out var id) ? new ProductType(id) : Unknown;
+ /// The string to parse.
+ /// The representing the parsed value.
+ public static ProductType Parse(string value) => new(ushort.Parse(value));
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value if successful;
+ /// Otherwise; .
+ public static ProductType TryParse(string? value) =>
+ ushort.TryParse(value, out var result) ? new ProductType(result) : Unknown;
///
public override string ToString() => Name;
diff --git a/src/L5Sharp.Core/Common/Revision.cs b/src/L5Sharp.Core/Common/Revision.cs
index 478c64ea..536d2e02 100644
--- a/src/L5Sharp.Core/Common/Revision.cs
+++ b/src/L5Sharp.Core/Common/Revision.cs
@@ -7,10 +7,13 @@ namespace L5Sharp.Core;
///
/// Represents a revision number that is expressed by as {Major}.{Minor}.
///
-public sealed class Revision : IComparable
+public sealed partial class Revision : IComparable, ILogixParsable
{
private const string RevisionSeparator = ".";
private readonly string _value;
+
+ [GeneratedRegex("^[0-9]+\\.[0-9]+$")]
+ private static partial Regex RevisionPattern();
///
/// Creates a new default with value 1.0.
@@ -31,7 +34,7 @@ public Revision(string value)
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Revision value can not be null or empty.");
- if (!Regex.IsMatch(value, @"^[0-9]+\.[0-9]+$"))
+ if (!RevisionPattern().IsMatch(value))
throw new FormatException(
$"Value '{value}' is invalid revision format. Revision format must be Major.Minor.");
@@ -95,6 +98,26 @@ public override bool Equals(object? obj)
///
public override string ToString() => _value;
+
+ ///
+ /// Parses the provided string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value.
+ public static Revision Parse(string value) => new(value);
+
+ ///
+ /// Tries to parse the provided string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value if successful; Otherwise, null.
+ public static Revision? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ return RevisionPattern().IsMatch(value) ? new Revision(value) : default;
+ }
///
/// Determines if the provided objects are equal.
diff --git a/src/L5Sharp.Core/Common/ScanRate.cs b/src/L5Sharp.Core/Common/ScanRate.cs
index 9522dc4c..e38981d8 100644
--- a/src/L5Sharp.Core/Common/ScanRate.cs
+++ b/src/L5Sharp.Core/Common/ScanRate.cs
@@ -12,7 +12,7 @@ namespace L5Sharp.Core;
/// .
/// This parameter will control the rate at which the component is scanned.
///
-public readonly struct ScanRate : IEquatable
+public readonly struct ScanRate : IEquatable, ILogixParsable
{
private readonly float _rate;
@@ -45,12 +45,19 @@ public ScanRate(float rate)
public static implicit operator ScanRate(float rate) => new(rate);
///
- /// Parses a string value into a .
+ /// Parses a string into a value.
///
- /// The string to parse.
- /// A value if the parse was successful; otherwise; the default value.
- public static ScanRate Parse(string str) =>
- float.TryParse(str, out var result) ? new ScanRate(result) : default;
+ /// The string to parse.
+ /// The representing the parsed value.
+ public static ScanRate Parse(string value) => float.Parse(value);
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value if successful; Otherwise; default.
+ public static ScanRate TryParse(string? value) =>
+ float.TryParse(value, out var result) ? new ScanRate(result) : default;
///
public override string ToString() => _rate.ToString(CultureInfo.CurrentCulture);
diff --git a/src/L5Sharp.Core/Common/TagName.cs b/src/L5Sharp.Core/Common/TagName.cs
index 5a01be04..f8557788 100644
--- a/src/L5Sharp.Core/Common/TagName.cs
+++ b/src/L5Sharp.Core/Common/TagName.cs
@@ -13,7 +13,7 @@ namespace L5Sharp.Core;
/// This value type class make working with string tag name easier by providing
/// methods fo analyzing and breaking the tag name into constituent parts (members).
///
-public sealed class TagName : IComparable, IEquatable
+public sealed class TagName : IComparable, IEquatable, ILogixParsable
{
private readonly string _tagName;
@@ -27,7 +27,7 @@ public sealed class TagName : IComparable, IEquatable
///
public const string AnchorPattern =
@"^[A-Za-z_][\w+:]{1,39}(?:(?:\[\d+\]|\[\d+,\d+\]|\[\d+,\d+,\d+\])?(?:\.[A-Za-z_]\w{1,39})?)+(?:\.[0-9][0-9]?)?$";
-
+
///
/// The regex pattern for Logix tag names without starting and ending anchors.
/// This pattern also includes a negative lookahead for removing text prior to parenthesis (i.e. instruction keys)
@@ -125,6 +125,25 @@ public TagName(string tagName)
///
public bool IsQualified => IsQualifiedTagName(_tagName);
+ ///
+ /// Gets the static empty value.
+ ///
+ public static TagName Empty => new(string.Empty);
+
+ ///
+ /// Parses the provided string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value.
+ public static TagName Parse(string value) => new(value);
+
+ ///
+ /// Tries to parse the provided string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value if successful; Otherwise, null.
+ public static TagName? TryParse(string? value) => value is not null ? new TagName(value) : null;
+
///
/// Determines if the provided string value is a valid tag name.
///
@@ -132,11 +151,6 @@ public TagName(string tagName)
/// true if the value is a valid qualified tag name; Otherwise, false.
public static bool IsTag(string value) => IsQualifiedTagName(value);
- ///
- /// Gets the static empty value.
- ///
- public static TagName Empty => new(string.Empty);
-
///
/// Concatenates two strings to produce a new value. This method will also insert the '.'
/// member separator character if not found at the beginning of right.
@@ -287,7 +301,7 @@ private static string GetRoot(string tagName)
return tagName[..tagName.IndexOf(ArrayCloseSeparator)];
}
- var index = tagName.IndexOfAny(new[] {MemberSeparator, ArrayOpenSeparator});
+ var index = tagName.IndexOfAny(new[] { MemberSeparator, ArrayOpenSeparator });
return index > 0 ? tagName[..index] : tagName;
}
@@ -298,7 +312,7 @@ private static string GetRoot(string tagName)
///
private static string GetMember(string tagName)
{
- var index = tagName.LastIndexOfAny(new[] {MemberSeparator, ArrayOpenSeparator});
+ var index = tagName.LastIndexOfAny(new[] { MemberSeparator, ArrayOpenSeparator });
var length = tagName.Length - index;
return index >= 0 ? tagName.Substring(index, length).TrimStart(MemberSeparator) : tagName;
}
@@ -308,7 +322,7 @@ private static string GetMember(string tagName)
/// We are no longer using regex to make this as efficient as possible since there could realistically be millions
/// of tag names this can get called on.
///
- private static IEnumerable GetMembers(string tagName) =>
+ private static IEnumerable GetMembers(string tagName) =>
NormalizeDelimiter(tagName).Split(MemberSeparator, StringSplitOptions.RemoveEmptyEntries);
///
diff --git a/src/L5Sharp.Core/Common/TaskPriority.cs b/src/L5Sharp.Core/Common/TaskPriority.cs
index 8a904cc1..9302b48d 100644
--- a/src/L5Sharp.Core/Common/TaskPriority.cs
+++ b/src/L5Sharp.Core/Common/TaskPriority.cs
@@ -12,7 +12,7 @@ namespace L5Sharp.Core;
/// .
/// This parameter will control the scan order of task components as related to other tasks.
///
-public readonly struct TaskPriority : IEquatable
+public readonly struct TaskPriority : IEquatable, ILogixParsable
{
private readonly byte _priority;
@@ -44,12 +44,19 @@ public TaskPriority(byte priority)
public static implicit operator TaskPriority(byte priority) => new(priority);
///
- /// Parses a string value into a .
+ /// Parses a string into a value.
///
- /// The string to parse.
- /// A value if the parse was successful; otherwise; the default value.
- public static TaskPriority Parse(string str) =>
- byte.TryParse(str, out var result) ? new TaskPriority(result) : default;
+ /// The string to parse.
+ /// The representing the parsed value.
+ public static TaskPriority Parse(string value) => byte.Parse(value);
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value if successful; Otherwise; default.
+ public static TaskPriority TryParse(string? value) =>
+ byte.TryParse(value, out var result) ? new TaskPriority(result) : default;
///
public bool Equals(TaskPriority other) => _priority == other._priority;
diff --git a/src/L5Sharp.Core/Common/Vendor.cs b/src/L5Sharp.Core/Common/Vendor.cs
index 80e55bc8..27056cce 100644
--- a/src/L5Sharp.Core/Common/Vendor.cs
+++ b/src/L5Sharp.Core/Common/Vendor.cs
@@ -10,7 +10,7 @@ namespace L5Sharp.Core;
/// Vendor's are defined by Rockwell and assigned unique Id and name.
/// Use as it is the most common vendor for compatible devices.
///
-public class Vendor
+public class Vendor : ILogixParsable
{
///
/// Creates a new value with the provided id and name.
@@ -61,11 +61,20 @@ public Vendor(ushort id, string? name = null)
public static implicit operator Vendor(ushort vendorId) => new(vendorId);
///
- /// Creates a new using the provided vendor Id.
+ /// Parses a string into a value.
///
- /// The unique valid that identifies the Vendor.
- /// A new object with the provided Id.
- public static Vendor Parse(string vendorId) => ushort.TryParse(vendorId, out var id) ? new Vendor(id) : Unknown;
+ /// The string to parse.
+ /// The representing the parsed value.
+ public static Vendor Parse(string value) => new(ushort.Parse(value));
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value if successful;
+ /// Otherwise; .
+ public static Vendor TryParse(string? value) =>
+ ushort.TryParse(value, out var result) ? new Vendor(result) : Unknown;
///
public override string ToString() => Name;
diff --git a/src/L5Sharp.Core/Common/Watchdog.cs b/src/L5Sharp.Core/Common/Watchdog.cs
index 0a602ab3..71c01ab7 100644
--- a/src/L5Sharp.Core/Common/Watchdog.cs
+++ b/src/L5Sharp.Core/Common/Watchdog.cs
@@ -12,7 +12,7 @@ namespace L5Sharp.Core;
/// .
/// This parameter will control how long a task can rung before triggering a major fault.
///
-public readonly struct Watchdog : IEquatable
+public readonly struct Watchdog : IEquatable, ILogixParsable
{
private readonly float _watchdog;
@@ -33,9 +33,9 @@ public Watchdog(float watchdog)
}
///
- ///
+ /// Creates a default 500ms value.
///
- /// A new Watchdog value.
+ /// A new value.
public static Watchdog Default() => new(500);
///
@@ -53,12 +53,19 @@ public Watchdog(float watchdog)
public static implicit operator Watchdog(float watchdog) => new(watchdog);
///
- /// Parses a string value into a .
+ /// Parses a string into a value.
///
- /// The string to parse.
- /// A value if the parse was successful; otherwise; the default value.
- public static Watchdog Parse(string str) =>
- float.TryParse(str, out var result) ? new Watchdog(result) : default;
+ /// The string to parse.
+ /// The representing the parsed value.
+ public static Watchdog Parse(string value) => float.Parse(value);
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value if successful; Otherwise; default.
+ public static Watchdog TryParse(string? value) =>
+ float.TryParse(value, out var result) ? new Watchdog(result) : default;
///
public override string ToString() => _watchdog.ToString(CultureInfo.CurrentCulture);
diff --git a/src/L5Sharp.Core/Components/Module.cs b/src/L5Sharp.Core/Components/Module.cs
index b9095dae..c6b74035 100644
--- a/src/L5Sharp.Core/Components/Module.cs
+++ b/src/L5Sharp.Core/Components/Module.cs
@@ -3,6 +3,7 @@
using System.Linq;
using System.Net;
using System.Xml.Linq;
+using JetBrains.Annotations;
namespace L5Sharp.Core;
@@ -29,11 +30,13 @@ public Module()
MajorFault = default;
SafetyEnabled = default;
Keying = ElectronicKeying.CompatibleModule;
- Ports = new LogixContainer();
+ Ports = [];
Communications = new Communications();
}
+
///
+ [UsedImplicitly]
public Module(XElement element) : base(element)
{
}
@@ -228,7 +231,7 @@ public Communications? Communications
/// This property is not directly part of the L5X structure, but is a helper to make accessing the slot number simple.
/// This property looks for an upstream with a valid slot byte number.
///
- public byte? Slot => Ports.FirstOrDefault(p => p.Upstream && p.Address.IsSlot)?.Address.ToSlot();
+ public byte? Slot => Ports.FirstOrDefault(p => p is { Upstream: true, Address.IsSlot: true })?.Address.ToSlot();
///
/// Gets the IP address of the current module if one exists. If the module does not have an IP, returns null.
@@ -346,9 +349,9 @@ public void Add(Module child, Address? address = default)
throw new InvalidOperationException(
$"The module '{Name}' does not have a port for downstream module connections.");
- if (parentPort.Type == "Ethernet" && address is null) address = Address.DefaultIP();
+ if (parentPort.Type == "Ethernet" && address is null) address = Address.IP();
if (parentPort.Address.IsSlot && IsAttached && address is null) address = NextSlot();
- address ??= Address.DefaultSlot();
+ address ??= Address.Slot();
var childPort = new Port {Id = 1, Type = parentPort.Type, Address = address, Upstream = true};
@@ -394,7 +397,7 @@ public static Module Add(string name, string catalogNumber, Address? address = n
{
Id = p.Number,
Type = p.Type,
- Address = p.Type == "Ethernet" ? Address.DefaultIP() : Address.DefaultSlot(),
+ Address = p.Type == "Ethernet" ? Address.IP() : Address.Slot(),
Upstream = !p.DownstreamOnly
}).ToList()),
Description = entry.Description
@@ -417,7 +420,7 @@ private Address NextSlot()
.OrderByDescending(b => b)
.FirstOrDefault();
- return next.HasValue ? Address.Slot(next.Value) : Address.DefaultSlot();
+ return next.HasValue ? Address.Slot(next.Value) : Address.Slot();
}
#endregion
diff --git a/src/L5Sharp.Core/Components/Tag.cs b/src/L5Sharp.Core/Components/Tag.cs
index 96895cc8..2ec12ed8 100644
--- a/src/L5Sharp.Core/Components/Tag.cs
+++ b/src/L5Sharp.Core/Components/Tag.cs
@@ -554,7 +554,7 @@ public Tag With(LogixType value)
///
/// Triggers when a nested data type value of this tag's changes.
///
- private void OnDataChanged(object sender, EventArgs e) => SetData(Root.Value);
+ private void OnDataChanged(object? sender, EventArgs e) => SetData(Root.Value);
///
/// Handles setting the data of the root tag and updating the root properties
diff --git a/src/L5Sharp.Core/Components/Task.cs b/src/L5Sharp.Core/Components/Task.cs
index b57bd5bd..29c15df7 100644
--- a/src/L5Sharp.Core/Components/Task.cs
+++ b/src/L5Sharp.Core/Components/Task.cs
@@ -70,9 +70,9 @@ public TaskPriority Priority
/// The scan rate (ms) of the task component. Default of 10.
///
/// >A value type representing the rate of the task.
- public ScanRate? Rate
+ public ScanRate Rate
{
- get => GetValue();
+ get => GetValue();
set => SetValue(value);
}
diff --git a/src/L5Sharp.Core/Elements/Block.cs b/src/L5Sharp.Core/Elements/Block.cs
index 595d17cd..b2338624 100644
--- a/src/L5Sharp.Core/Elements/Block.cs
+++ b/src/L5Sharp.Core/Elements/Block.cs
@@ -188,9 +188,9 @@ public override IEnumerable References()
{
var references = new List { new(Element, L5XName.Instruction, Type) };
- if (Operand is null || !Operand.IsTag)
+ if (Operand is null || !Operand.IsTag)
return references;
-
+
references.Add(new CrossReference(Element, L5XName.Tag, Operand.ToString(), Type));
references.AddRange(Tags.Select(t => new CrossReference(Element, L5XName.Tag, t, Type)));
return references;
@@ -948,8 +948,8 @@ private static Block NewRoutine(string routine, string[]? inputs = null, string[
element.Add(new XAttribute(L5XName.X, 0));
element.Add(new XAttribute(L5XName.Y, 0));
element.Add(new XAttribute(L5XName.Routine, routine));
- element.Add(new XAttribute(L5XName.In, string.Join(' ', inputs) ?? string.Empty));
- element.Add(new XAttribute(L5XName.Ret, string.Join(' ', outputs) ?? string.Empty));
+ element.Add(new XAttribute(L5XName.In, string.Join(' ', inputs ?? Enumerable.Empty())));
+ element.Add(new XAttribute(L5XName.Ret, string.Join(' ', outputs ?? Enumerable.Empty())));
return new Block(element);
}
@@ -986,13 +986,13 @@ private IEnumerable> GetInputs(Sheet sheet, strin
{
var block = sheet.Block(wire.FromID);
if (block is null) continue;
-
+
if (block.Type == L5XName.OCon)
{
arguments.AddRange(GetPair()?.Endpoints() ?? Enumerable.Empty>());
continue;
}
-
+
var arg = block.GetArguments(wire.FromParam);
arguments.Add(new KeyValuePair(arg, block.Type));
}
@@ -1016,7 +1016,7 @@ private IEnumerable> GetOutputs(Sheet sheet, stri
arguments.AddRange(GetPair()?.Endpoints() ?? Enumerable.Empty>());
continue;
}
-
+
var arg = block.GetArguments(wire.ToParam);
arguments.Add(new KeyValuePair(arg, block.Type));
}
@@ -1029,7 +1029,7 @@ private TagName GetArguments(string? param = null)
var operand = Operand is not null && Operand.IsTag ? new TagName(Operand.ToString()) : TagName.Empty;
var parameter = param is not null ? new TagName(param) : TagName.Empty;
return TagName.Concat(operand, parameter);
-
+
/*return L5XType switch
{
L5XName.IRef => Operand is not null && Operand.IsTag ? (TagName)Operand : TagName.Empty,
diff --git a/src/L5Sharp.Core/Elements/TextBox.cs b/src/L5Sharp.Core/Elements/TextBox.cs
index b7531424..218c90db 100644
--- a/src/L5Sharp.Core/Elements/TextBox.cs
+++ b/src/L5Sharp.Core/Elements/TextBox.cs
@@ -62,7 +62,7 @@ public void Attach(uint to)
var diagram = Element.Parent ??
throw new InvalidOperationException("Can not attach text to element that does not have a parent.");
- diagram.LastNode.AddAfterSelf(element);
+ diagram.LastNode?.AddAfterSelf(element);
}
///
diff --git a/src/L5Sharp.Core/Enums/Radix.cs b/src/L5Sharp.Core/Enums/Radix.cs
index d2a30fa8..e2205e4e 100644
--- a/src/L5Sharp.Core/Enums/Radix.cs
+++ b/src/L5Sharp.Core/Enums/Radix.cs
@@ -71,9 +71,6 @@ private Radix(string name, string value) : base(name, value)
///
public static readonly Radix DateTimeNs = new DateTimeNsRadix();
- ///
- public override string ToString() => Value;
-
///
/// Gets the default value for the provided logix type.
///
@@ -124,10 +121,9 @@ public static Radix Infer(string input)
if (name is null)
throw new FormatException(
- @$"Could not determine Radix from input '{input}'.
- Verify that the input string is an accepted Radix format.");
+ $"Could not determine Radix from input '{input}'. Verify that the input string is an accepted Radix format.");
- return FromName(name);
+ return Parse(name);
}
///
@@ -135,13 +131,13 @@ public static Radix Infer(string input)
///
/// The string input for which to infer the radix format.
/// If parsed successfully, then the representing the format of the input;
- /// Otherwise, null.
+ /// Otherwise, a radix format.
/// true if a Radix format was inferred from the string input; Otherwise, false.
- public static bool TryInfer(string input, out Radix? radix)
+ public static bool TryInfer(string input, out Radix radix)
{
var name = Identifiers.FirstOrDefault(i => i.Value.Invoke(input)).Key;
- radix = name is not null ? FromName(name) : null;
- return radix is not null;
+ radix = name is not null ? Parse(name) : Null;
+ return radix != Null;
}
///
@@ -170,14 +166,14 @@ public static bool TryInfer(string input, out Radix? radix)
///
/// A string that represents the value of the atomic type in the current radix base number style.
///
- public abstract string Format(AtomicType atomic);
+ public abstract string FormatValue(AtomicType atomic);
///
/// Parses a string input of a given Radix formatted value into an atomic value type.
///
/// The string value to parse.
/// An representing the value of the formatted string.
- public abstract AtomicType Parse(string input);
+ public abstract AtomicType ParseValue(string input);
///
/// Converts the provided to the specified base number.
@@ -265,40 +261,32 @@ private void ValidateType(AtomicType atomic)
{ nameof(DateTimeNs), s => DateTimeNs.HasFormat(s) }
};
- private class NullRadix : Radix
+ private class NullRadix() : Radix("NullType", "NullType")
{
- public NullRadix() : base("NullType", "NullType")
- {
- }
-
protected override string Specifier => string.Empty;
protected override bool HasFormat(string input) =>
throw new NotSupportedException($"{Name} Radix does not support formatting atomic values");
- public override string Format(AtomicType atomic) =>
+ public override string FormatValue(AtomicType atomic) =>
throw new NotSupportedException($"{Name} Radix does not support formatting atomic values");
- public override AtomicType Parse(string input) =>
+ public override AtomicType ParseValue(string input) =>
throw new NotSupportedException($"{Name} Radix does not support parsing atomic values");
}
- private class BinaryRadix : Radix
+ private class BinaryRadix() : Radix(nameof(Binary), nameof(Binary))
{
private const int BaseNumber = 2;
private const int CharsPerByte = 8;
private const string ByteSeparator = "_";
private const string Pattern = @"(?<=\d)(?=(\d\d\d\d)+(?!\d))";
- public BinaryRadix() : base(nameof(Binary), nameof(Binary))
- {
- }
-
protected override string Specifier => "2#";
protected override bool HasFormat(string input) => !input.IsEmpty() && input.StartsWith(Specifier);
- public override string Format(AtomicType atomic)
+ public override string FormatValue(AtomicType atomic)
{
ValidateType(atomic);
@@ -309,7 +297,7 @@ public override string Format(AtomicType atomic)
return $"{Specifier}{formatted}";
}
- public override AtomicType Parse(string input)
+ public override AtomicType ParseValue(string input)
{
ValidateFormat(input);
@@ -319,20 +307,16 @@ public override AtomicType Parse(string input)
}
}
- private class OctalRadix : Radix
+ private class OctalRadix() : Radix(nameof(Octal), nameof(Octal))
{
private const int BaseNumber = 8;
private const int CharsPerByte = 3;
private const string ByteSeparator = "_";
private const string Pattern = @"(?<=\d)(?=(\d\d\d)+(?!\d))";
- public OctalRadix() : base(nameof(Octal), nameof(Octal))
- {
- }
-
protected override string Specifier => "8#";
- public override string Format(AtomicType atomic)
+ public override string FormatValue(AtomicType atomic)
{
ValidateType(atomic);
@@ -343,7 +327,7 @@ public override string Format(AtomicType atomic)
return $"{Specifier}{formatted}";
}
- public override AtomicType Parse(string input)
+ public override AtomicType ParseValue(string input)
{
ValidateFormat(input);
@@ -353,12 +337,8 @@ public override AtomicType Parse(string input)
}
}
- private class DecimalRadix : Radix
+ private class DecimalRadix() : Radix(nameof(Decimal), nameof(Decimal))
{
- public DecimalRadix() : base(nameof(Decimal), nameof(Decimal))
- {
- }
-
protected override string Specifier => string.Empty;
protected override bool HasFormat(string input)
@@ -371,7 +351,7 @@ protected override bool HasFormat(string input)
return !input.IsEmpty() && input.All(char.IsDigit);
}
- public override string Format(AtomicType atomic)
+ public override string FormatValue(AtomicType atomic)
{
ValidateType(atomic);
@@ -390,7 +370,7 @@ public override string Format(AtomicType atomic)
};
}
- public override AtomicType Parse(string input)
+ public override AtomicType ParseValue(string input)
{
ValidateFormat(input);
@@ -423,20 +403,16 @@ public override AtomicType Parse(string input)
}
}
- private class HexRadix : Radix
+ private class HexRadix() : Radix(nameof(Hex), nameof(Hex))
{
private const int BaseNumber = 16;
private const int BitsPerByte = 2;
private const string ByteSeparator = "_";
private const string Pattern = @"(?<=\w)(?=(\w\w\w\w)+(?!\w))";
- public HexRadix() : base(nameof(Hex), nameof(Hex))
- {
- }
-
protected override string Specifier => "16#";
- public override string Format(AtomicType atomic)
+ public override string FormatValue(AtomicType atomic)
{
ValidateType(atomic);
@@ -447,7 +423,7 @@ public override string Format(AtomicType atomic)
return $"{Specifier}{formatted}";
}
- public override AtomicType Parse(string input)
+ public override AtomicType ParseValue(string input)
{
ValidateFormat(input);
@@ -457,12 +433,8 @@ public override AtomicType Parse(string input)
}
}
- private class FloatRadix : Radix
+ private class FloatRadix() : Radix(nameof(Float), nameof(Float))
{
- public FloatRadix() : base(nameof(Float), nameof(Float))
- {
- }
-
protected override string Specifier => string.Empty;
protected override bool HasFormat(string input)
@@ -476,7 +448,7 @@ protected override bool HasFormat(string input)
return input.Contains('.') && input.Replace(".", string.Empty).All(char.IsDigit);
}
- public override string Format(AtomicType atomic)
+ public override string FormatValue(AtomicType atomic)
{
ValidateType(atomic);
@@ -488,7 +460,7 @@ public override string Format(AtomicType atomic)
return ((float)(REAL)atomic).ToString("0.0######", CultureInfo.InvariantCulture);
}
- public override AtomicType Parse(string input)
+ public override AtomicType ParseValue(string input)
{
ValidateFormat(input);
@@ -496,12 +468,8 @@ public override AtomicType Parse(string input)
}
}
- private class ExponentialRadix : Radix
+ private class ExponentialRadix() : Radix(nameof(Exponential), nameof(Exponential))
{
- public ExponentialRadix() : base(nameof(Exponential), nameof(Exponential))
- {
- }
-
protected override string Specifier => "";
protected override bool HasFormat(string input)
@@ -518,7 +486,7 @@ protected override bool HasFormat(string input)
.All(char.IsDigit);
}
- public override string Format(AtomicType atomic)
+ public override string FormatValue(AtomicType atomic)
{
ValidateType(atomic);
@@ -530,7 +498,7 @@ public override string Format(AtomicType atomic)
return ((float)(REAL)atomic).ToString("e8", CultureInfo.InvariantCulture);
}
- public override AtomicType Parse(string input)
+ public override AtomicType ParseValue(string input)
{
ValidateFormat(input);
@@ -541,7 +509,7 @@ private static string ReplaceAll(string value, IEnumerable items, string
items.Aggregate(value, (str, cItem) => str.Replace(cItem, replacement));
}
- private class AsciiRadix : Radix
+ private class AsciiRadix() : Radix(nameof(Ascii), nameof(Ascii).ToUpper())
{
private const int BaseNumber = 16;
private const int BitsPerByte = 2;
@@ -559,16 +527,12 @@ private class AsciiRadix : Radix
{ "$'", "27" }
};
- public AsciiRadix() : base(nameof(Ascii), nameof(Ascii).ToUpper())
- {
- }
-
protected override string Specifier => "'";
protected override bool HasFormat(string input) =>
input.StartsWith(Specifier) && input.EndsWith(Specifier) && Regex.IsMatch(input, Pattern);
- public override string Format(AtomicType atomic)
+ public override string FormatValue(AtomicType atomic)
{
ValidateType(atomic);
@@ -579,7 +543,7 @@ public override string Format(AtomicType atomic)
return $"{Specifier}{formatted}{Specifier}";
}
- public override AtomicType Parse(string input)
+ public override AtomicType ParseValue(string input)
{
ValidateFormat(input);
@@ -658,20 +622,16 @@ private static IEnumerable Segment(string input, int length)
}
}
- private class DateTimeRadix : Radix
+ private class DateTimeRadix() : Radix(nameof(DateTime), "Date/Time")
{
private const string Separator = "_";
private const string Suffix = "Z";
private const string InsertPattern = @"(?<=\d\d\d)(?=(\d\d\d)+(?!\d))";
private const long TicksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000;
- public DateTimeRadix() : base(nameof(DateTime), "Date/Time")
- {
- }
-
protected override string Specifier => "DT#";
- public override string Format(AtomicType atomic)
+ public override string FormatValue(AtomicType atomic)
{
ValidateType(atomic);
@@ -690,7 +650,7 @@ public override string Format(AtomicType atomic)
return $"{Specifier}{str}{Suffix}";
}
- public override AtomicType Parse(string input)
+ public override AtomicType ParseValue(string input)
{
ValidateFormat(input);
@@ -707,18 +667,14 @@ public override AtomicType Parse(string input)
}
}
- private class DateTimeNsRadix : Radix
+ private class DateTimeNsRadix() : Radix(nameof(DateTimeNs), "Date/Time (ns)")
{
private const string Separator = "_";
private const string InsertPattern = @"(?<=\d\d\d)(?=(\d\d\d)+(?!\d))";
- public DateTimeNsRadix() : base(nameof(DateTimeNs), "Date/Time (ns)")
- {
- }
-
protected override string Specifier => "LDT#";
- public override string Format(AtomicType atomic)
+ public override string FormatValue(AtomicType atomic)
{
ValidateType(atomic);
@@ -737,7 +693,7 @@ public override string Format(AtomicType atomic)
return $"{Specifier}{str}";
}
- public override AtomicType Parse(string input)
+ public override AtomicType ParseValue(string input)
{
ValidateFormat(input);
diff --git a/src/L5Sharp.Core/Enums/TriggerOperation.cs b/src/L5Sharp.Core/Enums/TriggerOperation.cs
index 43597f2c..9d9237d5 100644
--- a/src/L5Sharp.Core/Enums/TriggerOperation.cs
+++ b/src/L5Sharp.Core/Enums/TriggerOperation.cs
@@ -11,20 +11,6 @@ private TriggerOperation(string name, int value) : base(name, value)
{
}
- ///
- /// Parses a string number into the corresponding .
- ///
- /// The string integer number to parse.
- /// A value.
- /// value is not a parsable integer.
- public static TriggerOperation Parse(string value)
- {
- if (!int.TryParse(value, out var number))
- throw new FormatException($"Could not parse Trigger Operation value {value}");
-
- return FromValue(number);
- }
-
///
/// Represents a ExactEqual value.
///
diff --git a/src/L5Sharp.Core/ILogixParsable.cs b/src/L5Sharp.Core/ILogixParsable.cs
new file mode 100644
index 00000000..c9449a79
--- /dev/null
+++ b/src/L5Sharp.Core/ILogixParsable.cs
@@ -0,0 +1,22 @@
+namespace L5Sharp.Core;
+
+///
+/// Represents an interface that defines methods to parse string values into an object of type T.
+///
+/// The type of object to parse the string value into.
+public interface ILogixParsable where T : ILogixParsable
+{
+ ///
+ /// Parses the provided string into an instance of this type.
+ ///
+ /// The string to parse.
+ /// A new instance of this type representing the parsed value.
+ static abstract T Parse(string value);
+
+ ///
+ /// Tries to parse the provided string into an instance of this type.
+ ///
+ /// The string to parse.
+ /// A new instance of this type representing the parsed value if successful; Otherwise, null.
+ static abstract T? TryParse(string? value);
+}
\ No newline at end of file
diff --git a/src/L5Sharp.Core/L5Sharp.Core.csproj b/src/L5Sharp.Core/L5Sharp.Core.csproj
index 460c82c4..9ab6901c 100644
--- a/src/L5Sharp.Core/L5Sharp.Core.csproj
+++ b/src/L5Sharp.Core/L5Sharp.Core.csproj
@@ -1,16 +1,15 @@
- netstandard2.1
- 10
+ 12enabletruetrueL5SharpTimothy Nunnink
- 0.18.3
- 0.18.3
- 0.18.3.0
+ 0.19.0
+ 0.19.0
+ 0.19.0.0A library for intuitively interacting with Rockwell's L5X import/export files.https://github.com/tnunnink/L5Sharpcsharp allen-bradely l5x logix plc-programming rockwell-automation logix5000
@@ -18,12 +17,13 @@
MITgit
- Added Parse function taking Type parameter.
+ Upgraded to .NET7. Reworked LogixParser.cs. Added new ILogixParsable.cs. Fixed tests and documentation.Copyright (c) Timothy Nunnink 2022README.mdL5Sharphttps://github.com/tnunnink/L5Sharpen-US
+ net7.0
diff --git a/src/L5Sharp.Core/L5X.cs b/src/L5Sharp.Core/L5X.cs
index 1b34ec83..9ff4b5bb 100644
--- a/src/L5Sharp.Core/L5X.cs
+++ b/src/L5Sharp.Core/L5X.cs
@@ -6,8 +6,7 @@
using System.Threading.Tasks;
using System.Xml.Linq;
using JetBrains.Annotations;
-using Task = L5Sharp.Core.Task;
-using task = System.Threading.Tasks.Task;
+using TTask = System.Threading.Tasks.Task;
namespace L5Sharp.Core;
@@ -144,7 +143,7 @@ public L5X(XElement content)
/// Gets the collection of components found in the L5X file.
///
/// A of components.
- public LogixContainer Tasks => new(GetContainer(L5XName.Tasks));
+ public LogixContainer Tasks => new(GetContainer(L5XName.Tasks));
///
/// The container collection of components found in the L5X file.
@@ -188,7 +187,7 @@ public static async Task LoadAsync(string fileName, CancellationToken token
{
await using var stream = new FileStream(fileName, FileMode.Open);
var element = await XElement.LoadAsync(stream, LoadOptions.SetLineInfo, token);
- var l5X = await task.Run(() => new L5X(element), token);
+ var l5X = await TTask.Run(() => new L5X(element), token);
return l5X;
}
@@ -517,7 +516,7 @@ public int Count() where TElement : LogixElement =>
public LogixComponent? Find(ComponentKey key)
{
return Index.Components.TryGetValue(key, out var components)
- ? components.Values.First()?.Deserialize()
+ ? components.Values.First().Deserialize()
: default;
}
@@ -583,7 +582,7 @@ public int Count() where TElement : LogixElement =>
var key = new ComponentKey(typeof(TComponent).L5XType(), name);
return Index.Components.TryGetValue(key, out var components)
- ? components.Values.First()?.Deserialize()
+ ? components.Values.First().Deserialize()
: default;
}
diff --git a/src/L5Sharp.Core/L5XInfo.cs b/src/L5Sharp.Core/L5XInfo.cs
index 5f4495cf..6d88bbfb 100644
--- a/src/L5Sharp.Core/L5XInfo.cs
+++ b/src/L5Sharp.Core/L5XInfo.cs
@@ -79,7 +79,7 @@ public string? Owner
/// Gets the date time that the L5X file was exported.
///
public DateTime? ExportDate => _element.Attribute(L5XName.ExportDate)?.Value is not null
- ? DateTime.ParseExact(_element.Attribute(L5XName.ExportDate)?.Value, L5X.DateTimeFormat, null)
+ ? DateTime.ParseExact(_element.Attribute(L5XName.ExportDate)?.Value!, L5X.DateTimeFormat, null)
: default;
///
diff --git a/src/L5Sharp.Core/LogixData.cs b/src/L5Sharp.Core/LogixData.cs
index b519ceb8..fb925faa 100644
--- a/src/L5Sharp.Core/LogixData.cs
+++ b/src/L5Sharp.Core/LogixData.cs
@@ -31,8 +31,8 @@ public static class LogixData
/// delegate functions. This is what we are using to create strongly typed logix type objects at runtime.
///
private static readonly Lazy>> Deserializers = new(() =>
- AppDomain.CurrentDomain.GetAssemblies().Distinct().SelectMany(Introspect)
- .ToDictionary(k => k.Key, k => k.Value), LazyThreadSafetyMode.ExecutionAndPublication);
+ AppDomain.CurrentDomain.GetAssemblies().Distinct().SelectMany(Introspect)
+ .ToDictionary(k => k.Key, k => k.Value), LazyThreadSafetyMode.ExecutionAndPublication);
///
/// Returns the singleton null object.
@@ -134,7 +134,7 @@ private static LogixType DeserializeData(XContainer element)
///
private static LogixType DeserializeFormatted(XElement element)
{
- DataFormat.TryFromName(element.Attribute(L5XName.Format)?.Value, out var format);
+ var format = element.Attribute(L5XName.Format)?.Value.TryParse();
if (format is null) return Null;
if (format == DataFormat.String) return DeserializeString(element);
return element.FirstNode is XElement root ? Deserialize(root) : Null;
@@ -142,7 +142,7 @@ private static LogixType DeserializeFormatted(XElement element)
///
/// Handles deserializing an element to a atomic value type logix type.
- /// This method will call upon to generate the known concrete atomic type we need.
+ /// This method will call upon to generate the known concrete atomic type we need.
/// This implementation ignores any specified Radix because we have found cases where the Radix does not match
/// the actual format of the value, therefore, we always infer the format, and then parse.
///
@@ -150,7 +150,7 @@ private static LogixType DeserializeAtomic(XElement element)
{
var dataType = element.Get(L5XName.DataType);
var value = element.Get(L5XName.Value);
- return Atomic.Parse(dataType, value);
+ return AtomicType.Parse(dataType, value);
}
///
@@ -166,12 +166,15 @@ private static LogixType DeserializeArray(XElement element)
: typeof(ComplexType);
var arrayType = typeof(ArrayType<>).MakeGenericType(type);
+ var arrayName = arrayType.FullName ??
+ throw new InvalidOperationException(
+ $"Could not determine the full name for array of type {arrayType}.");
- if (Deserializers.Value.TryGetValue(arrayType.FullName, out var cached))
+ if (Deserializers.Value.TryGetValue(arrayName, out var cached))
return cached.Invoke(element);
var deserializer = arrayType.Deserializer();
- Deserializers.Value.Add(arrayType.FullName, deserializer);
+ Deserializers.Value.Add(arrayName, deserializer);
return deserializer.Invoke(element);
}
@@ -185,7 +188,7 @@ private static LogixType DeserializeElement(XElement element)
var value = element.Attribute(L5XName.Value);
var structure = element.Element(L5XName.Structure);
- return value is not null ? Atomic.Parse(dataType, value.Value)
+ return value is not null ? AtomicType.Parse(dataType, value.Value)
: structure is not null ? DeserializeStructure(structure)
: throw element.L5XError(L5XName.Element);
}
@@ -220,14 +223,15 @@ private static LogixType DeserializeString(XElement element)
/// or string type. String structure is unique in that it will have a data value member called DATA with a ASCII
/// radix, a non-null element value, and a data type attribute value equal to that of the parent structure element attribute.
///
- private static bool HasStringStructure(XElement element)
+ private static bool HasStringStructure(XElement? element)
{
+ if (element is null) return false;
+
//If this is a structure or structure member it could potentially be the string structure.
if (element.Name == L5XName.Structure || element.Name == L5XName.StructureMember)
{
return element.Elements(L5XName.DataValueMember).Any(e =>
- e?.Value is not null
- && e.Attribute(L5XName.Name)?.Value == "DATA"
+ e.Attribute(L5XName.Name)?.Value == "DATA"
&& e.Attribute(L5XName.DataType)?.Value == e.Parent?.Attribute(L5XName.DataType)?.Value
&& e.Attribute(L5XName.Radix)?.Value == "ASCII");
}
@@ -253,8 +257,8 @@ private static IEnumerable>> Intr
var types = assembly.GetTypes().Where(t =>
(typeof(StructureType).IsAssignableFrom(t) || typeof(StringType).IsAssignableFrom(t))
&& t != typeof(ComplexType) && t != typeof(StringType)
- && t is {IsAbstract: false, IsPublic: true}
- && t.GetConstructor(new[] {typeof(XElement)}) is not null);
+ && t is { IsAbstract: false, IsPublic: true }
+ && t.GetConstructor(new[] { typeof(XElement) }) is not null);
foreach (var type in types)
{
@@ -271,7 +275,7 @@ private static IEnumerable FindLogixTypes(Assembly assembly)
{
return assembly.GetTypes().Where(t =>
typeof(LogixType).IsAssignableFrom(t)
- && t is {IsAbstract: false, IsPublic: true}
+ && t is { IsAbstract: false, IsPublic: true }
&& t != typeof(ComplexType) && t != typeof(StringType)
&& t != typeof(ArrayType) && t != typeof(ArrayType<>)
&& t != typeof(NullType));
diff --git a/src/L5Sharp.Core/LogixElement.cs b/src/L5Sharp.Core/LogixElement.cs
index 2b7258c0..9e831d6f 100644
--- a/src/L5Sharp.Core/LogixElement.cs
+++ b/src/L5Sharp.Core/LogixElement.cs
@@ -349,6 +349,9 @@ public void Replace(LogixElement element)
///
protected T? GetValue([CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
var value = Element.Attribute(name)?.Value;
return value is not null ? value.Parse() : default;
}
@@ -372,6 +375,9 @@ public void Replace(LogixElement element)
///
protected T? GetValue(Func selector, [CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
var value = selector.Invoke(Element)?.Attribute(name)?.Value;
return value is not null ? value.Parse() : default;
}
@@ -394,6 +400,9 @@ public void Replace(LogixElement element)
///
protected T? GetValue(XName child, [CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
var value = Element.Element(child)?.Attribute(name)?.Value;
return value is not null ? value.Parse() : default;
}
@@ -412,8 +421,11 @@ public void Replace(LogixElement element)
///
protected T GetRequiredValue([CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
var value = Element.Attribute(name)?.Value;
- return value is not null ? value.Parse() : throw Element.L5XError(name!);
+ return value is not null ? value.Parse() : throw Element.L5XError(name);
}
///
@@ -433,6 +445,9 @@ protected T GetRequiredValue([CallerMemberName] string? name = null)
///
protected T? GetProperty([CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
var value = Element.Element(name)?.Value;
return value is not null ? value.Parse() : default;
}
@@ -454,6 +469,9 @@ protected T GetRequiredValue([CallerMemberName] string? name = null)
///
protected T? GetComplex([CallerMemberName] string? name = null) where T : LogixElement
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
return Element.Element(name)?.Deserialize();
}
@@ -473,6 +491,9 @@ protected T GetRequiredValue([CallerMemberName] string? name = null)
protected LogixContainer GetContainer([CallerMemberName] string? name = null)
where TChild : LogixElement
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
var container = Element.Element(name);
if (container is null) throw Element.L5XError(name);
return new LogixContainer(container);
@@ -512,6 +533,9 @@ protected LogixContainer GetContainer([CallerMemberName] string?
///
protected DateTime? GetDateTime(string? format = null, [CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
format ??= "ddd MMM d HH:mm:ss yyyy";
var attribute = Element.Attribute(name);
@@ -535,6 +559,9 @@ protected LogixContainer GetContainer([CallerMemberName] string?
///
protected void SetValue(T? value, [CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
Element.SetAttributeValue(name, value);
}
@@ -557,8 +584,11 @@ protected void SetValue(T? value, [CallerMemberName] string? name = null)
///
protected void SetValue(T? value, Func selector, [CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
var element = selector.Invoke(Element);
- if (element is null) throw Element.L5XError(name!);
+ if (element is null) throw Element.L5XError(name);
element.SetAttributeValue(name, value);
}
@@ -579,6 +609,9 @@ protected void SetValue(T? value, Func selector, [Caller
///
protected void SetValue(T? value, XName child, [CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
if (value is null)
{
Element.Element(child)?.Attribute(name)?.Remove();
@@ -610,6 +643,9 @@ protected void SetValue(T? value, XName child, [CallerMemberName] string? nam
///
protected void SetRequiredValue(T value, [CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
if (value is null)
throw new ArgumentNullException(nameof(value), $"Property {name} can not be null.");
@@ -631,21 +667,26 @@ protected void SetRequiredValue(T value, [CallerMemberName] string? name = nu
///
protected void SetProperty(T value, [CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
var element = Element.Element(name);
-
+
if (value is null)
{
element?.Remove();
return;
}
-
+
+ var property = value.ToString() ?? throw new ArgumentException("Property value can not be null", nameof(value));
+
if (element is null)
{
- Element.Add(new XElement(name, new XCData(value.ToString())));
+ Element.Add(new XElement(name, new XCData(property)));
return;
}
- element.ReplaceWith(new XElement(name, new XCData(value.ToString())));
+ element.ReplaceWith(new XElement(name, new XCData(property)));
}
///
@@ -663,6 +704,9 @@ protected void SetProperty(T value, [CallerMemberName] string? name = null)
///
protected void SetComplex(T? value, [CallerMemberName] string? name = null) where T : ILogixSerializable
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
var element = Element.Element(name);
if (value is null)
@@ -695,14 +739,17 @@ protected void SetComplex(T? value, [CallerMemberName] string? name = null) w
protected void SetContainer(LogixContainer? value, [CallerMemberName] string? name = null)
where TChild : LogixElement
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
var element = Element.Element(name);
-
+
if (value is null)
{
element?.Remove();
return;
}
-
+
if (element is null)
{
Element.Add(value.Serialize());
@@ -725,6 +772,9 @@ protected void SetContainer(LogixContainer? value, [CallerMember
///
protected void SetDateTime(DateTime? value, string? format = null, [CallerMemberName] string? name = null)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException("Name can not be null or empty", nameof(name));
+
if (value is null)
{
Element.Attribute(name)?.Remove();
diff --git a/src/L5Sharp.Core/LogixEnum.cs b/src/L5Sharp.Core/LogixEnum.cs
index 8c0f03f1..2537b9be 100644
--- a/src/L5Sharp.Core/LogixEnum.cs
+++ b/src/L5Sharp.Core/LogixEnum.cs
@@ -38,6 +38,15 @@ protected LogixEnum(string name)
/// A common enumeration field name.
public string Name { get; }
+ ///
+ /// Retrieves all names for all defined in the library.
+ ///
+ /// A collection names for all enums.
+ public static IEnumerable Names()
+ {
+ return Enums.Value.SelectMany(v => v.Value).Select(x => x.Name);
+ }
+
///
/// Retrieves all names for a of the specified enum type.
///
@@ -106,7 +115,7 @@ private static Dictionary AllOptions()
{
var baseType = typeof(LogixEnum);
- return Assembly.GetAssembly(baseType).GetTypes()
+ return baseType.Assembly.GetTypes()
.Where(t => baseType.IsAssignableFrom(t))
.ToDictionary(t => t, t => GetOptions(t).ToArray());
}
@@ -125,7 +134,7 @@ private static IEnumerable GetOptions(Type type)
return type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
.Where(f => type.IsAssignableFrom(f.FieldType))
- .Select(f => (LogixEnum)f.GetValue(null))
+ .Select(f => (LogixEnum)f.GetValue(null)!)
.OrderBy(e => e.Name);
}
}
@@ -143,27 +152,16 @@ private static IEnumerable GetOptions(Type type)
/// The type of the inner value.
public abstract class LogixEnum : LogixEnum,
IEquatable>,
- IComparable>
+ IComparable>,
+ ILogixParsable
where TEnum : LogixEnum
where TValue : IEquatable, IComparable
{
- private static readonly Lazy> FromNamOptions =
- new(() => Options().ToDictionary(item => item.Name));
-
- private static readonly Lazy> FromNameIgnoreCaseOptions = new(() =>
+ private static readonly Lazy> NameLookup = new(() =>
Options().ToDictionary(item => item.Name, StringComparer.OrdinalIgnoreCase));
- private static readonly Lazy> FromValueOptions = new(() =>
- {
- var dictionary = new Dictionary();
-
- foreach (var option in Options())
- {
- dictionary.TryAdd(option.Value, option);
- }
-
- return dictionary;
- });
+ private static readonly Lazy> ValueLookup = new(() =>
+ Options().ToDictionary(item => item.ToString()));
///
/// Creates an enumeration with the specified name and value.
@@ -186,141 +184,61 @@ protected LogixEnum(string name, TValue value) : base(name)
/// Returns all enumeration options for the specified enumeration type.
///
/// An containing all enumeration values of the specified type.
- public static IEnumerable All() => FromNamOptions.Value.Values.ToList().AsReadOnly();
+ public static IEnumerable All() => Options().ToList().AsReadOnly();
///
- /// Gets the item associated with the specified name.
+ /// Parses the specified string representation of an enumeration name or value into its corresponding
+ /// enumeration type.
///
- /// The name of the item to get.
- /// true to ignore case during the comparison; otherwise, false.
- ///
- /// The item associated with the specified name.
- /// If the specified name is not found, throws a .
- ///
- /// is null.
- /// does not exist.
- ///
- ///
- public static TEnum FromName(string name, bool ignoreCase = false)
- {
- if (string.IsNullOrEmpty(name))
- throw new ArgumentException("Argument can not be null or empty.", name);
-
- return GetFromName(ignoreCase ? FromNameIgnoreCaseOptions.Value : FromNamOptions.Value);
-
- TEnum GetFromName(IReadOnlyDictionary dictionary)
- {
- if (!dictionary.TryGetValue(name, out var result))
- throw new KeyNotFoundException($"No {typeof(TEnum).Name} with Name \\\"{name}\\\" found.");
-
- return result;
- }
- }
-
- ///
- /// Gets the item associated with the specified name.
- ///
- /// The name of the item to get.
- ///
- /// When this method returns, contains the item associated with the specified name, if the key is found;
- /// otherwise, null. This parameter is passed uninitialized.
- ///
- /// true if the contains an item with the specified name; otherwise, false.
- ///
- /// is null.
- ///
- ///
- public static bool TryFromName(string? name, out TEnum? result) => TryFromName(name, false, out result);
-
- ///
- /// Gets the item associated with the specified name.
- ///
- /// The name of the item to get.
- /// true to ignore case during the comparison; otherwise, false.
- ///
- /// When this method returns, contains the item associated with the specified name, if the name is found;
- /// otherwise, null. This parameter is passed uninitialized.
- ///
- /// true if the contains an item with the specified name; otherwise, false.
- ///
- /// is null.
- ///
- ///
- public static bool TryFromName(string? name, bool ignoreCase, out TEnum? result)
- {
- if (!string.IsNullOrEmpty(name))
- return ignoreCase
- ? FromNameIgnoreCaseOptions.Value.TryGetValue(name, out result)
- : FromNamOptions.Value.TryGetValue(name, out result);
-
- result = default;
- return false;
- }
-
- ///
- /// Gets an item associated with the specified value.
- ///
- /// The value of the item to get.
- ///
- /// The first item found that is associated with the specified value.
- /// If the specified value is not found, throws a .
- ///
- /// does not exist.
- ///
- ///
- public static TEnum FromValue(TValue value)
+ /// The string representation of the enum. This can be the name or value.
+ /// The enum value corresponding to the specified string representation.
+ /// Thrown if the is null.
+ /// Thrown if no enum value with the specified string representation is found.
+ ///
+ /// This method will first check for enums by name. If none exist, then it will check the value lookup dictionary
+ /// of enumeration values converted to string. This combines factories for name and value
+ /// into a single method to avoid having to worry about the right one to use.
+ /// In this library we typically represent the XML value as the property which is also a string,
+ /// but we also in some places will relay on name, and we want to support both.
+ ///
+ public static TEnum Parse(string value)
{
if (value is null)
throw new ArgumentNullException(nameof(value));
- if (!FromValueOptions.Value.TryGetValue(value, out var result))
- throw new KeyNotFoundException($"No {typeof(TEnum).Name} with Value {value} found.");
+ if (NameLookup.Value.TryGetValue(value, out var named))
+ return named;
- return result;
- }
+ if (ValueLookup.Value.TryGetValue(value, out var literal))
+ return literal;
- ///
- /// Gets an item associated with the specified value.
- ///
- /// The value of the item to get.
- /// The value to return when item not found.
- ///
- /// The first item found that is associated with the specified value.
- /// If the specified value is not found, returns .
- ///
- ///
- ///
- public static TEnum FromValue(TValue value, TEnum defaultValue)
- {
- if (value is null)
- throw new ArgumentNullException(nameof(value));
-
- return FromValueOptions.Value.GetValueOrDefault(value, defaultValue);
+ throw new KeyNotFoundException($"No {typeof(TEnum).Name} with Value {value} found.");
}
///
- /// Gets an item associated with the specified value.
+ /// Tries to parse the specified string representation of an enumeration name or value into its corresponding
+ /// enumeration type.
///
- /// The value of the item to get.
- ///
- /// When this method returns, contains the item associated with the specified value, if the value is found;
- /// otherwise, null. This parameter is passed uninitialized.
- ///
- /// true if the contains an item with the specified name; otherwise, false.
- ///
- ///
- ///
- public static bool TryFromValue(TValue? value, out TEnum? result)
+ /// The string to parse. This can be the name or value.
+ /// The enum value corresponding to the specified string representation if found; Otherwise, null.
+ ///
+ /// This method will first check for enums by name. If none exist, then it will check the value lookup dictionary
+ /// of enumeration values converted to string. This combines factories for name and value
+ /// into a single method to avoid having to worry about the right one to use.
+ /// In this library we typically represent the XML value as the property which is also a string,
+ /// but we also in some places will relay on name, and we want to support both.
+ ///
+ public static TEnum? TryParse(string? value)
{
- if (value is not null)
- return FromValueOptions.Value.TryGetValue(value, out result);
-
- result = default;
- return false;
+ return value is not null
+ ? NameLookup.Value.TryGetValue(value, out var named)
+ ? named
+ : ValueLookup.Value.GetValueOrDefault(value)
+ : null;
}
///
- public override string ToString() => Value.ToString();
+ public sealed override string ToString() => Value.ToString()!;
///
public override bool Equals(object? obj)
@@ -367,8 +285,8 @@ public virtual bool Equals(LogixEnum? other)
///
/// An value to compare to this instance.
/// A signed number indicating the relative values of this instance and .
- public virtual int CompareTo(LogixEnum other) =>
- Value.CompareTo(other.Value);
+ public virtual int CompareTo(LogixEnum? other) =>
+ other is not null ? Value.CompareTo(other.Value) : -1;
///
/// Compares this instance to a specified and returns an indication if
@@ -411,40 +329,17 @@ public virtual int CompareTo(LogixEnum other) =>
public static bool operator >=(LogixEnum left, LogixEnum right) =>
left.CompareTo(right) >= 0;
-
///
/// Implicitly converts the provided to the underlying value.
///
- /// The enumeration type.
+ /// The enumeration type.
/// A value representing the enumeration
- public static implicit operator TValue(LogixEnum logixEnum) => logixEnum.Value;
-
+ public static implicit operator TValue(LogixEnum enumeration) => enumeration.Value;
///
/// Implicitly converts the provided value to a .
///
/// The enumeration value.
/// A enumeration value representing the value type.
- public static explicit operator LogixEnum(TValue value) => FromValue(value);
-
-
- /*private static TEnum[] GetOptions()
- {
- var baseType = typeof(TEnum);
-
- return Assembly.GetAssembly(baseType)
- .GetTypes()
- .Where(t => baseType.IsAssignableFrom(t))
- .SelectMany(GetFieldsOfType)
- .OrderBy(t => t.Name)
- .ToArray();
- }*/
-
- /*private static List GetFieldsOfType(Type type)
- {
- return type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
- .Where(f => type.IsAssignableFrom(f.FieldType))
- .Select(f => (TFieldType)f.GetValue(null))
- .ToList();
- }*/
+ public static explicit operator LogixEnum(TValue value) => Parse(value.ToString()!);
}
\ No newline at end of file
diff --git a/src/L5Sharp.Core/LogixIndex.cs b/src/L5Sharp.Core/LogixIndex.cs
index 560e9197..b62e7d63 100644
--- a/src/L5Sharp.Core/LogixIndex.cs
+++ b/src/L5Sharp.Core/LogixIndex.cs
@@ -57,7 +57,7 @@ private void AddComponent(XElement element)
///
private void AddReference(CrossReference reference)
{
- if (!References.TryAdd(reference.Key, new List { reference }))
+ if (!References.TryAdd(reference.Key, [reference]))
References[reference.Key].Add(reference);
}
@@ -91,8 +91,10 @@ private void AddReferences(XElement element)
/// components or references from the index. This is because after the object has changed we no longer have access
/// to the previous state.
///
- private void OnContentChanging(object sender, XObjectChangeEventArgs e)
+ private void OnContentChanging(object? sender, XObjectChangeEventArgs e)
{
+ if (sender is null) return;
+
if (e.ObjectChange is XObjectChange.Remove)
{
if (IsComponentElement(sender)) RemoveComponent((XElement)sender);
@@ -119,8 +121,10 @@ private void OnContentChanging(object sender, XObjectChangeEventArgs e)
/// and the element or property value is one that would refer to an indexed object, we will update the state of the index
/// to ensure consistency.
///
- private void OnContentChanged(object sender, XObjectChangeEventArgs e)
+ private void OnContentChanged(object? sender, XObjectChangeEventArgs e)
{
+ if (sender is null) return;
+
if (e.ObjectChange is XObjectChange.Add)
{
if (IsComponentElement(sender)) AddComponent((XElement)sender);
@@ -230,7 +234,7 @@ private void IndexDataTypeReferences()
{
var componentName = target.Attribute(L5XName.DataType)!.Value;
var reference = new CrossReference(target, L5XName.DataType, componentName);
- if (!References.TryAdd(reference.Key, new List { reference }))
+ if (!References.TryAdd(reference.Key, [reference]))
References[reference.Key].Add(reference);
}
}
diff --git a/src/L5Sharp.Core/LogixMember.cs b/src/L5Sharp.Core/LogixMember.cs
index 459fda68..8897926e 100644
--- a/src/L5Sharp.Core/LogixMember.cs
+++ b/src/L5Sharp.Core/LogixMember.cs
@@ -270,13 +270,13 @@ private static XElement SerializeStringMember(string name, StringType type)
///
/// Handles raising the event for the member.
///
- private void RaiseDataChanged(object sender) => DataChanged?.Invoke(sender, EventArgs.Empty);
+ private void RaiseDataChanged(object? sender) => DataChanged?.Invoke(sender, EventArgs.Empty);
///
/// Captures nested data type data change event and in turn fires local data changed event.
/// This routes/bubbles up the event sender to the root of the type/member hierarchy.
///
- private void OnDataTypeChanged(object sender, EventArgs e)
+ private void OnDataTypeChanged(object? sender, EventArgs e)
{
//If the sender is an atomic type (which is intercepted by atomic types)
//then we realize this is a value change and need to replace the member type with the new value.
diff --git a/src/L5Sharp.Core/LogixParser.cs b/src/L5Sharp.Core/LogixParser.cs
new file mode 100644
index 00000000..209f4251
--- /dev/null
+++ b/src/L5Sharp.Core/LogixParser.cs
@@ -0,0 +1,176 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Threading;
+
+namespace L5Sharp.Core;
+
+///
+/// Static class containing mappings for converting string values (XML typed value) to the strongly type object.
+///
+public static class LogixParser
+{
+ /*private static readonly Dictionary> Parsers = new()
+ {
+ //Types
+ { typeof(AtomicType), AtomicType.Parse },
+ };*/
+
+ private static readonly Lazy> Parsers = new(() =>
+ GetParsers().ToDictionary(t => t.Key, v => v.Value), LazyThreadSafetyMode.ExecutionAndPublication);
+
+ ///
+ /// Parses the provided string input to the specified type using the predefined L5X parser functions.
+ ///
+ /// The string input to parse.
+ /// The type of property to return.
+ /// The resulting parsed value.
+ /// When a parser was not found to the specified type.
+ /// If the resulting parsed type does not match the specified generic type parameter.
+ public static T Parse(this string input)
+ {
+ var parser = GetParser(typeof(T));
+ var value = parser(input);
+ if (value is not T typed)
+ throw new ArgumentException($"The input '{input}' could not be parsed to type '{typeof(T)}");
+ return typed;
+ }
+
+ ///
+ /// Parses the provided string input to the specified type using the predefined L5X parser functions.
+ ///
+ /// The string input to parse.
+ /// The type to parse the input to.
+ /// The resulting parsed object value.
+ /// When a parser was not found to the specified type.
+ public static object Parse(this string input, Type type)
+ {
+ var parser = GetParser(type);
+ var value = parser(input);
+ return value;
+ }
+
+ ///
+ /// Tries to parse the input string into the specified data type.
+ ///
+ /// The data type to parse the input string into.
+ /// The input string to be parsed.
+ ///
+ /// The parsed value if the input string can be successfully parsed into the specified data type;
+ /// otherwise, the default value of the specified data type.
+ ///
+ public static T? TryParse(this string input)
+ {
+ var parser = GetTryParser(typeof(T));
+ var value = parser(input);
+ return value is T typed ? typed : default;
+ }
+
+ ///
+ /// Tries to parse the input string as the specified type.
+ ///
+ /// The string to parse.
+ /// The type to parse the string as.
+ ///
+ /// The parsed value if the input string could be parsed as the specified type; otherwise, null.
+ ///
+ public static object? TryParse(this string input, Type type)
+ {
+ var parser = GetTryParser(type);
+ return parser(input);
+ }
+
+ ///
+ /// Retrieves the parser function for the specified type.
+ ///
+ /// The type for which the parser is requested.
+ /// The parser function that can parse a string into an object of the specified type.
+ /// Thrown when no parse function has been defined for the specified type.
+ ///
+ /// Simply looks to the local parser cache and returns one if found for the specified type.
+ /// Otherwise will use the of the current type and return the ConvertFrom function
+ /// if is capable of converting from a string type.
+ ///
+ private static Func GetParser(Type type)
+ {
+ if (Parsers.Value.TryGetValue(type, out var parsers))
+ return parsers.Parse;
+
+ //The fallback is just seeing if the type has a defined converter which primitive types do.
+ var converter = TypeDescriptor.GetConverter(type);
+ if (converter.CanConvertFrom(typeof(string)))
+ return s => converter.ConvertFrom(s)!;
+
+ throw new InvalidOperationException($"No parse function has been defined for type '{type}'");
+ }
+
+ private static Func GetTryParser(Type type)
+ {
+ if (Parsers.Value.TryGetValue(type, out var parsers))
+ return parsers.TryParse;
+
+ //The fallback is just seeing if the type has a defined converter which primitive types do.
+ var converter = TypeDescriptor.GetConverter(type);
+ if (converter.CanConvertFrom(typeof(string)))
+ return s => converter.ConvertFrom(s);
+
+ throw new InvalidOperationException($"No try parse function has been defined for type '{type}'");
+ }
+
+ private static IEnumerable> GetParsers()
+ {
+ var types = typeof(LogixParser).Assembly.GetTypes().Where(IsLogixParsable);
+
+ foreach (var type in types)
+ {
+ var parse = BuildParseFunction(type);
+ var tryParse = BuildTryParseFunction(type);
+ var parsers = new Parsers(parse, tryParse);
+ yield return new KeyValuePair(type, parsers);
+ }
+ }
+
+ private static Func BuildParseFunction(Type type)
+ {
+ var method = type.GetMethod("Parse",
+ BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy, [typeof(string)]);
+
+ if (method is null)
+ throw new InvalidOperationException($"No Parse method defined for type {type.Name}.");
+
+ var parameter = Expression.Parameter(typeof(string), "s");
+ var call = Expression.Call(method, parameter);
+ var converted = Expression.Convert(call, typeof(object));
+ var lambda = Expression.Lambda>(converted, parameter);
+ return lambda.Compile();
+ }
+
+ private static Func BuildTryParseFunction(Type type)
+ {
+ var method = type.GetMethod("TryParse",
+ BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy, [typeof(string)]);
+
+ if (method is null)
+ throw new InvalidOperationException($"No TryParse method defined for type {type.Name}.");
+
+ var parameter = Expression.Parameter(typeof(string), "s");
+ var call = Expression.Call(method, parameter);
+ var converted = Expression.Convert(call, typeof(object));
+ var lambda = Expression.Lambda>(converted, parameter);
+ return lambda.Compile();
+ }
+
+ private static bool IsLogixParsable(Type type)
+ {
+ return type.GetInterfaces().Any(i =>
+ i.IsGenericType &&
+ i.GetGenericTypeDefinition() == typeof(ILogixParsable<>)
+ && i.GetGenericArguments().First() == type)
+ && type is { IsPublic: true };
+ }
+}
+
+internal record Parsers(Func Parse, Func TryParse);
\ No newline at end of file
diff --git a/src/L5Sharp.Core/LogixType.cs b/src/L5Sharp.Core/LogixType.cs
index 078415d5..55004c97 100644
--- a/src/L5Sharp.Core/LogixType.cs
+++ b/src/L5Sharp.Core/LogixType.cs
@@ -108,7 +108,7 @@ public override bool Equals(object? obj)
///
/// The objet initiating the data changed event. This could be this object, or a descendent
/// member or type in the data hierarchy.
- protected void RaiseDataChanged(object sender) => DataChanged?.Invoke(sender, EventArgs.Empty);
+ protected void RaiseDataChanged(object? sender) => DataChanged?.Invoke(sender, EventArgs.Empty);
///
public override string ToString() => Name;
diff --git a/src/L5Sharp.Core/Types/ArrayType.cs b/src/L5Sharp.Core/Types/ArrayType.cs
index 0cb8c357..0aafe216 100644
--- a/src/L5Sharp.Core/Types/ArrayType.cs
+++ b/src/L5Sharp.Core/Types/ArrayType.cs
@@ -277,7 +277,7 @@ protected void SetIndex(string index, TLogixType value) where TLogix
///
/// This method needs to be attached to each member of the type to enable the bubbling up of nested member data changed events.
///
- private void OnMemberDataChanged(object sender, EventArgs e) => RaiseDataChanged(sender);
+ private void OnMemberDataChanged(object? sender, EventArgs e) => RaiseDataChanged(sender);
}
///
diff --git a/src/L5Sharp.Core/Types/Atomic.cs b/src/L5Sharp.Core/Types/Atomic.cs
deleted file mode 100644
index d69864ee..00000000
--- a/src/L5Sharp.Core/Types/Atomic.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace L5Sharp.Core;
-
-///
-/// A static factory for objects.
-///
-public static class Atomic
-{
- private static readonly Dictionary> Atomics = new()
- {
- { nameof(BOOL), BOOL.Parse },
- { "BIT", BOOL.Parse },
- { nameof(SINT), SINT.Parse },
- { nameof(INT), INT.Parse },
- { nameof(DINT), DINT.Parse },
- { nameof(LINT), LINT.Parse },
- { nameof(REAL), REAL.Parse },
- { nameof(LREAL), LREAL.Parse },
- { nameof(USINT), USINT.Parse },
- { nameof(UINT), UINT.Parse },
- { nameof(UDINT), UDINT.Parse },
- { nameof(ULINT), ULINT.Parse }
- };
-
- ///
- /// Parses the provided string value into an atomic type value.
- ///
- /// The value of the atomic type.
- /// A representing the value and format of the provided value.
- /// value does not have a valid format to be parsed as an atomic type.
- public static AtomicType Parse(string value)
- {
- return value.IsEquivalent("true") ? new BOOL(true)
- : value.IsEquivalent("false") ? new BOOL()
- : Radix.Infer(value).Parse(value);
- }
-
- ///
- /// Parses the provided string value into an atomic type value.
- ///
- /// The value of the atomic type.
- /// A representing the value and format of the provided value.
- /// value does not have a valid format to be parsed as an atomic type.
- public static TAtomic Parse(string value) where TAtomic : AtomicType
- {
- var atomic = value.IsEquivalent("true") ? new BOOL(true)
- : value.IsEquivalent("false") ? new BOOL()
- : Radix.Infer(value).Parse(value);
- return (TAtomic)Convert.ChangeType(atomic, typeof(TAtomic));
- }
-
- ///
- /// Parses the provided string value into the atomic type value specified by name.
- ///
- /// The name of the atomic type.
- /// The value of the atomic type.
- /// A representing the value and format of the provided value.
- /// name does not represent a valid atomic type.
- /// value does not have a valid format to be parsed as the specified atomic type.
- public static AtomicType Parse(string name, string value)
- {
- if (!Atomics.ContainsKey(name))
- throw new ArgumentException($"The type name '{name}' is not a valid {typeof(AtomicType)}");
-
- return Atomics[name].Invoke(value);
- }
-
- ///
- /// Attempts to parse the provided string input as an atomic type value with the inferred radix format.
- ///
- /// A string representing an atomic value to parse.
- /// If the parsed successfully, then the resulting value;
- /// Otherwise, null.
- /// true if the string input was parsed as an atomic type; Otherwise, false.
- public static bool TryParse(string value, out AtomicType? atomicType)
- {
- if (value.IsEquivalent("true") || value.IsEquivalent("false"))
- {
- atomicType = new BOOL(bool.Parse(value));
- return true;
- }
-
- if (Radix.TryInfer(value, out var radix) && radix is not null)
- {
- atomicType = radix.Parse(value);
- return true;
- }
-
- atomicType = default;
- return false;
- }
-
- ///
- /// Returns indication to whether the provided type name is the name of an atomic type.
- ///
- /// The type name to test.
- /// true if name is the name of any atomic type; otherwise, false.
- public static bool IsAtomic(string name) => Atomics.ContainsKey(name);
-}
\ No newline at end of file
diff --git a/src/L5Sharp.Core/Types/AtomicType.cs b/src/L5Sharp.Core/Types/AtomicType.cs
index aa907d19..25e13699 100644
--- a/src/L5Sharp.Core/Types/AtomicType.cs
+++ b/src/L5Sharp.Core/Types/AtomicType.cs
@@ -24,7 +24,7 @@ namespace L5Sharp.Core;
/// See
/// `Logix 5000 Controllers Import/Export` for more information.
///
-public abstract class AtomicType : LogixType
+public abstract class AtomicType : LogixType, ILogixParsable
{
///
public sealed override DataTypeFamily Family => DataTypeFamily.None;
@@ -52,6 +52,64 @@ public override IEnumerable Members
///
/// A representing the format of the atomic type value.
public abstract Radix Radix { get; }
+
+ ///
+ /// Parses the provided string value into an atomic type value.
+ ///
+ /// The string to parse.
+ /// An representing the parsed value and format of the provided string.
+ /// value does not have a valid Radix format to be parsed as an
+ /// atomic type.
+ public static AtomicType Parse(string value)
+ {
+ return value.IsEquivalent("true") ? new BOOL(true)
+ : value.IsEquivalent("false") ? new BOOL()
+ : Radix.Infer(value).ParseValue(value);
+ }
+
+ ///
+ /// Parses the provided string value into the atomic type value specified by name.
+ ///
+ /// The name of the atomic type.
+ /// The string value to parse.
+ /// An representing the parsed value and format of the provided string.
+ /// name does not represent a valid atomic type.
+ /// value does not have a valid format to be parsed as the specified atomic type.
+ public static AtomicType Parse(string name, string value)
+ {
+ return name switch
+ {
+ nameof(BOOL) => BOOL.Parse(value),
+ "BIT" => BOOL.Parse(value),
+ nameof(SINT) => SINT.Parse(value),
+ nameof(INT) => INT.Parse(value),
+ nameof(DINT) => DINT.Parse(value),
+ nameof(LINT) => LINT.Parse(value),
+ nameof(REAL) => REAL.Parse(value),
+ nameof(USINT) => USINT.Parse(value),
+ nameof(UINT) => UINT.Parse(value),
+ nameof(UDINT) => UDINT.Parse(value),
+ nameof(ULINT) => ULINT.Parse(value),
+ nameof(LREAL) => LREAL.Parse(value),
+ _ => throw new ArgumentException($"The type name '{name}' is not a valid {typeof(AtomicType)}")
+ };
+ }
+
+ ///
+ /// Tries to parse the provided string value into an atomic type value.
+ ///
+ /// The string to parse.
+ /// An representing the parsed value if successful; Otherwise, null.
+ public static AtomicType? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (value.IsEquivalent("true")) return new BOOL(true);
+ if (value.IsEquivalent("false")) return new BOOL(false);
+
+ return Radix.TryInfer(value, out var radix) ? radix.ParseValue(value) : default;
+ }
///
/// Returns the value as an array of values.
@@ -63,14 +121,14 @@ public override IEnumerable Members
/// Return the atomic value formatted using the current format.
///
/// A representing the formatted atomic value.
- public override string ToString() => Radix.Format(this);
+ public override string ToString() => Radix.FormatValue(this);
///
/// Returns the atomic value formatted in the specified format.
///
/// The radix format.
/// A representing the formatted atomic value.
- public string ToString(Radix radix) => radix.Format(this);
+ public string ToString(Radix radix) => radix.FormatValue(this);
///
/// Serialized the atomic type as the DataValue .
@@ -91,5 +149,5 @@ public override XElement Serialize()
///
/// The member sending the data changed event.
/// The event args.
- protected virtual void OnMemberDataChanged(object sender, EventArgs e) => RaiseDataChanged(sender);
+ protected virtual void OnMemberDataChanged(object? sender, EventArgs e) => RaiseDataChanged(sender);
}
\ No newline at end of file
diff --git a/src/L5Sharp.Core/Types/Atomics/BOOL.cs b/src/L5Sharp.Core/Types/Atomics/BOOL.cs
index bb9bceff..7991c6d2 100644
--- a/src/L5Sharp.Core/Types/Atomics/BOOL.cs
+++ b/src/L5Sharp.Core/Types/Atomics/BOOL.cs
@@ -8,7 +8,7 @@ namespace L5Sharp.Core;
/// Represents a BOOL Logix atomic data type, or a type analogous to a . This object is meant
/// to wrap the DataValue or DataValueMember data for the L5X tag data structure.
///
-public sealed class BOOL : AtomicType, IComparable, IConvertible
+public sealed class BOOL : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly bool _value;
@@ -30,7 +30,7 @@ public BOOL(bool value)
_value = value;
Radix = Radix.Decimal;
}
-
+
///
/// Creates a new with the provided value.
///
@@ -108,12 +108,12 @@ public override bool Equals(object? obj)
public override int GetHashCode() => _value.GetHashCode();
///
- /// Parses the provided string value to a new .
+ /// Parses the provided string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
/// The format can not be inferred from value.
- public static BOOL Parse(string value)
+ public new static BOOL Parse(string value)
{
if (bool.TryParse(value, out var result))
return new BOOL(result);
@@ -126,12 +126,33 @@ public static BOOL Parse(string value)
return new BOOL();
default:
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (bool)Convert.ChangeType(atomic, typeof(bool));
return new BOOL(converted, radix);
}
}
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static BOOL? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (bool.TryParse(value, out var primitive))
+ return new BOOL(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (bool)Convert.ChangeType(parsed, typeof(bool));
+ return new BOOL(converted, radix);
+ }
+
// Contains the implicit .NET conversions for the type.
#region Conversions
@@ -190,46 +211,46 @@ public static BOOL Parse(string value)
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value;
///
- byte IConvertible.ToByte(IFormatProvider provider) => _value ? (byte)1 : default;
+ byte IConvertible.ToByte(IFormatProvider? provider) => _value ? (byte)1 : default;
///
- char IConvertible.ToChar(IFormatProvider provider) =>
+ char IConvertible.ToChar(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Char)} is not supported.");
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider) =>
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(DateTime)} is not supported.");
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value ? (double)1 : default;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value ? (double)1 : default;
///
- short IConvertible.ToInt16(IFormatProvider provider) => _value ? (short)1 : default;
+ short IConvertible.ToInt16(IFormatProvider? provider) => _value ? (short)1 : default;
///
- int IConvertible.ToInt32(IFormatProvider provider) => _value ? 1 : default;
+ int IConvertible.ToInt32(IFormatProvider? provider) => _value ? 1 : default;
///
- long IConvertible.ToInt64(IFormatProvider provider) => _value ? (long)1 : default;
+ long IConvertible.ToInt64(IFormatProvider? provider) => _value ? (long)1 : default;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => _value ? (sbyte)1 : default;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => _value ? (sbyte)1 : default;
///
- float IConvertible.ToSingle(IFormatProvider provider) => _value ? (float)1 : default;
+ float IConvertible.ToSingle(IFormatProvider? provider) => _value ? (float)1 : default;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
@@ -259,13 +280,13 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => _value ? (ushort)1 : default;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => _value ? (ushort)1 : default;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => _value ? (uint)1 : default;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => _value ? (uint)1 : default;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => _value ? (ulong)1 : default;
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => _value ? (ulong)1 : default;
///
/// Converts the current atomic type to the specified atomic type.
diff --git a/src/L5Sharp.Core/Types/Atomics/DINT.cs b/src/L5Sharp.Core/Types/Atomics/DINT.cs
index 31ac9a8e..7658f797 100644
--- a/src/L5Sharp.Core/Types/Atomics/DINT.cs
+++ b/src/L5Sharp.Core/Types/Atomics/DINT.cs
@@ -5,7 +5,7 @@ namespace L5Sharp.Core;
///
/// Represents a DINT Logix atomic data type, or a type analogous to a .
///
-public sealed class DINT : AtomicType, IComparable, IConvertible
+public sealed class DINT : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly int _value;
@@ -108,23 +108,43 @@ public override bool Equals(object? obj)
public override int GetHashCode() => _value.GetHashCode();
///
- /// Parses the provided string value to a new .
+ /// Parses a string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
- /// The converted value returned null.
/// The format can not be inferred from value.
- public static DINT Parse(string value)
+ public new static DINT Parse(string value)
{
if (int.TryParse(value, out var result))
return new DINT(result);
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (int)Convert.ChangeType(atomic, typeof(int));
return new DINT(converted, radix);
}
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static DINT? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (int.TryParse(value, out var primitive))
+ return new DINT(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (int)Convert.ChangeType(parsed, typeof(int));
+ return new DINT(converted, radix);
+ }
+
///
/// Executes the logic to update the atomic value and forward the data changed event up the type/member hierarchy.
///
@@ -138,9 +158,9 @@ public static DINT Parse(string value)
/// member (Tag, DataValue, DataValueMember) so that it can replace it's data type with the new atomic value. This is
/// captured in .
///
- protected override void OnMemberDataChanged(object sender, EventArgs e)
+ protected override void OnMemberDataChanged(object? sender, EventArgs e)
{
- var member = (LogixMember)sender;
+ if (sender is not LogixMember member) return;
var bit = int.Parse(member.Name);
var value = member.DataType.As();
var result = value ? _value | 1 << bit : _value & ~(1 << bit);
@@ -191,45 +211,45 @@ protected override void OnMemberDataChanged(object sender, EventArgs e)
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value != 0;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value != 0;
///
- byte IConvertible.ToByte(IFormatProvider provider) => (byte)_value;
+ byte IConvertible.ToByte(IFormatProvider? provider) => (byte)_value;
///
- char IConvertible.ToChar(IFormatProvider provider) => (char)_value;
+ char IConvertible.ToChar(IFormatProvider? provider) => (char)_value;
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider) =>
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(DateTime)} is not supported.");
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value;
///
- short IConvertible.ToInt16(IFormatProvider provider) => (short)_value;
+ short IConvertible.ToInt16(IFormatProvider? provider) => (short)_value;
///
- int IConvertible.ToInt32(IFormatProvider provider) => _value;
+ int IConvertible.ToInt32(IFormatProvider? provider) => _value;
///
- long IConvertible.ToInt64(IFormatProvider provider) => _value;
+ long IConvertible.ToInt64(IFormatProvider? provider) => _value;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)_value;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => (sbyte)_value;
///
- float IConvertible.ToSingle(IFormatProvider provider) => _value;
+ float IConvertible.ToSingle(IFormatProvider? provider) => _value;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
@@ -259,13 +279,13 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => (ushort)_value;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => (ushort)_value;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => (uint)_value;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => (uint)_value;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => (ulong)_value;
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => (ulong)_value;
///
/// Converts the current atomic type to the specified atomic type.
diff --git a/src/L5Sharp.Core/Types/Atomics/INT.cs b/src/L5Sharp.Core/Types/Atomics/INT.cs
index 7ec313a7..fdbbef43 100644
--- a/src/L5Sharp.Core/Types/Atomics/INT.cs
+++ b/src/L5Sharp.Core/Types/Atomics/INT.cs
@@ -5,7 +5,7 @@ namespace L5Sharp.Core;
///
/// Represents a INT Logix atomic data type, or a type analogous to a .
///
-public sealed class INT : AtomicType, IComparable, IConvertible
+public sealed class INT : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly short _value;
@@ -116,28 +116,49 @@ public INT Set(int bit, BOOL value)
throw new ArgumentNullException(nameof(value));
if (bit is < 0 or >= 16)
- throw new ArgumentOutOfRangeException($"The bit {bit} is out of range for type {Name}", nameof(bit));
+ throw new ArgumentOutOfRangeException(nameof(bit), $"The bit {bit} is out of range for type {Name}");
var result = (short)(value ? _value | (short)(1 << bit) : _value & (short)~(1 << bit));
return new INT(result, Radix);
}
-
+
///
- /// Parses the provided string value to a new .
+ /// Parses a string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
/// The format can not be inferred from value.
- public static INT Parse(string value)
+ public new static INT Parse(string value)
{
if (short.TryParse(value, out var result))
return new INT(result);
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (short)Convert.ChangeType(atomic, typeof(short));
return new INT(converted, radix);
}
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static INT? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (short.TryParse(value, out var primitive))
+ return new INT(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (short)Convert.ChangeType(parsed, typeof(short));
+ return new INT(converted, radix);
+ }
///
/// Executes the logic to update the atomic value and forward the data changed event up the type/member hierarchy.
@@ -152,9 +173,9 @@ public static INT Parse(string value)
/// member (Tag, DataValue, DataValueMember) so that it can replace it's data type with the new atomic value. This is
/// captured in .
///
- protected override void OnMemberDataChanged(object sender, EventArgs e)
+ protected override void OnMemberDataChanged(object? sender, EventArgs e)
{
- var member = (LogixMember)sender;
+ if (sender is not LogixMember member) return;
var bit = int.Parse(member.Name);
var value = member.DataType.As();
var result = (short)(value ? _value | (short)(1 << bit) : _value & (short)~(1 << bit));
@@ -276,45 +297,45 @@ protected override void OnMemberDataChanged(object sender, EventArgs e)
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value != 0;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value != 0;
///
- byte IConvertible.ToByte(IFormatProvider provider) => (byte)_value;
+ byte IConvertible.ToByte(IFormatProvider? provider) => (byte)_value;
///
- char IConvertible.ToChar(IFormatProvider provider) => (char)_value;
+ char IConvertible.ToChar(IFormatProvider? provider) => (char)_value;
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider) =>
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(DateTime)} is not supported.");
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value;
///
- short IConvertible.ToInt16(IFormatProvider provider) => _value;
+ short IConvertible.ToInt16(IFormatProvider? provider) => _value;
///
- int IConvertible.ToInt32(IFormatProvider provider) => _value;
+ int IConvertible.ToInt32(IFormatProvider? provider) => _value;
///
- long IConvertible.ToInt64(IFormatProvider provider) => _value;
+ long IConvertible.ToInt64(IFormatProvider? provider) => _value;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)_value;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => (sbyte)_value;
///
- float IConvertible.ToSingle(IFormatProvider provider) => _value;
+ float IConvertible.ToSingle(IFormatProvider? provider) => _value;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
@@ -344,13 +365,13 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => (ushort)_value;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => (ushort)_value;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => (uint)_value;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => (uint)_value;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => (ulong)_value;
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => (ulong)_value;
///
/// Converts the current atomic type to the specified atomic type.
diff --git a/src/L5Sharp.Core/Types/Atomics/LINT.cs b/src/L5Sharp.Core/Types/Atomics/LINT.cs
index 52370963..f9d43902 100644
--- a/src/L5Sharp.Core/Types/Atomics/LINT.cs
+++ b/src/L5Sharp.Core/Types/Atomics/LINT.cs
@@ -5,7 +5,7 @@ namespace L5Sharp.Core;
///
/// Represents a LINT Logix atomic data type, or a type analogous to a .
///
-public sealed class LINT : AtomicType, IComparable, IConvertible
+public sealed class LINT : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly long _value;
@@ -103,21 +103,42 @@ public override bool Equals(object? obj)
public override int GetHashCode() => _value.GetHashCode();
///
- /// Parses the provided string value to a new .
+ /// Parses a string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
/// The format can not be inferred from value.
- public static LINT Parse(string value)
+ public new static LINT Parse(string value)
{
if (long.TryParse(value, out var result))
return new LINT(result);
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (long)Convert.ChangeType(atomic, typeof(long));
return new LINT(converted, radix);
}
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static LINT? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (long.TryParse(value, out var primitive))
+ return new LINT(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (long)Convert.ChangeType(parsed, typeof(long));
+ return new LINT(converted, radix);
+ }
///
/// Executes the logic to update the atomic value and forward the data changed event up the type/member hierarchy.
@@ -132,9 +153,9 @@ public static LINT Parse(string value)
/// member (Tag, DataValue, DataValueMember) so that it can replace it's data type with the new atomic value. This is
/// captured in .
///
- protected override void OnMemberDataChanged(object sender, EventArgs e)
+ protected override void OnMemberDataChanged(object? sender, EventArgs e)
{
- var member = (LogixMember)sender;
+ if (sender is not LogixMember member) return;
var bit = int.Parse(member.Name);
var value = member.DataType.As();
var result = value ? _value | (long)1 << bit : _value & ~(1 << bit);
@@ -185,16 +206,16 @@ protected override void OnMemberDataChanged(object sender, EventArgs e)
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value != 0;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value != 0;
///
- byte IConvertible.ToByte(IFormatProvider provider) => (byte)_value;
+ byte IConvertible.ToByte(IFormatProvider? provider) => (byte)_value;
///
- char IConvertible.ToChar(IFormatProvider provider) => (char)_value;
+ char IConvertible.ToChar(IFormatProvider? provider) => (char)_value;
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider)
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider)
{
var milliseconds = _value / 1000;
var microseconds = _value % 1000;
@@ -203,32 +224,32 @@ DateTime IConvertible.ToDateTime(IFormatProvider provider)
}
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value;
///
- short IConvertible.ToInt16(IFormatProvider provider) => (short)_value;
+ short IConvertible.ToInt16(IFormatProvider? provider) => (short)_value;
///
- int IConvertible.ToInt32(IFormatProvider provider) => (int)_value;
+ int IConvertible.ToInt32(IFormatProvider? provider) => (int)_value;
///
- long IConvertible.ToInt64(IFormatProvider provider) => _value;
+ long IConvertible.ToInt64(IFormatProvider? provider) => _value;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)_value;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => (sbyte)_value;
///
- float IConvertible.ToSingle(IFormatProvider provider) => _value;
+ float IConvertible.ToSingle(IFormatProvider? provider) => _value;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
@@ -258,13 +279,13 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => (ushort)_value;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => (ushort)_value;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => (uint)_value;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => (uint)_value;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => (ulong)_value;
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => (ulong)_value;
///
/// Converts the current atomic type to the specified atomic type.
diff --git a/src/L5Sharp.Core/Types/Atomics/LREAL.cs b/src/L5Sharp.Core/Types/Atomics/LREAL.cs
index c12a85c1..47c17ef1 100644
--- a/src/L5Sharp.Core/Types/Atomics/LREAL.cs
+++ b/src/L5Sharp.Core/Types/Atomics/LREAL.cs
@@ -7,7 +7,7 @@ namespace L5Sharp.Core;
///
/// Represents a LREAL Logix atomic data type, or a type analogous to a .
///
-public sealed class LREAL : AtomicType, IComparable, IConvertible
+public sealed class LREAL : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly double _value;
@@ -96,25 +96,48 @@ public override bool Equals(object? obj)
///
public override int GetHashCode() => _value.GetHashCode();
-
+
///
- /// Parses the provided string value to a new .
+ /// Parses a string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
/// The format can not be inferred from value.
- public static LREAL Parse(string value)
+ public new static LREAL Parse(string value)
{
- if (value.Contains("QNAN")) return new LREAL(float.NaN);
-
+ if (value.Contains("QNAN")) return new LREAL(double.NaN);
+
if (double.TryParse(value, out var result))
return new LREAL(result);
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (double)Convert.ChangeType(atomic, typeof(double));
return new LREAL(converted, radix);
}
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static LREAL? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (value.Contains("QNAN")) return new LREAL(double.NaN);
+
+ if (double.TryParse(value, out var primitive))
+ return new LREAL(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (double)Convert.ChangeType(parsed, typeof(double));
+ return new LREAL(converted, radix);
+ }
// Contains the implicit .NET conversions for the type.
@@ -160,46 +183,46 @@ public static LREAL Parse(string value)
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value != 0;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value != 0;
///
- byte IConvertible.ToByte(IFormatProvider provider) => (byte)_value;
+ byte IConvertible.ToByte(IFormatProvider? provider) => (byte)_value;
///
- char IConvertible.ToChar(IFormatProvider provider) =>
+ char IConvertible.ToChar(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Char)} is not supported.");
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider) =>
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(DateTime)} is not supported.");
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value;
///
- short IConvertible.ToInt16(IFormatProvider provider) => (short)_value;
+ short IConvertible.ToInt16(IFormatProvider? provider) => (short)_value;
///
- int IConvertible.ToInt32(IFormatProvider provider) => (int)_value;
+ int IConvertible.ToInt32(IFormatProvider? provider) => (int)_value;
///
- long IConvertible.ToInt64(IFormatProvider provider) => (long)_value;
+ long IConvertible.ToInt64(IFormatProvider? provider) => (long)_value;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)_value;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => (sbyte)_value;
///
- float IConvertible.ToSingle(IFormatProvider provider) => (float)_value;
+ float IConvertible.ToSingle(IFormatProvider? provider) => (float)_value;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
@@ -229,13 +252,13 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => (ushort)_value;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => (ushort)_value;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => (uint)_value;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => (uint)_value;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => (ulong)_value;
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => (ulong)_value;
///
/// Converts the current atomic type to the specified atomic type.
diff --git a/src/L5Sharp.Core/Types/Atomics/REAL.cs b/src/L5Sharp.Core/Types/Atomics/REAL.cs
index 1cc49f96..b1ca5c88 100644
--- a/src/L5Sharp.Core/Types/Atomics/REAL.cs
+++ b/src/L5Sharp.Core/Types/Atomics/REAL.cs
@@ -7,7 +7,7 @@ namespace L5Sharp.Core;
///
/// Represents a REAL Logix atomic data type, or a type analogous to a .
///
-public sealed class REAL : AtomicType, IComparable, IConvertible
+public sealed class REAL : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly float _value;
@@ -98,24 +98,47 @@ public override bool Equals(object? obj)
public override int GetHashCode() => _value.GetHashCode();
///
- /// Parses the provided string value to a new .
+ /// Parses a string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
/// The format can not be inferred from value.
- public static REAL Parse(string value)
+ public new static REAL Parse(string value)
{
if (value.Contains("QNAN")) return new REAL(float.NaN);
-
+
if (float.TryParse(value, out var result))
return new REAL(result);
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (float)Convert.ChangeType(atomic, typeof(float));
return new REAL(converted, radix);
}
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static REAL? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (value.Contains("QNAN")) return new REAL(float.NaN);
+
+ if (float.TryParse(value, out var primitive))
+ return new REAL(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (float)Convert.ChangeType(parsed, typeof(float));
+ return new REAL(converted, radix);
+ }
+
// Contains the implicit .NET conversions for the type.
#region Conversions
@@ -160,46 +183,46 @@ public static REAL Parse(string value)
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value != 0;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value != 0;
///
- byte IConvertible.ToByte(IFormatProvider provider) => (byte)_value;
+ byte IConvertible.ToByte(IFormatProvider? provider) => (byte)_value;
///
- char IConvertible.ToChar(IFormatProvider provider) =>
+ char IConvertible.ToChar(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Char)} is not supported.");
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider) =>
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(DateTime)} is not supported.");
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value;
///
- short IConvertible.ToInt16(IFormatProvider provider) => (short)_value;
+ short IConvertible.ToInt16(IFormatProvider? provider) => (short)_value;
///
- int IConvertible.ToInt32(IFormatProvider provider) => (int)_value;
+ int IConvertible.ToInt32(IFormatProvider? provider) => (int)_value;
///
- long IConvertible.ToInt64(IFormatProvider provider) => (long)_value;
+ long IConvertible.ToInt64(IFormatProvider? provider) => (long)_value;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)_value;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => (sbyte)_value;
///
- float IConvertible.ToSingle(IFormatProvider provider) => _value;
+ float IConvertible.ToSingle(IFormatProvider? provider) => _value;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
@@ -229,13 +252,13 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => (ushort)_value;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => (ushort)_value;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => (uint)_value;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => (uint)_value;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => (ulong)_value;
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => (ulong)_value;
///
/// Converts the current atomic type to the specified atomic type.
diff --git a/src/L5Sharp.Core/Types/Atomics/SINT.cs b/src/L5Sharp.Core/Types/Atomics/SINT.cs
index 6cf23da6..3ece9fb7 100644
--- a/src/L5Sharp.Core/Types/Atomics/SINT.cs
+++ b/src/L5Sharp.Core/Types/Atomics/SINT.cs
@@ -5,7 +5,7 @@ namespace L5Sharp.Core;
///
/// Represents a SINT Logix atomic data type, or a type analogous to .
///
-public sealed class SINT : AtomicType, IComparable, IConvertible
+public sealed class SINT : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly sbyte _value;
@@ -97,28 +97,49 @@ public override bool Equals(object? obj)
}
///
- public override byte[] GetBytes() => unchecked(new[] { (byte)_value });
+ public override byte[] GetBytes() => unchecked( [(byte)_value]);
///
public override int GetHashCode() => _value.GetHashCode();
-
+
///
- /// Parses the provided string value to a new .
+ /// Parses a string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
/// The format can not be inferred from value.
- public static SINT Parse(string value)
+ public new static SINT Parse(string value)
{
if (sbyte.TryParse(value, out var result))
return new SINT(result);
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (sbyte)Convert.ChangeType(atomic, typeof(sbyte));
return new SINT(converted, radix);
}
-
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static SINT? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (sbyte.TryParse(value, out var primitive))
+ return new SINT(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (sbyte)Convert.ChangeType(parsed, typeof(sbyte));
+ return new SINT(converted, radix);
+ }
+
///
/// Executes the logic to update the atomic value and forward the data changed event up the type/member hierarchy.
///
@@ -132,15 +153,15 @@ public static SINT Parse(string value)
/// member (Tag, DataValue, DataValueMember) so that it can replace it's data type with the new atomic value. This is
/// captured in .
///
- protected override void OnMemberDataChanged(object sender, EventArgs e)
+ protected override void OnMemberDataChanged(object? sender, EventArgs e)
{
- var member = (LogixMember)sender;
+ if (sender is not LogixMember member) return;
var bit = int.Parse(member.Name);
var value = member.DataType.As();
var result = (sbyte)(value ? _value | (sbyte)(1 << bit) : _value & (sbyte)~(1 << bit));
RaiseDataChanged(new SINT(result, Radix));
}
-
+
// Contains the implicit .NET conversions for the type.
#region Conversions
@@ -174,59 +195,59 @@ protected override void OnMemberDataChanged(object sender, EventArgs e)
public static implicit operator string(SINT value) => value.ToString();
#endregion
-
+
// Contains the IConvertible implementation for the type. I am explicitly implementing this interface for each
// atomic type to avoid polluting the API, and to have the implementation as performant as possible.
// To perform conversion, use the recommended .NET Convert.ChangeType() method and specify the target type.
-
+
#region Convertible
///
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value != 0;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value != 0;
///
- byte IConvertible.ToByte(IFormatProvider provider) => (byte)_value;
+ byte IConvertible.ToByte(IFormatProvider? provider) => (byte)_value;
///
- char IConvertible.ToChar(IFormatProvider provider) => (char)_value;
+ char IConvertible.ToChar(IFormatProvider? provider) => (char)_value;
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider) =>
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(DateTime)} is not supported.");
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value;
///
- short IConvertible.ToInt16(IFormatProvider provider) => _value;
+ short IConvertible.ToInt16(IFormatProvider? provider) => _value;
///
- int IConvertible.ToInt32(IFormatProvider provider) => _value;
+ int IConvertible.ToInt32(IFormatProvider? provider) => _value;
///
- long IConvertible.ToInt64(IFormatProvider provider) => _value;
+ long IConvertible.ToInt64(IFormatProvider? provider) => _value;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => _value;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => _value;
///
- float IConvertible.ToSingle(IFormatProvider provider) => _value;
+ float IConvertible.ToSingle(IFormatProvider? provider) => _value;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
-
+
return Type.GetTypeCode(conversionType) switch
{
TypeCode.Boolean => convertible.ToBoolean(provider),
@@ -253,14 +274,14 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => (ushort)_value;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => (ushort)_value;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => (uint)_value;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => (uint)_value;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => (ulong)_value;
-
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => (ulong)_value;
+
///
/// Converts the current atomic type to the specified atomic type.
///
@@ -291,7 +312,7 @@ private object ToAtomic(Type conversionType)
return new UDINT((uint)_value);
if (conversionType == typeof(ULINT))
return new ULINT((ulong)_value);
-
+
throw new InvalidCastException($"Cannot convert from {GetType().Name} to {conversionType.Name}.");
}
diff --git a/src/L5Sharp.Core/Types/Atomics/UDINT.cs b/src/L5Sharp.Core/Types/Atomics/UDINT.cs
index a9a5cf84..54fa3671 100644
--- a/src/L5Sharp.Core/Types/Atomics/UDINT.cs
+++ b/src/L5Sharp.Core/Types/Atomics/UDINT.cs
@@ -5,7 +5,7 @@ namespace L5Sharp.Core;
///
/// Represents a UDINT Logix atomic data type, or a type analogous to a .
///
-public sealed class UDINT : AtomicType, IComparable, IConvertible
+public sealed class UDINT : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly uint _value;
@@ -107,21 +107,42 @@ public override bool Equals(object? obj)
public override int GetHashCode() => _value.GetHashCode();
///
- /// Parses the provided string value to a new .
+ /// Parses a string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
/// The format can not be inferred from value.
- public static UDINT Parse(string value)
+ public new static UDINT Parse(string value)
{
if (uint.TryParse(value, out var result))
return new UDINT(result);
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (uint)Convert.ChangeType(atomic, typeof(uint));
return new UDINT(converted, radix);
}
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static UDINT? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (uint.TryParse(value, out var primitive))
+ return new UDINT(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (uint)Convert.ChangeType(parsed, typeof(uint));
+ return new UDINT(converted, radix);
+ }
///
/// Executes the logic to update the atomic value and forward the data changed event up the type/member hierarchy.
@@ -136,9 +157,9 @@ public static UDINT Parse(string value)
/// member (Tag, DataValue, DataValueMember) so that it can replace it's data type with the new atomic value. This is
/// captured in .
///
- protected override void OnMemberDataChanged(object sender, EventArgs e)
+ protected override void OnMemberDataChanged(object? sender, EventArgs e)
{
- var member = (LogixMember)sender;
+ if (sender is not LogixMember member) return;
var bit = int.Parse(member.Name);
var value = member.DataType.As();
var result = value ? _value | (uint)(1 << bit) : _value & (uint)~(1 << bit);
@@ -189,45 +210,45 @@ protected override void OnMemberDataChanged(object sender, EventArgs e)
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value != 0;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value != 0;
///
- byte IConvertible.ToByte(IFormatProvider provider) => (byte)_value;
+ byte IConvertible.ToByte(IFormatProvider? provider) => (byte)_value;
///
- char IConvertible.ToChar(IFormatProvider provider) => (char)_value;
+ char IConvertible.ToChar(IFormatProvider? provider) => (char)_value;
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider) =>
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(DateTime)} is not supported.");
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value;
///
- short IConvertible.ToInt16(IFormatProvider provider) => (short)_value;
+ short IConvertible.ToInt16(IFormatProvider? provider) => (short)_value;
///
- int IConvertible.ToInt32(IFormatProvider provider) => (int)_value;
+ int IConvertible.ToInt32(IFormatProvider? provider) => (int)_value;
///
- long IConvertible.ToInt64(IFormatProvider provider) => _value;
+ long IConvertible.ToInt64(IFormatProvider? provider) => _value;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)_value;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => (sbyte)_value;
///
- float IConvertible.ToSingle(IFormatProvider provider) => _value;
+ float IConvertible.ToSingle(IFormatProvider? provider) => _value;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
@@ -257,13 +278,13 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => (ushort)_value;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => (ushort)_value;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => _value;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => _value;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => _value;
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => _value;
///
/// Converts the current atomic type to the specified atomic type.
diff --git a/src/L5Sharp.Core/Types/Atomics/UINT.cs b/src/L5Sharp.Core/Types/Atomics/UINT.cs
index f8e2bf11..2674e603 100644
--- a/src/L5Sharp.Core/Types/Atomics/UINT.cs
+++ b/src/L5Sharp.Core/Types/Atomics/UINT.cs
@@ -5,7 +5,7 @@ namespace L5Sharp.Core;
///
/// Represents a UINT Logix atomic data type, or a type analogous to a .
///
-public sealed class UINT : AtomicType, IComparable, IConvertible
+public sealed class UINT : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly ushort _value;
@@ -103,21 +103,42 @@ public override bool Equals(object? obj)
public override int GetHashCode() => _value.GetHashCode();
///
- /// Parses the provided string value to a new .
+ /// Parses a string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
/// The format can not be inferred from value.
- public static UINT Parse(string value)
+ public new static UINT Parse(string value)
{
if (ushort.TryParse(value, out var result))
return new UINT(result);
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (ushort)Convert.ChangeType(atomic, typeof(ushort));
return new UINT(converted, radix);
}
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static UINT? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (ushort.TryParse(value, out var primitive))
+ return new UINT(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (ushort)Convert.ChangeType(parsed, typeof(ushort));
+ return new UINT(converted, radix);
+ }
///
/// Executes the logic to update the atomic value and forward the data changed event up the type/member hierarchy.
@@ -132,9 +153,9 @@ public static UINT Parse(string value)
/// member (Tag, DataValue, DataValueMember) so that it can replace it's data type with the new atomic value. This is
/// captured in .
///
- protected override void OnMemberDataChanged(object sender, EventArgs e)
+ protected override void OnMemberDataChanged(object? sender, EventArgs e)
{
- var member = (LogixMember)sender;
+ if (sender is not LogixMember member) return;
var bit = int.Parse(member.Name);
var value = member.DataType.As();
var result = (ushort)(value ? _value | (ushort)(1 << bit) : _value & (ushort)~(1 << bit));
@@ -185,45 +206,45 @@ protected override void OnMemberDataChanged(object sender, EventArgs e)
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value != 0;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value != 0;
///
- byte IConvertible.ToByte(IFormatProvider provider) => (byte)_value;
+ byte IConvertible.ToByte(IFormatProvider? provider) => (byte)_value;
///
- char IConvertible.ToChar(IFormatProvider provider) => (char)_value;
+ char IConvertible.ToChar(IFormatProvider? provider) => (char)_value;
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider) =>
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(DateTime)} is not supported.");
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value;
///
- short IConvertible.ToInt16(IFormatProvider provider) => (short)_value;
+ short IConvertible.ToInt16(IFormatProvider? provider) => (short)_value;
///
- int IConvertible.ToInt32(IFormatProvider provider) => _value;
+ int IConvertible.ToInt32(IFormatProvider? provider) => _value;
///
- long IConvertible.ToInt64(IFormatProvider provider) => _value;
+ long IConvertible.ToInt64(IFormatProvider? provider) => _value;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)_value;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => (sbyte)_value;
///
- float IConvertible.ToSingle(IFormatProvider provider) => _value;
+ float IConvertible.ToSingle(IFormatProvider? provider) => _value;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
@@ -253,13 +274,13 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => _value;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => _value;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => _value;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => _value;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => _value;
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => _value;
///
/// Converts the current atomic type to the specified atomic type.
diff --git a/src/L5Sharp.Core/Types/Atomics/ULINT.cs b/src/L5Sharp.Core/Types/Atomics/ULINT.cs
index b887ca66..d56544de 100644
--- a/src/L5Sharp.Core/Types/Atomics/ULINT.cs
+++ b/src/L5Sharp.Core/Types/Atomics/ULINT.cs
@@ -5,7 +5,7 @@ namespace L5Sharp.Core;
///
/// Represents a ULINT Logix atomic data type, or a type analogous to a .
///
-public sealed class ULINT : AtomicType, IComparable, IConvertible
+public sealed class ULINT : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly ulong _value;
@@ -103,21 +103,42 @@ public override bool Equals(object? obj)
public override int GetHashCode() => _value.GetHashCode();
///
- /// Parses the provided string value to a new .
+ /// Parses a string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
/// The format can not be inferred from value.
- public static ULINT Parse(string value)
+ public new static ULINT Parse(string value)
{
if (ulong.TryParse(value, out var result))
return new ULINT(result);
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (ulong)Convert.ChangeType(atomic, typeof(ulong));
return new ULINT(converted, radix);
}
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static ULINT? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (ulong.TryParse(value, out var primitive))
+ return new ULINT(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (ulong)Convert.ChangeType(parsed, typeof(ulong));
+ return new ULINT(converted, radix);
+ }
///
/// Executes the logic to update the atomic value and forward the data changed event up the type/member hierarchy.
@@ -132,9 +153,9 @@ public static ULINT Parse(string value)
/// member (Tag, DataValue, DataValueMember) so that it can replace it's data type with the new atomic value. This is
/// captured in .
///
- protected override void OnMemberDataChanged(object sender, EventArgs e)
+ protected override void OnMemberDataChanged(object? sender, EventArgs e)
{
- var member = (LogixMember)sender;
+ if (sender is not LogixMember member) return;
var bit = int.Parse(member.Name);
var value = member.DataType.As();
var result = value ? _value | (ulong)1 << bit : _value & (ulong)~(1 << bit);
@@ -185,45 +206,45 @@ protected override void OnMemberDataChanged(object sender, EventArgs e)
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value != 0;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value != 0;
///
- byte IConvertible.ToByte(IFormatProvider provider) => (byte)_value;
+ byte IConvertible.ToByte(IFormatProvider? provider) => (byte)_value;
///
- char IConvertible.ToChar(IFormatProvider provider) => (char)_value;
+ char IConvertible.ToChar(IFormatProvider? provider) => (char)_value;
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider) =>
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(DateTime)} is not supported.");
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value;
///
- short IConvertible.ToInt16(IFormatProvider provider) => (short)_value;
+ short IConvertible.ToInt16(IFormatProvider? provider) => (short)_value;
///
- int IConvertible.ToInt32(IFormatProvider provider) => (int)_value;
+ int IConvertible.ToInt32(IFormatProvider? provider) => (int)_value;
///
- long IConvertible.ToInt64(IFormatProvider provider) => (long)_value;
+ long IConvertible.ToInt64(IFormatProvider? provider) => (long)_value;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)_value;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => (sbyte)_value;
///
- float IConvertible.ToSingle(IFormatProvider provider) => _value;
+ float IConvertible.ToSingle(IFormatProvider? provider) => _value;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
@@ -253,13 +274,13 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => (ushort)_value;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => (ushort)_value;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => (uint)_value;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => (uint)_value;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => _value;
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => _value;
///
/// Converts the current atomic type to the specified atomic type.
diff --git a/src/L5Sharp.Core/Types/Atomics/USINT.cs b/src/L5Sharp.Core/Types/Atomics/USINT.cs
index 05325a7b..78022a67 100644
--- a/src/L5Sharp.Core/Types/Atomics/USINT.cs
+++ b/src/L5Sharp.Core/Types/Atomics/USINT.cs
@@ -5,7 +5,7 @@ namespace L5Sharp.Core;
///
/// Represents a USINT Logix atomic data type, or a type analogous to a .
///
-public sealed class USINT : AtomicType, IComparable, IConvertible
+public sealed class USINT : AtomicType, IComparable, IConvertible, ILogixParsable
{
private readonly byte _value;
@@ -97,27 +97,48 @@ public override bool Equals(object? obj)
}
///
- public override byte[] GetBytes() => new[] { _value };
+ public override byte[] GetBytes() => [_value];
///
public override int GetHashCode() => _value.GetHashCode();
///
- /// Parses the provided string value to a new .
+ /// Parses a string into a value.
///
- /// The string value to parse.
+ /// The string to parse.
/// A representing the parsed value.
/// The format can not be inferred from value.
- public static USINT Parse(string value)
+ public new static USINT Parse(string value)
{
if (byte.TryParse(value, out var result))
return new USINT(result);
var radix = Radix.Infer(value);
- var atomic = radix.Parse(value);
+ var atomic = radix.ParseValue(value);
var converted = (byte)Convert.ChangeType(atomic, typeof(byte));
return new USINT(converted, radix);
}
+
+ ///
+ /// Tries to parse a string into a value.
+ ///
+ /// The string to parse.
+ /// The parsed value if successful; Otherwise, null.
+ public new static USINT? TryParse(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ return default;
+
+ if (byte.TryParse(value, out var primitive))
+ return new USINT(primitive);
+
+ if (!Radix.TryInfer(value, out var radix))
+ return default;
+
+ var parsed = radix.ParseValue(value);
+ var converted = (byte)Convert.ChangeType(parsed, typeof(byte));
+ return new USINT(converted, radix);
+ }
///
/// Executes the logic to update the atomic value and forward the data changed event up the type/member hierarchy.
@@ -132,9 +153,9 @@ public static USINT Parse(string value)
/// member (Tag, DataValue, DataValueMember) so that it can replace it's data type with the new atomic value. This is
/// captured in .
///
- protected override void OnMemberDataChanged(object sender, EventArgs e)
+ protected override void OnMemberDataChanged(object? sender, EventArgs e)
{
- var member = (LogixMember)sender;
+ if (sender is not LogixMember member) return;
var bit = int.Parse(member.Name);
var value = member.DataType.As();
var result = (byte)(value ? _value | (byte)(1 << bit) : _value & (byte)~(1 << bit));
@@ -185,45 +206,45 @@ protected override void OnMemberDataChanged(object sender, EventArgs e)
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
///
- bool IConvertible.ToBoolean(IFormatProvider provider) => _value != 0;
+ bool IConvertible.ToBoolean(IFormatProvider? provider) => _value != 0;
///
- byte IConvertible.ToByte(IFormatProvider provider) => _value;
+ byte IConvertible.ToByte(IFormatProvider? provider) => _value;
///
- char IConvertible.ToChar(IFormatProvider provider) => (char)_value;
+ char IConvertible.ToChar(IFormatProvider? provider) => (char)_value;
///
- DateTime IConvertible.ToDateTime(IFormatProvider provider) =>
+ DateTime IConvertible.ToDateTime(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(DateTime)} is not supported.");
///
- decimal IConvertible.ToDecimal(IFormatProvider provider) =>
+ decimal IConvertible.ToDecimal(IFormatProvider? provider) =>
throw new InvalidCastException($"Conversion from {Name} to {nameof(Decimal)} is not supported.");
///
- double IConvertible.ToDouble(IFormatProvider provider) => _value;
+ double IConvertible.ToDouble(IFormatProvider? provider) => _value;
///
- short IConvertible.ToInt16(IFormatProvider provider) => _value;
+ short IConvertible.ToInt16(IFormatProvider? provider) => _value;
///
- int IConvertible.ToInt32(IFormatProvider provider) => _value;
+ int IConvertible.ToInt32(IFormatProvider? provider) => _value;
///
- long IConvertible.ToInt64(IFormatProvider provider) => _value;
+ long IConvertible.ToInt64(IFormatProvider? provider) => _value;
///
- sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)_value;
+ sbyte IConvertible.ToSByte(IFormatProvider? provider) => (sbyte)_value;
///
- float IConvertible.ToSingle(IFormatProvider provider) => _value;
+ float IConvertible.ToSingle(IFormatProvider? provider) => _value;
///
- string IConvertible.ToString(IFormatProvider provider) => ToString();
+ string IConvertible.ToString(IFormatProvider? provider) => ToString();
///
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
var convertible = (IConvertible)this;
@@ -253,13 +274,13 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
}
///
- ushort IConvertible.ToUInt16(IFormatProvider provider) => _value;
+ ushort IConvertible.ToUInt16(IFormatProvider? provider) => _value;
///
- uint IConvertible.ToUInt32(IFormatProvider provider) => _value;
+ uint IConvertible.ToUInt32(IFormatProvider? provider) => _value;
///
- ulong IConvertible.ToUInt64(IFormatProvider provider) => _value;
+ ulong IConvertible.ToUInt64(IFormatProvider? provider) => _value;
///
/// Converts the current atomic type to the specified atomic type.
diff --git a/src/L5Sharp.Core/Types/Predefined/ALARM_ANALOG.cs b/src/L5Sharp.Core/Types/Predefined/ALARM_ANALOG.cs
index 01250116..6debf2d1 100644
--- a/src/L5Sharp.Core/Types/Predefined/ALARM_ANALOG.cs
+++ b/src/L5Sharp.Core/Types/Predefined/ALARM_ANALOG.cs
@@ -87,7 +87,7 @@ public ALARM_ANALOG() : base(nameof(ALARM_ANALOG))
public ALARM_ANALOG(XElement element) : base(nameof(ALARM_ANALOG))
{
if (element is null) throw new ArgumentNullException(nameof(element));
- var members = element.Attributes().Select(a => new LogixMember(a.Name.ToString(), Atomic.Parse(a.Value)));
+ var members = element.Attributes().Select(a => new LogixMember(a.Name.ToString(), AtomicType.Parse(a.Value)));
AddMembers(members.ToList());
}
diff --git a/src/L5Sharp.Core/Types/Predefined/ALARM_DIGITAL.cs b/src/L5Sharp.Core/Types/Predefined/ALARM_DIGITAL.cs
index b64203be..0ad618de 100644
--- a/src/L5Sharp.Core/Types/Predefined/ALARM_DIGITAL.cs
+++ b/src/L5Sharp.Core/Types/Predefined/ALARM_DIGITAL.cs
@@ -50,7 +50,7 @@ public ALARM_DIGITAL() : base(nameof(ALARM_DIGITAL))
public ALARM_DIGITAL(XElement element) : base(nameof(ALARM_DIGITAL))
{
if (element is null) throw new ArgumentNullException(nameof(element));
- var members = element.Attributes().Select(a => new LogixMember(a.Name.ToString(), Atomic.Parse(a.Value)));
+ var members = element.Attributes().Select(a => new LogixMember(a.Name.ToString(), AtomicType.Parse(a.Value)));
AddMembers(members.ToList());
}
diff --git a/src/L5Sharp.Core/Types/Predefined/STRING.cs b/src/L5Sharp.Core/Types/Predefined/STRING.cs
index 9d2124fe..ebf25a40 100644
--- a/src/L5Sharp.Core/Types/Predefined/STRING.cs
+++ b/src/L5Sharp.Core/Types/Predefined/STRING.cs
@@ -6,7 +6,7 @@ namespace L5Sharp.Core;
///
/// Represents a predefined String Logix data type.
///
-public sealed class STRING : StringType
+public sealed class STRING : StringType, ILogixParsable
{
//This is the built in length of string types in Logix
private const int PredefinedLength = 82;
@@ -38,6 +38,21 @@ public STRING(string value) : base(nameof(STRING), value, PredefinedLength)
///
public override DataTypeClass Class => DataTypeClass.Predefined;
+ ///
+ /// Parses the provided string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value.
+ public static STRING Parse(string value) => new(value);
+
+ ///
+ /// Tries to parse the provided string into a value.
+ ///
+ /// The string to parse.
+ /// A representing the parsed value if successful; Otherwise, null.
+ public static STRING? TryParse(string? value) =>
+ value is not null && value.Length <= PredefinedLength ? new STRING(value) : null;
+
///
/// Converts the provided to a value.
///
diff --git a/src/L5Sharp.Core/Types/StringType.cs b/src/L5Sharp.Core/Types/StringType.cs
index b1c364d5..2ae0622c 100644
--- a/src/L5Sharp.Core/Types/StringType.cs
+++ b/src/L5Sharp.Core/Types/StringType.cs
@@ -289,7 +289,7 @@ private static SINT[] ToArray(string value)
var matches = Regex.Matches(value, LogixAsciiPattern);
return matches.Select(m =>
{
- var parsed = (SINT)Radix.Ascii.Parse($"'{m.Value}'");
+ var parsed = (SINT)Radix.Ascii.ParseValue($"'{m.Value}'");
return new SINT(parsed, Radix.Ascii);
}).ToArray();
}
diff --git a/src/L5Sharp.Core/Types/StructureType.cs b/src/L5Sharp.Core/Types/StructureType.cs
index 4e3ab2ee..f1bbeea5 100644
--- a/src/L5Sharp.Core/Types/StructureType.cs
+++ b/src/L5Sharp.Core/Types/StructureType.cs
@@ -304,7 +304,7 @@ protected void ReplaceMember(int index, LogixMember member)
///
/// The object that initiated the data change event.
/// The event arguments of the data changed event.
- protected virtual void OnMemberDataChanged(object sender, EventArgs e) => RaiseDataChanged(sender);
+ protected virtual void OnMemberDataChanged(object? sender, EventArgs e) => RaiseDataChanged(sender);
///
/// Remove old event handler and attach new to ensure no memory leak.
diff --git a/src/L5Sharp.Core/Utilities/L5XParser.cs b/src/L5Sharp.Core/Utilities/L5XParser.cs
deleted file mode 100644
index c16e3a36..00000000
--- a/src/L5Sharp.Core/Utilities/L5XParser.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-
-namespace L5Sharp.Core;
-
-///
-/// Static class containing mappings for converting string values (XML typed value) to the strongly type object.
-///
-public static class L5XParser
-{
- private static readonly Dictionary> Parsers = new()
- {
- //Types
- {typeof(BOOL), BOOL.Parse},
- {typeof(SINT), SINT.Parse},
- {typeof(INT), INT.Parse},
- {typeof(DINT), DINT.Parse},
- {typeof(LINT), LINT.Parse},
- {typeof(REAL), REAL.Parse},
- {typeof(LREAL), LREAL.Parse},
- {typeof(USINT), USINT.Parse},
- {typeof(UINT), UINT.Parse},
- {typeof(UDINT), UDINT.Parse},
- {typeof(ULINT), ULINT.Parse},
- {typeof(AtomicType), Atomic.Parse},
- {typeof(STRING), s => new STRING(s)},
- {typeof(DateTime), s => DateTime.Parse(s)},
- //Enums
- {typeof(CaptureSizeType), CaptureSizeType.FromValue},
- {typeof(ComponentType), ComponentType.FromValue},
- {typeof(ConnectionPriority), ConnectionPriority.FromValue},
- {typeof(ConnectionType), ConnectionType.FromValue},
- {typeof(DataFormat), DataFormat.FromValue},
- {typeof(DataTypeClass), DataTypeClass.FromValue},
- {typeof(DataTypeFamily), DataTypeFamily.FromValue},
- {typeof(ElectronicKeying), ElectronicKeying.FromValue},
- {typeof(ExternalAccess), ExternalAccess.FromValue},
- {typeof(Keyword), Keyword.FromValue},
- {typeof(OnlineEditType), OnlineEditType.FromValue},
- {typeof(Operator), Operator.FromValue},
- {typeof(PassThroughOption), PassThroughOption.FromValue},
- {typeof(PenType), PenType.FromValue},
- {typeof(ProductionTrigger), ProductionTrigger.FromValue},
- {typeof(ProgramType), ProgramType.FromValue},
- {typeof(Radix), Radix.FromValue},
- {typeof(RoutineType), RoutineType.FromValue},
- {typeof(RungType), RungType.FromValue},
- {typeof(SamplesType), SamplesType.FromValue},
- {typeof(Scope), Scope.FromValue},
- {typeof(SFCExecutionControl), SFCExecutionControl.FromValue},
- {typeof(SFCLastScan), SFCLastScan.FromValue},
- {typeof(SFCRestartPosition), SFCRestartPosition.FromValue},
- {typeof(SheetOrientation), SheetOrientation.FromValue},
- {typeof(SheetSize), SheetSize.FromValue},
- {typeof(TagType), TagType.FromValue},
- {typeof(TagUsage), TagUsage.FromValue},
- {typeof(TaskEventTrigger), TaskEventTrigger.FromValue},
- {typeof(TaskType), TaskType.FromValue},
- {typeof(TransmissionType), TransmissionType.FromValue},
- {typeof(TriggerOperation), TriggerOperation.Parse},
- {typeof(TriggerTargetType), TriggerTargetType.FromValue},
- {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)},
- {typeof(ProductType), ProductType.Parse},
- {typeof(Revision), s => new Revision(s)},
- {typeof(ScanRate), s => ScanRate.Parse(s)},
- {typeof(ScanRate?), s => ScanRate.Parse(s)},
- {typeof(TagName), s => new TagName(s)},
- {typeof(TaskPriority), s => TaskPriority.Parse(s)},
- {typeof(Vendor), Vendor.Parse},
- {typeof(Watchdog), s => Watchdog.Parse(s)},
- };
-
- ///
- /// Parses the provided string input to the specified type using the predefined L5X parser functions.
- ///
- /// The string input to parse.
- /// The type of property to return.
- /// The resulting parsed value.
- /// When a parser was not found to the specified type.
- /// If the resulting parsed type does not match the specified generic type parameter.
- public static T Parse(this string input)
- {
- var parser = GetParser(typeof(T));
-
- var value = parser(input);
-
- if (value is not T typed)
- throw new ArgumentException($"The input '{input}' could not be parsed to type '{typeof(T)}");
-
- return typed;
- }
-
- ///
- /// Parses the provided string input to the specified type using the predefined L5X parser functions.
- ///
- /// The string input to parse.
- /// The type to parse the input to.
- /// The resulting parsed object value.
- /// When a parser was not found to the specified type.
- public static object Parse(this string input, Type type)
- {
- var parser = GetParser(type);
-
- var value = parser(input);
-
- return value;
- }
-
- ///
- /// Gets a parser function for the specified type.
- ///
- ///
- /// Simply looks to predefined parser functions and returns one if found for the specified type.
- /// Otherwise will use the of the current type and return the ConvertFrom function
- /// if is capable of converting from a string type.
- ///
- /// The type to parse.
- ///
- /// A func that can convert from a string to an object for the provided type is the func is defined.
- ///
- private static Func GetParser(Type type)
- {
- if (Parsers.TryGetValue(type, out var parser))
- return parser;
-
- var converter = TypeDescriptor.GetConverter(type);
-
- if (converter.CanConvertFrom(typeof(string)))
- return s => converter.ConvertFrom(s);
-
- throw new InvalidOperationException($"No parse function has been defined for type '{type}'");
- }
-}
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Common/ArgumentTests.cs b/tests/L5Sharp.Tests/Common/ArgumentTests.cs
index 175faeda..99fe078c 100644
--- a/tests/L5Sharp.Tests/Common/ArgumentTests.cs
+++ b/tests/L5Sharp.Tests/Common/ArgumentTests.cs
@@ -113,17 +113,13 @@ public void IsString_AtomicArgument_ShouldBeFalse()
[Test]
public void Parse_Null_ShouldBeEmpty()
{
- var argument = Argument.Parse(null);
-
- argument.Should().Be(string.Empty);
+ FluentActions.Invoking(() => Argument.Parse(null)).Should().Throw();
}
[Test]
- public void Parse_EmptyString_ShouldBeEmpty()
+ public void Parse_EmptyString_ShouldThrowException()
{
- var argument = Argument.Parse(string.Empty);
-
- argument.Should().Be(string.Empty);
+ FluentActions.Invoking(() => Argument.Parse(string.Empty)).Should().Throw();
}
[Test]
diff --git a/tests/L5Sharp.Tests/Common/DimensionsTests.cs b/tests/L5Sharp.Tests/Common/DimensionsTests.cs
index b9ded19f..87b04d40 100644
--- a/tests/L5Sharp.Tests/Common/DimensionsTests.cs
+++ b/tests/L5Sharp.Tests/Common/DimensionsTests.cs
@@ -334,36 +334,32 @@ public void Parse_ThreeDimension_ShouldHaveExpectedValues()
[Test]
public void TryParse_Null_ShouldBeNullAndFalse()
{
- var result = Dimensions.TryParse(null!, out var dimensions);
+ var dimensions = Dimensions.TryParse(null!);
- result.Should().BeFalse();
dimensions.Should().BeNull();
}
[Test]
public void TryParse_Empty_ShouldBeNullAndFalse()
{
- var result = Dimensions.TryParse(string.Empty, out var dimensions);
+ var dimensions = Dimensions.TryParse(string.Empty);
- result.Should().BeFalse();
dimensions.Should().BeNull();
}
[Test]
public void TryParse_MoreThanThreeDimensions_ShouldBeNullAndFalse()
{
- var result = Dimensions.TryParse("1 4 3 8", out var dimensions);
-
- result.Should().BeFalse();
+ var dimensions = Dimensions.TryParse("1 4 3 8");
+
dimensions.Should().BeNull();
}
[Test]
public void TryParse_ValidPattern_ShouldNotBeNullAndTrue()
{
- var result = Dimensions.TryParse("1", out var dimensions);
+ var dimensions = Dimensions.TryParse("1");
- result.Should().BeTrue();
dimensions.Should().NotBeNull();
dimensions?.X.Should().Be(1);
dimensions?.Y.Should().Be(0);
@@ -374,9 +370,8 @@ public void TryParse_ValidPattern_ShouldNotBeNullAndTrue()
[Test]
public void TryParse_TwoDimension_ShouldHaveExpectedValues()
{
- var result = Dimensions.TryParse("3 10", out var dimensions);
-
- result.Should().BeTrue();
+ var dimensions = Dimensions.TryParse("3 10");
+
dimensions.Should().NotBeNull();
dimensions?.X.Should().Be(3);
dimensions?.Y.Should().Be(10);
@@ -387,9 +382,8 @@ public void TryParse_TwoDimension_ShouldHaveExpectedValues()
[Test]
public void TryParse_ThreeDimension_ShouldHaveExpectedValues()
{
- var result = Dimensions.TryParse("3 10, 6", out var dimensions);
-
- result.Should().BeTrue();
+ var dimensions = Dimensions.TryParse("3 10, 6");
+
dimensions.Should().NotBeNull();
dimensions?.X.Should().Be(3);
dimensions?.Y.Should().Be(10);
@@ -486,7 +480,7 @@ public void Equals_ObjectOverloadNull_ShouldBeFalse()
{
var d1 = new Dimensions(5);
- var result = d1.Equals((object)null);
+ var result = d1.Equals((object)null!);
result.Should().BeFalse();
}
diff --git a/tests/L5Sharp.Tests/Common/ProductTypeTests.cs b/tests/L5Sharp.Tests/Common/ProductTypeTests.cs
index bf311860..cdbabad6 100644
--- a/tests/L5Sharp.Tests/Common/ProductTypeTests.cs
+++ b/tests/L5Sharp.Tests/Common/ProductTypeTests.cs
@@ -110,11 +110,9 @@ public void Parse_ValidNumber_ShouldBeExpected()
}
[Test]
- public void Parse_InvalidNumber_ShouldBeUnknown()
+ public void Parse_InvalidNumber_ShouldThrowException()
{
- var productType = ProductType.Parse("-11");
-
- productType.Should().Be(ProductType.Unknown);
+ FluentActions.Invoking(() => ProductType.Parse("-11")) .Should().Throw();
}
[Test]
diff --git a/tests/L5Sharp.Tests/Common/VendorTests.cs b/tests/L5Sharp.Tests/Common/VendorTests.cs
index d5753084..36951d47 100644
--- a/tests/L5Sharp.Tests/Common/VendorTests.cs
+++ b/tests/L5Sharp.Tests/Common/VendorTests.cs
@@ -80,11 +80,9 @@ public void Parse_ValidNumber_ShouldBeExpected()
}
[Test]
- public void Parse_InvalidNumber_ShouldBeUnknown()
+ public void Parse_InvalidNumber_ShouldThrowException()
{
- var vendor = Vendor.Parse("-11");
-
- vendor.Should().Be(Vendor.Unknown);
+ FluentActions.Invoking(() => Vendor.Parse("-11")) .Should().Throw();
}
[Test]
diff --git a/tests/L5Sharp.Tests/Enums/RadixAsciiTests.cs b/tests/L5Sharp.Tests/Enums/RadixAsciiTests.cs
index 67f1e879..f697cde1 100644
--- a/tests/L5Sharp.Tests/Enums/RadixAsciiTests.cs
+++ b/tests/L5Sharp.Tests/Enums/RadixAsciiTests.cs
@@ -19,7 +19,7 @@ public void Format_AsciiValidSint_ShouldBeExpectedFormat()
{
var radix = Radix.Ascii;
- var result = radix.Format(new SINT(20));
+ var result = radix.FormatValue(new SINT(20));
result.Should().Be("'$14'");
}
@@ -27,7 +27,7 @@ public void Format_AsciiValidSint_ShouldBeExpectedFormat()
[Test]
public void Format_Ascii_ShouldBeExpected()
{
- var ascii = Radix.Ascii.Format(new DINT(123456));
+ var ascii = Radix.Ascii.FormatValue(new DINT(123456));
ascii.Should().Be("'$00$01$E2@'");
}
@@ -37,7 +37,7 @@ public void Format_AsciiValidInt_ShouldBeExpectedFormat()
{
var radix = Radix.Ascii;
- var result = radix.Format(new INT(20));
+ var result = radix.FormatValue(new INT(20));
result.Should().Be("'$00$14'");
}
@@ -47,7 +47,7 @@ public void Format_AsciiValidDint_ShouldBeExpectedFormat()
{
var radix = Radix.Ascii;
- var result = radix.Format(new DINT(20));
+ var result = radix.FormatValue(new DINT(20));
result.Should().Be("'$00$00$00$14'");
}
@@ -57,7 +57,7 @@ public void Format_AsciiValidLint_ShouldBeExpectedFormat()
{
var radix = Radix.Ascii;
- var result = radix.Format(new LINT(20));
+ var result = radix.FormatValue(new LINT(20));
result.Should().Be("'$00$00$00$00$00$00$00$14'");
}
@@ -69,7 +69,7 @@ public void Format_AllValuesFrom32To126_ShouldMatchTheConvertedCharacter()
{
var value = new SINT(i);
- var formatted = Radix.Ascii.Format(value);
+ var formatted = Radix.Ascii.FormatValue(value);
var expected = Convert.ToChar(i).ToString();
formatted.Should().Be($"'{expected}'");
@@ -84,7 +84,7 @@ public void Format_127ToMaxByte_ShouldBeHexValue()
[Test]
public void Parse_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Ascii.Parse(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Ascii.ParseValue(null!)).Should().Throw();
}
[Test]
@@ -92,27 +92,27 @@ public void Parse_InvalidSpecifier_ShouldThrowArgumentException()
{
const string invalid = "00@";
- FluentActions.Invoking(() => Radix.Ascii.Parse(invalid)).Should().Throw()
+ FluentActions.Invoking(() => Radix.Ascii.ParseValue(invalid)).Should().Throw()
.WithMessage($"Input '{invalid}' does not have expected Ascii format.");
}
[Test]
public void Parse_LengthZero_ShouldThrowArgumentOutOfRangeException()
{
- FluentActions.Invoking(() => Radix.Ascii.Parse("''")).Should().Throw();
+ FluentActions.Invoking(() => Radix.Ascii.ParseValue("''")).Should().Throw();
}
[Test]
public void Parse_LengthTooLarge_ShouldThrowArgumentOutOfRangeException()
{
- FluentActions.Invoking(() => Radix.Ascii.Parse("'$00$00$00$00$00$00$00$00$00'")).Should()
+ FluentActions.Invoking(() => Radix.Ascii.ParseValue("'$00$00$00$00$00$00$00$00$00'")).Should()
.Throw();
}
[Test]
public void Parse_Tab_ShouldBeExpected()
{
- var atomic = Radix.Ascii.Parse("'$t'");
+ var atomic = Radix.Ascii.ParseValue("'$t'");
atomic.Should().Be(9);
}
@@ -120,7 +120,7 @@ public void Parse_Tab_ShouldBeExpected()
[Test]
public void Parse_LineFeed_ShouldBeExpected()
{
- var atomic = Radix.Ascii.Parse("'$l'");
+ var atomic = Radix.Ascii.ParseValue("'$l'");
atomic.Should().Be(10);
}
@@ -128,7 +128,7 @@ public void Parse_LineFeed_ShouldBeExpected()
[Test]
public void Parse_FormFeed_ShouldBeExpected()
{
- var atomic = Radix.Ascii.Parse("'$p'");
+ var atomic = Radix.Ascii.ParseValue("'$p'");
atomic.Should().Be(12);
}
@@ -136,7 +136,7 @@ public void Parse_FormFeed_ShouldBeExpected()
[Test]
public void Parse_Return_ShouldBeExpected()
{
- var atomic = Radix.Ascii.Parse("'$r'");
+ var atomic = Radix.Ascii.ParseValue("'$r'");
atomic.Should().Be(13);
}
@@ -144,7 +144,7 @@ public void Parse_Return_ShouldBeExpected()
[Test]
public void Parse_DollarSign_ShouldBeExpected()
{
- var atomic = Radix.Ascii.Parse("'$$'");
+ var atomic = Radix.Ascii.ParseValue("'$$'");
atomic.Should().Be(36);
}
@@ -152,7 +152,7 @@ public void Parse_DollarSign_ShouldBeExpected()
[Test]
public void Parse_SingleQuote_ShouldBeExpected()
{
- var atomic = Radix.Ascii.Parse("'$''");
+ var atomic = Radix.Ascii.ParseValue("'$''");
atomic.Should().Be(39);
}
@@ -160,7 +160,7 @@ public void Parse_SingleQuote_ShouldBeExpected()
[Test]
public void Parse_ValidSint_ShouldBeExpected()
{
- var value = Radix.Ascii.Parse("'$14'");
+ var value = Radix.Ascii.ParseValue("'$14'");
value.Should().Be(new SINT(20));
}
@@ -168,7 +168,7 @@ public void Parse_ValidSint_ShouldBeExpected()
[Test]
public void Parse_ValidDint_ShouldBeExpected()
{
- var value = Radix.Ascii.Parse("'$00$01$E2@'");
+ var value = Radix.Ascii.ParseValue("'$00$01$E2@'");
value.Should().Be(new DINT(123456));
}
@@ -176,7 +176,7 @@ public void Parse_ValidDint_ShouldBeExpected()
[Test]
public void Parse_Valid_ShouldBeExpected()
{
- var value = Radix.Ascii.Parse("'$12D$F1A'");
+ var value = Radix.Ascii.ParseValue("'$12D$F1A'");
value.Should().Be(new DINT(306508097));
}
diff --git a/tests/L5Sharp.Tests/Enums/RadixBinaryTests.cs b/tests/L5Sharp.Tests/Enums/RadixBinaryTests.cs
index 3ccb15c7..04d9ee3f 100644
--- a/tests/L5Sharp.Tests/Enums/RadixBinaryTests.cs
+++ b/tests/L5Sharp.Tests/Enums/RadixBinaryTests.cs
@@ -17,19 +17,19 @@ public void Binary_WhenCalled_ShouldBeExpected()
[Test]
public void Format_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Binary.Format(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Binary.FormatValue(null!)).Should().Throw();
}
[Test]
public void Format_NonSupportedAtomic_ShouldThrowNotSupportedException()
{
- FluentActions.Invoking(() => Radix.Binary.Format(new REAL())).Should().Throw();
+ FluentActions.Invoking(() => Radix.Binary.FormatValue(new REAL())).Should().Throw();
}
[Test]
public void Format_BoolFalse_ShouldBeExpected()
{
- var result = Radix.Binary.Format(new BOOL());
+ var result = Radix.Binary.FormatValue(new BOOL());
result.Should().Be("2#0");
}
@@ -37,7 +37,7 @@ public void Format_BoolFalse_ShouldBeExpected()
[Test]
public void Format_BoolTrue_ShouldBeExpected()
{
- var result = Radix.Binary.Format(new BOOL(true));
+ var result = Radix.Binary.FormatValue(new BOOL(true));
result.Should().Be("2#1");
}
@@ -45,7 +45,7 @@ public void Format_BoolTrue_ShouldBeExpected()
[Test]
public void Format_ValidSint_ShouldBeExpectedFormat()
{
- var result = Radix.Binary.Format(new SINT(20));
+ var result = Radix.Binary.FormatValue(new SINT(20));
result.Should().Be("2#0001_0100");
}
@@ -53,7 +53,7 @@ public void Format_ValidSint_ShouldBeExpectedFormat()
[Test]
public void Format_ValidInt_ShouldBeExpectedFormat()
{
- var result = Radix.Binary.Format(new INT(20));
+ var result = Radix.Binary.FormatValue(new INT(20));
result.Should().Be("2#0000_0000_0001_0100");
}
@@ -61,7 +61,7 @@ public void Format_ValidInt_ShouldBeExpectedFormat()
[Test]
public void Format_ValidDint_ShouldBeExpectedFormat()
{
- var result = Radix.Binary.Format(new DINT(20));
+ var result = Radix.Binary.FormatValue(new DINT(20));
result.Should().Be("2#0000_0000_0000_0000_0000_0000_0001_0100");
}
@@ -69,7 +69,7 @@ public void Format_ValidDint_ShouldBeExpectedFormat()
[Test]
public void Format_ValidLint_ShouldBeExpectedFormat()
{
- var result = Radix.Binary.Format(new LINT(20));
+ var result = Radix.Binary.FormatValue(new LINT(20));
result.Should().Be("2#0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_0100");
}
@@ -77,7 +77,7 @@ public void Format_ValidLint_ShouldBeExpectedFormat()
[Test]
public void Parse_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Binary.Parse(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Binary.ParseValue(null!)).Should().Throw();
}
[Test]
@@ -85,20 +85,20 @@ public void Parse_NoSpecifier_ShouldThrowArgumentException()
{
const string invalid = "00010100";
- FluentActions.Invoking(() => Radix.Binary.Parse(invalid)).Should().Throw()
+ FluentActions.Invoking(() => Radix.Binary.ParseValue(invalid)).Should().Throw()
.WithMessage($"Input '{invalid}' does not have expected Binary format.");
}
[Test]
public void Parse_LengthZero_ShouldThrowArgumentException()
{
- FluentActions.Invoking(() => Radix.Binary.Parse("2#")).Should().Throw();
+ FluentActions.Invoking(() => Radix.Binary.ParseValue("2#")).Should().Throw();
}
[Test]
public void Parse_LengthGreaterThan68_ShouldThrowArgumentOutOfRangeException()
{
- var result = Radix.Binary.Parse(
+ var result = Radix.Binary.ParseValue(
"2#0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_0100_0000");
result.Should().NotBeNull();
@@ -107,7 +107,7 @@ public void Parse_LengthGreaterThan68_ShouldThrowArgumentOutOfRangeException()
[Test]
public void Parse_BoolFalse_ShouldBeFalse()
{
- var atomic = Radix.Binary.Parse("2#0");
+ var atomic = Radix.Binary.ParseValue("2#0");
atomic.Should().Be(false);
}
@@ -115,7 +115,7 @@ public void Parse_BoolFalse_ShouldBeFalse()
[Test]
public void Parse_BoolTrue_ShouldBeTrue()
{
- var atomic = Radix.Binary.Parse("2#1");
+ var atomic = Radix.Binary.ParseValue("2#1");
atomic.Should().Be(true);
}
@@ -123,7 +123,7 @@ public void Parse_BoolTrue_ShouldBeTrue()
[Test]
public void Parse_ValidSint_ShouldBeExpected()
{
- var value = Radix.Binary.Parse("2#0001_0100");
+ var value = Radix.Binary.ParseValue("2#0001_0100");
value.Should().Be(new SINT(20));
}
@@ -131,7 +131,7 @@ public void Parse_ValidSint_ShouldBeExpected()
[Test]
public void Parse_ValidInt_ShouldBeExpected()
{
- var value = Radix.Binary.Parse("2#0000_0000_0001_0100");
+ var value = Radix.Binary.ParseValue("2#0000_0000_0001_0100");
value.Should().Be(new INT(20));
}
@@ -139,7 +139,7 @@ public void Parse_ValidInt_ShouldBeExpected()
[Test]
public void Parse_ValidDint_ShouldBeExpected()
{
- var value = Radix.Binary.Parse("2#0000_0000_0000_0000_0000_0000_0001_0100");
+ var value = Radix.Binary.ParseValue("2#0000_0000_0000_0000_0000_0000_0001_0100");
value.Should().Be(new DINT(20));
}
@@ -148,7 +148,7 @@ public void Parse_ValidDint_ShouldBeExpected()
[Test]
public void Parse_ValidLint_ShouldBeExpected()
{
- var value = Radix.Binary.Parse(
+ var value = Radix.Binary.ParseValue(
"2#0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_0100");
value.Should().Be(new LINT(20));
diff --git a/tests/L5Sharp.Tests/Enums/RadixDateTimeNsTests.cs b/tests/L5Sharp.Tests/Enums/RadixDateTimeNsTests.cs
index 3b1c75a7..927cfa55 100644
--- a/tests/L5Sharp.Tests/Enums/RadixDateTimeNsTests.cs
+++ b/tests/L5Sharp.Tests/Enums/RadixDateTimeNsTests.cs
@@ -17,13 +17,13 @@ public void DateTimeNs_WhenCalled_ShouldBeExpected()
[Test]
public void Format_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.DateTimeNs.Format(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.DateTimeNs.FormatValue(null!)).Should().Throw();
}
[Test]
public void Format_NonSupportedAtomic_ShouldThrowNotSupportedException()
{
- FluentActions.Invoking(() => Radix.DateTimeNs.Format(new REAL())).Should().Throw();
+ FluentActions.Invoking(() => Radix.DateTimeNs.FormatValue(new REAL())).Should().Throw();
}
[Test]
@@ -31,7 +31,7 @@ public void Format_ValidTimeExample1_ShouldBeExpectedFormat()
{
var radix = Radix.DateTimeNs;
- var result = radix.Format(new LINT(1638277952000000));
+ var result = radix.FormatValue(new LINT(1638277952000000));
result.Should().Be("LDT#1970-01-19-17:04:37.952_000_000(UTC-06:00)");
}
@@ -41,7 +41,7 @@ public void Format_ValidTimeExample2_ShouldBeExpectedFormat()
{
var radix = Radix.DateTimeNs;
- var result = radix.Format(new LINT(1641016800000000000));
+ var result = radix.FormatValue(new LINT(1641016800000000000));
result.Should().Be("LDT#2022-01-01-00:00:00.000_000_000(UTC-06:00)");
}
@@ -51,7 +51,7 @@ public void Format_ValidTimeExample3_ShouldBeExpectedFormat()
{
var radix = Radix.DateTimeNs;
- var result = radix.Format(new LINT(1641016800000001001));
+ var result = radix.FormatValue(new LINT(1641016800000001001));
//loss of accuracy causes the 1 nano second to be lost
result.Should().Be("LDT#2022-01-01-00:00:00.000_001_000(UTC-06:00)");
@@ -62,7 +62,7 @@ public void Format_ValidTimeExample4_ShouldBeExpectedFormat()
{
var radix = Radix.DateTimeNs;
- var result = radix.Format(new LINT(1641016800000001500));
+ var result = radix.FormatValue(new LINT(1641016800000001500));
result.Should().Be("LDT#2022-01-01-00:00:00.000_001_500(UTC-06:00)");
}
@@ -72,7 +72,7 @@ public void Parse_ValidTimeExample1_ShouldBeExpectedValue()
{
var radix = Radix.DateTimeNs;
- var result = radix.Parse("LDT#1970-01-19-17:04:37.952_000_000(UTC-06:00)");
+ var result = radix.ParseValue("LDT#1970-01-19-17:04:37.952_000_000(UTC-06:00)");
result.Should().Be(1638277952000000);
}
@@ -80,7 +80,7 @@ public void Parse_ValidTimeExample1_ShouldBeExpectedValue()
[Test]
public void Parse_NoSpecifier_ShouldThrowFormatException()
{
- FluentActions.Invoking(() => Radix.DateTimeNs.Parse("2021-11-30-07:12:32.000_000_000(UTC-06:00)")).Should()
+ FluentActions.Invoking(() => Radix.DateTimeNs.ParseValue("2021-11-30-07:12:32.000_000_000(UTC-06:00)")).Should()
.Throw();
}
@@ -89,7 +89,7 @@ public void Parse_ValidTimeExample2_ShouldBeExpectedFormat()
{
var radix = Radix.DateTimeNs;
- var result = radix.Parse("LDT#2022-01-01-00:00:00.000_000_000(UTC-06:00)");
+ var result = radix.ParseValue("LDT#2022-01-01-00:00:00.000_000_000(UTC-06:00)");
result.Should().Be(1641016800000000000);
}
diff --git a/tests/L5Sharp.Tests/Enums/RadixDateTimeTests.cs b/tests/L5Sharp.Tests/Enums/RadixDateTimeTests.cs
index 1ae2ab09..d57a3bd7 100644
--- a/tests/L5Sharp.Tests/Enums/RadixDateTimeTests.cs
+++ b/tests/L5Sharp.Tests/Enums/RadixDateTimeTests.cs
@@ -17,13 +17,13 @@ public void DateTime_WhenCalled_ShouldBeExpected()
[Test]
public void Format_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.DateTime.Format(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.DateTime.FormatValue(null!)).Should().Throw();
}
[Test]
public void Format_NonSupportedAtomic_ShouldThrowNotSupportedException()
{
- FluentActions.Invoking(() => Radix.DateTime.Format(new REAL())).Should().Throw();
+ FluentActions.Invoking(() => Radix.DateTime.FormatValue(new REAL())).Should().Throw();
}
[Test]
@@ -31,7 +31,7 @@ public void Format_ValidTimeExample1_ShouldBeExpectedFormat()
{
var radix = Radix.DateTime;
- var result = radix.Format(new LINT(1638277952000000));
+ var result = radix.FormatValue(new LINT(1638277952000000));
result.Should().Be("DT#2021-11-30-13:12:32.000_000Z");
}
@@ -41,7 +41,7 @@ public void Format_ValidTimeExample2_ShouldBeExpectedFormat()
{
var radix = Radix.DateTime;
- var result = radix.Format(new LINT(1641016800100100));
+ var result = radix.FormatValue(new LINT(1641016800100100));
result.Should().Be("DT#2022-01-01-06:00:00.100_100Z");
}
@@ -49,7 +49,7 @@ public void Format_ValidTimeExample2_ShouldBeExpectedFormat()
[Test]
public void Parse_NoSpecifier_ShouldThrowFormatException()
{
- FluentActions.Invoking(() => Radix.DateTime.Parse("2021-11-30-07:12:32.000_000Z")).Should()
+ FluentActions.Invoking(() => Radix.DateTime.ParseValue("2021-11-30-07:12:32.000_000Z")).Should()
.Throw();
}
@@ -58,7 +58,7 @@ public void Parse_ValidTimeExample1_ShouldBeExpectedValue()
{
var radix = Radix.DateTime;
- var result = radix.Parse("DT#2021-11-30-13:12:32.000_000Z");
+ var result = radix.ParseValue("DT#2021-11-30-13:12:32.000_000Z");
result.Should().Be(1638277952000000);
}
@@ -68,7 +68,7 @@ public void Parse_ValidTimeExample2_ShouldBeExpectedValue()
{
var radix = Radix.DateTime;
- var result = radix.Parse("DT#2022-01-01-06:00:00.100_100Z");
+ var result = radix.ParseValue("DT#2022-01-01-06:00:00.100_100Z");
result.Should().Be(1641016800100100);
}
diff --git a/tests/L5Sharp.Tests/Enums/RadixDecimalTests.cs b/tests/L5Sharp.Tests/Enums/RadixDecimalTests.cs
index b788a5cf..2c35598f 100644
--- a/tests/L5Sharp.Tests/Enums/RadixDecimalTests.cs
+++ b/tests/L5Sharp.Tests/Enums/RadixDecimalTests.cs
@@ -17,19 +17,19 @@ public void Decimal_WhenCalled_ShouldBeExpected()
[Test]
public void Format_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Decimal.Format(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Decimal.FormatValue(null!)).Should().Throw();
}
[Test]
public void Format_NonSupportedAtomic_ShouldThrowNotSupportedException()
{
- FluentActions.Invoking(() => Radix.Decimal.Format(new REAL())).Should().Throw();
+ FluentActions.Invoking(() => Radix.Decimal.FormatValue(new REAL())).Should().Throw();
}
[Test]
public void Format_BoolFalse_ShouldBeExpected()
{
- var result = Radix.Decimal.Format(new BOOL());
+ var result = Radix.Decimal.FormatValue(new BOOL());
result.Should().Be("0");
}
@@ -37,7 +37,7 @@ public void Format_BoolFalse_ShouldBeExpected()
[Test]
public void Format_BoolTrue_ShouldBeExpected()
{
- var result = Radix.Decimal.Format(new BOOL(true));
+ var result = Radix.Decimal.FormatValue(new BOOL(true));
result.Should().Be("1");
}
@@ -45,7 +45,7 @@ public void Format_BoolTrue_ShouldBeExpected()
[Test]
public void Format_ValidSint_ShouldBeExpectedFormat()
{
- var result = Radix.Decimal.Format(new SINT(20));
+ var result = Radix.Decimal.FormatValue(new SINT(20));
result.Should().Be("20");
}
@@ -53,7 +53,7 @@ public void Format_ValidSint_ShouldBeExpectedFormat()
[Test]
public void Format_ValidInt_ShouldBeExpectedFormat()
{
- var result = Radix.Decimal.Format(new INT(20));
+ var result = Radix.Decimal.FormatValue(new INT(20));
result.Should().Be("20");
}
@@ -61,7 +61,7 @@ public void Format_ValidInt_ShouldBeExpectedFormat()
[Test]
public void Format_ValidDint_ShouldBeExpectedFormat()
{
- var result = Radix.Decimal.Format(new DINT(20));
+ var result = Radix.Decimal.FormatValue(new DINT(20));
result.Should().Be("20");
}
@@ -69,7 +69,7 @@ public void Format_ValidDint_ShouldBeExpectedFormat()
[Test]
public void Format_ValidLint_ShouldBeExpectedFormat()
{
- var result = Radix.Decimal.Format(new LINT(20));
+ var result = Radix.Decimal.FormatValue(new LINT(20));
result.Should().Be("20");
}
@@ -77,20 +77,20 @@ public void Format_ValidLint_ShouldBeExpectedFormat()
[Test]
public void Parse_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Decimal.Parse(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Decimal.ParseValue(null!)).Should().Throw();
}
[Test]
public void Parse_Invalid_ShouldThrowArgumentException()
{
- FluentActions.Invoking(() => Radix.Decimal.Parse("null")).Should().Throw()
+ FluentActions.Invoking(() => Radix.Decimal.ParseValue("null")).Should().Throw()
.WithMessage("Input 'null' does not have expected Decimal format.");
}
[Test]
public void Parse_InvalidLength_ShouldThrowArgumentOutOfRangeException()
{
- FluentActions.Invoking(() => Radix.Decimal.Parse("92233720368547758070"))
+ FluentActions.Invoking(() => Radix.Decimal.ParseValue("92233720368547758070"))
.Should().Throw()
.WithMessage("Input '92233720368547758070' is out of range for the Decimal Radix. (Parameter 'Input')");
}
@@ -98,7 +98,7 @@ public void Parse_InvalidLength_ShouldThrowArgumentOutOfRangeException()
[Test]
public void Parse_sbyte_ShouldBeExpected()
{
- var atomic = (SINT)Radix.Decimal.Parse(sbyte.MaxValue.ToString());
+ var atomic = (SINT)Radix.Decimal.ParseValue(sbyte.MaxValue.ToString());
atomic.Should().Be(new SINT(sbyte.MaxValue));
}
@@ -106,7 +106,7 @@ public void Parse_sbyte_ShouldBeExpected()
[Test]
public void Parse_byte_ShouldBeExpected()
{
- var atomic = (USINT)Radix.Decimal.Parse(byte.MaxValue.ToString());
+ var atomic = (USINT)Radix.Decimal.ParseValue(byte.MaxValue.ToString());
atomic.Should().Be(new USINT(byte.MaxValue));
}
@@ -114,7 +114,7 @@ public void Parse_byte_ShouldBeExpected()
[Test]
public void Parse_short_ShouldBeExpected()
{
- var atomic = (INT)Radix.Decimal.Parse(short.MaxValue.ToString());
+ var atomic = (INT)Radix.Decimal.ParseValue(short.MaxValue.ToString());
atomic.Should().Be(new INT(short.MaxValue));
}
@@ -122,7 +122,7 @@ public void Parse_short_ShouldBeExpected()
[Test]
public void Parse_ushort_ShouldBeExpected()
{
- var atomic = (UINT)Radix.Decimal.Parse(ushort.MaxValue.ToString());
+ var atomic = (UINT)Radix.Decimal.ParseValue(ushort.MaxValue.ToString());
atomic.Should().Be(new UINT(ushort.MaxValue));
}
@@ -130,7 +130,7 @@ public void Parse_ushort_ShouldBeExpected()
[Test]
public void Parse_int_ShouldBeExpected()
{
- var atomic = (DINT)Radix.Decimal.Parse(int.MaxValue.ToString());
+ var atomic = (DINT)Radix.Decimal.ParseValue(int.MaxValue.ToString());
atomic.Should().Be(int.MaxValue);
}
@@ -138,7 +138,7 @@ public void Parse_int_ShouldBeExpected()
[Test]
public void Parse_uint_ShouldBeExpected()
{
- var atomic = (UDINT)Radix.Decimal.Parse(uint.MaxValue.ToString());
+ var atomic = (UDINT)Radix.Decimal.ParseValue(uint.MaxValue.ToString());
atomic.Should().Be(uint.MaxValue);
}
@@ -146,7 +146,7 @@ public void Parse_uint_ShouldBeExpected()
[Test]
public void Parse_long_ShouldBeExpected()
{
- var atomic = (LINT)Radix.Decimal.Parse(long.MaxValue.ToString());
+ var atomic = (LINT)Radix.Decimal.ParseValue(long.MaxValue.ToString());
atomic.Should().Be(long.MaxValue);
}
@@ -154,7 +154,7 @@ public void Parse_long_ShouldBeExpected()
[Test]
public void Parse_ulong_ShouldBeExpected()
{
- var atomic = (ULINT)Radix.Decimal.Parse(ulong.MaxValue.ToString());
+ var atomic = (ULINT)Radix.Decimal.ParseValue(ulong.MaxValue.ToString());
atomic.Should().Be(ulong.MaxValue);
}
diff --git a/tests/L5Sharp.Tests/Enums/RadixExponentialTests.cs b/tests/L5Sharp.Tests/Enums/RadixExponentialTests.cs
index 8344068a..9b1fb59a 100644
--- a/tests/L5Sharp.Tests/Enums/RadixExponentialTests.cs
+++ b/tests/L5Sharp.Tests/Enums/RadixExponentialTests.cs
@@ -19,19 +19,19 @@ public void Exponential_WhenCalled_ShouldBeExpected()
[Test]
public void Format_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Exponential.Format(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Exponential.FormatValue(null!)).Should().Throw();
}
[Test]
public void Format_NonSupportedAtomic_ShouldThrowNotSupportedException()
{
- FluentActions.Invoking(() => Radix.Exponential.Format(new DINT())).Should().Throw();
+ FluentActions.Invoking(() => Radix.Exponential.FormatValue(new DINT())).Should().Throw();
}
[Test]
public void Format_Zero_ShouldBeExpectedFormat()
{
- var result = Radix.Exponential.Format(new REAL());
+ var result = Radix.Exponential.FormatValue(new REAL());
result.Should().Be("0.00000000e+000");
}
@@ -41,7 +41,7 @@ public void Format_ValidReal_ShouldBeExpectedFormat()
{
var fixture = new Fixture();
var value = fixture.Create();
- var result = Radix.Exponential.Format(new REAL(value));
+ var result = Radix.Exponential.FormatValue(new REAL(value));
result.Should().Be(value.ToString("e8", CultureInfo.InvariantCulture));
}
@@ -49,7 +49,7 @@ public void Format_ValidReal_ShouldBeExpectedFormat()
[Test]
public void Format_CustomRealSevenDecimal_ShouldBeExpectedFormat()
{
- var result = Radix.Exponential.Format(new REAL(1.123e3f));
+ var result = Radix.Exponential.FormatValue(new REAL(1.123e3f));
result.Should().Be("1.12300000e+003");
}
@@ -57,19 +57,19 @@ public void Format_CustomRealSevenDecimal_ShouldBeExpectedFormat()
[Test]
public void Parse_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Exponential.Parse(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Exponential.ParseValue(null!)).Should().Throw();
}
[Test]
public void Parse_Empty_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Exponential.Parse(string.Empty)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Exponential.ParseValue(string.Empty)).Should().Throw();
}
[Test]
public void Parse_Exponential_ShouldBeExpected()
{
- var result = Radix.Exponential.Parse("1.12300000e+002");
+ var result = Radix.Exponential.ParseValue("1.12300000e+002");
result.Should().Be(112.3f);
}
diff --git a/tests/L5Sharp.Tests/Enums/RadixFloatTests.cs b/tests/L5Sharp.Tests/Enums/RadixFloatTests.cs
index 6022f1f1..fc0da0dd 100644
--- a/tests/L5Sharp.Tests/Enums/RadixFloatTests.cs
+++ b/tests/L5Sharp.Tests/Enums/RadixFloatTests.cs
@@ -19,19 +19,19 @@ public void Float_WhenCalled_ShouldBeExpected()
[Test]
public void Format_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Float.Format(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Float.FormatValue(null!)).Should().Throw();
}
[Test]
public void Format_NonSupportedAtomic_ShouldThrowRadixNotSupportedException()
{
- FluentActions.Invoking(() => Radix.Float.Format(new DINT())).Should().Throw();
+ FluentActions.Invoking(() => Radix.Float.FormatValue(new DINT())).Should().Throw();
}
[Test]
public void Format_Zero_ShouldBeExpectedFormat()
{
- var result = Radix.Float.Format(new REAL());
+ var result = Radix.Float.FormatValue(new REAL());
result.Should().Be("0.0");
}
@@ -41,7 +41,7 @@ public void Format_ValidReal_ShouldBeExpectedFormat()
{
var fixture = new Fixture();
var value = fixture.Create();
- var result = Radix.Float.Format(new REAL(value));
+ var result = Radix.Float.FormatValue(new REAL(value));
result.Should().Be(value.ToString("0.0###", CultureInfo.InvariantCulture));
}
@@ -49,7 +49,7 @@ public void Format_ValidReal_ShouldBeExpectedFormat()
[Test]
public void Format_CustomRealSevenDecimal_ShouldBeExpectedFormat()
{
- var result = Radix.Float.Format(new REAL(0.1234567f));
+ var result = Radix.Float.FormatValue(new REAL(0.1234567f));
result.Should().Be("0.1234567");
}
@@ -57,7 +57,7 @@ public void Format_CustomRealSevenDecimal_ShouldBeExpectedFormat()
[Test]
public void Format_CustomRealMoreThanFourDecimal_ShouldBeExpectedFormat()
{
- var result = Radix.Float.Format(new REAL(0.12345678f));
+ var result = Radix.Float.FormatValue(new REAL(0.12345678f));
result.Should().Be("0.1234568");
}
@@ -65,7 +65,7 @@ public void Format_CustomRealMoreThanFourDecimal_ShouldBeExpectedFormat()
[Test]
public void Format_CustomRealOneDecimal_ShouldBeExpectedFormat()
{
- var result = Radix.Float.Format(new REAL(1234.5f));
+ var result = Radix.Float.FormatValue(new REAL(1234.5f));
result.Should().Be("1234.5");
}
@@ -73,19 +73,19 @@ public void Format_CustomRealOneDecimal_ShouldBeExpectedFormat()
[Test]
public void Parse_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Float.Parse(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Float.ParseValue(null!)).Should().Throw();
}
[Test]
public void Parse_Empty_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Float.Parse(string.Empty)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Float.ParseValue(string.Empty)).Should().Throw();
}
[Test]
public void Parse_Float_ShouldBeExpected()
{
- var result = Radix.Float.Parse(1.23.ToString(CultureInfo.InvariantCulture));
+ var result = Radix.Float.ParseValue(1.23.ToString(CultureInfo.InvariantCulture));
result.As().Should().Be(1.23f);
}
diff --git a/tests/L5Sharp.Tests/Enums/RadixHexTests.cs b/tests/L5Sharp.Tests/Enums/RadixHexTests.cs
index 6b46f81e..baf48a71 100644
--- a/tests/L5Sharp.Tests/Enums/RadixHexTests.cs
+++ b/tests/L5Sharp.Tests/Enums/RadixHexTests.cs
@@ -17,19 +17,19 @@ public void Hex_WhenCalled_ShouldBeExpected()
[Test]
public void Format_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Hex.Format(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Hex.FormatValue(null!)).Should().Throw();
}
[Test]
public void Format_NonSupportedAtomic_ShouldThrowNotSupportedException()
{
- FluentActions.Invoking(() => Radix.Hex.Format(new REAL())).Should().Throw();
+ FluentActions.Invoking(() => Radix.Hex.FormatValue(new REAL())).Should().Throw();
}
[Test]
public void Format_ValidBoolFalse_ShouldBeExpected()
{
- var result = Radix.Hex.Format(new BOOL());
+ var result = Radix.Hex.FormatValue(new BOOL());
result.Should().Be("16#0");
}
@@ -37,7 +37,7 @@ public void Format_ValidBoolFalse_ShouldBeExpected()
[Test]
public void Format_ValidBoolTrue_ShouldBeExpected()
{
- var result = Radix.Hex.Format(new BOOL(true));
+ var result = Radix.Hex.FormatValue(new BOOL(true));
result.Should().Be("16#1");
}
@@ -45,7 +45,7 @@ public void Format_ValidBoolTrue_ShouldBeExpected()
[Test]
public void Format_ValidSint_ShouldBeExpectedFormat()
{
- var result = Radix.Hex.Format(new SINT(20));
+ var result = Radix.Hex.FormatValue(new SINT(20));
result.Should().Be("16#14");
}
@@ -53,7 +53,7 @@ public void Format_ValidSint_ShouldBeExpectedFormat()
[Test]
public void Format_ValidInt_ShouldBeExpectedFormat()
{
- var result = Radix.Hex.Format(new INT(20));
+ var result = Radix.Hex.FormatValue(new INT(20));
result.Should().Be("16#0014");
}
@@ -61,7 +61,7 @@ public void Format_ValidInt_ShouldBeExpectedFormat()
[Test]
public void Format_ValidDint_ShouldBeExpectedFormat()
{
- var result = Radix.Hex.Format(new DINT(20));
+ var result = Radix.Hex.FormatValue(new DINT(20));
result.Should().Be("16#0000_0014");
}
@@ -69,7 +69,7 @@ public void Format_ValidDint_ShouldBeExpectedFormat()
[Test]
public void Format_Dint1234567_ShouldBeExpectedFormat()
{
- var result = Radix.Hex.Format(new DINT(1234567));
+ var result = Radix.Hex.FormatValue(new DINT(1234567));
result.Should().Be("16#0012_d687");
}
@@ -77,7 +77,7 @@ public void Format_Dint1234567_ShouldBeExpectedFormat()
[Test]
public void Format_ValidLint_ShouldBeExpectedFormat()
{
- var result = Radix.Hex.Format(new LINT(20));
+ var result = Radix.Hex.FormatValue(new LINT(20));
result.Should().Be("16#0000_0000_0000_0014");
}
@@ -85,33 +85,33 @@ public void Format_ValidLint_ShouldBeExpectedFormat()
[Test]
public void Parse_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Hex.Parse(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Hex.ParseValue(null!)).Should().Throw();
}
[Test]
public void Parse_InvalidSpecifier_ShouldThrowArgumentException()
{
- FluentActions.Invoking(() => Radix.Hex.Parse("0000_0024")).Should().Throw()
+ FluentActions.Invoking(() => Radix.Hex.ParseValue("0000_0024")).Should().Throw()
.WithMessage("Input '0000_0024' does not have expected Hex format.");
}
[Test]
public void Parse_LengthZero_ShouldThrowArgumentException()
{
- FluentActions.Invoking(() => Radix.Hex.Parse("16#")).Should().Throw();
+ FluentActions.Invoking(() => Radix.Hex.ParseValue("16#")).Should().Throw();
}
[Test]
public void Parse_OutOfRangeValue_ShouldThrowArgumentOutOfRangeException()
{
- FluentActions.Invoking(() => Radix.Hex.Parse(
+ FluentActions.Invoking(() => Radix.Hex.ParseValue(
"16#0000_0000_0000_0000_0024_0000")).Should().Throw();
}
[Test]
public void Parse_ValidBool_ShouldBeExpected()
{
- var value = Radix.Hex.Parse("16#0");
+ var value = Radix.Hex.ParseValue("16#0");
value.Should().Be(false);
}
@@ -119,7 +119,7 @@ public void Parse_ValidBool_ShouldBeExpected()
[Test]
public void Parse_ValidSint_ShouldBeExpected()
{
- var value = Radix.Hex.Parse("16#14");
+ var value = Radix.Hex.ParseValue("16#14");
value.Should().Be(20);
}
@@ -127,7 +127,7 @@ public void Parse_ValidSint_ShouldBeExpected()
[Test]
public void Parse_ValidInt_ShouldBeExpected()
{
- var value = Radix.Hex.Parse("16#0014");
+ var value = Radix.Hex.ParseValue("16#0014");
value.Should().Be(20);
}
@@ -135,7 +135,7 @@ public void Parse_ValidInt_ShouldBeExpected()
[Test]
public void Parse_ValidDint_ShouldBeExpected()
{
- var value = Radix.Hex.Parse("16#0000_0014");
+ var value = Radix.Hex.ParseValue("16#0000_0014");
value.Should().Be(20);
}
@@ -143,7 +143,7 @@ public void Parse_ValidDint_ShouldBeExpected()
[Test]
public void Parse_ValidLint_ShouldBeExpected()
{
- var value = Radix.Hex.Parse("16#0000_0000_0000_0014");
+ var value = Radix.Hex.ParseValue("16#0000_0000_0000_0014");
value.Should().Be(20);
}
diff --git a/tests/L5Sharp.Tests/Enums/RadixNullTests.cs b/tests/L5Sharp.Tests/Enums/RadixNullTests.cs
index 0bca2a1f..3ea57798 100644
--- a/tests/L5Sharp.Tests/Enums/RadixNullTests.cs
+++ b/tests/L5Sharp.Tests/Enums/RadixNullTests.cs
@@ -19,13 +19,13 @@ public void Null_WhenCalled_ShouldBeExpected()
[Test]
public void Format_WhenCalled_ReturnsNull()
{
- FluentActions.Invoking(() => Radix.Null.Format(new DINT())).Should().Throw();
+ FluentActions.Invoking(() => Radix.Null.FormatValue(new DINT())).Should().Throw();
}
[Test]
public void Parse_WhenCalled_ReturnsNull()
{
- FluentActions.Invoking(() => Radix.Null.Parse("Doesn't matter")).Should().Throw();
+ FluentActions.Invoking(() => Radix.Null.ParseValue("Doesn't matter")).Should().Throw();
}
}
}
\ No newline at end of file
diff --git a/tests/L5Sharp.Tests/Enums/RadixOctalTests.cs b/tests/L5Sharp.Tests/Enums/RadixOctalTests.cs
index 17a17370..9fec95f8 100644
--- a/tests/L5Sharp.Tests/Enums/RadixOctalTests.cs
+++ b/tests/L5Sharp.Tests/Enums/RadixOctalTests.cs
@@ -17,19 +17,19 @@ public void Octal_WhenCalled_ShouldBeExpected()
[Test]
public void Format_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Octal.Format(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Octal.FormatValue(null!)).Should().Throw();
}
[Test]
public void Format_NonSupportedAtomic_ShouldThrowNotSupportedException()
{
- FluentActions.Invoking(() => Radix.Octal.Format(new REAL())).Should().Throw();
+ FluentActions.Invoking(() => Radix.Octal.FormatValue(new REAL())).Should().Throw();
}
[Test]
public void Format_BoolFalse_ShouldBeExpected()
{
- var result = Radix.Octal.Format(new BOOL());
+ var result = Radix.Octal.FormatValue(new BOOL());
result.Should().Be("8#0");
}
@@ -37,7 +37,7 @@ public void Format_BoolFalse_ShouldBeExpected()
[Test]
public void Format_BoolTrue_ShouldBeExpected()
{
- var result = Radix.Octal.Format(new BOOL(true));
+ var result = Radix.Octal.FormatValue(new BOOL(true));
result.Should().Be("8#1");
}
@@ -45,7 +45,7 @@ public void Format_BoolTrue_ShouldBeExpected()
[Test]
public void Format_ValidSint_ShouldBeExpectedFormat()
{
- var result = Radix.Octal.Format(new SINT(20));
+ var result = Radix.Octal.FormatValue(new SINT(20));
result.Should().Be("8#024");
}
@@ -53,7 +53,7 @@ public void Format_ValidSint_ShouldBeExpectedFormat()
[Test]
public void Format_ValidInt_ShouldBeExpectedFormat()
{
- var result = Radix.Octal.Format(new INT(20));
+ var result = Radix.Octal.FormatValue(new INT(20));
result.Should().Be("8#000_024");
}
@@ -61,7 +61,7 @@ public void Format_ValidInt_ShouldBeExpectedFormat()
[Test]
public void Format_ValidDint_ShouldBeExpectedFormat()
{
- var result = Radix.Octal.Format(new DINT(20));
+ var result = Radix.Octal.FormatValue(new DINT(20));
result.Should().Be("8#000_000_000_024");
}
@@ -69,7 +69,7 @@ public void Format_ValidDint_ShouldBeExpectedFormat()
[Test]
public void Format_ValidLint_ShouldBeExpectedFormat()
{
- var result = Radix.Octal.Format(new LINT(20));
+ var result = Radix.Octal.FormatValue(new LINT(20));
result.Should().Be("8#000_000_000_000_000_000_000_024");
}
@@ -77,33 +77,33 @@ public void Format_ValidLint_ShouldBeExpectedFormat()
[Test]
public void Parse_Null_ShouldThrowArgumentNullException()
{
- FluentActions.Invoking(() => Radix.Octal.Parse(null!)).Should().Throw();
+ FluentActions.Invoking(() => Radix.Octal.ParseValue(null!)).Should().Throw();
}
[Test]
public void Parse_InvalidSpecifier_ShouldThrowArgumentException()
{
- FluentActions.Invoking(() => Radix.Octal.Parse("00_000_000_024")).Should().Throw()
+ FluentActions.Invoking(() => Radix.Octal.ParseValue("00_000_000_024")).Should().Throw()
.WithMessage("Input '00_000_000_024' does not have expected Octal format.");
}
[Test]
public void Parse_LengthZero_ShouldThrowArgumentException()
{
- FluentActions.Invoking(() => Radix.Octal.Parse("8#")).Should().Throw();
+ FluentActions.Invoking(() => Radix.Octal.ParseValue("8#")).Should().Throw();
}
[Test]
public void Parse_OutOfRangeValue_ShouldThrowArgumentOutOfRangeException()
{
- FluentActions.Invoking(() => Radix.Octal.Parse(
+ FluentActions.Invoking(() => Radix.Octal.ParseValue(
"8#000_000_000_000_000_000_000_000_024")).Should().Throw();
}
[Test]
public void Parse_ValidBool_ShouldBeExpected()
{
- var value = Radix.Octal.Parse("8#0");
+ var value = Radix.Octal.ParseValue("8#0");
value.Should().Be(false);
}
@@ -111,7 +111,7 @@ public void Parse_ValidBool_ShouldBeExpected()
[Test]
public void Parse_ValidSint_ShouldBeExpected()
{
- var value = Radix.Octal.Parse("8#024");
+ var value = Radix.Octal.ParseValue("8#024");
value.Should().Be(20);
}
@@ -119,7 +119,7 @@ public void Parse_ValidSint_ShouldBeExpected()
[Test]
public void Parse_ValidInt_ShouldBeExpected()
{
- var value = Radix.Octal.Parse("8#000_024");
+ var value = Radix.Octal.ParseValue("8#000_024");
value.Should().Be(20);
}
@@ -127,7 +127,7 @@ public void Parse_ValidInt_ShouldBeExpected()
[Test]
public void Parse_ValidDint_ShouldBeExpected()
{
- var value = Radix.Octal.Parse("8#00_000_000_024");
+ var value = Radix.Octal.ParseValue("8#00_000_000_024");
value.Should().Be(20);
}
@@ -136,7 +136,7 @@ public void Parse_ValidDint_ShouldBeExpected()
[Test]
public void Parse_ValidLint_ShouldBeExpected()
{
- var value = Radix.Octal.Parse("8#0_000_000_000_000_000_000_024");
+ var value = Radix.Octal.ParseValue("8#0_000_000_000_000_000_000_024");
value.Should().Be(20);
}
diff --git a/tests/L5Sharp.Tests/LogixParserTests.cs b/tests/L5Sharp.Tests/LogixParserTests.cs
new file mode 100644
index 00000000..4baf19e7
--- /dev/null
+++ b/tests/L5Sharp.Tests/LogixParserTests.cs
@@ -0,0 +1,133 @@
+using FluentAssertions;
+
+namespace L5Sharp.Tests;
+
+[TestFixture]
+public class LogixParserTests
+{
+ [Test]
+ [TestCase("true", true, typeof(bool))]
+ [TestCase("123", 123, typeof(int))]
+ [TestCase("lol", "lol", typeof(string))]
+ [TestCase("1.234", 1.234, typeof(decimal))]
+ public void Parse_Primitives_ShouldBeExpectedValue(string input, object expected, Type type)
+ {
+ var result = input.Parse(type);
+
+ result.Should().Be(expected);
+ result.Should().BeOfType(type);
+ }
+
+ [Test]
+ public void Parse_DateTime_ShouldBeExpected()
+ {
+ var result = "1/1/2024 1:23:45 AM".Parse();
+
+ result.Should().BeAfter(new DateTime(2024, 1, 1));
+ }
+
+ [Test]
+ public void Parse_LogixEnum_ShouldBeExpected()
+ {
+ var result = "ReadWrite".Parse();
+
+ result.Should().NotBeNull();
+ result.Should().Be(ExternalAccess.ReadWrite);
+ }
+
+ [Test]
+ public void Parse_Radix_ShouldBeExpected()
+ {
+ var result = "Decimal".Parse();
+
+ result.Should().NotBeNull();
+ result.Should().Be(Radix.Decimal);
+ }
+
+ [Test]
+ public void Parse_RadixByValue_ShouldBeExpected()
+ {
+ var result = "ASCII".Parse();
+
+ result.Should().NotBeNull();
+ result.Should().Be(Radix.Ascii);
+ }
+
+ [Test]
+ public void Parse_TriggerOperationWhichHasIntValueType_ShouldBeExpected()
+ {
+ var result = "1".Parse();
+
+ result.Should().NotBeNull();
+ result.Should().Be(TriggerOperation.TriggerLevel);
+ }
+
+ [Test]
+ public void Parse_TriggerOperationWhichHasIntValueTypeByName_ShouldBeExpected()
+ {
+ var result = "PositiveSlope".Parse();
+
+ result.Should().NotBeNull();
+ result.Should().Be(TriggerOperation.PositiveSlope);
+ }
+
+ [Test]
+ public void Parse_OperatorWhichHasSymbolValues_ShouldBeExpected()
+ {
+ var result = "=".Parse();
+
+ result.Should().NotBeNull();
+ result.Should().Be(Operator.Equal);
+ }
+
+ [Test]
+ public void ParseByType_LogixEnum_ShouldBeExpected()
+ {
+ var result = "RLL".Parse(typeof(RoutineType));
+
+ result.Should().NotBeNull();
+ result.Should().Be(RoutineType.RLL);
+ }
+
+ [Test]
+ public void TryParse_LogixEnum_ShouldBeExpected()
+ {
+ var result = "Periodic".TryParse();
+
+ result.Should().NotBeNull();
+ result.Should().Be(TaskType.Periodic);
+ }
+
+ [Test]
+ public void TryParse_InvalidEnum_ShouldBeNull()
+ {
+ var result = "Fake".TryParse