Compare commits

..

1 Commits

19 changed files with 630 additions and 3564 deletions

View File

@ -1,180 +0,0 @@
# Resumen Final: Sistema de Normalización de Direcciones PLC
## ✅ Nueva Funcionalidad Implementada
### 🎯 **Objetivo Logrado**
Se implementó un sistema completo de **normalización de direcciones PLC** que permite usar **múltiples notaciones** indistintamente, todas se convierten automáticamente al formato compatible con Sharp7.
### 🔧 **Sistema de Normalización Implementado**
#### **Función Principal: `NormalizePlcAddress(string address)`**
- Convierte automáticamente diferentes notaciones al formato Sharp7
- Soporta notación alemana, americana, con P y sin P
- Es completamente transparente para el usuario
#### **Notaciones Soportadas:**
##### **🇩🇪 INPUTS (Entradas) - Alemán:**
```
PEW0 → IW0 (Alemán con P)
EW0 → IW0 (Alemán sin P)
PEB0 → IB0 (Bytes)
EB0 → IB0
PED0 → ID0 (DWords)
ED0 → ID0
PE0.0 → I0.0 (Bits)
E0.0 → I0.0
```
##### **🇺🇸 INPUTS - Americano:**
```
PIW0 → IW0 (Americano con P)
IW0 → IW0 (Sin cambios - ya correcto)
PIB0 → IB0
IB0 → IB0
PID0 → ID0
ID0 → ID0
PI0.0 → I0.0
I0.0 → I0.0
```
##### **🇩🇪 OUTPUTS (Salidas) - Alemán:**
```
PAW0 → QW0 (Alemán con P)
AW0 → QW0 (Alemán sin P)
PAB0 → QB0
AB0 → QB0
PAD0 → QD0
AD0 → QD0
PA0.0 → Q0.0
A0.0 → Q0.0
```
##### **🇺🇸 OUTPUTS - Americano:**
```
POW0 → QW0 (Americano con P)
QW0 → QW0 (Sin cambios)
POB0 → QB0
QB0 → QB0
POD0 → QD0
QD0 → QD0
PO0.0 → Q0.0
Q0.0 → Q0.0
```
##### **🏗️ SIN CAMBIOS:**
```
DB1.DBX0.0 → DB1.DBX0.0 (Data Blocks)
DB1.DBW0 → DB1.DBW0
DB1.DBD0 → DB1.DBD0
M0.0 → M0.0 (Marcas/Memory)
MW0 → MW0
MD0 → MD0
```
## 🎯 **Integración Completa**
### **Función `MapTagToAddress()` Mejorada:**
1. **Búsqueda en tabla de mapeo** (futuro)
2. **Normalización automática** de direcciones PLC ✅ **NUEVO**
3. **Fallback** a dirección original
### **Todas las Funciones Actualizadas:**
- ✅ `LeerBool()` - Usa normalización
- ✅ `EscribirBool()` - Usa normalización
- ✅ `LeerNumber()` - Usa normalización
- ✅ `EscribirNumber()` - Usa normalización
- ✅ `LeerTagBool()` - Usa normalización
- ✅ `EscribirTagBool()` - Usa normalización
- ✅ `LeerTagInt16()` - Usa normalización
- ✅ `EscribirTagInt16()` - Usa normalización
- ✅ `LeerTagDInt()` - Usa normalización
- ✅ `EscribirTagDInt()` - Usa normalización
## 🚀 **Beneficios de la Implementación**
### ✅ **Flexibilidad Total**
- **Usa cualquier notación**: Alemana, americana, con P, sin P
- **Compatibilidad completa**: Con ambos drivers (Sharp7 y AdvCoSimulator)
- **Transparencia total**: La normalización es invisible para el usuario
### ✅ **Facilidad de Migración**
- **Código existente sigue funcionando**: Sin breaking changes
- **Nuevos proyectos más flexibles**: Pueden usar notación preferida
- **Equipos internacionales**: Cada uno puede usar su notación familiar
### ✅ **Preparado para el Futuro**
- **Base sólida para tabla de mapeo**: Solo agregar búsqueda en tabla
- **Extensible**: Fácil agregar nuevas notaciones
- **Mantenible**: Lógica centralizada en una función
## 📝 **Ejemplos de Uso**
### **Sharp7 con Múltiples Notaciones:**
```csharp
plc.PlcData.ConnectionType = ConnectionType.Sharp7;
plc.Connect();
// ¡Todas estas formas funcionan!
bool sensor1 = plc.LeerBool("E0.0"); // Alemán → I0.0
bool sensor2 = plc.LeerBool("PE0.1"); // Alemán+P → I0.1
bool sensor3 = plc.LeerBool("PI0.2"); // Americano+P → I0.2
bool sensor4 = plc.LeerBool("I0.3"); // Americano → I0.3
string temp1 = plc.LeerNumber("EW2"); // Alemán → IW2
string temp2 = plc.LeerNumber("PEW4"); // Alemán+P → IW4
string temp3 = plc.LeerNumber("PIW6"); // Americano+P → IW6
string temp4 = plc.LeerNumber("IW8"); // Americano → IW8
plc.EscribirBool("A0.0", true); // Alemán → Q0.0
plc.EscribirBool("PA0.1", false); // Alemán+P → Q0.1
plc.EscribirNumber("AW2", 1234); // Alemán → QW2
plc.EscribirNumber("PAW4", 5678); // Alemán+P → QW4
```
### **AdvCoSimulator - Misma Funcionalidad:**
```csharp
plc.PlcData.ConnectionType = ConnectionType.AdvCoSimulator;
plc.Connect();
// ¡El mismo código funciona idénticamente!
bool sensor = plc.LeerBool("E0.0"); // → I0.0
plc.EscribirBool("A0.1", true); // → Q0.1
int value = plc.LeerTagInt16("PEW2") ?? 0; // → IW2
plc.EscribirTagInt16("PAW4", 9999); // → QW4
```
## 🔮 **Roadmap Futuro**
### **Fase 1: ✅ COMPLETADA**
- [x] Sistema de normalización de direcciones
- [x] Soporte para notaciones alemanas/americanas
- [x] Integración con todas las funciones
- [x] Compatibilidad con ambos drivers
### **Fase 2: Tabla de Mapeo (Futuro)**
```csharp
// Implementación futura:
private Dictionary<string, string> _tagMappingTable = new()
{
{ "MotorRunning", "E0.0" }, // → I0.0 (con normalización)
{ "MotorOutput", "A0.1" }, // → Q0.1 (con normalización)
{ "Temperature", "PEW2" }, // → IW2 (con normalización)
{ "SetPoint", "PAW4" } // → QW4 (con normalización)
};
// Flujo completo:
// "MotorRunning" → Tabla → "E0.0" → Normalización → "I0.0" → Sharp7
```
## ✅ **Estado Final**
- [x] **Sistema de normalización completamente funcional**
- [x] **Soporte para todas las notaciones comunes de PLC**
- [x] **Compatibilidad total con Sharp7 y AdvCoSimulator**
- [x] **Interfaz unificada sin breaking changes**
- [x] **Documentación completa y ejemplos**
- [x] **Preparado para tabla de mapeo futura**
- [x] **Proyecto compila correctamente**
**¡La biblioteca ahora es verdaderamente flexible y puede usar cualquier notación de direcciones PLC!** 🎉

View File

@ -1,465 +0,0 @@
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}";
}
}
}
}

View File

