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}"; } } } }