LibS7Adv/AdvCoSimulatorConnection.cs

466 lines
15 KiB
C#

using Siemens.Simatic.Simulation.Runtime;
using System;
namespace LibS7Adv
{
public class AdvCoSimulatorConnection : IPlcConnection
{
private IInstance? _instance;
private bool _isUpdating = false;
private DateTime _lastUpdateStartTime = DateTime.MinValue;
private readonly TimeSpan _updateTimeout = TimeSpan.FromSeconds(10);
public bool IsConnected { get; private set; }
public string Status { get; private set; } = "offline";
public string LastError { get; private set; } = "";
public ConnectionType ConnectionType => ConnectionType.AdvCoSimulator;
public bool Connect(string ipAddress, string instanceName)
{
try
{
_instance = SimulationRuntimeManager.CreateInterface(instanceName);
if (_instance != null)
{
_instance.OnSoftwareConfigurationChanged += Instance_OnSoftwareConfigurationChanged;
UpdateTagList();
Status = _instance.OperatingState.ToString();
IsConnected = true;
LastError = "";
return true;
}
}
catch (Exception ex)
{
LastError = ex.Message;
Status = "offline";
}
return false;
}
public void Disconnect()
{
IsConnected = false;
Status = "offline";
_instance = null;
}
private void Instance_OnSoftwareConfigurationChanged(IInstance instance, SOnSoftwareConfigChangedParameter event_param)
{
_isUpdating = true;
_lastUpdateStartTime = DateTime.Now;
UpdateTagList();
}
private void UpdateTagList()
{
_isUpdating = true;
try
{
_instance?.UpdateTagList(ETagListDetails.IO | ETagListDetails.DB | ETagListDetails.M, true);
_isUpdating = false;
}
catch (Exception ex)
{
LastError = ex.Message;
}
}
private bool CheckUpdateStatus()
{
if (_isUpdating)
{
if (DateTime.Now - _lastUpdateStartTime > _updateTimeout)
{
_lastUpdateStartTime = DateTime.Now;
UpdateTagList();
}
return false;
}
return true;
}
public bool? ReadBool(string address)
{
try
{
if (!IsConnected || !CheckUpdateStatus())
{
LastError = !IsConnected ? "Not Connected" : "";
return false;
}
var tag = PLCViewModel.ParseTagAddress(address);
if (tag.tagType == EDataType.Unknown)
{
return _instance?.ReadBool(address);
}
else if (tag.tagType == EDataType.Bool)
{
if (tag.areaType == EArea.Output)
return _instance?.OutputArea.ReadBit(tag.word_offset, tag.bit);
if (tag.areaType == EArea.Marker)
return _instance?.MarkerArea.ReadBit(tag.word_offset, tag.bit);
}
return false;
}
catch (Exception ex)
{
HandleException(ex, address);
return false;
}
}
public bool WriteBool(string address, bool value)
{
try
{
if (!IsConnected || !CheckUpdateStatus())
{
LastError = !IsConnected ? "Not Connected" : "";
return false;
}
var tag = PLCViewModel.ParseTagAddress(address);
if (tag.tagType == EDataType.Unknown)
{
_instance?.WriteBool(address, value);
}
else if (tag.tagType == EDataType.Bool)
{
if (tag.areaType == EArea.Input)
_instance?.InputArea.WriteBit(tag.word_offset, tag.bit, value);
if (tag.areaType == EArea.Output)
_instance?.OutputArea.WriteBit(tag.word_offset, tag.bit, value);
if (tag.areaType == EArea.Marker)
_instance?.MarkerArea.WriteBit(tag.word_offset, tag.bit, value);
}
return true;
}
catch (Exception ex)
{
HandleException(ex, address);
return false;
}
}
public bool ReadOutputBool(byte pByte, int pBit)
{
try
{
if (!CheckUpdateStatus()) return false;
return _instance?.OutputArea.ReadBit(pByte, (byte)pBit) ?? false;
}
catch (Exception ex)
{
HandleException(ex, $"Byte {pByte}, Bit {pBit}");
return false;
}
}
public void WriteInputBool(byte pByte, int pBit, bool value)
{
try
{
if (!CheckUpdateStatus()) return;
_instance?.InputArea.WriteBit(pByte, (byte)pBit, value);
}
catch (Exception ex)
{
HandleException(ex, $"Byte {pByte}, Bit {pBit}");
}
}
public string ReadNumber(string address, bool signed = false)
{
try
{
if (!IsConnected || !CheckUpdateStatus())
{
LastError = !IsConnected ? "Not Connected" : "";
return "";
}
var tag = PLCViewModel.ParseTagAddress(address.Trim());
if (tag.tagType == EDataType.Unknown)
{
var data = _instance?.Read(address);
return data?.ToString() ?? "";
}
else if (tag.tagType == EDataType.Byte)
{
if (tag.areaType == EArea.Output)
{
var val = _instance?.OutputArea.ReadByte(tag.word_offset);
if (signed && val > 127)
return (val - 256).ToString();
return val?.ToString() ?? "";
}
if (tag.areaType == EArea.Marker)
{
var val = _instance?.MarkerArea.ReadByte(tag.word_offset);
if (signed && val > 127)
return (val - 256).ToString();
return val?.ToString() ?? "";
}
}
else if (tag.tagType == EDataType.Word)
{
if (tag.areaType == EArea.Output)
{
byte[] bytes = _instance?.OutputArea.ReadBytes(tag.word_offset, 2);
if (bytes != null)
{
Array.Reverse(bytes);
int value = signed ? BitConverter.ToInt16(bytes, 0) : BitConverter.ToUInt16(bytes, 0);
return value.ToString();
}
}
if (tag.areaType == EArea.Marker)
{
byte[] bytes = _instance?.MarkerArea.ReadBytes(tag.word_offset, 2);
if (bytes != null)
{
Array.Reverse(bytes);
int value = signed ? BitConverter.ToInt16(bytes, 0) : BitConverter.ToUInt16(bytes, 0);
return value.ToString();
}
}
}
return "";
}
catch (Exception ex)
{
HandleException(ex, address);
return "";
}
}
public bool WriteNumber(string address, object value)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return false;
// Intentar detectar el tipo de valor y usar el método específico apropiado
if (value is int intValue)
{
// Para enteros, intentamos WriteTagInt16 primero, luego WriteTagDInt si es necesario
if (intValue >= short.MinValue && intValue <= short.MaxValue)
{
return WriteTagInt16(address, intValue);
}
else
{
return WriteTagDInt(address, intValue);
}
}
else if (value is short shortValue)
{
return WriteTagInt16(address, shortValue);
}
else if (value is byte byteValue)
{
return WriteTagInt16(address, byteValue);
}
else
{
// Para otros tipos (incluidos float/double), usar WriteTag genérico
return WriteTag(address, value);
}
}
catch (Exception ex)
{
HandleException(ex, address);
return false;
}
}
public object? ReadTag(string tagName)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return new SDataValue();
return _instance?.Read(tagName) ?? new SDataValue();
}
catch (Exception ex)
{
HandleException(ex, tagName);
return new SDataValue();
}
}
public bool WriteTag(string tagName, object value)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return false;
if (value is SDataValue sValue)
{
_instance?.Write(tagName, sValue);
return true;
}
return false;
}
catch (Exception ex)
{
HandleException(ex, tagName);
return false;
}
}
public bool? ReadTagBool(string tagName)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return false;
return _instance?.ReadBool(tagName);
}
catch (Exception ex)
{
HandleException(ex, tagName);
return false;
}
}
public bool WriteTagBool(string tagName, bool value)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return false;
_instance?.WriteBool(tagName, value);
return true;
}
catch (Exception ex)
{
HandleException(ex, tagName);
return false;
}
}
public int? ReadTagInt16(string tagName)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return 0;
return _instance?.ReadInt16(tagName);
}
catch (Exception ex)
{
HandleException(ex, tagName);
return 0;
}
}
public bool WriteTagInt16(string tagName, int value)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return false;
_instance?.WriteInt16(tagName, (short)value);
return true;
}
catch (Exception ex)
{
HandleException(ex, tagName);
return false;
}
}
public int? ReadTagDInt(string tagName)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return 0;
return _instance?.ReadInt32(tagName);
}
catch (Exception ex)
{
HandleException(ex, tagName);
return 0;
}
}
public bool WriteTagDInt(string tagName, int value)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return false;
_instance?.WriteInt32(tagName, value);
return true;
}
catch (Exception ex)
{
HandleException(ex, tagName);
return false;
}
}
public byte[]? ReadBytes(EArea area, uint offset, int length)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return null;
return area switch
{
EArea.Input => _instance?.InputArea.ReadBytes(offset, (uint)length),
EArea.Output => _instance?.OutputArea.ReadBytes(offset, (uint)length),
EArea.Marker => _instance?.MarkerArea.ReadBytes(offset, (uint)length),
_ => null
};
}
catch (Exception ex)
{
HandleException(ex, $"Area {area}, Offset {offset}");
return null;
}
}
public bool WriteBytes(EArea area, uint offset, byte[] data)
{
try
{
if (!IsConnected || !CheckUpdateStatus()) return false;
switch (area)
{
case EArea.Input:
_instance?.InputArea.WriteBytes(offset, data);
break;
case EArea.Output:
_instance?.OutputArea.WriteBytes(offset, data);
break;
case EArea.Marker:
_instance?.MarkerArea.WriteBytes(offset, data);
break;
default:
return false;
}
return true;
}
catch (Exception ex)
{
HandleException(ex, $"Area {area}, Offset {offset}");
return false;
}
}
private void HandleException(Exception ex, string context)
{
if (ex.Message.Contains("NotUpToDate") ||
ex is SimulationRuntimeException sre && sre.RuntimeErrorCode == ERuntimeErrorCode.NotUpToDate)
{
_isUpdating = true;
_lastUpdateStartTime = DateTime.Now;
Task.Run(() => UpdateTagList());
LastError = $"{context}: Tags no actualizados, reintentando...";
}
else
{
LastError = $"{context}: {ex.Message}";
}
}
}
}