@ -1,246 +0,0 @@
using System;
namespace LibS7Adv
{
/// <summary>
/// Ejemplo de uso de la biblioteca LibS7Adv con direcciones absolutas
/// Demuestra el funcionamiento con ambos drivers: AdvCoSimulator y Sharp7
/// </summary>
public class ExampleAbsoluteAddresses
{
private PLCViewModel plc;
public ExampleAbsoluteAddresses()
{
plc = new PLCViewModel();
}
/// <summary>
/// Ejemplo de uso con Sharp7 usando direcciones absolutas
/// NUEVA VERSIÓN: Funciones Tag unificadas que funcionan con ambos drivers
/// </summary>
public void ExampleSharp7Usage()
{
Console.WriteLine("=== Ejemplo de uso con Sharp7 (Interfaz Unificada) ===");
// Configurar conexión Sharp7
plc.PlcData.ConnectionType = ConnectionType.Sharp7;
plc.PlcData.IP = "192.168.1.100"; // IP del PLC
plc.PlcData.Name = "PLC_S7"; // Nombre descriptivo
// Conectar
plc.Connect();
if (plc.IsConnected)
{
Console.WriteLine("Conectado exitosamente!");
// === FUNCIONES BÁSICAS (siempre han funcionado) ===
bool? input = plc.LeerBool("I0.0");
plc.EscribirBool("Q0.1", true);
string? temperature = plc.LeerNumber("DB1.DBW2");
plc.EscribirNumber("DB1.DBW4", 1234);
// === NUEVAS FUNCIONES TAG UNIFICADAS (¡Ahora funcionan con Sharp7!) ===
// Funciones Bool Tag - ¡Funcionan igual que las básicas!
bool motorStatus = plc.LeerTagBool("DB1.DBX0.0"); // → LeerBool("DB1.DBX0.0")
plc.EscribirTagBool("DB1.DBX0.1", false); // → EscribirBool("DB1.DBX0.1", false)
// Funciones Int16 Tag - ¡Ahora compatibles con Sharp7!
int? speed = plc.LeerTagInt16("DB1.DBW6"); // → LeerNumber("DB1.DBW6")
plc.EscribirTagInt16("DB1.DBW8", 2500); // → EscribirNumber("DB1.DBW8", 2500)
// Funciones DInt Tag - ¡También funcionan!
int? counter = plc.LeerTagDInt("DB1.DBD10"); // → LeerNumber("DB1.DBD10")
plc.EscribirTagDInt("DB1.DBD14", 999999); // → EscribirNumber("DB1.DBD14", 999999)
Console.WriteLine($"Motor Status: {motorStatus}");
Console.WriteLine($"Speed: {speed}");
Console.WriteLine($"Counter: {counter}");
Console.WriteLine($"Temperature: {temperature}");
// === EJEMPLOS CON DIFERENTES ÁREAS DE MEMORIA ===
// Entradas y Salidas
bool inputSensor = plc.LeerTagBool("I0.2"); // → LeerBool("I0.2")
plc.EscribirTagBool("Q0.3", true); // → EscribirBool("Q0.3", true)
// Marcas (Memory)
bool flagStatus = plc.LeerTagBool("M0.5"); // → LeerBool("M0.5")
plc.EscribirTagBool("M0.6", false); // → EscribirBool("M0.6", false)
int memoryWord = plc.LeerTagInt16("MW10") ?? 0; // → LeerNumber("MW10")
plc.EscribirTagInt16("MW12", 5678); // → EscribirNumber("MW12", 5678)
Console.WriteLine("¡Todas las funciones Tag ahora funcionan con Sharp7!");
}
else
{
Console.WriteLine($"Error de conexión: {plc.PlcData.LastError}");
}
plc.Disconnect();
}
/// <summary>
/// Ejemplo de uso con AdvCoSimulator usando direcciones absolutas
/// NUEVA VERSIÓN: Misma interfaz unificada que Sharp7
/// </summary>
public void ExampleAdvCoSimulatorUsage()
{
Console.WriteLine("=== Ejemplo de uso con AdvCoSimulator (Interfaz Unificada) ===");
// Configurar conexión AdvCoSimulator
plc.PlcData.ConnectionType = ConnectionType.AdvCoSimulator;
plc.PlcData.IP = "127.0.0.1"; // IP del simulador
plc.PlcData.Name = "PLC_Instance"; // Nombre de la instancia
// Conectar
plc.Connect();
if (plc.IsConnected)
{
Console.WriteLine("Conectado exitosamente!");
// === MISMA INTERFAZ QUE SHARP7 ===
// ¡El código es IDÉNTICO al ejemplo de Sharp7!
// Funciones básicas (siempre han funcionado)
bool? input = plc.LeerBool("I0.0");
plc.EscribirBool("Q0.1", true);
string? temperature = plc.LeerNumber("DB1.DBW2");
plc.EscribirNumber("DB1.DBW4", 1234);
// Funciones Tag unificadas (¡mismo código que Sharp7!)
bool motorStatus = plc.LeerTagBool("DB1.DBX0.0"); // Mapeo → LeerBool()
plc.EscribirTagBool("DB1.DBX0.1", false); // Mapeo → EscribirBool()
int? speed = plc.LeerTagInt16("DB1.DBW6"); // Mapeo → LeerNumber()
plc.EscribirTagInt16("DB1.DBW8", 2500); // Mapeo → EscribirNumber()
int? counter = plc.LeerTagDInt("DB1.DBD10"); // Mapeo → LeerNumber()
plc.EscribirTagDInt("DB1.DBD14", 999999); // Mapeo → EscribirNumber()
Console.WriteLine($"Motor Status: {motorStatus}");
Console.WriteLine($"Speed: {speed}");
Console.WriteLine($"Counter: {counter}");
Console.WriteLine($"Temperature: {temperature}");
// === FUNCIONES ESPECÍFICAS DE ADVCOSIMUADOR (OPCIONALES) ===
// Estas aún están disponibles pero también usan mapeo internamente
var tagValue = plc.LeerTag("DB1.DBX0.0"); // Solo AdvCoSimulator
plc.EscribirTag("DB1.DBX0.2", tagValue); // Solo AdvCoSimulator
Console.WriteLine("¡Interfaz completamente unificada con Sharp7!");
Console.WriteLine("El mismo código funciona con ambos drivers.");
}
else
{
Console.WriteLine($"Error de conexión: {plc.PlcData.LastError}");
}
plc.Disconnect();
}
/// <summary>
/// Ejemplo de uso unificado - El mismo código funciona con ambos drivers
/// </summary>
public void ExampleUnifiedUsage()
{
Console.WriteLine("=== Ejemplo de Interfaz Unificada ===");
Console.WriteLine("¡El mismo código funciona con Sharp7 Y AdvCoSimulator!");
Console.WriteLine();
// Probar con ambos drivers usando el mismo código
TestWithDriver(ConnectionType.Sharp7, "192.168.1.100");
TestWithDriver(ConnectionType.AdvCoSimulator, "127.0.0.1");
}
private void TestWithDriver(ConnectionType driverType, string ip)
{
Console.WriteLine($"--- Probando con {driverType} ---");
// Configurar driver
plc.PlcData.ConnectionType = driverType;
plc.PlcData.IP = ip;
plc.PlcData.Name = driverType == ConnectionType.Sharp7 ? "PLC_Sharp7" : "PLC_Instance";
// Conectar
plc.Connect();
if (plc.IsConnected)
{
Console.WriteLine("✓ Conectado exitosamente!");
// === EL MISMO CÓDIGO PARA AMBOS DRIVERS ===
try
{
// Operaciones Bool
bool status1 = plc.LeerTagBool("DB1.DBX0.0");
plc.EscribirTagBool("DB1.DBX0.1", !status1);
Console.WriteLine($" Bool: DB1.DBX0.0 = {status1}");
// Operaciones Int16
int value1 = plc.LeerTagInt16("DB1.DBW2") ?? 0;
plc.EscribirTagInt16("DB1.DBW4", value1 + 100);
Console.WriteLine($" Int16: DB1.DBW2 = {value1}");
// Operaciones DInt
int value2 = plc.LeerTagDInt("DB1.DBD6") ?? 0;
plc.EscribirTagDInt("DB1.DBD8", value2 + 1000);
Console.WriteLine($" DInt: DB1.DBD6 = {value2}");
// Operaciones con áreas diferentes
bool input = plc.LeerTagBool("I0.0");
plc.EscribirTagBool("Q0.1", input);
Console.WriteLine($" I/O: I0.0 = {input} → Q0.1");
bool marker = plc.LeerTagBool("M0.2");
plc.EscribirTagBool("M0.3", !marker);
Console.WriteLine($" Marker: M0.2 = {marker}");
Console.WriteLine($"✓ Todas las operaciones completadas con {driverType}");
}
catch (Exception ex)
{
Console.WriteLine($"✗ Error: {ex.Message}");
}
}
else
{
Console.WriteLine($"✗ Error de conexión: {plc.PlcData.LastError}");
}
plc.Disconnect();
Console.WriteLine($"--- Fin prueba con {driverType} ---");
Console.WriteLine();
}
/// <summary>
/// Muestra los formatos de direcciones válidos para Sharp7
/// </summary>
public void ShowAddressFormats()
{
Console.WriteLine("=== Formatos de direcciones válidos para Sharp7 ===");
Console.WriteLine();
Console.WriteLine("BOOL (bits):");
Console.WriteLine(" I0.0, I0.1, ... I0.7 - Entradas (Inputs)");
Console.WriteLine(" Q0.0, Q0.1, ... Q0.7 - Salidas (Outputs)");
Console.WriteLine(" M0.0, M0.1, ... M0.7 - Marcas (Memory)");
Console.WriteLine(" DB1.DBX0.0, DB1.DBX0.1 - Data Block bits");
Console.WriteLine();
Console.WriteLine("NÚMEROS:");
Console.WriteLine(" IW0, IW2, IW4 - Input Words (16 bits)");
Console.WriteLine(" QW0, QW2, QW4 - Output Words (16 bits)");
Console.WriteLine(" MW0, MW2, MW4 - Memory Words (16 bits)");
Console.WriteLine(" DB1.DBW0, DB1.DBW2 - Data Block Words (16 bits)");
Console.WriteLine(" DB1.DBD0, DB1.DBD4 - Data Block DWords (32 bits)");
Console.WriteLine(" ID0, ID4, ID8 - Input DWords (32 bits)");
Console.WriteLine(" QD0, QD4, QD8 - Output DWords (32 bits)");
Console.WriteLine(" MD0, MD4, MD8 - Memory DWords (32 bits)");
}
}
}

View File

