S7Explorer/Parsers/S7ProjectParser.cs

370 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using S7Explorer.Models;
namespace S7Explorer.Parsers
{
public class S7ProjectParser
{
private readonly string _projectFilePath;
private readonly string _projectDirectory;
public S7ProjectParser(string projectFilePath)
{
_projectFilePath = projectFilePath;
_projectDirectory = Path.GetDirectoryName(projectFilePath);
if (string.IsNullOrEmpty(_projectDirectory))
throw new ArgumentException("No se pudo determinar el directorio del proyecto");
}
public async Task<S7Project> ParseProjectAsync()
{
return await Task.Run(() =>
{
// Crear objeto de proyecto
var project = new S7Project(_projectFilePath);
try
{
// Estructura básica del proyecto
var devicesFolder = new S7Object
{
Name = "Dispositivos",
ObjectType = S7ObjectType.Folder,
Parent = project
};
project.Children.Add(devicesFolder);
// Parsear dispositivos
ParseDevices(devicesFolder);
// Para uso en el futuro: más carpetas de alto nivel
var sharedFolder = new S7Object
{
Name = "Datos Globales",
ObjectType = S7ObjectType.Folder,
Parent = project
};
project.Children.Add(sharedFolder);
return project;
}
catch (Exception ex)
{
// En caso de error, al menos retornamos un proyecto básico con el error
var errorObject = new S7Object
{
Name = "Error al parsear proyecto",
Description = ex.Message,
ObjectType = S7ObjectType.Folder,
Parent = project
};
project.Children.Add(errorObject);
return project;
}
});
}
private void ParseDevices(S7Object devicesFolder)
{
try
{
// Obtener lista de dispositivos a partir del archivo de información
var deviceIdInfos = ParseDeviceIdInfos();
foreach (var deviceInfo in deviceIdInfos)
{
var device = new S7Object
{
Name = deviceInfo.Name,
ObjectType = S7ObjectType.Device,
Parent = devicesFolder
};
devicesFolder.Children.Add(device);
// Crear carpetas para cada tipo de bloque
var dbFolder = CreateBlockFolder(device, "Bloques de datos (DB)");
var fbFolder = CreateBlockFolder(device, "Bloques de función (FB)");
var fcFolder = CreateBlockFolder(device, "Funciones (FC)");
var obFolder = CreateBlockFolder(device, "Bloques de organización (OB)");
var symbolsFolder = CreateBlockFolder(device, "Tabla de símbolos");
// Parsear bloques y símbolos si se dispone de IDs
if (deviceInfo.SymbolListId.HasValue)
{
ParseSymbols(symbolsFolder, deviceInfo.SymbolListId.Value);
}
if (deviceInfo.SubblockListId.HasValue)
{
ParseBlocks(dbFolder, fbFolder, fcFolder, obFolder, deviceInfo.SubblockListId.Value);
}
}
}
catch (Exception ex)
{
// Si falla el parseo, añadimos un nodo de error pero seguimos con el resto
var errorNode = new S7Object
{
Name = "Error al parsear dispositivos",
Description = ex.Message,
ObjectType = S7ObjectType.Folder,
Parent = devicesFolder
};
devicesFolder.Children.Add(errorNode);
}
}
private S7Object CreateBlockFolder(S7Object parent, string name)
{
var folder = new S7Object
{
Name = name,
ObjectType = S7ObjectType.Folder,
Parent = parent
};
parent.Children.Add(folder);
return folder;
}
private List<DeviceIdInfo> ParseDeviceIdInfos()
{
var result = new List<DeviceIdInfo>();
// Esto es una simplificación - en una implementación real
// necesitarías parsear los archivos reales como en el proyecto C++
var s7resoffPath = Path.Combine(_projectDirectory, "hrs", "S7RESOFF.DBF");
if (File.Exists(s7resoffPath))
{
try
{
// Leer la tabla de dispositivos
var records = DbfParser.ReadDbfFile(s7resoffPath, new[] { "ID", "NAME", "RSRVD4_L" });
foreach (var record in records)
{
var device = new DeviceIdInfo
{
Name = DbfParser.ConvertCP1252ToUtf8(record["NAME"]),
};
// Procesamiento simplificado para IDs de Subblock y Symbol List
device.SubblockListId = DbfParser.StringToInt(record["RSRVD4_L"]);
// NOTA: En una implementación completa, tendrías que leer linkhrs.lnk
// para obtener SubblockListId y SymbolListId reales
result.Add(device);
}
}
catch (Exception)
{
// Error de parseo - ignorar y continuar
}
}
// Si no se encuentran dispositivos, crear uno de ejemplo
if (result.Count == 0)
{
result.Add(new DeviceIdInfo
{
Name = "Dispositivo de ejemplo"
});
}
return result;
}
private void ParseSymbols(S7Object symbolsFolder, int symbolListId)
{
// Esta es una implementación de muestra
// En una versión completa, leerías los archivos YDBs reales
// Crear algunos símbolos de ejemplo
var symbols = new List<S7Symbol>
{
new S7Symbol {
Name = "Motor_Start",
Address = "I0.0",
DataType = "BOOL",
Comment = "Pulsador de inicio del motor"
},
new S7Symbol {
Name = "Motor_Stop",
Address = "I0.1",
DataType = "BOOL",
Comment = "Pulsador de parada del motor"
},
new S7Symbol {
Name = "Motor_Running",
Address = "Q0.0",
DataType = "BOOL",
Comment = "Motor en marcha"
},
new S7Symbol {
Name = "Temperature",
Address = "IW64",
DataType = "INT",
Comment = "Temperatura del proceso"
},
};
foreach (var symbol in symbols)
{
symbol.Parent = symbolsFolder;
symbolsFolder.Children.Add(symbol);
}
}
private void ParseBlocks(S7Object dbFolder, S7Object fbFolder, S7Object fcFolder, S7Object obFolder, int subblockListId)
{
// Esta es una implementación de muestra
// En una versión completa, leerías los archivos SUBBLK.DBF reales
// Crear algunos bloques de ejemplo
AddSampleDataBlocks(dbFolder);
AddSampleFunctionBlocks(fbFolder);
AddSampleFunctions(fcFolder);
AddSampleOrgBlocks(obFolder);
}
private void AddSampleDataBlocks(S7Object dbFolder)
{
var dbs = new List<S7DataBlock>
{
new S7DataBlock {
Name = "Datos_proceso",
Number = "DB1",
Size = 124,
IsInstanceDb = false,
Description = "Datos de proceso",
Modified = DateTime.Now.AddDays(-5)
},
new S7DataBlock {
Name = "Parámetros",
Number = "DB2",
Size = 234,
IsInstanceDb = false,
Description = "Parámetros de configuración",
Modified = DateTime.Now.AddDays(-10)
},
new S7DataBlock {
Name = "Motor_Inst",
Number = "DB10",
Size = 86,
IsInstanceDb = true,
Description = "Instancia de FB1",
Modified = DateTime.Now.AddDays(-2)
}
};
foreach (var db in dbs)
{
db.Parent = dbFolder;
dbFolder.Children.Add(db);
}
}
private void AddSampleFunctionBlocks(S7Object fbFolder)
{
var fbs = new List<S7FunctionBlock>
{
new S7FunctionBlock {
Name = "Motor_Control",
Number = "FB1",
Size = 328,
Language = "SCL",
Description = "Control de motor",
Modified = DateTime.Now.AddDays(-15)
},
new S7FunctionBlock {
Name = "PID_Control",
Number = "FB2",
Size = 512,
Language = "SCL",
Description = "Controlador PID",
Modified = DateTime.Now.AddDays(-20)
}
};
foreach (var fb in fbs)
{
fb.Parent = fbFolder;
fbFolder.Children.Add(fb);
}
}
private void AddSampleFunctions(S7Object fcFolder)
{
var fcs = new List<S7Function>
{
new S7Function {
Name = "Calc_Setpoint",
Number = "FC1",
Size = 124,
ReturnType = "REAL",
Description = "Cálculo de punto de consigna",
Modified = DateTime.Now.AddDays(-8)
},
new S7Function {
Name = "Scale_Analog",
Number = "FC2",
Size = 68,
ReturnType = "REAL",
Description = "Escalado de valor analógico",
Modified = DateTime.Now.AddDays(-12)
}
};
foreach (var fc in fcs)
{
fc.Parent = fcFolder;
fcFolder.Children.Add(fc);
}
}
private void AddSampleOrgBlocks(S7Object obFolder)
{
var obs = new List<S7Block>
{
new S7Block {
Name = "Main",
Number = "OB1",
Size = 256,
ObjectType = S7ObjectType.Organization,
Description = "Ciclo principal",
Modified = DateTime.Now.AddDays(-1)
},
new S7Block {
Name = "Clock_100ms",
Number = "OB35",
Size = 124,
ObjectType = S7ObjectType.Organization,
Description = "Interrupción cíclica 100ms",
Modified = DateTime.Now.AddDays(-7)
}
};
foreach (var ob in obs)
{
ob.Parent = obFolder;
obFolder.Children.Add(ob);
}
}
// Clase interna para la información de los dispositivos
private class DeviceIdInfo
{
public string Name { get; set; }
public int? SubblockListId { get; set; }
public int? SymbolListId { get; set; }
}
}
}