using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Sharp7;
namespace LibS7Adv
{
///
/// Implementación de conexión PLC usando la librería Sharp7 (C#)
///
public class Sharp7Connection : IPlcConnection
{
private S7Client _client;
private bool _isConnected;
private string _ipAddress = "";
private int _rack;
private int _slot;
private string _lastError = "";
private readonly Dictionary _areaMapping;
public bool IsConnected => _isConnected && _client != null && _client.Connected;
public string Status => IsConnected ? "Conectado" : "Desconectado";
public string LastError => _lastError;
public ConnectionType ConnectionType => LibS7Adv.ConnectionType.Sharp7;
public Sharp7Connection()
{
_client = new S7Client();
_isConnected = false;
_lastError = "";
// Mapeo de áreas de memoria entre nuestro enum y Sharp7
_areaMapping = new Dictionary
{
{ EArea.Input, (int)S7Area.PE },
{ EArea.Output, (int)S7Area.PA },
{ EArea.Marker, (int)S7Area.MK },
{ EArea.DataBlock, (int)S7Area.DB }
};
}
public bool Connect(string ipAddress, string instanceName)
{
try
{
_ipAddress = ipAddress;
// Para Sharp7, usamos valores por defecto para rack y slot
_rack = 0;
_slot = 2; // Valor típico para S7-300
int result = _client.ConnectTo(ipAddress, _rack, _slot);
_isConnected = result == 0;
if (!_isConnected)
{
_lastError = $"Error conectando: {result}";
}
else
{
_lastError = "";
}
return _isConnected;
}
catch (Exception ex)
{
_isConnected = false;
_lastError = ex.Message;
return false;
}
}
public void Disconnect()
{
try
{
_client?.Disconnect();
_lastError = "";
}
catch (Exception ex)
{
_lastError = ex.Message;
}
finally
{
_isConnected = false;
}
}
public bool? ReadBool(string address)
{
if (!IsConnected) return null;
try
{
var parsed = ParseAddress(address);
if (parsed == null) return null;
// Para bits, necesitamos calcular el offset en bits
int bitOffset = (parsed.Offset * 8) + parsed.Bit;
byte[] buffer = new byte[1];
int result = _client.ReadArea(parsed.Area, parsed.DbNumber, bitOffset, 1, (int)S7WordLength.Bit, buffer);
if (result == 0)
{
return S7.GetBitAt(buffer, 0, 0);
}
_lastError = $"Error leyendo bool: {result}";
return null;
}
catch (Exception ex)
{
_lastError = ex.Message;
return null;
}
}
public bool WriteBool(string address, bool value)
{
if (!IsConnected) return false;
try
{
var parsed = ParseAddress(address);
if (parsed == null) return false;
// Para bits, necesitamos calcular el offset en bits
int bitOffset = (parsed.Offset * 8) + parsed.Bit;
byte[] buffer = new byte[1];
S7.SetBitAt(buffer, 0, 0, value);
int result = _client.WriteArea(parsed.Area, parsed.DbNumber, bitOffset, 1, (int)S7WordLength.Bit, buffer);
if (result != 0)
{
_lastError = $"Error escribiendo bool: {result}";
return false;
}
_lastError = "";
return true;
}
catch (Exception ex)
{
_lastError = ex.Message;
return false;
}
}
public bool ReadOutputBool(byte pByte, int pBit)
{
if (!IsConnected) return false;
try
{
int bitOffset = (pByte * 8) + pBit;
byte[] buffer = new byte[1];
int result = _client.ReadArea((int)S7Area.PA, 0, bitOffset, 1, (int)S7WordLength.Bit, buffer);
if (result == 0)
{
return S7.GetBitAt(buffer, 0, 0);
}
_lastError = $"Error leyendo output: {result}";
return false;
}
catch (Exception ex)
{
_lastError = ex.Message;
return false;
}
}
public void WriteInputBool(byte pByte, int pBit, bool value)
{
if (!IsConnected) return;
try
{
int bitOffset = (pByte * 8) + pBit;
byte[] buffer = new byte[1];
S7.SetBitAt(buffer, 0, 0, value);
int result = _client.WriteArea((int)S7Area.PE, 0, bitOffset, 1, (int)S7WordLength.Bit, buffer);
if (result != 0)
{
_lastError = $"Error escribiendo input: {result}";
}
else
{
_lastError = "";
}
}
catch (Exception ex)
{
_lastError = ex.Message;
}
}
public string ReadNumber(string address, bool signed = false)
{
if (!IsConnected) return "";
try
{
var parsed = ParseNumberAddress(address);
if (parsed == null) return "";
byte[] buffer;
int wordLen;
int amount = 1;
// Determinar el tamaño del buffer y WordLen según el tipo de dato
switch (parsed.DataType.ToUpper())
{
case "B":
case "BYTE":
buffer = new byte[1];
wordLen = (int)S7WordLength.Byte;
break;
case "W":
case "WORD":
buffer = new byte[2];
wordLen = (int)S7WordLength.Word;
break;
case "DW":
case "DWORD":
buffer = new byte[4];
wordLen = (int)S7WordLength.DWord;
break;
case "R":
case "REAL":
buffer = new byte[4];
wordLen = (int)S7WordLength.Real;
break;
default:
return "";
}
int result = _client.ReadArea(parsed.Area, parsed.DbNumber, parsed.Offset, amount, wordLen, buffer);
if (result == 0)
{
return parsed.DataType.ToUpper() switch
{
"B" or "BYTE" => S7.GetByteAt(buffer, 0).ToString(),
"W" or "WORD" => signed ? S7.GetIntAt(buffer, 0).ToString() : S7.GetWordAt(buffer, 0).ToString(),
"DW" or "DWORD" => signed ? S7.GetDIntAt(buffer, 0).ToString() : S7.GetDWordAt(buffer, 0).ToString(),
"R" or "REAL" => S7.GetRealAt(buffer, 0).ToString(),
_ => ""
};
}
_lastError = $"Error leyendo número: {result}";
return "";
}
catch (Exception ex)
{
_lastError = ex.Message;
return "";
}
}
public bool WriteNumber(string address, object value)
{
if (!IsConnected) return false;
try
{
var parsed = ParseNumberAddress(address);
if (parsed == null) return false;
byte[] buffer;
int wordLen;
int amount = 1;
// Convertir el valor
double numValue = Convert.ToDouble(value);
// Determinar el tamaño del buffer y WordLen según el tipo de dato
switch (parsed.DataType.ToUpper())
{
case "B":
case "BYTE":
buffer = new byte[1];
wordLen = (int)S7WordLength.Byte;
S7.SetByteAt(buffer, 0, (byte)numValue);
break;
case "W":
case "WORD":
buffer = new byte[2];
wordLen = (int)S7WordLength.Word;
S7.SetWordAt(buffer, 0, (ushort)numValue);
break;
case "DW":
case "DWORD":
buffer = new byte[4];
wordLen = (int)S7WordLength.DWord;
S7.SetDWordAt(buffer, 0, (uint)numValue);
break;
case "R":
case "REAL":
buffer = new byte[4];
wordLen = (int)S7WordLength.Real;
S7.SetRealAt(buffer, 0, (float)numValue);
break;
default:
return false;
}
int result = _client.WriteArea(parsed.Area, parsed.DbNumber, parsed.Offset, amount, wordLen, buffer);
if (result != 0)
{
_lastError = $"Error escribiendo número: {result}";
return false;
}
_lastError = "";
return true;
}
catch (Exception ex)
{
_lastError = ex.Message;
return false;
}
}
// Tags no son soportados en Sharp7 (solo direccionamiento directo)
public object? ReadTag(string tagName)
{
throw new NotSupportedException("Sharp7 no soporta tags simbólicos. Use direccionamiento directo como 'DB1.DBX0.0'");
}
public bool WriteTag(string tagName, object value)
{
throw new NotSupportedException("Sharp7 no soporta tags simbólicos. Use direccionamiento directo como 'DB1.DBX0.0'");
}
public bool? ReadTagBool(string tagName)
{
throw new NotSupportedException("Sharp7 no soporta tags simbólicos. Use direccionamiento directo como 'DB1.DBX0.0'");
}
public bool WriteTagBool(string tagName, bool value)
{
throw new NotSupportedException("Sharp7 no soporta tags simbólicos. Use direccionamiento directo como 'DB1.DBX0.0'");
}
public int? ReadTagInt16(string tagName)
{
throw new NotSupportedException("Sharp7 no soporta tags simbólicos. Use direccionamiento directo como 'DB1.DBW0'");
}
public bool WriteTagInt16(string tagName, int value)
{
throw new NotSupportedException("Sharp7 no soporta tags simbólicos. Use direccionamiento directo como 'DB1.DBW0'");
}
public int? ReadTagDInt(string tagName)
{
throw new NotSupportedException("Sharp7 no soporta tags simbólicos. Use direccionamiento directo como 'DB1.DBD0'");
}
public bool WriteTagDInt(string tagName, int value)
{
throw new NotSupportedException("Sharp7 no soporta tags simbólicos. Use direccionamiento directo como 'DB1.DBD0'");
}
public byte[]? ReadBytes(EArea area, uint offset, int length)
{
if (!IsConnected) return null;
try
{
if (!_areaMapping.TryGetValue(area, out int s7Area))
return null;
byte[] buffer = new byte[length];
int result = _client.ReadArea(s7Area, 0, (int)offset, length, (int)S7WordLength.Byte, buffer);
if (result == 0)
{
return buffer;
}
_lastError = $"Error leyendo bytes: {result}";
return null;
}
catch (Exception ex)
{
_lastError = ex.Message;
return null;
}
}
public bool WriteBytes(EArea area, uint offset, byte[] data)
{
if (!IsConnected) return false;
try
{
if (!_areaMapping.TryGetValue(area, out int s7Area))
return false;
int result = _client.WriteArea(s7Area, 0, (int)offset, data.Length, (int)S7WordLength.Byte, data);
if (result != 0)
{
_lastError = $"Error escribiendo bytes: {result}";
return false;
}
_lastError = "";
return true;
}
catch (Exception ex)
{
_lastError = ex.Message;
return false;
}
}
private AddressParsed? ParseAddress(string address)
{
try
{
// Ejemplos: "DB1.DBX0.0", "I0.0", "Q0.1", "M0.0"
if (address.StartsWith("DB"))
{
// DB1.DBX0.0
var parts = address.Split('.');
if (parts.Length >= 3)
{
int dbNum = int.Parse(parts[0].Substring(2));
int offset = int.Parse(parts[1].Substring(3));
int bit = int.Parse(parts[2]);
return new AddressParsed { Area = (int)S7Area.DB, DbNumber = dbNum, Offset = offset, Bit = bit };
}
}
else if (address.StartsWith("I"))
{
// I0.0
var parts = address.Split('.');
if (parts.Length >= 2)
{
int offset = int.Parse(parts[0].Substring(1));
int bit = int.Parse(parts[1]);
return new AddressParsed { Area = (int)S7Area.PE, DbNumber = 0, Offset = offset, Bit = bit };
}
}
else if (address.StartsWith("Q"))
{
// Q0.0
var parts = address.Split('.');
if (parts.Length >= 2)
{
int offset = int.Parse(parts[0].Substring(1));
int bit = int.Parse(parts[1]);
return new AddressParsed { Area = (int)S7Area.PA, DbNumber = 0, Offset = offset, Bit = bit };
}
}
else if (address.StartsWith("M"))
{
// M0.0
var parts = address.Split('.');
if (parts.Length >= 2)
{
int offset = int.Parse(parts[0].Substring(1));
int bit = int.Parse(parts[1]);
return new AddressParsed { Area = (int)S7Area.MK, DbNumber = 0, Offset = offset, Bit = bit };
}
}
return null;
}
catch
{
return null;
}
}
private NumberAddressParsed? ParseNumberAddress(string address)
{
try
{
// Ejemplos: "DB1.DBW0", "DB1.DBD0", "DB1.DBB0", "IW0", "QW0", "MW0"
if (address.StartsWith("DB"))
{
// DB1.DBW0
var parts = address.Split('.');
if (parts.Length >= 2)
{
int dbNum = int.Parse(parts[0].Substring(2));
string dataTypePart = parts[1].Substring(2); // W0, D0, B0, etc.
string dataType = dataTypePart.Substring(0, 1); // W, D, B
int offset = int.Parse(dataTypePart.Substring(1));
return new NumberAddressParsed { Area = (int)S7Area.DB, DbNumber = dbNum, Offset = offset, DataType = dataType };
}
}
else if (address.StartsWith("IW") || address.StartsWith("IB") || address.StartsWith("ID"))
{
string dataType = address.Substring(1, 1);
int offset = int.Parse(address.Substring(2));
return new NumberAddressParsed { Area = (int)S7Area.PE, DbNumber = 0, Offset = offset, DataType = dataType };
}
else if (address.StartsWith("QW") || address.StartsWith("QB") || address.StartsWith("QD"))
{
string dataType = address.Substring(1, 1);
int offset = int.Parse(address.Substring(2));
return new NumberAddressParsed { Area = (int)S7Area.PA, DbNumber = 0, Offset = offset, DataType = dataType };
}
else if (address.StartsWith("MW") || address.StartsWith("MB") || address.StartsWith("MD"))
{
string dataType = address.Substring(1, 1);
int offset = int.Parse(address.Substring(2));
return new NumberAddressParsed { Area = (int)S7Area.MK, DbNumber = 0, Offset = offset, DataType = dataType };
}
return null;
}
catch
{
return null;
}
}
public void Dispose()
{
Disconnect();
_client = null!;
}
private class AddressParsed
{
public int Area { get; set; }
public int DbNumber { get; set; }
public int Offset { get; set; }
public int Bit { get; set; }
}
private class NumberAddressParsed
{
public int Area { get; set; }
public int DbNumber { get; set; }
public int Offset { get; set; }
public required string DataType { get; set; }
}
}
}