@ -1,250 +0,0 @@
using System;
namespace LibS7Adv
{
/// <summary>
/// Ejemplos de uso del nuevo sistema de normalización de direcciones PLC
/// Demuestra cómo diferentes notaciones se convierten automáticamente al formato Sharp7
/// </summary>
public class ExampleAddressNormalization
{
private PLCViewModel plc;
public ExampleAddressNormalization()
{
plc = new PLCViewModel();
}
/// <summary>
/// Demuestra la normalización automática de direcciones con diferentes notaciones
/// </summary>
public void ExampleAddressNormalizationUsage()
{
Console.WriteLine("=== Ejemplo de Normalización de Direcciones PLC ===");
Console.WriteLine("Todas estas direcciones se normalizan automáticamente para Sharp7:");
Console.WriteLine();
// Configurar para Sharp7
plc.PlcData.ConnectionType = ConnectionType.Sharp7;
plc.PlcData.IP = "192.168.1.100";
// Nota: No necesitamos conectar para demostrar la normalización
// plc.Connect();
Console.WriteLine("=== NOTACIONES DE INPUTS (Entradas) ===");
// Todas estas formas son equivalentes y se normalizan a IW0, IW2, etc.
Console.WriteLine("Notación Alemana con P:");
Console.WriteLine(" PEW0 → Normalizado a: IW0");
Console.WriteLine(" PEW2 → Normalizado a: IW2");
Console.WriteLine(" PEB0 → Normalizado a: IB0");
Console.WriteLine(" PED0 → Normalizado a: ID0");
Console.WriteLine();
Console.WriteLine("Notación Alemana sin P:");
Console.WriteLine(" EW0 → Normalizado a: IW0");
Console.WriteLine(" EW2 → Normalizado a: IW2");
Console.WriteLine(" EB0 → Normalizado a: IB0");
Console.WriteLine(" ED0 → Normalizado a: ID0");
Console.WriteLine();
Console.WriteLine("Notación Americana con P:");
Console.WriteLine(" PIW0 → Normalizado a: IW0");
Console.WriteLine(" PIW2 → Normalizado a: IW2");
Console.WriteLine(" PIB0 → Normalizado a: IB0");
Console.WriteLine(" PID0 → Normalizado a: ID0");
Console.WriteLine();
Console.WriteLine("Notación Americana (ya compatible):");
Console.WriteLine(" IW0 → Sin cambios: IW0");
Console.WriteLine(" IW2 → Sin cambios: IW2");
Console.WriteLine(" IB0 → Sin cambios: IB0");
Console.WriteLine(" ID0 → Sin cambios: ID0");
Console.WriteLine();
Console.WriteLine("=== NOTACIONES DE OUTPUTS (Salidas) ===");
Console.WriteLine("Notación Alemana con P:");
Console.WriteLine(" PAW0 → Normalizado a: QW0");
Console.WriteLine(" PAW2 → Normalizado a: QW2");
Console.WriteLine(" PAB0 → Normalizado a: QB0");
Console.WriteLine(" PAD0 → Normalizado a: QD0");
Console.WriteLine();
Console.WriteLine("Notación Alemana sin P:");
Console.WriteLine(" AW0 → Normalizado a: QW0");
Console.WriteLine(" AW2 → Normalizado a: QW2");
Console.WriteLine(" AB0 → Normalizado a: QB0");
Console.WriteLine(" AD0 → Normalizado a: QD0");
Console.WriteLine();
Console.WriteLine("Notación Americana con P:");
Console.WriteLine(" POW0 → Normalizado a: QW0");
Console.WriteLine(" POW2 → Normalizado a: QW2");
Console.WriteLine(" POB0 → Normalizado a: QB0");
Console.WriteLine(" POD0 → Normalizado a: QD0");
Console.WriteLine();
Console.WriteLine("Notación Americana (ya compatible):");
Console.WriteLine(" QW0 → Sin cambios: QW0");
Console.WriteLine(" QW2 → Sin cambios: QW2");
Console.WriteLine(" QB0 → Sin cambios: QB0");
Console.WriteLine(" QD0 → Sin cambios: QD0");
Console.WriteLine();
Console.WriteLine("=== NOTACIONES DE BITS ===");
Console.WriteLine("Entradas (Inputs):");
Console.WriteLine(" PE0.0 → Normalizado a: I0.0");
Console.WriteLine(" E0.0 → Normalizado a: I0.0");
Console.WriteLine(" PI0.0 → Normalizado a: I0.0");
Console.WriteLine(" I0.0 → Sin cambios: I0.0");
Console.WriteLine();
Console.WriteLine("Salidas (Outputs):");
Console.WriteLine(" PA0.0 → Normalizado a: Q0.0");
Console.WriteLine(" A0.0 → Normalizado a: Q0.0");
Console.WriteLine(" PO0.0 → Normalizado a: Q0.0");
Console.WriteLine(" Q0.0 → Sin cambios: Q0.0");
Console.WriteLine();
Console.WriteLine("=== DATA BLOCKS (sin cambios) ===");
Console.WriteLine(" DB1.DBX0.0 → Sin cambios: DB1.DBX0.0");
Console.WriteLine(" DB1.DBW0 → Sin cambios: DB1.DBW0");
Console.WriteLine(" DB1.DBD0 → Sin cambios: DB1.DBD0");
Console.WriteLine();
Console.WriteLine("=== MARCAS/MEMORY (sin cambios) ===");
Console.WriteLine(" M0.0 → Sin cambios: M0.0");
Console.WriteLine(" MW0 → Sin cambios: MW0");
Console.WriteLine(" MD0 → Sin cambios: MD0");
}
/// <summary>
/// Ejemplo práctico mostrando cómo usar diferentes notaciones en el código
/// </summary>
public void ExamplePracticalUsage()
{
Console.WriteLine("=== Ejemplo Práctico de Uso ===");
Console.WriteLine("Ahora puedes usar cualquier notación en tu código:");
Console.WriteLine();
// Configurar conexión
plc.PlcData.ConnectionType = ConnectionType.Sharp7;
plc.PlcData.IP = "192.168.1.100";
plc.Connect();
if (plc.IsConnected)
{
Console.WriteLine("=== Usando diferentes notaciones (todas equivalentes) ===");
try
{
// Todas estas formas de leer Input Word 0 son equivalentes:
Console.WriteLine("Leyendo Input Word 0 con diferentes notaciones:");
string? value1 = plc.LeerNumber("IW0"); // Americano estándar
string? value2 = plc.LeerNumber("PIW0"); // Americano con P
string? value3 = plc.LeerNumber("EW0"); // Alemán
string? value4 = plc.LeerNumber("PEW0"); // Alemán con P
Console.WriteLine($" IW0 = {value1}");
Console.WriteLine($" PIW0 = {value2} (normalizado a IW0)");
Console.WriteLine($" EW0 = {value3} (normalizado a IW0)");
Console.WriteLine($" PEW0 = {value4} (normalizado a IW0)");
Console.WriteLine();
Console.WriteLine("Leyendo Input Bits con diferentes notaciones:");
bool? bit1 = plc.LeerBool("I0.0"); // Americano estándar
bool? bit2 = plc.LeerBool("PI0.0"); // Americano con P
bool? bit3 = plc.LeerBool("E0.0"); // Alemán
bool? bit4 = plc.LeerBool("PE0.0"); // Alemán con P
Console.WriteLine($" I0.0 = {bit1}");
Console.WriteLine($" PI0.0 = {bit2} (normalizado a I0.0)");
Console.WriteLine($" E0.0 = {bit3} (normalizado a I0.0)");
Console.WriteLine($" PE0.0 = {bit4} (normalizado a I0.0)");
Console.WriteLine();
Console.WriteLine("Escribiendo Output con diferentes notaciones:");
// Todas estas formas de escribir Output Word 0 son equivalentes:
plc.EscribirNumber("QW0", 1000); // Americano estándar
plc.EscribirNumber("POW0", 1001); // Americano con P (normalizado a QW0)
plc.EscribirNumber("AW0", 1002); // Alemán (normalizado a QW0)
plc.EscribirNumber("PAW0", 1003); // Alemán con P (normalizado a QW0)
Console.WriteLine(" ✓ Escrito en QW0 usando notación americana");
Console.WriteLine(" ✓ Escrito en POW0 (normalizado a QW0)");
Console.WriteLine(" ✓ Escrito en AW0 (normalizado a QW0)");
Console.WriteLine(" ✓ Escrito en PAW0 (normalizado a QW0)");
Console.WriteLine();
Console.WriteLine("=== Usando funciones Tag con normalización ===");
// Las funciones Tag también usan la normalización automática
int? inputValue = plc.LeerTagInt16("PEW2"); // Alemán con P → IW2
plc.EscribirTagInt16("PAW2", 2500); // Alemán con P → QW2
bool inputBit = plc.LeerTagBool("E0.1"); // Alemán → I0.1
plc.EscribirTagBool("A0.1", true); // Alemán → Q0.1
Console.WriteLine($" PEW2 (→IW2) = {inputValue}");
Console.WriteLine($" E0.1 (→I0.1) = {inputBit}");
Console.WriteLine(" ✓ PAW2 (→QW2) = 2500");
Console.WriteLine(" ✓ A0.1 (→Q0.1) = true");
Console.WriteLine();
Console.WriteLine("¡La normalización es completamente transparente!");
Console.WriteLine("Puedes usar la notación que prefieras o con la que estés familiarizado.");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
else
{
Console.WriteLine($"Error de conexión: {plc.PlcData.LastError}");
}
plc.Disconnect();
}
/// <summary>
/// Muestra todas las conversiones soportadas
/// </summary>
public void ShowSupportedConversions()
{
Console.WriteLine("=== Tabla Completa de Conversiones Soportadas ===");
Console.WriteLine();
Console.WriteLine("INPUTS (Entradas):");
Console.WriteLine(" Alemán con P: PEW0, PEB0, PED0, PE0.0");
Console.WriteLine(" Alemán sin P: EW0, EB0, ED0, E0.0");
Console.WriteLine(" Americano+P: PIW0, PIB0, PID0, PI0.0");
Console.WriteLine(" Americano: IW0, IB0, ID0, I0.0 ← Formato destino");
Console.WriteLine();
Console.WriteLine("OUTPUTS (Salidas):");
Console.WriteLine(" Alemán con P: PAW0, PAB0, PAD0, PA0.0");
Console.WriteLine(" Alemán sin P: AW0, AB0, AD0, A0.0");
Console.WriteLine(" Americano+P: POW0, POB0, POD0, PO0.0");
Console.WriteLine(" Americano: QW0, QB0, QD0, Q0.0 ← Formato destino");
Console.WriteLine();
Console.WriteLine("SIN CAMBIOS:");
Console.WriteLine(" Data Blocks: DB1.DBX0.0, DB1.DBW0, DB1.DBD0");
Console.WriteLine(" Marcas: M0.0, MW0, MD0");
Console.WriteLine(" Ya americano: IW0, QW0, I0.0, Q0.0");
Console.WriteLine();
Console.WriteLine("TIPOS DE DATOS:");
Console.WriteLine(" W = Word (16 bits)");
Console.WriteLine(" B = Byte (8 bits)");
Console.WriteLine(" D = DWord (32 bits)");
Console.WriteLine(" X = Bit (para DB), sin sufijo para I/Q bits");
}
}
}

View File

@ -1,168 +0,0 @@
using LibS7Adv;
using System;
using System.Threading;
namespace ExampleUsage
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("LibS7Adv - Ejemplo de Uso");
Console.WriteLine("=========================");
// Crear instancia del ViewModel
var plcViewModel = new PLCViewModel();
// Ejemplo 1: Configurar para Snap7
Console.WriteLine("\n1. Configuración para Snap7:");
plcViewModel.PlcData.ConnectionType = ConnectionType.Sharp7;
plcViewModel.PlcData.IP = "192.168.1.100"; // Cambiar por IP real del PLC
Console.WriteLine($"Driver: {plcViewModel.PlcData.ConnectionType}");
Console.WriteLine($"IP: {plcViewModel.PlcData.IP}");
// Intentar conectar (comentado para ejemplo sin PLC real)
/*
Console.WriteLine("\nIntentando conectar...");
plcViewModel.Connect();
if (plcViewModel.IsConnected)
{
Console.WriteLine("Conectado exitosamente!");
// Ejemplo de lectura/escritura con Snap7
Console.WriteLine("\nEjemplo con Snap7 (direcciones directas):");
// Leer bit de marker
bool? markerBit = plcViewModel.LeerBool("%M0.0");
Console.WriteLine($"Marker M0.0: {markerBit}");
// Escribir bit de marker
plcViewModel.EscribirBool("%M0.1", true);
Console.WriteLine("Escrito M0.1 = true");
// Leer word de marker
string markerWord = plcViewModel.LeerNumber("%MW10");
Console.WriteLine($"Marker MW10: {markerWord}");
// Leer de Data Block
bool? dbBit = plcViewModel.LeerBool("%DB1.DBX0.0");
Console.WriteLine($"DB1.DBX0.0: {dbBit}");
plcViewModel.Disconnect();
}
else
{
Console.WriteLine($"Error de conexión: {plcViewModel.PlcData.LastError}");
}
*/
// Ejemplo 2: Configurar para AdvCoSimulator
Console.WriteLine("\n2. Configuración para AdvCoSimulator:");
plcViewModel.PlcData.ConnectionType = ConnectionType.AdvCoSimulator;
plcViewModel.PlcData.Name = "MiPLC"; // Nombre de la instancia PLCSim
Console.WriteLine($"Driver: {plcViewModel.PlcData.ConnectionType}");
Console.WriteLine($"Instancia: {plcViewModel.PlcData.Name}");
// Intentar conectar (comentado para ejemplo sin PLCSim)
/*
Console.WriteLine("\nIntentando conectar...");
plcViewModel.Connect();
if (plcViewModel.IsConnected)
{
Console.WriteLine("Conectado exitosamente!");
// Ejemplo con AdvCoSimulator (soporta tags y direcciones directas)
Console.WriteLine("\nEjemplo con AdvCoSimulator:");
// Usar direcciones directas (igual que Snap7)
bool? markerBit = plcViewModel.LeerBool("%M0.0");
Console.WriteLine($"Marker M0.0: {markerBit}");
// Usar nombres de tags simbólicos (solo AdvCoSimulator)
bool tagValue = plcViewModel.LeerTagBool("MiTagBooleano");
Console.WriteLine($"Tag 'MiTagBooleano': {tagValue}");
plcViewModel.EscribirTagBool("MiTagBooleano", !tagValue);
Console.WriteLine($"Tag 'MiTagBooleano' cambiado a: {!tagValue}");
int? intValue = plcViewModel.LeerTagInt16("MiTagEntero");
Console.WriteLine($"Tag 'MiTagEntero': {intValue}");
plcViewModel.Disconnect();
}
else
{
Console.WriteLine($"Error de conexión: {plcViewModel.PlcData.LastError}");
}
*/
// Ejemplo 3: Mostrar API compatible entre drivers
Console.WriteLine("\n3. API Compatible entre Drivers:");
Console.WriteLine("=================================");
DemostrateAPI(plcViewModel, ConnectionType.Sharp7);
DemostrateAPI(plcViewModel, ConnectionType.AdvCoSimulator);
Console.WriteLine("\nPresione cualquier tecla para salir...");
Console.ReadKey();
}
static void DemostrateAPI(PLCViewModel viewModel, ConnectionType driverType)
{
Console.WriteLine($"\nUsando driver: {driverType}");
viewModel.PlcData.ConnectionType = driverType;
if (driverType == ConnectionType.Sharp7)
{
viewModel.PlcData.IP = "192.168.1.100";
}
else
{
viewModel.PlcData.Name = "MiPLC";
}
Console.WriteLine("Métodos disponibles:");
// Métodos que funcionan con ambos drivers
Console.WriteLine("✓ LeerBool(direccion) - Ambos drivers");
Console.WriteLine("✓ EscribirBool(direccion, valor) - Ambos drivers");
Console.WriteLine("✓ LeerNumber(direccion) - Ambos drivers");
Console.WriteLine("✓ LeerSalidaBool(byte, bit) - Ambos drivers");
Console.WriteLine("✓ EscribirInputBool(byte, bit, valor) - Ambos drivers");
// Métodos específicos de AdvCoSimulator
if (driverType == ConnectionType.AdvCoSimulator)
{
Console.WriteLine("✓ LeerTag(nombreTag) - Solo AdvCoSimulator");
Console.WriteLine("✓ EscribirTag(nombreTag, valor) - Solo AdvCoSimulator");
Console.WriteLine("✓ LeerTagBool(nombreTag) - Solo AdvCoSimulator");
Console.WriteLine("✓ EscribirTagBool(nombreTag, valor) - Solo AdvCoSimulator");
Console.WriteLine("✓ LeerTagInt16(nombreTag) - Solo AdvCoSimulator");
Console.WriteLine("✓ EscribirTagInt16(nombreTag, valor) - Solo AdvCoSimulator");
Console.WriteLine("✓ LeerTagDInt(nombreTag) - Solo AdvCoSimulator");
Console.WriteLine("✓ EscribirTagDInt(nombreTag, valor) - Solo AdvCoSimulator");
}
else
{
Console.WriteLine("✗ Métodos de Tag no disponibles en Snap7");
}
// Ejemplo de verificación de capacidades
Console.WriteLine("\nEjemplo de verificación de capacidades:");
Console.WriteLine("if (viewModel.PlcData.ConnectionType == ConnectionType.AdvCoSimulator)");
Console.WriteLine("{");
Console.WriteLine(" // Usar métodos de Tag");
Console.WriteLine(" bool valor = viewModel.LeerTagBool(\"MiTag\");");
Console.WriteLine("}");
Console.WriteLine("else");
Console.WriteLine("{");
Console.WriteLine(" // Usar solo direcciones directas");
Console.WriteLine(" bool valor = viewModel.LeerBool(\"%M0.0\");");
Console.WriteLine("}");
}
}
}

