diff --git a/Examples/Examples.csproj b/Examples/Examples.csproj index 8b8151e..b9f9fac 100644 --- a/Examples/Examples.csproj +++ b/Examples/Examples.csproj @@ -6,7 +6,7 @@ Exe - net5.0 + net6.0 diff --git a/Examples/Program.cs b/Examples/Program.cs index 232a912..f707c6a 100644 --- a/Examples/Program.cs +++ b/Examples/Program.cs @@ -1,56 +1,10 @@ using System; using System.Threading.Tasks; -using System.Linq; -using System.Text.Json; using MewtocolNet; -using MewtocolNet.RegisterAttributes; -using System.Collections; using MewtocolNet.Logging; namespace Examples { - - public class TestRegisters : RegisterCollectionBase { - - //corresponds to a R100 boolean register in the PLC - [Register(100, RegisterType.R)] - public bool TestBool1 { get; private set; } - - //corresponds to a R100 boolean register in the PLC - [Register(RegisterType.X, SpecialAddress.D)] - public bool TestBoolInputXD { get; private set; } - - //corresponds to a DT1101 - DT1104 string register in the PLC with (STRING[4]) - [Register(1101, 4)] - public string TestString1 { get; private set; } - - //corresponds to a DT7000 16 bit int register in the PLC - [Register(7000)] - public short TestInt16 { get; private set; } - - //corresponds to a DTD7001 - DTD7002 32 bit int register in the PLC - [Register(7001)] - public int TestInt32 { get; private set; } - - //corresponds to a DTD7001 - DTD7002 32 bit float register in the PLC (REAL) - [Register(7003)] - public float TestFloat32 { get; private set; } - - //corresponds to a DT7005 - DT7009 string register in the PLC with (STRING[5]) - [Register(7005, 5)] - public string TestString2 { get; private set; } - - //corresponds to a DT7010 as a 16bit word/int and parses the word as single bits - [Register(7010)] - public BitArray TestBitRegister { get; private set; } - - //corresponds to a DT1204 as a 16bit word/int takes the bit at index 9 and writes it back as a boolean - [Register(1204, 9, BitCount.B16)] - public bool BitValue { get; private set; } - - - } - class Program { static void Main(string[] args) { @@ -58,7 +12,7 @@ namespace Examples { Task.Factory.StartNew(async () => { //attaching the logger - Logger.LogLevel = LogLevel.Critical; + Logger.LogLevel = LogLevel.Verbose; Logger.OnNewLogMessage((date, msg) => { Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}"); }); @@ -88,8 +42,10 @@ namespace Examples { interf.SetRegister(nameof(registers.TestInt32), (registers.TestInt32 + 1)); //adds 11.11 each time the plc connects to the PLCs REAL regíster interf.SetRegister(nameof(registers.TestFloat32), (float)(registers.TestFloat32 + 11.11)); - - interf.SetRegister(nameof(registers.TestString2), new Random().Next(0, 99999).ToString()); + //writes 'Hello' to the PLCs string register + interf.SetRegister(nameof(registers.TestString2), "Hello"); + //set the current second to the PLCs TIME register + interf.SetRegister(nameof(registers.TestTime), TimeSpan.FromSeconds(DateTime.Now.Second)); }); diff --git a/Examples/TestRegisters.cs b/Examples/TestRegisters.cs new file mode 100644 index 0000000..1e0a21b --- /dev/null +++ b/Examples/TestRegisters.cs @@ -0,0 +1,52 @@ +using MewtocolNet; +using MewtocolNet.RegisterAttributes; +using System; +using System.Collections; + +namespace Examples { + public class TestRegisters : RegisterCollectionBase { + + //corresponds to a R100 boolean register in the PLC + [Register(100, RegisterType.R)] + public bool TestBool1 { get; private set; } + + //corresponds to a R100 boolean register in the PLC + [Register(RegisterType.X, SpecialAddress.D)] + public bool TestBoolInputXD { get; private set; } + + //corresponds to a DT1101 - DT1104 string register in the PLC with (STRING[4]) + [Register(1101, 4)] + public string TestString1 { get; private set; } + + //corresponds to a DT7000 16 bit int register in the PLC + [Register(7000)] + public short TestInt16 { get; private set; } + + //corresponds to a DTD7001 - DTD7002 32 bit int register in the PLC + [Register(7001)] + public int TestInt32 { get; private set; } + + //corresponds to a DTD7001 - DTD7002 32 bit float register in the PLC (REAL) + [Register(7003)] + public float TestFloat32 { get; private set; } + + //corresponds to a DT7005 - DT7009 string register in the PLC with (STRING[5]) + [Register(7005, 5)] + public string TestString2 { get; private set; } + + //corresponds to a DT7010 as a 16bit word/int and parses the word as single bits + [Register(7010)] + public BitArray TestBitRegister { get; private set; } + + //corresponds to a DT1204 as a 16bit word/int takes the bit at index 9 and writes it back as a boolean + [Register(1204, 9, BitCount.B16)] + public bool BitValue { get; private set; } + + //corresponds to a DT7012 - DT7013 as a 32bit time value that gets parsed as a timespan (TIME) + //the smallest value to communicate to the PLC is 10ms + [Register(7012)] + public TimeSpan TestTime { get; private set; } + + + } +} diff --git a/MewtocolNet/Mewtocol/DynamicInterface.cs b/MewtocolNet/Mewtocol/DynamicInterface.cs index a596069..513c1b9 100644 --- a/MewtocolNet/Mewtocol/DynamicInterface.cs +++ b/MewtocolNet/Mewtocol/DynamicInterface.cs @@ -21,6 +21,13 @@ namespace MewtocolNet { #region Register Polling + internal void KillPoller () { + + ContinousReaderRunning = false; + cTokenAutoUpdater.Cancel(); + + } + /// /// Attaches a continous reader that reads back the Registers and Contacts /// @@ -32,123 +39,134 @@ namespace MewtocolNet { Logger.Log("Poller is attaching", LogLevel.Info, this); - Task.Factory.StartNew(async () => { + try { - var plcinf = await GetPLCInfoAsync(); - if (plcinf == null) { - Logger.Log("PLC not reachable, stopping logger", LogLevel.Info, this); - return; - } + Task.Factory.StartNew(async () => { - PolledCycle += MewtocolInterface_PolledCycle; - void MewtocolInterface_PolledCycle () { - - StringBuilder stringBuilder = new StringBuilder(); - foreach (var reg in GetAllRegisters()) { - string address = $"{reg.GetRegisterString()}{reg.GetStartingMemoryArea()}".PadRight(8, (char)32); - stringBuilder.AppendLine($"{address}{(reg.Name != null ? $" ({reg.Name})" : "")}: {reg.GetValueString()}"); + var plcinf = await GetPLCInfoAsync(); + if (plcinf == null) { + Logger.Log("PLC not reachable, stopping logger", LogLevel.Info, this); + return; } - Logger.Log($"Registers loaded are: \n" + - $"--------------------\n" + - $"{stringBuilder.ToString()}" + - $"--------------------", - LogLevel.Verbose, this); + PolledCycle += MewtocolInterface_PolledCycle; + void MewtocolInterface_PolledCycle () { - Logger.Log("Logger did its first cycle successfully", LogLevel.Info, this); - - PolledCycle -= MewtocolInterface_PolledCycle; - } + StringBuilder stringBuilder = new StringBuilder(); + foreach (var reg in GetAllRegisters()) { + string address = $"{reg.GetRegisterString()}{reg.GetStartingMemoryArea()}".PadRight(8, (char)32); + stringBuilder.AppendLine($"{address}{(reg.Name != null ? $" ({reg.Name})" : "")}: {reg.GetValueString()}"); + } - ContinousReaderRunning = true; + Logger.Log($"Registers loaded are: \n" + + $"--------------------\n" + + $"{stringBuilder.ToString()}" + + $"--------------------", + LogLevel.Verbose, this); - while (true) { + Logger.Log("Logger did its first cycle successfully", LogLevel.Info, this); - //do priority tasks first - if(PriorityTasks.Count > 0) { + PolledCycle -= MewtocolInterface_PolledCycle; + } - await PriorityTasks.FirstOrDefault(x => !x.IsCompleted); + ContinousReaderRunning = true; + + while (ContinousReaderRunning) { + + //do priority tasks first + if (PriorityTasks.Count > 0) { + + await PriorityTasks.FirstOrDefault(x => !x.IsCompleted); + + } + + foreach (var registerPair in Registers) { + + var reg = registerPair.Value; + + if (reg is NRegister shortReg) { + var lastVal = shortReg.Value; + var readout = (await ReadNumRegister(shortReg, stationNumber)).Register.Value; + if (lastVal != readout) { + shortReg.LastValue = readout; + InvokeRegisterChanged(shortReg); + shortReg.TriggerNotifyChange(); + } + } + if (reg is NRegister ushortReg) { + var lastVal = ushortReg.Value; + var readout = (await ReadNumRegister(ushortReg, stationNumber)).Register.Value; + if (lastVal != readout) { + ushortReg.LastValue = readout; + InvokeRegisterChanged(ushortReg); + ushortReg.TriggerNotifyChange(); + } + } + if (reg is NRegister intReg) { + var lastVal = intReg.Value; + var readout = (await ReadNumRegister(intReg, stationNumber)).Register.Value; + if (lastVal != readout) { + intReg.LastValue = readout; + InvokeRegisterChanged(intReg); + intReg.TriggerNotifyChange(); + } + } + if (reg is NRegister uintReg) { + var lastVal = uintReg.Value; + var readout = (await ReadNumRegister(uintReg, stationNumber)).Register.Value; + if (lastVal != readout) { + uintReg.LastValue = readout; + InvokeRegisterChanged(uintReg); + uintReg.TriggerNotifyChange(); + } + } + if (reg is NRegister floatReg) { + var lastVal = floatReg.Value; + var readout = (await ReadNumRegister(floatReg, stationNumber)).Register.Value; + if (lastVal != readout) { + floatReg.LastValue = readout; + InvokeRegisterChanged(floatReg); + floatReg.TriggerNotifyChange(); + } + } + if (reg is NRegister tsReg) { + var lastVal = tsReg.Value; + var readout = (await ReadNumRegister(tsReg, stationNumber)).Register.Value; + if (lastVal != readout) { + tsReg.LastValue = readout; + InvokeRegisterChanged(tsReg); + tsReg.TriggerNotifyChange(); + } + } + if (reg is BRegister boolReg) { + var lastVal = boolReg.Value; + var readout = (await ReadBoolRegister(boolReg, stationNumber)).Register.Value; + if (lastVal != readout) { + boolReg.LastValue = readout; + InvokeRegisterChanged(boolReg); + boolReg.TriggerNotifyChange(); + } + } + if (reg is SRegister stringReg) { + var lastVal = stringReg.Value; + var readout = (await ReadStringRegister(stringReg, stationNumber)).Register.Value; + if (lastVal != readout) { + InvokeRegisterChanged(stringReg); + stringReg.TriggerNotifyChange(); + } + + } + + } + + //invoke cycle polled event + InvokePolledCycleDone(); } - //await Task.Delay(pollingDelayMs); + }, cTokenAutoUpdater.Token); - foreach (var registerPair in Registers) { - - var reg = registerPair.Value; - - if (reg is NRegister shortReg) { - var lastVal = shortReg.Value; - var readout = (await ReadNumRegister(shortReg, stationNumber)).Register.Value; - if (lastVal != readout) { - shortReg.LastValue = readout; - InvokeRegisterChanged(shortReg); - shortReg.TriggerNotifyChange(); - } - } - if (reg is NRegister ushortReg) { - var lastVal = ushortReg.Value; - var readout = (await ReadNumRegister(ushortReg, stationNumber)).Register.Value; - if (lastVal != readout) { - ushortReg.LastValue = readout; - InvokeRegisterChanged(ushortReg); - ushortReg.TriggerNotifyChange(); - } - } - if (reg is NRegister intReg) { - var lastVal = intReg.Value; - var readout = (await ReadNumRegister(intReg, stationNumber)).Register.Value; - if (lastVal != readout) { - intReg.LastValue = readout; - InvokeRegisterChanged(intReg); - intReg.TriggerNotifyChange(); - } - } - if (reg is NRegister uintReg) { - var lastVal = uintReg.Value; - var readout = (await ReadNumRegister(uintReg, stationNumber)).Register.Value; - if (lastVal != readout) { - uintReg.LastValue = readout; - InvokeRegisterChanged(uintReg); - uintReg.TriggerNotifyChange(); - } - } - if (reg is NRegister floatReg) { - var lastVal = floatReg.Value; - var readout = (await ReadNumRegister(floatReg, stationNumber)).Register.Value; - if (lastVal != readout) { - floatReg.LastValue = readout; - InvokeRegisterChanged(floatReg); - floatReg.TriggerNotifyChange(); - } - } - if (reg is BRegister boolReg) { - var lastVal = boolReg.Value; - var readout = (await ReadBoolRegister(boolReg, stationNumber)).Register.Value; - if (lastVal != readout) { - boolReg.LastValue = readout; - InvokeRegisterChanged(boolReg); - boolReg.TriggerNotifyChange(); - } - } - if (reg is SRegister stringReg) { - var lastVal = stringReg.Value; - var readout = (await ReadStringRegister(stringReg, stationNumber)).Register.Value; - if (lastVal != readout) { - InvokeRegisterChanged(stringReg); - stringReg.TriggerNotifyChange(); - } - - } - - } - - //invoke cycle polled event - InvokePolledCycleDone(); - - } - - }, cTokenAutoUpdater.Token); + } catch (TaskCanceledException) { } } @@ -265,6 +283,8 @@ namespace MewtocolNet { Registers.Add(_address, new NRegister(_address, _name)); } else if (regType == typeof(string)) { Registers.Add(_address, new SRegister(_address, _length, _name)); + } else if (regType == typeof(TimeSpan)) { + Registers.Add(_address, new NRegister(_address, _name)); } else if (regType == typeof(bool)) { Registers.Add(_address, new BRegister(_address, RegisterType.R, _name)); } else { diff --git a/MewtocolNet/Mewtocol/MewtocolHelpers.cs b/MewtocolNet/Mewtocol/MewtocolHelpers.cs index 4e9432e..9743194 100644 --- a/MewtocolNet/Mewtocol/MewtocolHelpers.cs +++ b/MewtocolNet/Mewtocol/MewtocolHelpers.cs @@ -45,12 +45,17 @@ namespace MewtocolNet { } internal static string ParseDTByteString (this string _onString, int _blockSize = 4) { + + if (_onString == null) + return null; + var res = new Regex(@"\%([0-9]{2})\$RD(.{" + _blockSize + "})").Match(_onString); if (res.Success) { string val = res.Groups[2].Value; return val; } return null; + } internal static bool? ParseRCSingleBit (this string _onString, int _blockSize = 4) { @@ -73,6 +78,8 @@ namespace MewtocolNet { internal static string ReverseByteOrder (this string _onString) { + if(_onString == null) return null; + //split into 2 chars var stringBytes = _onString.SplitInParts(2).ToList(); @@ -93,17 +100,38 @@ namespace MewtocolNet { } internal static string BuildDTString (this string _inString, short _stringReservedSize) { + StringBuilder sb = new StringBuilder(); - //06000600 - short stringSize = (short)_inString.Length; - var sizeBytes = BitConverter.GetBytes(stringSize).ToHexString(); + + //clamp string lenght + if (_inString.Length > _stringReservedSize) { + _inString = _inString.Substring(0, _stringReservedSize); + } + + //actual string content + var hexstring = _inString.GetAsciiHexFromString(); + + var sizeBytes = BitConverter.GetBytes((short)(hexstring.Length / 2)).ToHexString(); + + if (hexstring.Length >= 2) { + + var remainderBytes = (hexstring.Length / 2) % 2; + + if (remainderBytes != 0) { + hexstring += "20"; + } + + } + var reservedSizeBytes = BitConverter.GetBytes(_stringReservedSize).ToHexString(); + //reserved string count bytes sb.Append(reservedSizeBytes); //string count actual bytes sb.Append(sizeBytes); - //actual string content - sb.Append(_inString.GetAsciiHexFromString()); + + + sb.Append(hexstring); return sb.ToString(); } diff --git a/MewtocolNet/Mewtocol/MewtocolInterface.cs b/MewtocolNet/Mewtocol/MewtocolInterface.cs index fcafb13..050ef51 100644 --- a/MewtocolNet/Mewtocol/MewtocolInterface.cs +++ b/MewtocolNet/Mewtocol/MewtocolInterface.cs @@ -32,19 +32,24 @@ namespace MewtocolNet { /// public event Action RegisterChanged; + /// + /// The current connection state of the interface + /// + public bool IsConnected { get; private set; } + /// /// Generic information about the connected PLC /// - public PLCInfo PlcInfo {get;private set;} + public PLCInfo PlcInfo { get; private set; } /// /// The registered data registers of the PLC /// public Dictionary Registers { get; set; } = new Dictionary(); - private string ip {get;set;} - private int port {get;set;} - private int stationNumber {get;set;} + private string ip; + private int port; + private int stationNumber; /// /// The current IP of the PLC connection @@ -82,6 +87,8 @@ namespace MewtocolNet { if (usePoller) AttachPoller(); + IsConnected = true; + } RegisterChanged += (o) => { @@ -240,6 +247,10 @@ namespace MewtocolNet { } + if (prop.PropertyType == typeof(TimeSpan)) { + AddRegister(cAttribute.MemoryArea, _name: propName); + } + } } @@ -404,6 +415,12 @@ namespace MewtocolNet { } + if (foundRegister.GetType() == typeof(NRegister)) { + + _ = WriteNumRegister((NRegister)foundRegister, (TimeSpan)value, StationNumber); + + } + if (foundRegister.GetType() == typeof(SRegister)) { _ = WriteStringRegister((SRegister)foundRegister, (string)value, StationNumber); @@ -417,7 +434,7 @@ namespace MewtocolNet { #region Low level command handling /// - /// Sends a command to the PLC and awaits results + /// Calculates checksum and sends a command to the PLC then awaits results /// /// MEWTOCOL Formatted request string ex: %01#RT /// Auto close of frame [true]%01#RT01\r [false]%01#RT @@ -490,35 +507,42 @@ namespace MewtocolNet { using (TcpClient client = new TcpClient() { ReceiveBufferSize = 64, NoDelay = true, ExclusiveAddressUse = true }) { try { + await client.ConnectAsync(ip, port); - } catch(SocketException) { + + using (NetworkStream stream = client.GetStream()) { + var message = _blockString.ToHexASCIIBytes(); + var messageAscii = BitConverter.ToString(message).Replace("-", " "); + //send request + using (var sendStream = new MemoryStream(message)) { + await sendStream.CopyToAsync(stream); + Logger.Log($"OUT MSG: {_blockString}", LogLevel.Critical, this); + //log message sent + ASCIIEncoding enc = new ASCIIEncoding(); + string characters = enc.GetString(message); + } + //await result + StringBuilder response = new StringBuilder(); + byte[] responseBuffer = new byte[256]; + do { + int bytes = stream.Read(responseBuffer, 0, responseBuffer.Length); + response.Append(Encoding.UTF8.GetString(responseBuffer, 0, bytes)); + } + while (stream.DataAvailable); + sw.Stop(); + Logger.Log($"IN MSG ({(int)sw.Elapsed.TotalMilliseconds}ms): {_blockString}", LogLevel.Critical, this); + return response.ToString(); + } + + } catch(Exception) { + + IsConnected = false; + KillPoller(); + Logger.Log("The PLC connection was closed", LogLevel.Error, this); return null; + } - using (NetworkStream stream = client.GetStream()) { - var message = _blockString.ToHexASCIIBytes(); - var messageAscii = BitConverter.ToString(message).Replace("-", " "); - //send request - using (var sendStream = new MemoryStream(message)) { - await sendStream.CopyToAsync(stream); - Logger.Log($"OUT MSG: {_blockString}", LogLevel.Critical, this); - //log message sent - ASCIIEncoding enc = new ASCIIEncoding(); - string characters = enc.GetString(message); - } - //await result - StringBuilder response = new StringBuilder(); - byte[] responseBuffer = new byte[256]; - do { - int bytes = stream.Read(responseBuffer, 0, responseBuffer.Length); - response.Append(Encoding.UTF8.GetString(responseBuffer, 0, bytes)); - } - while (stream.DataAvailable); - sw.Stop(); - Logger.Log($"IN MSG ({(int)sw.Elapsed.TotalMilliseconds}ms): {_blockString}", LogLevel.Critical, this); - return response.ToString(); - } - } } diff --git a/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs b/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs index 282639c..59db7e8 100644 --- a/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs +++ b/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs @@ -53,6 +53,11 @@ namespace MewtocolNet { #region Bool register reading / writing + /// + /// Reads the given boolean register from the PLC + /// + /// The register to read + /// Station number to access public async Task ReadBoolRegister (BRegister _toRead, int _stationNumber = 1) { string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#RCS{_toRead.BuildMewtocolIdent()}"; @@ -79,6 +84,12 @@ namespace MewtocolNet { } + /// + /// Writes to the given bool register on the PLC + /// + /// The register to write to + /// Station number to access + /// The success state of the write operation public async Task WriteBoolRegister (BRegister _toWrite, bool value, int _stationNumber = 1) { string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#WCS{_toWrite.BuildMewtocolIdent()}{(value ? "1" : "0")}"; @@ -94,7 +105,7 @@ namespace MewtocolNet { #region Number register reading / writing /// - /// Reads the given numeric register from PLC + /// Reads the given numeric register from the PLC /// /// Type of number (short, ushort, int, uint, float) /// The register to read @@ -107,6 +118,13 @@ namespace MewtocolNet { string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#RD{_toRead.BuildMewtocolIdent()}"; var result = await SendCommandAsync(requeststring); + if(!result.Success || string.IsNullOrEmpty(result.Response)) { + return new NRegisterResult { + Result = result, + Register = _toRead + }; + } + if (numType == typeof(short)) { var resultBytes = result.Response.ParseDTByteString(4).ReverseByteOrder(); @@ -142,6 +160,17 @@ namespace MewtocolNet { (_toRead as NRegister).LastValue = finalFloat; + } else if (numType == typeof(TimeSpan)) { + + var resultBytes = result.Response.ParseDTByteString(8).ReverseByteOrder(); + //convert to unsigned int first + var vallong = long.Parse(resultBytes, NumberStyles.HexNumber); + var valMillis = vallong * 10; + var ts = TimeSpan.FromMilliseconds(valMillis); + + //minmax writable / readable value is 10ms + (_toRead as NRegister).LastValue = ts; + } var finalRes = new NRegisterResult { @@ -153,12 +182,12 @@ namespace MewtocolNet { } /// - /// Reads the given numeric register from PLC + /// Reads the given numeric register from the PLC /// /// Type of number (short, ushort, int, uint, float) /// The register to write /// Station number to access - /// A result with the given NumberRegister and a result struct + /// The success state of the write operation public async Task WriteNumRegister (NRegister _toWrite, T _value, int _stationNumber = 1) { byte[] toWriteVal; @@ -180,6 +209,15 @@ namespace MewtocolNet { toWriteVal = BitConverter.GetBytes(fl.Value); + } else if (numType == typeof(TimeSpan)) { + + var fl = _value as TimeSpan?; + if (fl == null) + throw new NullReferenceException("Timespan cannot be null"); + + var tLong = (uint)(fl.Value.TotalMilliseconds / 10); + toWriteVal = BitConverter.GetBytes(tLong); + } else { toWriteVal = null; } @@ -196,14 +234,20 @@ namespace MewtocolNet { #region String register reading / writing - public async Task ReadStringRegister (SRegister _toRead, int _stationNumber = 1) { + //string is build up like this + //04 00 04 00 53 50 33 35 13 + //0, 1 = reserved size + //1, 2 = current size + //3,4,5,6 = ASCII encoded chars (SP35) + //7,8 = checksum - //string is build up like this - //04 00 04 00 53 50 33 35 13 - //0, 1 = reserved size - //1, 2 = current size - //3,4,5,6 = ASCII encoded chars (SP35) - //7,8 = checksum + /// + /// Reads back the value of a string register + /// + /// The register to read + /// The station number of the PLC + /// + public async Task ReadStringRegister (SRegister _toRead, int _stationNumber = 1) { string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#RD{_toRead.BuildMewtocolIdent()}"; var result = await SendCommandAsync(requeststring); @@ -215,6 +259,13 @@ namespace MewtocolNet { }; } + /// + /// Writes a string to a string register + /// + /// The register to write + /// The value to write, if the strings length is longer than the cap size it gets trimmed to the max char length + /// The station number of the PLC + /// The success state of the write operation public async Task WriteStringRegister(SRegister _toWrite, string _value, int _stationNumber = 1) { if (_value == null) _value = ""; @@ -223,13 +274,14 @@ namespace MewtocolNet { } string stationNum = _stationNumber.ToString().PadLeft(2, '0'); - string dataArea = _toWrite.BuildMewtocolIdent(); string dataString = _value.BuildDTString(_toWrite.ReservedSize); + string dataArea = _toWrite.BuildCustomIdent(dataString.Length / 4); + string requeststring = $"%{stationNum}#WD{dataArea}{dataString}"; - Console.WriteLine($"reserved: {_toWrite.MemoryLength}, size: {_value.Length}"); - var result = await SendCommandAsync(requeststring); + + return result.Success && result.Response.StartsWith($"%{ _stationNumber.ToString().PadLeft(2, '0')}#WD"); } diff --git a/MewtocolNet/Mewtocol/Subregisters/NRegister.cs b/MewtocolNet/Mewtocol/Subregisters/NRegister.cs index 49bbf5a..3232711 100644 --- a/MewtocolNet/Mewtocol/Subregisters/NRegister.cs +++ b/MewtocolNet/Mewtocol/Subregisters/NRegister.cs @@ -42,6 +42,8 @@ namespace MewtocolNet.Responses { MemoryLength = 1; } else if (numType == typeof(float)) { MemoryLength = 1; + } else if (numType == typeof(TimeSpan)) { + MemoryLength = 1; } else { throw new NotSupportedException($"The type {numType} is not allowed for Number Registers"); } diff --git a/MewtocolNet/Mewtocol/Subregisters/Register.cs b/MewtocolNet/Mewtocol/Subregisters/Register.cs index 84f996b..2cd6b6b 100644 --- a/MewtocolNet/Mewtocol/Subregisters/Register.cs +++ b/MewtocolNet/Mewtocol/Subregisters/Register.cs @@ -75,6 +75,9 @@ namespace MewtocolNet.Responses { if (this is NRegister floatReg) { return floatReg.Value.ToString(); } + if (this is NRegister tsReg) { + return tsReg.Value.ToString(); + } if (this is BRegister boolReg) { return boolReg.Value.ToString(); } @@ -130,6 +133,9 @@ namespace MewtocolNet.Responses { if (this is NRegister floatReg) { return "DDT"; } + if (this is NRegister tsReg) { + return "DDT"; + } if (this is BRegister boolReg) { return boolReg.RegType.ToString(); } diff --git a/MewtocolNet/Mewtocol/Subregisters/SRegister.cs b/MewtocolNet/Mewtocol/Subregisters/SRegister.cs index 98ca12e..6da8b4d 100644 --- a/MewtocolNet/Mewtocol/Subregisters/SRegister.cs +++ b/MewtocolNet/Mewtocol/Subregisters/SRegister.cs @@ -40,9 +40,25 @@ namespace MewtocolNet.Responses { } public override 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 string BuildCustomIdent (int overwriteWordLength) { + + if (overwriteWordLength <= 0) + throw new Exception("overwriteWordLength cant be 0 or less"); + + StringBuilder asciistring = new StringBuilder("D"); + + asciistring.Append(MemoryAdress.ToString().PadLeft(5, '0')); + asciistring.Append((MemoryAdress + overwriteWordLength - 1).ToString().PadLeft(5, '0')); + return asciistring.ToString(); }