Made registers use the IRegister interface

- cleanup and refactoring
- fully implemented auto prop register generator unit tests #4
- added plc test program c30 fpx-h
- fixed bitarray setback
- cleaned up examples and added new ones with addition of attributes for later additions
This commit is contained in:
Felix Weiß
2023-06-15 20:04:38 +02:00
parent 6ca8e9de96
commit 09f4da54a9
23 changed files with 1478 additions and 811 deletions

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -25,7 +26,7 @@ namespace MewtocolNet {
public bool PollerActive => !pollerTaskStopped;
internal event Action PolledCycle;
internal volatile bool pollerTaskRunning;
internal volatile bool pollerTaskStopped;
internal volatile bool pollerIsPaused;
@@ -214,7 +215,7 @@ namespace MewtocolNet {
/// <param name="_name">A naming definition for QOL, doesn't effect PLC and is optional</param>
public void AddRegister (int _address, RegisterType _type, string _name = null) {
Register toAdd = null;
IRegister toAdd = null;
//as number registers
if (_type == RegisterType.DT_short) {
@@ -243,30 +244,29 @@ namespace MewtocolNet {
internal void AddRegister (Type _colType, int _address, RegisterType _type, string _name = null) {
Register toAdd = null;
IRegister toAdd = null;
//as number registers
if (_type == RegisterType.DT_short) {
toAdd = new NRegister<short>(_address, _name);
toAdd = new NRegister<short>(_address, _name).WithCollectionType(_colType);
}
if (_type == RegisterType.DT_ushort) {
toAdd = new NRegister<ushort>(_address, _name);
toAdd = new NRegister<ushort>(_address, _name).WithCollectionType(_colType);
}
if (_type == RegisterType.DDT_int) {
toAdd = new NRegister<int>(_address, _name);
toAdd = new NRegister<int>(_address, _name).WithCollectionType(_colType);
}
if (_type == RegisterType.DDT_uint) {
toAdd = new NRegister<uint>(_address, _name);
toAdd = new NRegister<uint>(_address, _name).WithCollectionType(_colType);
}
if (_type == RegisterType.DDT_float) {
toAdd = new NRegister<float>(_address, _name);
toAdd = new NRegister<float>(_address, _name).WithCollectionType(_colType);
}
if (toAdd == null) {
toAdd = new BRegister(_address, _type, _name);
toAdd = new BRegister(_address, _type, _name).WithCollectionType(_colType);
}
toAdd.collectionType = _colType;
Registers.Add(toAdd);
}
@@ -328,7 +328,7 @@ namespace MewtocolNet {
throw new NotSupportedException($"_lenght parameter only allowed for register of type string");
}
Register toAdd;
IRegister toAdd;
if (regType == typeof(short)) {
toAdd = new NRegister<short>(_address, _name);
@@ -359,7 +359,8 @@ namespace MewtocolNet {
}
internal void AddRegister<T> (Type _colType, int _address, int _length = 1, string _name = null, bool _isBitwise = false, Type _enumType = null) {
//Internal register adding for auto register collection building
internal void AddRegister<T> (Type _colType, int _address, PropertyInfo boundProp, int _length = 1, bool _isBitwise = false, Type _enumType = null) {
Type regType = typeof(T);
@@ -367,45 +368,51 @@ namespace MewtocolNet {
throw new NotSupportedException($"_lenght parameter only allowed for register of type string");
}
if (Registers.Any(x => x.MemoryAdress == _address) && _isBitwise) {
if (Registers.Any(x => x.MemoryAddress == _address) && _isBitwise) {
return;
}
Register reg = null;
IRegister reg = null;
string propName = boundProp.Name;
//rename the property name to prevent duplicate names in case of a bitwise prop
if(_isBitwise && regType == typeof(short))
propName = $"Auto_Bitwise_DT{_address}";
if (_isBitwise && regType == typeof(int))
propName = $"Auto_Bitwise_DDT{_address}";
if (regType == typeof(short)) {
reg = new NRegister<short>(_address, _name, _isBitwise);
reg = new NRegister<short>(_address, propName, _isBitwise, _enumType).WithCollectionType(_colType);
} else if (regType == typeof(ushort)) {
reg = new NRegister<ushort>(_address, _name);
reg = new NRegister<ushort>(_address, propName).WithCollectionType(_colType);
} else if (regType == typeof(int)) {
reg = new NRegister<int>(_address, _name, _isBitwise, _enumType);
reg = new NRegister<int>(_address, propName, _isBitwise, _enumType).WithCollectionType(_colType);
} else if (regType == typeof(uint)) {
reg = new NRegister<uint>(_address, _name);
reg = new NRegister<uint>(_address, propName).WithCollectionType(_colType);
} else if (regType == typeof(float)) {
reg = new NRegister<float>(_address, _name);
reg = new NRegister<float>(_address, propName).WithCollectionType(_colType);
} else if (regType == typeof(string)) {
reg = new SRegister(_address, _length, _name);
reg = new SRegister(_address, _length, propName).WithCollectionType(_colType);
} else if (regType == typeof(TimeSpan)) {
reg = new NRegister<TimeSpan>(_address, _name);
reg = new NRegister<TimeSpan>(_address, propName).WithCollectionType(_colType);
} else if (regType == typeof(bool)) {
reg = new BRegister(_address, RegisterType.R, _name);
reg = new BRegister(_address, RegisterType.R, propName).WithCollectionType(_colType);
}
if (reg == null) {
throw new NotSupportedException($"The type {regType} is not allowed for Registers \n" +
$"Allowed are: short, ushort, int, uint, float and string");
} else {
if (Registers.Any(x => x.GetRegisterPLCName() == reg.GetRegisterPLCName()) && !_isBitwise) {
throw new NotSupportedException($"Cannot add a register multiple times, " +
$"make sure that all register attributes or AddRegister assignments have different adresses.");
}
reg.collectionType = _colType;
Registers.Add(reg);
}
if (Registers.Any(x => x.GetRegisterPLCName() == reg.GetRegisterPLCName()) && !_isBitwise) {
throw new NotSupportedException($"Cannot add a register multiple times, " +
$"make sure that all register attributes or AddRegister assignments have different adresses.");
}
Registers.Add(reg);
}
#endregion
@@ -416,7 +423,7 @@ namespace MewtocolNet {
/// Gets a register that was added by its name
/// </summary>
/// <returns></returns>
public Register GetRegister (string name) {
public IRegister GetRegister (string name) {
return Registers.FirstOrDefault(x => x.Name == name);
@@ -427,13 +434,18 @@ namespace MewtocolNet {
/// </summary>
/// <typeparam name="T">The type of register</typeparam>
/// <returns>A casted register or the <code>default</code> value</returns>
public T GetRegister<T> (string name) where T : Register {
public T GetRegister<T> (string name) where T : IRegister {
try {
var reg = Registers.FirstOrDefault(x => x.Name == name);
return reg as T;
return (T)reg;
} catch (InvalidCastException) {
return default(T);
}
}
#endregion
@@ -443,7 +455,7 @@ namespace MewtocolNet {
/// <summary>
/// Gets a list of all added registers
/// </summary>
public List<Register> GetAllRegisters () {
public List<IRegister> GetAllRegisters () {
return Registers;
@@ -453,7 +465,7 @@ namespace MewtocolNet {
#region Event Invoking
internal void InvokeRegisterChanged (Register reg) {
internal void InvokeRegisterChanged (IRegister reg) {
RegisterChanged?.Invoke(reg);

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace MewtocolNet {
/// <summary>
/// An interface for all register types
/// </summary>
public interface IRegister {
/// <summary>
/// Gets called whenever the value was changed
/// </summary>
event Action<object> ValueChanged;
/// <summary>
/// The name of the register
/// </summary>
string Name { get; }
/// <summary>
/// The current value of the register
/// </summary>
object Value { get; }
/// <summary>
/// The plc memory address of the register
/// </summary>
int MemoryAddress { get; }
/// <summary>
/// Indicates if the register is processed bitwise
/// </summary>
/// <returns>True if bitwise</returns>
bool IsUsedBitwise();
/// <summary>
/// Generates a string describing the starting memory address of the register
/// </summary>
string GetStartingMemoryArea();
/// <summary>
/// Gets the current value formatted as a readable string
/// </summary>
string GetValueString();
/// <summary>
/// Builds the identifier for the mewtocol query string
/// </summary>
/// <returns></returns>
string BuildMewtocolQuery();
/// <summary>
/// Builds a register string that prepends the memory address fe. DDT or DT, X, Y etc
/// </summary>
string GetRegisterString();
/// <summary>
/// Builds a combined name for the attached property to uniquely identify the property register binding
/// </summary>
/// <returns></returns>
string GetCombinedName();
/// <summary>
/// Gets the name of the class that contains the attached property
/// </summary>
/// <returns></returns>
string GetContainerName();
/// <summary>
/// Builds a register name after the PLC convention <br/>
/// Example <code>DDT100, XA, X6, Y1, DT3300</code>
/// </summary>
string GetRegisterPLCName();
/// <summary>
/// Clears the current value of the register and resets it to default
/// </summary>
void ClearValue();
/// <summary>
/// Triggers a notifychanged update event
/// </summary>
void TriggerNotifyChange();
/// <summary>
/// Gets the type of the class collection its attached property is in or null
/// </summary>
/// <returns>The class name or null if manually added</returns>
Type GetCollectionType();
/// <summary>
/// Builds a readable string with all important register informations
/// </summary>
string ToString();
}
}

View File

@@ -15,8 +15,11 @@ using System.ComponentModel;
using System.Net;
using System.Threading;
using MewtocolNet.Queue;
using System.Reflection;
using System.Timers;
namespace MewtocolNet {
namespace MewtocolNet
{
/// <summary>
/// The PLC com interface class
@@ -36,7 +39,7 @@ namespace MewtocolNet {
/// <summary>
/// Gets triggered when a registered data register changes its value
/// </summary>
public event Action<Register> RegisterChanged;
public event Action<IRegister> RegisterChanged;
/// <summary>
/// Gets triggered when a property of the interface changes
@@ -114,7 +117,7 @@ namespace MewtocolNet {
/// <summary>
/// The registered data registers of the PLC
/// </summary>
public List<Register> Registers { get; set; } = new List<Register>();
public List<IRegister> Registers { get; set; } = new List<IRegister>();
private string ip;
private int port;
@@ -209,7 +212,7 @@ namespace MewtocolNet {
RegisterChanged += (o) => {
string address = $"{o.GetRegisterString()}{o.MemoryAdress}".PadRight(5, (char)32);
string address = $"{o.GetRegisterString()}{o.GetStartingMemoryArea()}".PadRight(5, (char)32);
Logger.Log($"{address} " +
$"{(o.Name != null ? $"({o.Name}) " : "")}" +
@@ -442,40 +445,46 @@ namespace MewtocolNet {
}
if (prop.PropertyType == typeof(short)) {
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, _name: propName);
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, prop);
}
if (prop.PropertyType == typeof(ushort)) {
AddRegister<ushort>(collection.GetType(), cAttribute.MemoryArea, _name: propName);
AddRegister<ushort>(collection.GetType(), cAttribute.MemoryArea, prop);
}
if (prop.PropertyType == typeof(int)) {
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, _name: propName);
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, prop);
}
if (prop.PropertyType == typeof(uint)) {
AddRegister<uint>(collection.GetType(), cAttribute.MemoryArea, _name: propName);
AddRegister<uint>(collection.GetType(), cAttribute.MemoryArea, prop);
}
if (prop.PropertyType == typeof(float)) {
AddRegister<float>(collection.GetType(), cAttribute.MemoryArea, _name: propName);
AddRegister<float>(collection.GetType(), cAttribute.MemoryArea, prop);
}
if (prop.PropertyType == typeof(string)) {
AddRegister<string>(collection.GetType(), cAttribute.MemoryArea, cAttribute.StringLength, _name: propName);
AddRegister<string>(collection.GetType(), cAttribute.MemoryArea, prop, cAttribute.StringLength);
}
if (prop.PropertyType.IsEnum) {
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, _name: propName, _enumType: prop.PropertyType);
if (cAttribute.BitCount == BitCount.B16) {
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, prop, _enumType: prop.PropertyType);
} else {
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, prop, _enumType: prop.PropertyType);
}
}
//read number as bit array
if (prop.PropertyType == typeof(BitArray)) {
if (cAttribute.BitCount == BitCount.B16) {
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, _name: propName, _isBitwise: true);
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true);
} else {
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, _name: propName, _isBitwise: true);
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true);
}
}
@@ -486,15 +495,15 @@ namespace MewtocolNet {
//var bitwiseCount = Registers.Count(x => x.Value.isUsedBitwise);
if (cAttribute.BitCount == BitCount.B16) {
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, _name: $"Auto_Bitwise_DT{cAttribute.MemoryArea}", _isBitwise: true);
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true);
} else {
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, _name: $"Auto_Bitwise_DDT{cAttribute.MemoryArea}", _isBitwise: true);
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true);
}
}
if (prop.PropertyType == typeof(TimeSpan)) {
AddRegister<TimeSpan>(collection.GetType(), cAttribute.MemoryArea, _name: propName);
AddRegister<TimeSpan>(collection.GetType(), cAttribute.MemoryArea, prop);
}
}
@@ -505,43 +514,51 @@ namespace MewtocolNet {
RegisterChanged += (reg) => {
//if the register is also used bitwise assign the boolean bit value to the according prop
if(reg.isUsedBitwise) {
//register is used bitwise
if(reg.IsUsedBitwise()) {
for (int i = 0; i < props.Length; i++) {
var prop = props[i];
var bitWiseFound = prop.GetCustomAttributes(true)
.FirstOrDefault(y => y.GetType() == typeof(RegisterAttribute) && ((RegisterAttribute)y).MemoryArea == reg.MemoryAdress);
.FirstOrDefault(y => y.GetType() == typeof(RegisterAttribute) && ((RegisterAttribute)y).MemoryArea == reg.MemoryAddress);
if(bitWiseFound != null) {
if(bitWiseFound != null && reg is NRegister<short> reg16) {
var casted = (RegisterAttribute)bitWiseFound;
var bitIndex = casted.AssignedBitIndex;
var bytes = BitConverter.GetBytes(reg16.Value);
BitArray bitAr = new BitArray(bytes);
BitArray bitAr = null;
if (bitIndex < bitAr.Length && bitIndex >= 0) {
if (reg is NRegister<short> reg16) {
var bytes = BitConverter.GetBytes((short)reg16.Value);
bitAr = new BitArray(bytes);
} else if(reg is NRegister<int> reg32) {
var bytes = BitConverter.GetBytes((int)reg32.Value);
bitAr = new BitArray(bytes);
}
if (bitAr != null && bitIndex < bitAr.Length && bitIndex >= 0) {
//set the specific bit index if needed
prop.SetValue(collection, bitAr[bitIndex]);
collection.TriggerPropertyChanged(prop.Name);
}
} else if (bitWiseFound != null && reg is NRegister<int> reg32) {
var casted = (RegisterAttribute)bitWiseFound;
var bitIndex = casted.AssignedBitIndex;
var bytes = BitConverter.GetBytes(reg32.Value);
BitArray bitAr = new BitArray(bytes);
prop.SetValue(collection, bitAr[bitIndex]);
collection.TriggerPropertyChanged(prop.Name);
} else if (bitAr != null) {
//set the specific bit array if needed
prop.SetValue(collection, bitAr);
collection.TriggerPropertyChanged(prop.Name);
}
}
}
}
//updating normal properties
var foundToUpdate = props.FirstOrDefault(x => x.Name == reg.Name);
if (foundToUpdate != null) {
@@ -557,67 +574,25 @@ namespace MewtocolNet {
//check if bit parse mode
if (registerAttr.AssignedBitIndex == -1) {
//setting back booleans
if (foundToUpdate.PropertyType == typeof(bool)) {
foundToUpdate.SetValue(collection, ((BRegister)reg).Value);
}
HashSet<Type> NumericTypes = new HashSet<Type> {
typeof(bool),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(float),
typeof(TimeSpan),
typeof(string)
};
//setting back numbers
var regValue = ((IRegister)reg).Value;
if (foundToUpdate.PropertyType == typeof(short)) {
foundToUpdate.SetValue(collection, ((NRegister<short>)reg).Value);
}
if (foundToUpdate.PropertyType == typeof(ushort)) {
foundToUpdate.SetValue(collection, ((NRegister<ushort>)reg).Value);
}
if (foundToUpdate.PropertyType == typeof(int)) {
foundToUpdate.SetValue(collection, ((NRegister<int>)reg).Value);
}
if (foundToUpdate.PropertyType == typeof(uint)) {
foundToUpdate.SetValue(collection, ((NRegister<uint>)reg).Value);
}
if (foundToUpdate.PropertyType == typeof(float)) {
foundToUpdate.SetValue(collection, ((NRegister<float>)reg).Value);
if (NumericTypes.Any(x => foundToUpdate.PropertyType == x)) {
foundToUpdate.SetValue(collection, regValue);
}
if (foundToUpdate.PropertyType.IsEnum) {
foundToUpdate.SetValue(collection, ((NRegister<int>)reg).Value);
}
if (foundToUpdate.PropertyType == typeof(TimeSpan)) {
foundToUpdate.SetValue(collection, ((NRegister<TimeSpan>)reg).Value);
}
//setting back strings
if (foundToUpdate.PropertyType == typeof(string)) {
foundToUpdate.SetValue(collection, ((SRegister)reg).Value);
}
}
if (foundToUpdate.PropertyType == typeof(BitArray)) {
//setting back bit registers
if (reg is NRegister<short> shortReg) {
var bytes = BitConverter.GetBytes(shortReg.Value);
BitArray bitAr = new BitArray(bytes);
foundToUpdate.SetValue(collection, bitAr);
}
if (reg is NRegister<int> intReg) {
var bytes = BitConverter.GetBytes(intReg.Value);
BitArray bitAr = new BitArray(bytes);
foundToUpdate.SetValue(collection, bitAr);
foundToUpdate.SetValue(collection, regValue);
}
}

View File

@@ -166,7 +166,7 @@ namespace MewtocolNet {
/// <param name="_toRead">The register to read</param>
public async Task<BRegisterResult> ReadBoolRegister (BRegister _toRead) {
string requeststring = $"%{GetStationNumber()}#RCS{_toRead.BuildMewtocolIdent()}";
string requeststring = $"%{GetStationNumber()}#RCS{_toRead.BuildMewtocolQuery()}";
var result = await SendCommandAsync(requeststring);
if(!result.Success) {
@@ -198,7 +198,7 @@ namespace MewtocolNet {
/// <returns>The success state of the write operation</returns>
public async Task<bool> WriteBoolRegister (BRegister _toWrite, bool value) {
string requeststring = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolIdent()}{(value ? "1" : "0")}";
string requeststring = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolQuery()}{(value ? "1" : "0")}";
var result = await SendCommandAsync(requeststring);
@@ -220,7 +220,7 @@ namespace MewtocolNet {
Type numType = typeof(T);
string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolIdent()}";
string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}";
var result = await SendCommandAsync(requeststring);
var failedResult = new NRegisterResult<T> {
@@ -335,7 +335,7 @@ namespace MewtocolNet {
toWriteVal = null;
}
string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolIdent()}{toWriteVal.ToHexString()}";
string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolQuery()}{toWriteVal.ToHexString()}";
var result = await SendCommandAsync(requeststring);
@@ -362,7 +362,7 @@ namespace MewtocolNet {
/// <returns></returns>
public async Task<SRegisterResult> ReadStringRegister (SRegister _toRead, int _stationNumber = 1) {
string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolIdent()}";
string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}";
var result = await SendCommandAsync(requeststring);
if (result.Success)
_toRead.SetValueFromPLC(result.Response.ParseDTString());

View File

@@ -8,7 +8,8 @@ using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace MewtocolNet.RegisterAttributes {
namespace MewtocolNet.RegisterAttributes
{
/// <summary>
/// A register collection base with full auto read and notification support built in
@@ -34,6 +35,22 @@ namespace MewtocolNet.RegisterAttributes {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Use this on the setter method of a property to enable automatic property register writing
/// </summary>
public static void AutoSetter<T> (object value, ref T privateField) {
if(value is IRegister reg) {
privateField = (T)reg.Value;
return;
}
privateField = (T)value;
}
/// <summary>
/// Gets called when the register collection base was linked to its parent mewtocol interface
/// </summary>

View File

@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Text;
using MewtocolNet;
@@ -7,17 +8,47 @@ namespace MewtocolNet.Registers {
/// <summary>
/// Defines a register containing a boolean
/// </summary>
public class BRegister : Register {
public class BRegister : IRegister, INotifyPropertyChanged {
/// <summary>
/// Gets called whenever the value was changed
/// </summary>
public event Action<object> ValueChanged;
/// <summary>
/// Triggers when a property on the register changes
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
internal RegisterType RegType { get; private set; }
internal SpecialAddress SpecialAddress { get; private set; }
internal bool LastValue;
internal SpecialAddress SpecialAddress { get; private set; }
internal Type collectionType;
/// <summary>
/// The type of collection the register is in or null of added manually
/// </summary>
public Type CollectionType => collectionType;
internal bool lastValue;
/// <summary>
/// The value of the register
/// </summary>
public bool Value => LastValue;
public object Value => lastValue;
internal string name;
/// <summary>
/// The register name or null of not defined
/// </summary>
public string Name => name;
internal int memoryAdress;
/// <summary>
/// The registers memory adress if not a special register
/// </summary>
public int MemoryAddress => memoryAdress;
/// <summary>
/// Defines a register containing a number
@@ -27,7 +58,11 @@ namespace MewtocolNet.Registers {
/// <param name="_name">Name of the register</param>
public BRegister (int _address, RegisterType _type = RegisterType.R, string _name = null) {
if (_address > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999");
if (_address > 99999) throw new NotSupportedException("Memory addresses cant be greater than 99999");
if (_type != RegisterType.X && _type != RegisterType.Y && _type != RegisterType.R)
throw new NotSupportedException("The register type cant be numeric, use X, Y or R");
memoryAdress = _address;
name = _name;
@@ -44,7 +79,10 @@ namespace MewtocolNet.Registers {
public BRegister (SpecialAddress _address, RegisterType _type = RegisterType.R, string _name = null) {
if (_address == SpecialAddress.None)
throw new NotSupportedException("Special adress cant be none");
throw new NotSupportedException("Special address cant be none");
if (_type != RegisterType.X && _type != RegisterType.Y && _type != RegisterType.R)
throw new NotSupportedException("The register type cant be numeric, use X, Y or R");
SpecialAddress = _address;
name = _name;
@@ -53,15 +91,22 @@ namespace MewtocolNet.Registers {
}
internal BRegister WithCollectionType(Type colType) {
collectionType = colType;
return this;
}
/// <summary>
/// Builds the register area name
/// </summary>
public override string BuildMewtocolIdent () {
public string BuildMewtocolQuery () {
//build area code from register type
StringBuilder asciistring = new StringBuilder(RegType.ToString());
if(SpecialAddress == SpecialAddress.None) {
asciistring.Append(MemoryAdress.ToString().PadLeft(4, '0'));
asciistring.Append(MemoryAddress.ToString().PadLeft(4, '0'));
} else {
asciistring.Append(SpecialAddress.ToString().PadLeft(4, '0'));
}
@@ -71,11 +116,52 @@ namespace MewtocolNet.Registers {
}
internal void SetValueFromPLC (bool val) {
LastValue = val;
lastValue = val;
TriggerChangedEvnt(this);
TriggerNotifyChange();
}
public string GetStartingMemoryArea() {
if (SpecialAddress != SpecialAddress.None)
return SpecialAddress.ToString();
return MemoryAddress.ToString();
}
public bool IsUsedBitwise() => false;
public Type GetCollectionType() => CollectionType;
public string GetValueString() => Value.ToString();
public void ClearValue() => SetValueFromPLC(false);
public string GetRegisterString() => RegType.ToString();
public string GetCombinedName() => $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}";
public string GetContainerName() => $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}";
public string GetRegisterPLCName() {
if (SpecialAddress != SpecialAddress.None) {
return $"{GetRegisterString()}{SpecialAddress}";
}
return $"{GetRegisterString()}{MemoryAddress}";
}
internal void TriggerChangedEvnt(object changed) => ValueChanged?.Invoke(changed);
public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
public override string ToString() => $"{GetRegisterPLCName()} - Value: {GetValueString()}";
}
}

View File

@@ -1,18 +1,63 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Text;
namespace MewtocolNet.Registers {
/// <summary>
/// Defines a register containing a number
/// </summary>
/// <typeparam name="T">The type of the numeric value</typeparam>
public class NRegister<T> : Register {
public class NRegister<T> : IRegister {
internal T LastValue;
/// <summary>
/// Gets called whenever the value was changed
/// </summary>
public event Action<object> ValueChanged;
/// <summary>
/// Triggers when a property on the register changes
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
internal Type collectionType;
/// <summary>
/// The type of collection the register is in or null of added manually
/// </summary>
public Type CollectionType => collectionType;
internal T lastValue;
/// <summary>
/// The value of the register
/// </summary>
public T Value => LastValue;
public object Value => lastValue;
internal string name;
/// <summary>
/// The register name or null of not defined
/// </summary>
public string Name => name;
internal int memoryAdress;
/// <summary>
/// The registers memory adress if not a special register
/// </summary>
public int MemoryAddress => memoryAdress;
internal int memoryLength;
/// <summary>
/// The rgisters memory length
/// </summary>
public int MemoryLength => memoryLength;
internal bool isUsedBitwise { get; set; }
internal Type enumType { get; set; }
/// <summary>
/// Defines a register containing a number
@@ -23,6 +68,7 @@ namespace MewtocolNet.Registers {
if (_adress > 99999)
throw new NotSupportedException("Memory adresses cant be greater than 99999");
memoryAdress = _adress;
name = _name;
Type numType = typeof(T);
@@ -71,15 +117,159 @@ namespace MewtocolNet.Registers {
}
internal NRegister<T> WithCollectionType (Type colType) {
collectionType = colType;
return this;
}
internal void SetValueFromPLC (object val) {
LastValue = (T)val;
lastValue = (T)val;
TriggerChangedEvnt(this);
TriggerNotifyChange();
}
public string GetStartingMemoryArea () => this.MemoryAddress.ToString();
public Type GetCollectionType() => CollectionType;
public bool IsUsedBitwise() => isUsedBitwise;
public string GetValueString() {
//is number or bitwise
if(enumType == null) {
return $"{Value}{(isUsedBitwise ? $" [{GetBitwise().ToBitString()}]" : "")}";
}
//is enum
var dict = new Dictionary<int, string>();
foreach (var name in Enum.GetNames(enumType)) {
int enumKey = (int)Enum.Parse(enumType, name);
if (!dict.ContainsKey(enumKey)) {
dict.Add(enumKey, name);
}
}
if (enumType != null && Value is short shortVal) {
if (dict.ContainsKey(shortVal)) {
return $"{Value} ({dict[shortVal]})";
} else {
return $"{Value} (Missing Enum)";
}
}
if (enumType != null && Value is int intVal) {
if (dict.ContainsKey(intVal)) {
return $"{Value} ({dict[intVal]})";
} else {
return $"{Value} (Missing Enum)";
}
}
return Value.ToString();
}
/// <summary>
/// Gets the register bitwise if its a 16 or 32 bit int
/// </summary>
/// <returns>A bitarray</returns>
public BitArray GetBitwise() {
if (this is NRegister<short> shortReg) {
var bytes = BitConverter.GetBytes((short)Value);
BitArray bitAr = new BitArray(bytes);
return bitAr;
}
if (this is NRegister<int> intReg) {
var bytes = BitConverter.GetBytes((int)Value);
BitArray bitAr = new BitArray(bytes);
return bitAr;
}
return null;
}
public string BuildMewtocolQuery() {
StringBuilder asciistring = new StringBuilder("D");
asciistring.Append(MemoryAddress.ToString().PadLeft(5, '0'));
asciistring.Append((MemoryAddress + MemoryLength).ToString().PadLeft(5, '0'));
return asciistring.ToString();
}
public string GetRegisterString() {
if(Value is short) {
return "DT";
}
if (Value is ushort) {
return "DT";
}
if (Value is int) {
return "DDT";
}
if (Value is uint) {
return "DDT";
}
if (Value is float) {
return "DDT";
}
if (Value is TimeSpan) {
return "DDT";
}
throw new NotSupportedException("Numeric type is not supported");
}
public string GetCombinedName() => $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}";
public string GetContainerName() => $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}";
public string GetRegisterPLCName() => $"{GetRegisterString()}{MemoryAddress}";
public void ClearValue() => SetValueFromPLC(default(T));
internal void TriggerChangedEvnt(object changed) => ValueChanged?.Invoke(changed);
public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
public override string ToString() => $"{GetRegisterPLCName()} - Value: {GetValueString()}";
}
}

View File

@@ -21,12 +21,14 @@ namespace MewtocolNet.Registers {
/// Trys to get the value of there is one
/// </summary>
public bool TryGetValue (out T value) {
if(Result.Success) {
value = Register.Value;
value = (T)Register.Value;
return true;
}
value = default(T);
return false;
}
}

View File

@@ -1,388 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace MewtocolNet.Registers {
/// <summary>
/// A class describing a register
/// </summary>
public abstract class Register : INotifyPropertyChanged {
/// <summary>
/// Gets called whenever the value was changed
/// </summary>
public event Action<object> ValueChanged;
/// <summary>
/// Triggers when a property on the register changes
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
internal Type collectionType;
/// <summary>
/// The type of collection the register is in or null of added manually
/// </summary>
public Type CollectionType => collectionType;
internal string name;
/// <summary>
/// The register name or null of not defined
/// </summary>
public string Name => name;
internal int memoryAdress;
/// <summary>
/// The registers memory adress if not a special register
/// </summary>
public int MemoryAdress => memoryAdress;
internal int memoryLength;
/// <summary>
/// The rgisters memory length
/// </summary>
public int MemoryLength => memoryLength;
/// <summary>
/// The value of the register auto converted to a string
/// </summary>
public string StringValue => GetValueString();
/// <summary>
/// The name the register would have in the PLC
/// </summary>
public string RegisterPLCName => GetRegisterPLCName();
/// <summary>
/// The combined name with the holding register class type infront
/// </summary>
public string CombinedName => GetCombinedName();
/// <summary>
/// The name of the class that contains this register or empty if it was added manually
/// </summary>
public string ContainerName => GetContainerName();
internal bool isUsedBitwise { get; set; }
internal Type enumType { get; set; }
internal Register () {
ValueChanged += (obj) => {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StringValue)));
};
}
/// <summary>
/// Builds the register area name
/// </summary>
public virtual string BuildMewtocolIdent() {
StringBuilder asciistring = new StringBuilder("D");
asciistring.Append(MemoryAdress.ToString().PadLeft(5, '0'));
asciistring.Append((MemoryAdress + MemoryLength).ToString().PadLeft(5, '0'));
return asciistring.ToString();
}
internal void TriggerChangedEvnt(object changed) {
ValueChanged?.Invoke(changed);
}
internal void TriggerNotifyChange () {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
}
internal void ClearValue () {
if (enumType != null && this is NRegister<int> intEnumReg) {
intEnumReg.SetValueFromPLC((int)0);
}
if (this is NRegister<short> shortReg) {
shortReg.SetValueFromPLC((short)0);
}
if (this is NRegister<ushort> ushortReg) {
ushortReg.SetValueFromPLC((ushort)0);
}
if (this is NRegister<int> intReg) {
intReg.SetValueFromPLC((int)0);
}
if (this is NRegister<uint> uintReg) {
uintReg.SetValueFromPLC((uint)0);
}
if (this is NRegister<float> floatReg) {
floatReg.SetValueFromPLC((float)0);
}
if (this is NRegister<TimeSpan> tsReg) {
tsReg.SetValueFromPLC(TimeSpan.Zero);
}
if (this is BRegister boolReg) {
boolReg.SetValueFromPLC(false);
}
if (this is SRegister stringReg) {
stringReg.SetValueFromPLC(null);
}
}
/// <summary>
/// Gets the starting memory are either numeric or A,B,C,D etc for special areas like inputs
/// </summary>
/// <returns></returns>
public string GetStartingMemoryArea () {
if (this is BRegister bReg && bReg.SpecialAddress != SpecialAddress.None) {
return bReg.SpecialAddress.ToString();
}
return this.MemoryAdress.ToString();
}
/// <summary>
/// Gets the current value in the adress as a string
/// </summary>
/// <returns></returns>
public string GetValueString () {
if (enumType != null && this is NRegister<int> intEnumReg) {
var dict = new Dictionary<int, string>();
foreach (var name in Enum.GetNames(enumType)) {
int enumKey = (int)Enum.Parse(enumType, name);
if(!dict.ContainsKey(enumKey)) {
dict.Add(enumKey, name);
}
}
if(dict.ContainsKey(intEnumReg.Value)) {
return $"{intEnumReg.Value} ({dict[intEnumReg.Value]})";
} else {
return $"{intEnumReg.Value} (Missing Enum)";
}
}
if (this is NRegister<short> shortReg) {
return $"{shortReg.Value}{(isUsedBitwise ? $" [{shortReg.GetBitwise().ToBitString()}]" : "")}";
}
if (this is NRegister<ushort> ushortReg) {
return ushortReg.Value.ToString();
}
if (this is NRegister<int> intReg) {
return $"{intReg.Value}{(isUsedBitwise ? $" [{intReg.GetBitwise().ToBitString()}]" : "")}";
}
if (this is NRegister<uint> uintReg) {
return uintReg.Value.ToString();
}
if (this is NRegister<float> floatReg) {
return floatReg.Value.ToString();
}
if (this is NRegister<TimeSpan> tsReg) {
return tsReg.Value.ToString();
}
if (this is BRegister boolReg) {
return boolReg.Value.ToString();
}
if (this is SRegister stringReg) {
return stringReg.Value ?? "";
}
return "Type of the register is not supported.";
}
/// <summary>
/// Gets the register bitwise if its a 16 or 32 bit int
/// </summary>
/// <returns>A bitarray</returns>
public BitArray GetBitwise () {
if (this is NRegister<short> shortReg) {
var bytes = BitConverter.GetBytes(shortReg.Value);
BitArray bitAr = new BitArray(bytes);
return bitAr;
}
if (this is NRegister<int> intReg) {
var bytes = BitConverter.GetBytes(intReg.Value);
BitArray bitAr = new BitArray(bytes);
return bitAr;
}
return null;
}
/// <summary>
/// Gets the register dataarea string DT for 16bit and DDT for 32 bit types
/// </summary>
public string GetRegisterString () {
if (this is NRegister<short> shortReg) {
return "DT";
}
if (this is NRegister<ushort> ushortReg) {
return "DT";
}
if (this is NRegister<int> intReg) {
return "DDT";
}
if (this is NRegister<uint> uintReg) {
return "DDT";
}
if (this is NRegister<float> floatReg) {
return "DDT";
}
if (this is NRegister<TimeSpan> tsReg) {
return "DDT";
}
if (this is BRegister boolReg) {
return boolReg.RegType.ToString();
}
if (this is SRegister stringReg) {
return "DT";
}
return "Type of the register is not supported.";
}
/// <summary>
/// Builds a register from a given register string like DT100 / XA / Y1
/// </summary>
/// <param name="regString">The input string to parse</param>
/// <returns>A built register</returns>
public static Register FromString (string regString, string name = null) {
var match = Regex.Match(regString, @"(X|Y|R)([A-F]|[0-9_.-]{1,5})");
if (match != null && match.Success) {
var typeGroup = match.Groups[1].Value;
var areaGroup = match.Groups[2].Value;
bool isBool = false;
var parsedRegType = RegisterType.R;
if (new string[] { "X", "Y", "R" }.Contains(typeGroup)) {
switch (typeGroup) {
case "X":
parsedRegType = RegisterType.X;
isBool = true;
break;
case "Y":
parsedRegType = RegisterType.Y;
isBool = true;
break;
case "R":
parsedRegType = RegisterType.R;
isBool = true;
break;
}
}
if(!isBool) {
throw new NotSupportedException($"Register with value {regString} is not of type bool");
}
if (int.TryParse(areaGroup, out var parsedNum) && isBool) {
return new BRegister(parsedNum, parsedRegType, name);
} else if(Enum.TryParse<SpecialAddress>(areaGroup, out var parsedSpecial) && isBool) {
return new BRegister(parsedSpecial, parsedRegType, name);
}
}
throw new NotSupportedException($"Register with value {regString} is not supported");
}
public static NRegister<T> FromString<T> (string regString, string name = null) {
var match = Regex.Match(regString, @"(DT|DDT)([0-9_.-]{1,5})");
if (match != null && match.Success) {
var typeGroup = match.Groups[1].Value;
var areaGroup = match.Groups[2].Value;
bool isTypeDoubleSize = false;
bool isSupportedNumericFormat = false;
if(typeGroup == "")
switch (typeGroup) {
case "DT":
isSupportedNumericFormat = true;
break;
case "DDT":
isTypeDoubleSize = true;
isSupportedNumericFormat = true;
break;
}
if(typeof(T).IsDoubleNumericRegisterType() != isTypeDoubleSize) {
throw new NotSupportedException($"Input register type was {typeGroup}, the cast type was not of the same size");
}
if (int.TryParse(areaGroup, out var parsedNum) && typeof(T).IsNumericSupportedType() && isSupportedNumericFormat ) {
return new NRegister<T>(parsedNum, name);
}
}
throw new NotSupportedException($"Register with value {regString} is not supported");
}
public static SRegister FromString (string regString, int reserved, string name = null) {
var match = Regex.Match(regString, @"(DT)([0-9_.-]{1,5})");
if (match != null && match.Success) {
}
throw new NotSupportedException($"Register with value {regString} is not supported");
}
internal string GetCombinedName () {
return $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}";
}
internal string GetContainerName () {
return $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}";
}
internal string GetRegisterPLCName () {
if (this is BRegister bReg && bReg.SpecialAddress != SpecialAddress.None) {
return $"{GetRegisterString()}{bReg.SpecialAddress}";
}
return $"{GetRegisterString()}{MemoryAdress}";
}
}
}

View File

@@ -1,18 +1,54 @@
using System;
using System.ComponentModel;
using System.Text;
namespace MewtocolNet.Registers {
/// <summary>
/// Defines a register containing a string
/// </summary>
public class SRegister : Register {
private string lastVal = "";
public class SRegister : IRegister {
/// <summary>
/// The current value of the register
/// Gets called whenever the value was changed
/// </summary>
public string Value => lastVal;
public event Action<object> ValueChanged;
/// <summary>
/// Triggers when a property on the register changes
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
internal Type collectionType;
/// <summary>
/// The type of collection the register is in or null of added manually
/// </summary>
public Type CollectionType => collectionType;
internal string lastValue;
/// <summary>
/// The value of the register
/// </summary>
public object Value => lastValue;
internal string name;
/// <summary>
/// The register name or null of not defined
/// </summary>
public string Name => name;
internal int memoryAdress;
/// <summary>
/// The registers memory adress if not a special register
/// </summary>
public int MemoryAddress => memoryAdress;
internal int memoryLength;
/// <summary>
/// The registers memory length
/// </summary>
public int MemoryLength => memoryLength;
internal short ReservedSize { get; set; }
@@ -35,15 +71,22 @@ namespace MewtocolNet.Registers {
memoryLength = (int)Math.Round(wordsize + 1);
}
internal SRegister WithCollectionType(Type colType) {
collectionType = colType;
return this;
}
/// <summary>
/// Builds the register identifier for the mewotocol protocol
/// </summary>
public override string BuildMewtocolIdent() {
public string BuildMewtocolQuery() {
StringBuilder asciistring = new StringBuilder("D");
asciistring.Append(MemoryAdress.ToString().PadLeft(5, '0'));
asciistring.Append((MemoryAdress + MemoryLength).ToString().PadLeft(5, '0'));
asciistring.Append(MemoryAddress.ToString().PadLeft(5, '0'));
asciistring.Append((MemoryAddress + MemoryLength).ToString().PadLeft(5, '0'));
return asciistring.ToString();
}
@@ -55,18 +98,45 @@ namespace MewtocolNet.Registers {
StringBuilder asciistring = new StringBuilder("D");
asciistring.Append(MemoryAdress.ToString().PadLeft(5, '0'));
asciistring.Append((MemoryAdress + overwriteWordLength - 1).ToString().PadLeft(5, '0'));
asciistring.Append(MemoryAddress.ToString().PadLeft(5, '0'));
asciistring.Append((MemoryAddress + overwriteWordLength - 1).ToString().PadLeft(5, '0'));
return asciistring.ToString();
}
public Type GetCollectionType() => CollectionType;
public bool IsUsedBitwise() => false;
internal void SetValueFromPLC (string val) {
lastVal = val;
lastValue = val;
TriggerChangedEvnt(this);
TriggerNotifyChange();
}
public string GetStartingMemoryArea() => this.MemoryAddress.ToString();
public string GetValueString() => Value?.ToString() ?? "";
public string GetRegisterString() => "DT";
public string GetCombinedName() => $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}";
public string GetContainerName() => $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}";
public string GetRegisterPLCName() => $"{GetRegisterString()}{MemoryAddress}";
public void ClearValue() => SetValueFromPLC(null);
internal void TriggerChangedEvnt(object changed) => ValueChanged?.Invoke(changed);
public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
public override string ToString () => $"{GetRegisterPLCName()} - Value: {GetValueString()}";
}
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Mewtocol.NET</PackageId>
<Version>0.6.2</Version>
<Version>0.7.0</Version>
<Authors>Felix Weiss</Authors>
<Company>Womed</Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@@ -18,4 +18,9 @@
<DocumentationFile>..\Builds\MewtocolNet.xml</DocumentationFile>
<OutputPath>..\Builds</OutputPath>
</PropertyGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>MewtocolTests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Project>