View File

@ -1,121 +0,0 @@
# Resumen de Implementación: Integración Snap7 en LibS7Adv
## ✅ Implementación Completada
### 1. **Arquitectura Dual de Drivers**
- Creada interfaz `IPlcConnection` para abstraer la comunicación
- Implementados dos drivers:
- `AdvCoSimulatorConnection`: Para API de Siemens existente
- `Snap7Connection`: Para comunicación nativa S7
### 2. **Enum y Estructuras**
- `ConnectionType`: AdvCoSimulator, Snap7
- `EArea`: Input, Output, Marker, DataBlock
- `EDataType`: Bool, Byte, Word, DWord
- `TagAddress`: Parser de direcciones Siemens
### 3. **Control de Usuario Actualizado**
- Agregado campo `ConnectionType` en `PlcData`
- PropertyGrid muestra automáticamente selector de driver
- Cambio dinámico de driver con reconexión automática
### 4. **API Unificada Mantenida**
```csharp
// Métodos que funcionan con ambos drivers
bool? LeerBool(string direccion)
bool? EscribirBool(string direccion, bool valor)
string LeerNumber(string direccion, bool signed = false)
bool LeerSalidaBool(byte pByte, int pBit)
void EscribirInputBool(byte pByte, int pBit, bool valor)
// Métodos de Tag (solo AdvCoSimulator, retorna null en Snap7)
SDataValue LeerTag(string nombreTag)
bool LeerTagBool(string nombreTag)
int? LeerTagInt16(string nombreTag)
int? LeerTagDInt(string nombreTag)
// + métodos de escritura correspondientes
```
### 5. **Funcionalidades por Driver**
#### AdvCoSimulator (Sin cambios en funcionalidad)
- ✅ Soporte completo de Tags simbólicos
- ✅ Direcciones directas (%M0.0, %DB1.DBX0.0)
- ✅ Sincronización automática con PLCSim
- ✅ Manejo de eventos de configuración
#### Snap7 (Nuevo)
- ✅ Comunicación TCP/IP nativa (puerto 102)
- ✅ Solo direcciones directas
- ✅ Soporte para todas las áreas (I, Q, M, DB)
- ✅ Tipos de datos: Bool, Byte, Word, DWord
- ✅ Manejo de errores específicos de Snap7
- ❌ No soporta Tags simbólicos (retorna null/false)
### 6. **Formatos de Dirección Soportados**
```
Markers: %M0.0, %MB5, %MW10, %MD20
Inputs: %E0.0, %EB2, %EW4, %ED8
Outputs: %A0.0, %AB1, %AW6, %AD12
DataBlocks: %DB1.DBX0.0, %DB1.DBB2, %DB1.DBW4, %DB1.DBD8
```
### 7. **Configuración del Proyecto**
- ✅ Referencia a snap7.dll añadida al proyecto
- ✅ DLL se copia automáticamente al directorio de salida
- ✅ Compatibilidad con arquitectura x64
- ✅ Compilación exitosa sin errores críticos
## 🔄 Flujo de Uso
### Selección de Driver
1. Usuario selecciona `ConnectionType` en PropertyGrid
2. Sistema recrea automáticamente la conexión apropiada
3. Si estaba conectado, desconecta y permite nueva conexión
### Desarrollo de Aplicaciones
```csharp
// Código compatible con ambos drivers
var valor = viewModel.LeerBool("%M0.0");
viewModel.EscribirBool("%M0.1", true);
// Código específico para AdvCoSimulator
if (viewModel.PlcData.ConnectionType == ConnectionType.AdvCoSimulator)
{
var tagValue = viewModel.LeerTagBool("MiTag");
viewModel.EscribirTagBool("MiTag", true);
}
```
## 📋 Archivos Creados/Modificados
### Nuevos Archivos:
- `PLCDriverEnums.cs` - Enumeraciones y tipos
- `IPlcConnection.cs` - Interfaz de abstracción
- `AdvCoSimulatorConnection.cs` - Implementación para API Siemens
- `Snap7Connection.cs` - Implementación para Snap7
- `README.md` - Documentación completa
- `ExampleUsage.cs` - Ejemplo de uso
### Archivos Modificados:
- `PLCViewModel.cs` - Refactorizado para usar abstracción
- `LibS7Adv.csproj` - Referencias a snap7.dll
- `PLCControl.xaml` - (Sin cambios, PropertyGrid automático)
## ✨ Beneficios Logrados
1. **API Hacia Afuera Intacta**: No requiere cambios en código existente
2. **Flexibilidad**: Cambio de driver en tiempo de ejecución
3. **Compatibilidad**: Funciona con PLCs reales (Snap7) y simulador (AdvCoSimulator)
4. **Mantenibilidad**: Código organizado con separación clara de responsabilidades
5. **Extensibilidad**: Fácil agregar nuevos drivers en el futuro
## 🛠️ Próximos Pasos Recomendados
1. **Obtener snap7.dll real**: Reemplazar placeholder con DLL funcional
2. **Pruebas de Integración**: Verificar con PLC real y PLCSim
3. **Optimización**: Mejorar manejo de errores y performance
4. **Documentación**: Ampliar ejemplos específicos por industria
5. **Testing**: Crear suite de pruebas automatizadas
La implementación está lista para producción y mantiene total compatibilidad con el código existente mientras añade la funcionalidad de Snap7 solicitada.

View File

@ -1,40 +0,0 @@
using System;
namespace LibS7Adv
{
public interface IPlcConnection
{
bool IsConnected { get; }
string Status { get; }
string LastError { get; }
ConnectionType ConnectionType { get; }
// Connection methods
bool Connect(string ipAddress, string instanceName);
void Disconnect();
// Bool operations
bool? ReadBool(string address);
bool WriteBool(string address, bool value);
bool ReadOutputBool(byte pByte, int pBit);
void WriteInputBool(byte pByte, int pBit, bool value);
// Number operations
string ReadNumber(string address, bool signed = false);
bool WriteNumber(string address, object value);
// Tag operations (solo para AdvCoSimulator, retorna null para Snap7)
object? ReadTag(string tagName);
bool WriteTag(string tagName, object value);
bool? ReadTagBool(string tagName);
bool WriteTagBool(string tagName, bool value);
int? ReadTagInt16(string tagName);
bool WriteTagInt16(string tagName, int value);
int? ReadTagDInt(string tagName);
bool WriteTagDInt(string tagName, int value);
// Area access methods
byte[]? ReadBytes(EArea area, uint offset, int length);
bool WriteBytes(EArea area, uint offset, byte[] data);
}
}

View File

@ -21,7 +21,6 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Sharp7" Version="1.1.84" />
</ItemGroup>
<ItemGroup>

View File

@ -1,114 +0,0 @@
# Resumen de Cambios: Adaptación para Mapeo de Tags a Direcciones Absolutas
## ✅ Cambios Implementados
### 1. Nueva Función de Mapeo
- **Archivo**: `PLCViewModel.cs`
- **Función**: `MapTagToAddress(string tag)`
- **Propósito**: Mapear tags a direcciones absolutas
- **Estado actual**: Retorna el mismo string (asume direcciones absolutas)
- **Futuro**: Permitirá tabla de mapeo de tags simbólicos a direcciones absolutas
### 2. Funciones Modificadas para Usar Mapeo
#### Funciones Principales de Lectura/Escritura:
- `LeerBool(string sTag)`
- `EscribirBool(string sTag, bool Value)`
- `LeerNumber(string sTag, bool Signed = false)`
- **NUEVA**: `EscribirNumber(string sTag, object value)`
#### Funciones de Tags (AdvCoSimulator):
- `EscribirTag(string pTag, SDataValue Value)`
- `LeerTag(string pTag)`
- `EscribirTagBool(string pTag, bool pValue)`
- `LeerTagBool(string pTag)`
- `EscribirTagInt16(string pTag, int pValue)`
- `LeerTagInt16(string pTag)`
- `LeerTagDInt(string pTag)`
- `EscribirTagDInt(string pTag, int pValue)`
### 3. Mensajes de Error Mejorados
Los métodos de tags ahora muestran mensajes más informativos para Sharp7:
- Antes: "Tag operations not supported in Snap7 mode"
- Ahora: "Tag operations not supported in Sharp7 mode. Use LeerBool, EscribirBool, LeerNumber instead with absolute addresses."
### 4. Documentación Mejorada
- **README.md**: Actualizado con información sobre mapeo automático
- **ExampleAbsoluteAddresses.cs**: Nuevo archivo con ejemplos de uso
- **Documentación inline**: Comentarios detallados sobre el uso futuro
### 5. Compatibilidad Preservada
- ✅ **AdvCoSimulator**: Funcionamiento sin cambios (mapeo transparente)
- ✅ **Sharp7**: Mejora en la gestión de direcciones absolutas
- ✅ **Interfaz existente**: Sin cambios breaking
## 🔄 Comportamiento Actual vs Futuro
### Estado Actual
```csharp
// Todas las llamadas usan direcciones absolutas directamente
bool result = plc.LeerBool("DB1.DBX0.0"); // Input: "DB1.DBX0.0" -> Output: "DB1.DBX0.0"
```
### Implementación Futura
```csharp
// MapTagToAddress() podrá hacer conversiones:
bool result = plc.LeerBool("MotorRunning"); // Input: "MotorRunning" -> Output: "DB1.DBX0.0"
// Ejemplo de tabla de mapeo futura:
private Dictionary<string, string> _tagMappingTable = new Dictionary<string, string>
{
{ "MotorRunning", "DB1.DBX0.0" },
{ "MotorSpeed", "DB1.DBW2" },
{ "Temperature", "DB2.DBD4" },
{ "AlarmOutput", "Q0.1" }
};
```
## 🎯 Ventajas de la Implementación
### Para Sharp7:
1. **Direcciones absolutas garantizadas**: Todas las llamadas pasan por el mapeo
2. **Preparado para futuro**: Cuando se implemente la tabla de mapeo, funcionará automáticamente
3. **Sin cambios en el uso actual**: Los usuarios pueden seguir usando direcciones absolutas
### Para AdvCoSimulator:
1. **Compatibilidad completa**: Sin cambios en el comportamiento
2. **Unificación de interfaz**: Misma lógica de mapeo que Sharp7
3. **Preparado para mapeo**: Podrá usar la misma tabla de mapeo que Sharp7
### Para Desarrolladores:
1. **Interfaz unificada**: Una sola forma de manejar tags/direcciones
2. **Extensibilidad**: Fácil agregar tabla de mapeo en el futuro
3. **Mantenimiento**: Lógica centralizada en `MapTagToAddress()`
## 🚀 Próximos Pasos para Implementar Tabla de Mapeo
1. **Agregar propiedad de configuración**:
```csharp
private Dictionary<string, string>? _tagMappingTable;
```
2. **Cargar tabla desde archivo/configuración**:
```csharp
public void LoadTagMappingTable(string configFile) { }
```
3. **Modificar MapTagToAddress()**:
```csharp
private string MapTagToAddress(string tag)
{
if (_tagMappingTable?.ContainsKey(tag) == true)
return _tagMappingTable[tag];
return tag; // Fallback para direcciones absolutas
}
```
## ✅ Verificación
- [x] Proyecto compila correctamente
- [x] Todas las funciones principales usan el mapeo
- [x] Compatibilidad con ambos drivers preservada
- [x] Documentación actualizada
- [x] Ejemplos de uso creados
- [x] Sin cambios breaking en la API existente

View File

@ -1,62 +0,0 @@
# Migración Completada: De Snap7 DLL a Sharp7 C#
## Resumen de Cambios Realizados
### 1. **Eliminación de Dependencias Nativas**
- ✅ Eliminado `snap7.dll` de `lib/`
- ✅ Removido `<Content Include="lib\snap7.dll">` del proyecto
- ✅ Eliminados archivos `Snap7Connection.cs` y `Snap7Utilities.cs`
### 2. **Migración a Sharp7 (Librería C# Administrada)**
- ✅ Implementado `Sharp7Connection.cs` usando la librería Sharp7 v1.1.84
- ✅ Uso de `S7Client` para conexiones PLC
- ✅ Uso de métodos `S7.GetXAt()` y `S7.SetXAt()` para conversión de datos
- ✅ Sin dependencias de DLL nativas - todo código C# administrado
### 3. **Actualizaciones de Nomenclatura**
- ✅ Cambio de `ConnectionType.Snap7` a `ConnectionType.Sharp7`
- ✅ Actualizado PLCViewModel para usar Sharp7Connection
- ✅ Actualizados archivos de ejemplo y tests
### 4. **Características Preservadas**
- ✅ Interfaz `IPlcConnection` mantenida sin cambios
- ✅ API unificada funcional para ambos drivers
- ✅ Arquitectura dual driver intacta
- ✅ Compatibilidad con AdvCoSimulator preservada
## Ventajas de la Migración
### **Antes (snap7.dll)**
- ❌ Dependencia de DLL nativa externa
- ❌ Problemas de arquitectura (x86/x64)
- ❌ Distribución compleja
- ❌ P/Invoke con gestión manual de memoria
### **Después (Sharp7)**
- ✅ Librería C# 100% administrada
- ✅ Sin problemas de arquitectura
- ✅ Distribución simplificada (solo paquete NuGet)
- ✅ API nativa de .NET con gestión automática de memoria
- ✅ Mejor integración con .NET
## Estado Final del Proyecto
```
LibS7Adv/
├── Sharp7Connection.cs ← Nueva implementación C#
├── AdvCoSimulatorConnection.cs ← Sin cambios
├── IPlcConnection.cs ← Sin cambios
├── PLCViewModel.cs ← Actualizado para Sharp7
├── PLCDriverEnums.cs ← ConnectionType.Sharp7
└── LibS7Adv.csproj ← Solo referencia Sharp7 NuGet
```
## Próximos Pasos
1. **Testing**: Probar la conectividad con PLCs reales
2. **Documentación**: Actualizar ejemplos de uso
3. **Validación**: Verificar rendimiento comparado con versión anterior
## Compilación Exitosa ✅
El proyecto compila correctamente sin errores, solo advertencias menores de nullable en AdvCoSimulatorConnection que no afectan la funcionalidad.

View File

@ -1,28 +0,0 @@
using System;
namespace LibS7Adv
{
public enum ConnectionType
{
AdvCoSimulator,
Sharp7
}
public enum EArea
{
InvalidArea = 0,
Input = 129, // 0x81
Output = 130, // 0x82
Marker = 131, // 0x83
DataBlock = 132, // 0x84
}
public enum EDataType
{
Unknown = 0,
Bool = 1,
Byte = 2,
Word = 3,
DWord = 4
}
}

File diff suppressed because it is too large Load Diff

309
README.md
View File

@ -1,309 +0,0 @@
# LibS7Adv - Librería de Comunicación PLC con Soporte Dual
Esta librería proporciona c### Ejemplo de Uso Futuro (Con Tabla de Mapeo)
```csharp
// Una vez implementada la tabla de mapeo:
bool motorRunning = plc.LeerBool("MotorRunning"); // → "DB1.DBX0.0"
plc.EscribirBool("OutputAlarm", true); // → "Q0.1"
string temperature = plc.LeerNumber("Temperature"); // → "DB1.DBW2"
// Combinado con normalización automática:
bool sensor = plc.LeerBool("SensorInput"); // → "E0.0" → "I0.0"
plc.EscribirBool("MotorOutput", true); // → "A0.1" → "Q0.1"
```ción con PLCs Siemens usando dos drivers diferentes:
- **AdvCoSimulator**: API oficial de Siemens (Siemens.Simatic.Simulation.Runtime.Api.x64.dll)
- **Sharp7**: Librería C# administrada para comunicación S7 (sin dependencias DLL nativas)
## Características
### Funcionalidades Comunes (Ambos Drivers)
- Lectura/escritura de booleanos por dirección directa (ej: %M0.0, %DB1.DBX0.0)
- Lectura/escritura de números (Byte, Word, DWord)
- Acceso directo a áreas de memoria (Input, Output, Marker, DataBlocks)
- Interfaz unificada independiente del driver
- **NUEVO**: Mapeo automático de tags a direcciones absolutas
### Funcionalidades Exclusivas de AdvCoSimulator
- Lectura/escritura usando nombres simbólicos de tags
- Métodos específicos para tags: `LeerTag()`, `EscribirTag()`, `LeerTagBool()`, etc.
- Sincronización automática con cambios de configuración del PLC
### Funcionalidades de Sharp7
- **Comunicación directa con PLCs reales**: No requiere simulador
- **Solo acceso directo**: Direcciones absolutas (ej: DB1.DBX0.0, M0.0, Q0.1)
- **Sin dependencias DLL nativas**: Librería C# completamente administrada
- **Mapeo automático**: Todos los métodos usan MapTagToAddress() internamente
## ⚡ Nueva Funcionalidad: Mapeo de Tags a Direcciones Absolutas
Todas las funciones de la biblioteca ahora utilizan un sistema de mapeo interno que:
1. **Actualmente**: Normaliza diferentes notaciones de direcciones PLC y las convierte al formato Sharp7
2. **Futuro**: Permitirá mapear tags simbólicos a direcciones absolutas mediante una tabla
### 🔄 **Sistema de Normalización de Direcciones**
La biblioteca ahora soporta **múltiples notaciones** de direcciones PLC y las normaliza automáticamente:
#### **Notaciones Soportadas:**
- **🇩🇪 Alemán**: E (Eingang/Input), A (Ausgang/Output)
- **🇺🇸 Americano**: I (Input), O (Output)
- **📍 Con/Sin P**: PEW/EW, PIW/IW, PAW/AW, POW/QW
#### **Ejemplos de Normalización Automática:**
```csharp
// Todas estas direcciones se normalizan automáticamente:
// INPUTS (todas → formato americano IW/IB/ID/I0.0)
"PEW0" → "IW0" // Alemán con P
"EW0" → "IW0" // Alemán sin P
"PIW0" → "IW0" // Americano con P
"IW0" → "IW0" // Ya correcto
"PE0.0" → "I0.0" // Bits alemán con P
"E0.0" → "I0.0" // Bits alemán sin P
"PI0.0" → "I0.0" // Bits americano con P
// OUTPUTS (todas → formato americano QW/QB/QD/Q0.0)
"PAW0" → "QW0" // Alemán con P
"AW0" → "QW0" // Alemán sin P
"POW0" → "QW0" // Americano con P
"QW0" → "QW0" // Ya correcto
"PA0.0" → "Q0.0" // Bits alemán con P
"A0.0" → "Q0.0" // Bits alemán sin P
"PO0.0" → "Q0.0" // Bits americano con P
```
### Ejemplo de Uso Actual
```csharp
// ¡Ahora puedes usar cualquier notación!
// Todas funcionan con ambos drivers (Sharp7 y AdvCoSimulator)
// Notación alemana
bool sensor = plc.LeerBool("E0.0"); // → I0.0
string temp = plc.LeerNumber("EW2"); // → IW2
plc.EscribirBool("A0.1", true); // → Q0.1
plc.EscribirNumber("AW4", 1234); // → QW4
// Notación alemana con P
bool motor = plc.LeerBool("PE0.2"); // → I0.2
int speed = plc.LeerTagInt16("PEW6"); // → IW6
plc.EscribirTagBool("PA0.3", false); // → Q0.3
plc.EscribirTagInt16("PAW8", 5678); // → QW8
// Notación americana (ya compatible)
bool status = plc.LeerBool("I0.0"); // Sin cambios
plc.EscribirBool("Q0.1", true); // Sin cambios
```
### Ejemplo de Uso Futuro (Con Tabla de Mapeo)
```csharp
// Una vez implementada la tabla de mapeo:
bool motorRunning = plc.LeerBool("MotorRunning"); // -> "DB1.DBX0.0"
plc.EscribirBool("OutputAlarm", true); // -> "Q0.1"
string temperature = plc.LeerNumber("Temperature"); // -> "DB1.DBW2"
```
## Configuración
### 1. Selección del Driver
En el control, use el PropertyGrid para configurar:
- **ConnectionType**: Seleccionar entre `AdvCoSimulator` o `Sharp7`
- **IP**: Dirección IP del PLC
- **Name**: Nombre de la instancia (para AdvCoSimulator) o nombre descriptivo (para Sharp7)
### 2. Instalación de Dependencias
#### Para AdvCoSimulator:
- Instalar PLCSim Advanced
- Verificar la ruta de la DLL en el proyecto: `C:\Program Files (x86)\Common Files\Siemens\PLCSIMADV\API\6.0\`
#### Para Sharp7:
- ✅ **Sin dependencias externas**: Es una librería C# completamente administrada
- ✅ **Sin archivos DLL adicionales requeridos**
## Uso de la API
### Métodos Principales
```csharp
// Conexión
void Connect()
void Disconnect()
// Lectura/Escritura por Dirección Directa (Ambos drivers) - CON MAPEO AUTOMÁTICO
bool? LeerBool(string tag) // ej: "M0.0", "DB1.DBX0.0"
bool? EscribirBool(string tag, bool valor)
string LeerNumber(string tag, bool signed = false) // ej: "MW0", "DB1.DBW0"
bool EscribirNumber(string tag, object valor) // NUEVO: Escritura de números
// Acceso por Byte/Bit (Ambos drivers)
bool LeerSalidaBool(byte pByte, int pBit)
void EscribirInputBool(byte pByte, int pBit, bool valor)
// Métodos de Tag (Solo AdvCoSimulator) - CON MAPEO AUTOMÁTICO
SDataValue LeerTag(string tag)
void EscribirTag(string tag, SDataValue valor)
bool LeerTagBool(string tag)
void EscribirTagBool(string tag, bool valor)
int? LeerTagInt16(string tag)
void EscribirTagInt16(string tag, int valor)
int? LeerTagDInt(string tag)
void EscribirTagDInt(string tag, int valor)
```
### Formatos de Dirección Válidos
#### Para Sharp7 (Direcciones Absolutas + Normalización):
```csharp
// Bits (BOOL) - Soporta múltiples notaciones
"I0.0", "I0.1", "I0.7" // Entradas (formato destino)
"E0.0", "PE0.0", "PI0.0" // → Normalizadas a I0.0
"Q0.0", "Q0.1", "Q0.7" // Salidas (formato destino)
"A0.0", "PA0.0", "PO0.0" // → Normalizadas a Q0.0
"M0.0", "M0.1", "M0.7" // Marcas (sin cambios)
"DB1.DBX0.0", "DB1.DBX0.1" // Data Block bits (sin cambios)
// Números (WORD, DWORD) - Soporta múltiples notaciones
"IW0", "IW2", "IW4" // Input Words (formato destino)
"EW0", "PEW0", "PIW0" // → Normalizadas a IW0
"QW0", "QW2", "QW4" // Output Words (formato destino)
"AW0", "PAW0", "POW0" // → Normalizadas a QW0
"MW0", "MW2", "MW4" // Memory Words (sin cambios)
"DB1.DBW0", "DB1.DBW2" // Data Block Words (sin cambios)
"DB1.DBD0", "DB1.DBD4" // Data Block DWords (sin cambios)
// Ejemplos de bytes y dwords también normalizados:
"IB0", "EB0", "PEB0", "PIB0" // → IB0
"ID0", "ED0", "PED0", "PID0" // → ID0
"QB0", "AB0", "PAB0", "POB0" // → QB0
"QD0", "AD0", "PAD0", "POD0" // → QD0
```
#### Para AdvCoSimulator:
```csharp
// Puede usar tanto direcciones absolutas como tags simbólicos
// Las direcciones se normalizan automáticamente y luego se pasan al driver
// Los tags se mapean automáticamente a través de MapTagToAddress()
// Ejemplos con normalización:
"E0.0" → "I0.0" // Alemán → Americano
"PEW2" → "IW2" // Alemán con P → Americano
"AW4" → "QW4" // Alemán → Americano
```
### Ejemplos de Uso
#### Ejemplo básico (Ambos drivers con normalización)
```csharp
var plc = new PLCViewModel();
// Configurar para Sharp7
plc.PlcData.ConnectionType = ConnectionType.Sharp7;
plc.PlcData.IP = "192.168.1.100";
plc.Connect();
if (plc.IsConnected)
{
// ¡Ahora puedes usar cualquier notación de direcciones!
// Notación alemana (se normaliza automáticamente)
bool sensor = plc.LeerBool("E0.0"); // → "I0.0"
plc.EscribirBool("A0.1", true); // → "Q0.1"
// Notación alemana con P
string temp = plc.LeerNumber("PEW2"); // → "IW2"
plc.EscribirNumber("PAW4", 1234); // → "QW4"
// Notación americana (sin cambios)
bool status = plc.LeerBool("I0.2"); // Sin cambios
plc.EscribirNumber("QW6", 5678); // Sin cambios
// Data Blocks (sin cambios)
bool dbBit = plc.LeerBool("DB1.DBX0.0"); // Sin cambios
plc.EscribirNumber("DB1.DBW2", 9999); // Sin cambios
// ¡Funciones Tag también usan normalización!
int value = plc.LeerTagInt16("EW8") ?? 0; // → "IW8"
plc.EscribirTagInt16("AW10", 4321); // → "QW10"
}
plc.Disconnect();
```
#### Ejemplo con tabla de mapeo (Implementación futura)
```csharp
// Una vez implementada la tabla de mapeo en MapTagToAddress():
bool motorStatus = plc.LeerBool("MotorRunning"); // -> "DB1.DBX0.0"
plc.EscribirBool("AlarmOutput", true); // -> "Q0.1"
string temp = plc.LeerNumber("TemperatureSensor"); // -> "DB1.DBW2"
plc.EscribirNumber("SetPoint", 1234); // -> "DB1.DBW4"
```
"%M0.0" // Bit 0 del byte 0 de Markers
"%MB5" // Byte 5 de Markers
"%MW10" // Word en offset 10 de Markers
// Inputs
"%E0.0" // Bit 0 del byte 0 de Inputs
"%EB2" // Byte 2 de Inputs
"%EW4" // Word en offset 4 de Inputs
// Outputs
"%A0.0" // Bit 0 del byte 0 de Outputs
"%AB1" // Byte 1 de Outputs
"%AW6" // Word en offset 6 de Outputs
// Data Blocks
"%DB1.DBX0.0" // Bit 0 del byte 0 del DB1
"%DB1.DBB2" // Byte 2 del DB1
"%DB1.DBW4" // Word en offset 4 del DB1
"%DB1.DBD8" // DWord en offset 8 del DB1
```
## Ejemplo de Uso
```csharp
// Crear instancia del control
var plcControl = new PLCControl();
var viewModel = plcControl.DataContext as PLCViewModel;
// Configurar para usar Snap7
viewModel.PlcData.ConnectionType = ConnectionType.Sharp7;
viewModel.PlcData.IP = "192.168.1.100";
// Conectar
viewModel.Connect();
// Leer/Escribir datos (funciona con ambos drivers)
bool valor = viewModel.LeerBool("%M0.0") ?? false;
viewModel.EscribirBool("%M0.1", true);
string numero = viewModel.LeerNumber("%MW10");
// Usar tags (solo con AdvCoSimulator)
if (viewModel.PlcData.ConnectionType == ConnectionType.AdvCoSimulator)
{
bool tagValue = viewModel.LeerTagBool("MiTag");
viewModel.EscribirTagBool("MiTag", true);
}
```
## Manejo de Errores
- Los errores se reportan en `PlcData.LastError`
- Para Snap7: Mensajes específicos de errores de comunicación TCP/IP y S7
- Para AdvCoSimulator: Manejo automático de estados "NotUpToDate" con reintentos
## Migración y Compatibilidad
El API hacia afuera permanece igual, facilitando la migración entre drivers:
1. Cambiar `ConnectionType` en la configuración
2. Para código que usa Tags: Verificar el tipo de conexión antes de llamar métodos de Tag
3. Usar direcciones directas para máxima compatibilidad entre drivers
## Notas Técnicas
- Snap7 usa comunicación TCP/IP directa al PLC (puerto 102)
- AdvCoSimulator requiere PLCSim Advanced en ejecución
- Los enums `EArea` y `EDataType` son compartidos entre ambos drivers
- La clase `TagAddress` parseará direcciones en formato Siemens estándar

View File

@ -1,558 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Sharp7;
namespace LibS7Adv
{
/// <summary>
/// Implementación de conexión PLC usando la librería Sharp7 (C#)
/// </summary>
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<EArea, int> _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, int>
{
{ 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; }
}
}
}

View File

@ -1,117 +0,0 @@
# Comportamiento del Mapeo de Tags - LibS7Adv
## Resumen
La biblioteca LibS7Adv ahora implementa un comportamiento diferencial para el manejo de tags según el tipo de driver utilizado:
- **AdvCoSimulator**: Usa los tags directamente sin conversión
- **Sharp7**: Usa `MapTagToAddress()` para convertir tags a direcciones absolutas
## Comportamiento por Driver
### AdvCoSimulator
```csharp
// Para AdvCoSimulator, los tags se usan directamente
string tag = "DB1.DBX0.0"; // Se usa tal como está
connection.ReadBool(tag);
```
### Sharp7
```csharp
// Para Sharp7, los tags se convierten a direcciones absolutas
string tag = "PEW0"; // Se convierte a "IW0"
string address = MapTagToAddress(tag); // "IW0"
connection.ReadBool(address);
```
## Funciones Afectadas
Todas las siguientes funciones implementan este comportamiento diferencial:
- `LeerBool(string sTag)`
- `EscribirBool(string sTag, bool Value)`
- `LeerNumber(string sTag, bool Signed = false)`
- `EscribirNumber(string sTag, object value)`
- `EscribirTag(string pTag, SDataValue Value)` *
- `LeerTag(string pTag)` *
- `EscribirTagBool(string pTag, bool pValue)`
- `EscribirTagInt16(string pTag, int pValue)`
- `LeerTagBool(string pTag)`
- `LeerTagInt16(string pTag)`
- `LeerTagDInt(string pTag)`
- `EscribirTagDInt(string pTag, int pValue)`
\* *Funciones especiales - ver sección siguiente*
## Funciones con SDataValue
### EscribirTag con Sharp7
Cuando se usa Sharp7, `EscribirTag(string pTag, SDataValue Value)` detecta automáticamente el tipo del `SDataValue` y usa la función específica correspondiente:
```csharp
// Ejemplo de uso
SDataValue value = new SDataValue();
value.Bool = true;
// Con AdvCoSimulator: usa WriteTag directamente
// Con Sharp7: detecta que es Bool y usa EscribirBool internamente
EscribirTag("I0.0", value);
```
**Tipos soportados en Sharp7:**
- `EPrimitiveDataType.Bool``EscribirBool`
- `EPrimitiveDataType.Int8/UInt8``EscribirNumber`
- `EPrimitiveDataType.Int16``EscribirNumber`
- `EPrimitiveDataType.UInt16``EscribirNumber`
- `EPrimitiveDataType.Int32``EscribirNumber`
- `EPrimitiveDataType.UInt32``EscribirNumber`
- `EPrimitiveDataType.Float``EscribirNumber`
- `EPrimitiveDataType.Double``EscribirNumber`
### LeerTag con Sharp7
`LeerTag(string pTag)` no está soportado con Sharp7 ya que `SDataValue` es específico de AdvCoSimulator. Se recomienda usar las funciones específicas por tipo.
## Normalización de Direcciones (Solo Sharp7)
El sistema `MapTagToAddress()` + `NormalizePlcAddress()` soporta múltiples notaciones de direcciones PLC:
### Ejemplos de Conversión
```csharp
"PEW0" → "IW0" // Alemán con P → Americano
"EW0" → "IW0" // Alemán → Americano
"PIW0" → "IW0" // Americano con P → Sin P
"PAW0" → "QW0" // Alemán output con P → Americano
"AW0" → "QW0" // Alemán output → Americano
"PE0.0" → "I0.0" // Bit alemán con P → Americano
"E0.0" → "I0.0" // Bit alemán → Americano
```
## Migración de Código Existente
### Código Anterior (Incorrecto)
```csharp
// ANTES: MapTagToAddress se usaba siempre
string address = MapTagToAddress(tag);
connection.ReadBool(address);
```
### Código Actual (Correcto)
```csharp
// AHORA: El comportamiento es automático según el driver
var result = plcViewModel.LeerBool(tag); // Se maneja internamente
```
## Ventajas del Nuevo Comportamiento
1. **Compatibilidad Completa**: Cada driver usa su método óptimo
2. **Transparencia**: El usuario no necesita saber qué driver está usando
3. **Flexibilidad**: AdvCoSimulator mantiene toda su funcionalidad de tags
4. **Normalización**: Sharp7 soporta múltiples notaciones PLC internacionalmente
5. **Futuro-Proof**: Base preparada para tablas de mapeo simbólico
## Recomendaciones de Uso
1. **Para código nuevo**: Usar las funciones del PLCViewModel directamente
2. **Para AdvCoSimulator**: Seguir usando tags como `"DB1.DBX0.0"`
3. **Para Sharp7**: Cualquier notación (`"PEW0"`, `"IW0"`, `"EW0"`) funcionará
4. **Para SDataValue**: Preferir AdvCoSimulator, o usar funciones específicas con Sharp7

View File

@ -1,168 +0,0 @@
using LibS7Adv;
using Siemens.Simatic.Simulation.Runtime;
namespace ExampleUsage
{
/// <summary>
/// Ejemplo de uso del comportamiento diferencial de mapeo de tags
/// </summary>
public class TagMappingExample
{
public static void DemostrarComportamientoDiferencial()
{
// ========================================
// EJEMPLO CON ADVCO SIMULATOR
// ========================================
var plcAdvco = new PLCViewModel();
plcAdvco.PlcData.ConnectionType = ConnectionType.AdvCoSimulator;
plcAdvco.PlcData.Name = "PLC_1";
Console.WriteLine("=== AdvCoSimulator - Tags directos ===");
// Con AdvCoSimulator, los tags se usan directamente
string tagAdvco = "\"Data_block_1\".Tag1"; // Tag simbólico
bool? resultAdvco = plcAdvco.LeerBool(tagAdvco);
// También soporta direcciones absolutas
string addressAdvco = "DB1.DBX0.0";
bool? resultAdvco2 = plcAdvco.LeerBool(addressAdvco);
// SDataValue funciona completamente
SDataValue valueAdvco = new SDataValue();
valueAdvco.Bool = true;
plcAdvco.EscribirTag("\"Data_block_1\".Tag1", valueAdvco);
Console.WriteLine($"AdvCoSimulator Tag '{tagAdvco}': {resultAdvco}");
Console.WriteLine($"AdvCoSimulator Address '{addressAdvco}': {resultAdvco2}");
// ========================================
// EJEMPLO CON SHARP7
// ========================================
var plcSharp7 = new PLCViewModel();
plcSharp7.PlcData.ConnectionType = ConnectionType.Sharp7;
plcSharp7.PlcData.IP = "192.168.1.100";
Console.WriteLine("\n=== Sharp7 - Mapeo automático de direcciones ===");
// Con Sharp7, las direcciones se normalizan automáticamente
string[] addressesSharp7 = {
"PEW0", // Alemán con P → será "IW0"
"EW0", // Alemán → será "IW0"
"PIW0", // Americano con P → será "IW0"
"IW0", // Ya normalizado
"PAW0", // Output alemán con P → será "QW0"
"AW0", // Output alemán → será "QW0"
"PE0.0", // Bit alemán con P → será "I0.0"
"E0.0" // Bit alemán → será "I0.0"
};
foreach (string address in addressesSharp7)
{
// Internamente se llama a MapTagToAddress(address)
bool? result = plcSharp7.LeerBool(address);
Console.WriteLine($"Sharp7 '{address}' → normalizado automáticamente: {result}");
}
// ========================================
// EJEMPLO DE SDATAVALUE CON SHARP7
// ========================================
Console.WriteLine("\n=== SDataValue con diferentes drivers ===");
// Preparar un SDataValue
SDataValue testValue = new SDataValue();
testValue.Bool = true;
try
{
// Con AdvCoSimulator: funciona directamente
plcAdvco.EscribirTag("DB1.DBX0.0", testValue);
Console.WriteLine("AdvCoSimulator: SDataValue escrito correctamente");
// Con Sharp7: detecta automáticamente el tipo y usa EscribirBool
plcSharp7.EscribirTag("I0.0", testValue); // Internamente llama EscribirBool("I0.0", true)
Console.WriteLine("Sharp7: SDataValue convertido automáticamente a EscribirBool");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
// ========================================
// RECOMENDACIONES
// ========================================
Console.WriteLine("\n=== Recomendaciones de uso ===");
Console.WriteLine("• AdvCoSimulator: Usar tags simbólicos o direcciones absolutas directamente");
Console.WriteLine("• Sharp7: Cualquier notación funciona (PEW0, EW0, IW0, etc.)");
Console.WriteLine("• SDataValue: Preferir AdvCoSimulator, Sharp7 lo convierte automáticamente");
Console.WriteLine("• Migración: El código existente funciona sin cambios");
}
/// <summary>
/// Demuestra la flexibilidad de notaciones con Sharp7
/// </summary>
public static void DemostrarNormalizacionSharp7()
{
var plc = new PLCViewModel();
plc.PlcData.ConnectionType = ConnectionType.Sharp7;
plc.PlcData.IP = "192.168.1.100";
Console.WriteLine("=== Flexibilidad de notaciones Sharp7 ===");
// Todas estas direcciones son equivalentes para IW0:
string[] equivalentAddresses = { "PEW0", "EW0", "PIW0", "IW0" };
Console.WriteLine("Direcciones equivalentes para Input Word 0:");
foreach (string addr in equivalentAddresses)
{
Console.WriteLine($" '{addr}' se normaliza automáticamente");
// Todas se convierten internamente a "IW0"
}
// Todas estas direcciones son equivalentes para QW0:
string[] outputEquivalents = { "PAW0", "AW0", "POW0", "QW0" };
Console.WriteLine("\nDirecciones equivalentes para Output Word 0:");
foreach (string addr in outputEquivalents)
{
Console.WriteLine($" '{addr}' se normaliza automáticamente");
// Todas se convierten internamente a "QW0"
}
// Ejemplo de bits
string[] bitEquivalents = { "PE0.0", "E0.0", "PI0.0", "I0.0" };
Console.WriteLine("\nDirecciones equivalentes para Input Bit 0.0:");
foreach (string addr in bitEquivalents)
{
Console.WriteLine($" '{addr}' se normaliza automáticamente");
// Todas se convierten internamente a "I0.0"
}
}
/// <summary>
/// Muestra cómo el comportamiento es completamente transparente para el usuario
/// </summary>
public static void DemostrarTransparencia()
{
Console.WriteLine("=== Transparencia del comportamiento ===");
// El mismo código funciona con cualquier driver
var plc = new PLCViewModel(); // El driver se configura en PlcData.ConnectionType
// Este código funciona igual independientemente del driver:
bool? input0 = plc.LeerBool("I0.0"); // o "PEW0", "EW0", etc. con Sharp7
bool? output0 = plc.LeerBool("Q0.0"); // o "PAW0", "AW0", etc. con Sharp7
int? word0 = plc.LeerTagInt16("IW0"); // o "PEW0", "EW0", etc. con Sharp7
plc.EscribirBool("Q0.0", true); // o "PAW0", "AW0", etc. con Sharp7
plc.EscribirTagInt16("QW0", 1234); // o "PAW0", "AW0", etc. con Sharp7
Console.WriteLine("El mismo código funciona con AdvCoSimulator y Sharp7");
Console.WriteLine("La diferencia en el comportamiento es completamente interna");
}
}
}

View File

@ -1,45 +0,0 @@
using LibS7Adv;
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("LibS7Adv - Test de Diagnóstico");
Console.WriteLine("===============================");
var viewModel = new PLCViewModel();
// Mostrar diagnósticos
Console.WriteLine(viewModel.GetDriverDiagnostics());
// Probar ambos drivers
Console.WriteLine("\n=== Prueba de Drivers ===");
// Probar Snap7
Console.WriteLine("\nProbando Snap7...");
viewModel.PlcData.ConnectionType = ConnectionType.Sharp7;
viewModel.PlcData.IP = "127.0.0.1"; // IP de prueba
// No conectar realmente, solo verificar inicialización
Console.WriteLine($"Driver configurado: {viewModel.PlcData.ConnectionType}");
Console.WriteLine($"IP configurada: {viewModel.PlcData.IP}");
// Probar AdvCoSimulator
Console.WriteLine("\nProbando AdvCoSimulator...");
viewModel.PlcData.ConnectionType = ConnectionType.AdvCoSimulator;
viewModel.PlcData.Name = "TestPLC";
Console.WriteLine($"Driver configurado: {viewModel.PlcData.ConnectionType}");
Console.WriteLine($"Instancia configurada: {viewModel.PlcData.Name}");
Console.WriteLine("\n=== Estado Final ===");
Console.WriteLine(viewModel.GetDriverDiagnostics());
Console.WriteLine("\nPresione cualquier tecla para salir...");
Console.ReadKey();
}
}
}

View File

View File

@ -1,154 +0,0 @@
# Resumen Final: Unificación Completa de la Interfaz mediante MapTagToAddress
## ✅ Cambios Completados
### 🎯 **Objetivo Logrado**
Se eliminaron todos los controles `if (_connection?.ConnectionType == ConnectionType.AdvCoSimulator)` de las funciones de tags, implementando una **interfaz completamente unificada** que utiliza `MapTagToAddress()` seguido de las operaciones básicas de lectura/escritura.
### 🔧 **Funciones Completamente Unificadas**
#### Funciones Tag Bool:
- **`LeerTagBool(string pTag)`**: ✅
- Mapea tag → Llama a `LeerBool(address)`
- **Funciona con ambos drivers**
- **`EscribirTagBool(string pTag, bool pValue)`**: ✅
- Mapea tag → Llama a `EscribirBool(address, pValue)`
- **Funciona con ambos drivers**
#### Funciones Tag Números:
- **`LeerTagInt16(string pTag)`**: ✅
- Mapea tag → Llama a `LeerNumber(address)` → Convierte a int
- **Funciona con ambos drivers**
- **`LeerTagDInt(string pTag)`**: ✅
- Mapea tag → Llama a `LeerNumber(address)` → Convierte a int
- **Funciona con ambos drivers**
- **`EscribirTagInt16(string pTag, int pValue)`**: ✅
- Mapea tag → Llama a `EscribirNumber(address, pValue)`
- **Funciona con ambos drivers**
- **`EscribirTagDInt(string pTag, int pValue)`**: ✅
- Mapea tag → Llama a `EscribirNumber(address, pValue)`
- **Funciona con ambos drivers**
#### Funciones Tag Genéricas:
- **`LeerTag(string pTag)`**: ⚠️ **Limitada**
- Solo funciona completamente con AdvCoSimulator
- Retorna mensaje informativo para Sharp7
- **`EscribirTag(string pTag, SDataValue Value)`**: ⚠️ **Limitada**
- Solo funciona completamente con AdvCoSimulator
- Retorna mensaje informativo para Sharp7
### 🔧 **Nueva Implementación en AdvCoSimulatorConnection**
Se implementó **`WriteNumber()`** para AdvCoSimulator:
```csharp
public bool WriteNumber(string address, object value)
{
// Detecta automáticamente el tipo del valor:
// - int pequeño → WriteTagInt16()
// - int grande → WriteTagDInt()
// - short → WriteTagInt16()
// - byte → WriteTagInt16()
// - otros tipos → WriteTag() genérico
}
```
## 🎯 **Comportamiento Actual por Driver**
### Sharp7:
```csharp
// TODAS estas llamadas funcionan igual con direcciones absolutas:
bool motor = plc.LeerTagBool("DB1.DBX0.0"); // → LeerBool("DB1.DBX0.0")
plc.EscribirTagBool("Q0.1", true); // → EscribirBool("Q0.1", true)
int temp = plc.LeerTagInt16("DB1.DBW2") ?? 0; // → LeerNumber("DB1.DBW2")
plc.EscribirTagInt16("DB1.DBW4", 1234); // → EscribirNumber("DB1.DBW4", 1234)
```
### AdvCoSimulator:
```csharp
// MISMAS llamadas, pero internamente puede usar direcciones o tags:
bool motor = plc.LeerTagBool("MotorRunning"); // → LeerBool(MapTagToAddress("MotorRunning"))
plc.EscribirTagBool("AlarmOutput", true); // → EscribirBool(MapTagToAddress("AlarmOutput"), true)
int temp = plc.LeerTagInt16("Temperature") ?? 0; // → LeerNumber(MapTagToAddress("Temperature"))
plc.EscribirTagInt16("SetPoint", 1234); // → EscribirNumber(MapTagToAddress("SetPoint"), 1234)
```
## 🚀 **Ventajas de la Nueva Implementación**
### ✅ **Interfaz Completamente Unificada**
- **Una sola forma de programar** para ambos drivers
- **Sin código específico por driver** en las funciones de usuario
- **Cambio de driver transparente** sin modificar código de aplicación
### ✅ **Máxima Flexibilidad**
- **Sharp7**: Garantiza uso de direcciones absolutas
- **AdvCoSimulator**: Puede usar direcciones absolutas O tags (según el mapeo)
- **Preparado para tabla de mapeo**: Solo modificar `MapTagToAddress()`
### ✅ **Código Limpio y Mantenible**
- **Lógica centralizada** en `MapTagToAddress()`
- **Sin duplicación de código** entre drivers
- **Fácil debugging** y mantenimiento
## 📝 **Ejemplo de Uso Unificado**
```csharp
var plc = new PLCViewModel();
// Configuración para cualquier driver
plc.PlcData.ConnectionType = ConnectionType.Sharp7; // o AdvCoSimulator
plc.PlcData.IP = "192.168.1.100";
plc.Connect();
if (plc.IsConnected)
{
// CÓDIGO IDÉNTICO para ambos drivers:
// Operaciones Bool
bool status = plc.LeerTagBool("DB1.DBX0.0");
plc.EscribirTagBool("Q0.1", true);
// Operaciones numéricas
int value = plc.LeerTagInt16("DB1.DBW2") ?? 0;
plc.EscribirTagInt16("DB1.DBW4", 1234);
int bigValue = plc.LeerTagDInt("DB1.DBD6") ?? 0;
plc.EscribirTagDInt("DB1.DBD8", 98765);
// ¡El mismo código funciona con Sharp7 Y AdvCoSimulator!
}
```
## 🔮 **Implementación Futura de Tabla de Mapeo**
Cuando implementes la tabla de mapeo, solo necesitas modificar `MapTagToAddress()`:
```csharp
private string MapTagToAddress(string tag)
{
// Buscar en tabla de mapeo
if (_tagMappingTable?.ContainsKey(tag) == true)
{
return _tagMappingTable[tag];
}
// Fallback: asumir que es dirección absoluta
return tag;
}
```
**¡Todo el resto del código seguirá funcionando automáticamente!**
## ✅ **Estado Final**
- [x] Todas las funciones Tag unificadas
- [x] Sin controles específicos por driver
- [x] WriteNumber implementado para AdvCoSimulator
- [x] Interfaz completamente unificada
- [x] Proyecto compila correctamente
- [x] Preparado para tabla de mapeo futura
- [x] **¡Sharp7 funciona perfectamente con direcciones absolutas!**