Segunda Version con FC, FB y OB
This commit is contained in:
parent
8356c6d2f5
commit
ea96ba2bb9
|
@ -0,0 +1,69 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace S7Explorer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extensiones LINQ para funcionalidades específicas de la aplicación
|
||||||
|
/// </summary>
|
||||||
|
public static class LinqExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Cuenta elementos en una colección que cumplen un predicado
|
||||||
|
/// </summary>
|
||||||
|
public static int Count<T>(this IEnumerable<T> source, Func<T, bool> predicate)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return source.Where(predicate).Count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifica si una colección contiene al menos un elemento
|
||||||
|
/// </summary>
|
||||||
|
public static bool Any<T>(this IEnumerable<T> source)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return source.Any(item => true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifica si una colección contiene al menos un elemento que cumpla un predicado
|
||||||
|
/// </summary>
|
||||||
|
public static bool Any<T>(this IEnumerable<T> source, Func<T, bool> predicate)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using (var enumerator = source.GetEnumerator())
|
||||||
|
{
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
if (predicate(enumerator.Current))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selecciona elementos de una colección que cumplen un predicado
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
if (predicate(item))
|
||||||
|
yield return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace S7Explorer.Models
|
namespace S7Explorer.Models
|
||||||
{
|
{
|
||||||
|
@ -10,6 +13,7 @@ namespace S7Explorer.Models
|
||||||
private string _version;
|
private string _version;
|
||||||
private DateTime? _modified;
|
private DateTime? _modified;
|
||||||
private int _size;
|
private int _size;
|
||||||
|
private string _language;
|
||||||
|
|
||||||
[DisplayName("Autor")]
|
[DisplayName("Autor")]
|
||||||
public string AuthorName
|
public string AuthorName
|
||||||
|
@ -81,6 +85,20 @@ namespace S7Explorer.Models
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DisplayName("Lenguaje")]
|
||||||
|
public string Language
|
||||||
|
{
|
||||||
|
get => _language;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_language != value)
|
||||||
|
{
|
||||||
|
_language = value;
|
||||||
|
OnPropertyChanged(nameof(Language));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public S7Block()
|
public S7Block()
|
||||||
{
|
{
|
||||||
// Establece valores predeterminados específicos para bloques
|
// Establece valores predeterminados específicos para bloques
|
||||||
|
@ -113,49 +131,20 @@ namespace S7Explorer.Models
|
||||||
|
|
||||||
public class S7FunctionBlock : S7Block
|
public class S7FunctionBlock : S7Block
|
||||||
{
|
{
|
||||||
private string _language;
|
|
||||||
|
|
||||||
[DisplayName("Lenguaje")]
|
|
||||||
public string Language
|
|
||||||
{
|
|
||||||
get => _language;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_language != value)
|
|
||||||
{
|
|
||||||
_language = value;
|
|
||||||
OnPropertyChanged(nameof(Language));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public S7FunctionBlock()
|
public S7FunctionBlock()
|
||||||
{
|
{
|
||||||
ObjectType = S7ObjectType.FunctionBlock;
|
ObjectType = S7ObjectType.FunctionBlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class S7Function : S7Block
|
/// <summary>
|
||||||
|
/// Clase para representar un parámetro de función
|
||||||
|
/// </summary>
|
||||||
|
public class FunctionParameter
|
||||||
{
|
{
|
||||||
private string _returnType;
|
public string Name { get; set; }
|
||||||
|
public string DataType { get; set; }
|
||||||
[DisplayName("Tipo de Retorno")]
|
public string Direction { get; set; } // IN, OUT, IN/OUT
|
||||||
public string ReturnType
|
public string Description { get; set; }
|
||||||
{
|
|
||||||
get => _returnType;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_returnType != value)
|
|
||||||
{
|
|
||||||
_returnType = value;
|
|
||||||
OnPropertyChanged(nameof(ReturnType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public S7Function()
|
|
||||||
{
|
|
||||||
ObjectType = S7ObjectType.Function;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using S7Explorer.Parsers;
|
||||||
|
|
||||||
|
namespace S7Explorer.Models
|
||||||
|
{
|
||||||
|
public class S7Function : S7Block
|
||||||
|
{
|
||||||
|
private string _returnType;
|
||||||
|
private List<FunctionParameter> _parameters;
|
||||||
|
private string _interface;
|
||||||
|
|
||||||
|
[DisplayName("Tipo de Retorno")]
|
||||||
|
[Description("Tipo de dato que retorna la función")]
|
||||||
|
[Category("Interfaz")]
|
||||||
|
public string ReturnType
|
||||||
|
{
|
||||||
|
get => _returnType;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_returnType != value)
|
||||||
|
{
|
||||||
|
_returnType = value;
|
||||||
|
OnPropertyChanged(nameof(ReturnType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
[JsonIgnore]
|
||||||
|
public List<FunctionParameter> Parameters
|
||||||
|
{
|
||||||
|
get => _parameters;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_parameters != value)
|
||||||
|
{
|
||||||
|
_parameters = value;
|
||||||
|
OnPropertyChanged(nameof(Parameters));
|
||||||
|
|
||||||
|
// Actualizar la interfaz formateada cuando se cambian los parámetros
|
||||||
|
UpdateFormattedInterface();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DisplayName("Interfaz")]
|
||||||
|
[Description("Interfaz de la función con sus parámetros")]
|
||||||
|
[Category("Interfaz")]
|
||||||
|
[EditorAttribute(typeof(System.ComponentModel.Design.MultilineStringEditor),
|
||||||
|
typeof(System.Drawing.Design.UITypeEditor))]
|
||||||
|
public string Interface
|
||||||
|
{
|
||||||
|
get => _interface;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_interface != value)
|
||||||
|
{
|
||||||
|
_interface = value;
|
||||||
|
OnPropertyChanged(nameof(Interface));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propiedades adicionales específicas de FC
|
||||||
|
|
||||||
|
[DisplayName("Parámetros de Entrada")]
|
||||||
|
[Description("Número de parámetros de entrada")]
|
||||||
|
[Category("Estadísticas")]
|
||||||
|
public int InputParameterCount =>
|
||||||
|
Parameters?.Count(p => p.Direction == "IN") ?? 0;
|
||||||
|
|
||||||
|
[DisplayName("Parámetros de Salida")]
|
||||||
|
[Description("Número de parámetros de salida")]
|
||||||
|
[Category("Estadísticas")]
|
||||||
|
public int OutputParameterCount =>
|
||||||
|
Parameters?.Count(p => p.Direction == "OUT") ?? 0;
|
||||||
|
|
||||||
|
[DisplayName("Parámetros IN/OUT")]
|
||||||
|
[Description("Número de parámetros de entrada/salida")]
|
||||||
|
[Category("Estadísticas")]
|
||||||
|
public int InOutParameterCount =>
|
||||||
|
Parameters?.Count(p => p.Direction == "IN/OUT") ?? 0;
|
||||||
|
|
||||||
|
[DisplayName("Total Parámetros")]
|
||||||
|
[Description("Número total de parámetros")]
|
||||||
|
[Category("Estadísticas")]
|
||||||
|
public int TotalParameterCount =>
|
||||||
|
Parameters?.Count ?? 0;
|
||||||
|
|
||||||
|
public S7Function()
|
||||||
|
{
|
||||||
|
ObjectType = S7ObjectType.Function;
|
||||||
|
Parameters = new List<FunctionParameter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza la representación formateada de la interfaz basada en los parámetros
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateFormattedInterface()
|
||||||
|
{
|
||||||
|
if (Parameters == null || Parameters.Count == 0)
|
||||||
|
{
|
||||||
|
Interface = "// No hay parámetros definidos";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new System.Text.StringBuilder();
|
||||||
|
|
||||||
|
// Agrupar por dirección
|
||||||
|
var inputParams = Parameters.Where(p => p.Direction == "IN").ToList();
|
||||||
|
var outputParams = Parameters.Where(p => p.Direction == "OUT").ToList();
|
||||||
|
var inOutParams = Parameters.Where(p => p.Direction == "IN/OUT").ToList();
|
||||||
|
|
||||||
|
// Añadir tipo de retorno
|
||||||
|
builder.AppendLine($"FUNCTION {Name} : {ReturnType ?? "VOID"}");
|
||||||
|
builder.AppendLine();
|
||||||
|
|
||||||
|
// Añadir parámetros de entrada
|
||||||
|
if (inputParams.Any())
|
||||||
|
{
|
||||||
|
builder.AppendLine("VAR_INPUT");
|
||||||
|
foreach (var param in inputParams)
|
||||||
|
{
|
||||||
|
builder.Append($" {param.Name} : {param.DataType}");
|
||||||
|
if (!string.IsNullOrEmpty(param.Description))
|
||||||
|
builder.Append($"; // {param.Description}");
|
||||||
|
builder.AppendLine();
|
||||||
|
}
|
||||||
|
builder.AppendLine("END_VAR");
|
||||||
|
builder.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Añadir parámetros de salida
|
||||||
|
if (outputParams.Any())
|
||||||
|
{
|
||||||
|
builder.AppendLine("VAR_OUTPUT");
|
||||||
|
foreach (var param in outputParams)
|
||||||
|
{
|
||||||
|
builder.Append($" {param.Name} : {param.DataType}");
|
||||||
|
if (!string.IsNullOrEmpty(param.Description))
|
||||||
|
builder.Append($"; // {param.Description}");
|
||||||
|
builder.AppendLine();
|
||||||
|
}
|
||||||
|
builder.AppendLine("END_VAR");
|
||||||
|
builder.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Añadir parámetros de entrada/salida
|
||||||
|
if (inOutParams.Any())
|
||||||
|
{
|
||||||
|
builder.AppendLine("VAR_IN_OUT");
|
||||||
|
foreach (var param in inOutParams)
|
||||||
|
{
|
||||||
|
builder.Append($" {param.Name} : {param.DataType}");
|
||||||
|
if (!string.IsNullOrEmpty(param.Description))
|
||||||
|
builder.Append($"; // {param.Description}");
|
||||||
|
builder.AppendLine();
|
||||||
|
}
|
||||||
|
builder.AppendLine("END_VAR");
|
||||||
|
}
|
||||||
|
|
||||||
|
Interface = builder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ namespace S7Explorer.Parsers
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Abrir tabla DBF con codificación específica
|
// Abrir tabla DBF con codificación específica (importante para caracteres especiales en STEP7)
|
||||||
using var stream = File.OpenRead(filePath);
|
using var stream = File.OpenRead(filePath);
|
||||||
using var table = Table.Open(stream);
|
using var table = Table.Open(stream);
|
||||||
|
|
||||||
|
@ -33,14 +33,23 @@ namespace S7Explorer.Parsers
|
||||||
{
|
{
|
||||||
// Obtener valor y convertir a string si no es null
|
// Obtener valor y convertir a string si no es null
|
||||||
var value = reader.GetValue(fieldName);
|
var value = reader.GetValue(fieldName);
|
||||||
record[fieldName] = value?.ToString() ?? string.Empty;
|
|
||||||
|
|
||||||
// Manejamos específicamente bytes para campos como "MC5CODE"
|
// Manejar los diferentes tipos de datos
|
||||||
// que pueden contener datos binarios codificados como CP1252
|
|
||||||
if (value is byte[] byteValue)
|
if (value is byte[] byteValue)
|
||||||
{
|
{
|
||||||
|
// Para campos de tipo binario como MC5CODE
|
||||||
record[fieldName] = Encoding.GetEncoding(1252).GetString(byteValue);
|
record[fieldName] = Encoding.GetEncoding(1252).GetString(byteValue);
|
||||||
}
|
}
|
||||||
|
else if (value is DateTime dateValue)
|
||||||
|
{
|
||||||
|
// Mantener formato consistente para fechas
|
||||||
|
record[fieldName] = dateValue.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Para todos los demás tipos
|
||||||
|
record[fieldName] = value?.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Add(record);
|
result.Add(record);
|
||||||
|
@ -60,7 +69,21 @@ namespace S7Explorer.Parsers
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (int.TryParse(value, out int result))
|
// Eliminar espacios y caracteres no numéricos iniciales
|
||||||
|
string trimmedValue = value.Trim();
|
||||||
|
int startIndex = 0;
|
||||||
|
while (startIndex < trimmedValue.Length && !char.IsDigit(trimmedValue[startIndex]))
|
||||||
|
startIndex++;
|
||||||
|
|
||||||
|
if (startIndex >= trimmedValue.Length)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Extraer solo los dígitos
|
||||||
|
string numericPart = new string(trimmedValue.Substring(startIndex)
|
||||||
|
.TakeWhile(char.IsDigit)
|
||||||
|
.ToArray());
|
||||||
|
|
||||||
|
if (int.TryParse(numericPart, out int result))
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -72,8 +95,45 @@ namespace S7Explorer.Parsers
|
||||||
if (string.IsNullOrEmpty(input))
|
if (string.IsNullOrEmpty(input))
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Primero decodificar como Windows-1252 y luego encodear como UTF-8
|
||||||
byte[] bytes = Encoding.GetEncoding(1252).GetBytes(input);
|
byte[] bytes = Encoding.GetEncoding(1252).GetBytes(input);
|
||||||
return Encoding.UTF8.GetString(bytes);
|
return Encoding.UTF8.GetString(bytes);
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// En caso de error, devolver el string original
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Busca un archivo DBF en varias ubicaciones posibles basadas en el patrón de archivos STEP7
|
||||||
|
public static string FindDbfFile(string basePath, string relativePath)
|
||||||
|
{
|
||||||
|
string path = Path.Combine(basePath, relativePath);
|
||||||
|
if (File.Exists(path))
|
||||||
|
return path;
|
||||||
|
|
||||||
|
// Comprobar si se puede encontrar en un directorio padre
|
||||||
|
string parentPath = Directory.GetParent(basePath)?.FullName;
|
||||||
|
if (!string.IsNullOrEmpty(parentPath))
|
||||||
|
{
|
||||||
|
path = Path.Combine(parentPath, relativePath);
|
||||||
|
if (File.Exists(path))
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intentar buscar por nombre de archivo en subdirectorios
|
||||||
|
string fileName = Path.GetFileName(relativePath);
|
||||||
|
foreach (var subdir in Directory.GetDirectories(basePath, "*", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
path = Path.Combine(subdir, fileName);
|
||||||
|
if (File.Exists(path))
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,439 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using S7Explorer.Models;
|
||||||
|
|
||||||
|
namespace S7Explorer.Parsers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parser específico para los bloques de función (FC) de STEP7
|
||||||
|
/// </summary>
|
||||||
|
public class FCParser
|
||||||
|
{
|
||||||
|
private readonly string _projectDirectory;
|
||||||
|
|
||||||
|
public FCParser(string projectDirectory)
|
||||||
|
{
|
||||||
|
_projectDirectory = projectDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parsea los bloques FC desde los archivos SUBBLK.DBF del proyecto
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="subblockListId">ID de la lista de subbloques del dispositivo</param>
|
||||||
|
/// <returns>Lista de objetos S7Function representando los bloques FC</returns>
|
||||||
|
public List<S7Function> ParseFunctionBlocks(int subblockListId)
|
||||||
|
{
|
||||||
|
var functions = new List<S7Function>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Construir la ruta al archivo SUBBLK.DBF que contiene información de los bloques
|
||||||
|
string subblockFolder = $"{_projectDirectory}\\ombstx\\offline\\{subblockListId:X8}";
|
||||||
|
string subblkPath = Path.Combine(subblockFolder, "SUBBLK.DBF");
|
||||||
|
|
||||||
|
if (!File.Exists(subblkPath))
|
||||||
|
{
|
||||||
|
return CreateSampleFunctions(); // En caso de que no exista, usar datos de muestra
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leer datos del archivo DBF
|
||||||
|
var records = DbfParser.ReadDbfFile(subblkPath, new[]
|
||||||
|
{
|
||||||
|
"SUBBLKTYP", "BLKNUMBER", "BLKNAME", "AUTHOR", "FAMILY",
|
||||||
|
"VERSION", "CREATEDATE", "MODDATE", "INTERFLEN", "MC5LEN", "MC5CODE"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filtrar solo los registros que correspondan a FCs (tipo 00003)
|
||||||
|
var fcRecords = records.Where(r => r["SUBBLKTYP"] == "00003").ToList();
|
||||||
|
|
||||||
|
foreach (var record in fcRecords)
|
||||||
|
{
|
||||||
|
// Convertir número de bloque a entero
|
||||||
|
if (!int.TryParse(record["BLKNUMBER"], out int blockNumber))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Crear objeto FC con los datos del registro
|
||||||
|
var fc = new S7Function
|
||||||
|
{
|
||||||
|
Number = $"FC{blockNumber}",
|
||||||
|
Name = DbfParser.ConvertCP1252ToUtf8(record["BLKNAME"]).Trim(),
|
||||||
|
AuthorName = DbfParser.ConvertCP1252ToUtf8(record["AUTHOR"]).Trim(),
|
||||||
|
Family = DbfParser.ConvertCP1252ToUtf8(record["FAMILY"]).Trim(),
|
||||||
|
Version = record["VERSION"].Trim(),
|
||||||
|
Size = ParseBlockSize(record["MC5LEN"]),
|
||||||
|
ObjectType = S7ObjectType.Function,
|
||||||
|
Language = DetermineLanguageFromMC5Code(record["MC5CODE"]),
|
||||||
|
ReturnType = DetermineReturnTypeFromMC5Code(record["MC5CODE"])
|
||||||
|
};
|
||||||
|
|
||||||
|
// Intentar extraer fecha de modificación
|
||||||
|
if (DateTime.TryParse(record["MODDATE"], out DateTime modDate))
|
||||||
|
{
|
||||||
|
fc.Modified = modDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extraer más información del código MC5
|
||||||
|
ExtractAdditionalInfoFromMC5Code(fc, record["MC5CODE"]);
|
||||||
|
|
||||||
|
// Analizar variables de entrada/salida del interfaz
|
||||||
|
AnalyzeInterfaceData(fc, record["MC5CODE"]);
|
||||||
|
|
||||||
|
functions.Add(fc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordenar los bloques por número
|
||||||
|
functions = functions.OrderBy(f => ExtractNumber(f.Number)).ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// En caso de error, añadir un FC de error para informar al usuario
|
||||||
|
var errorFc = new S7Function
|
||||||
|
{
|
||||||
|
Name = "Error_Parsing_FC",
|
||||||
|
Number = "FC999",
|
||||||
|
Description = $"Error al parsear FCs: {ex.Message}",
|
||||||
|
ObjectType = S7ObjectType.Function
|
||||||
|
};
|
||||||
|
functions.Add(errorFc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si no se encontraron FCs, usar datos de muestra
|
||||||
|
if (functions.Count == 0)
|
||||||
|
{
|
||||||
|
return CreateSampleFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
return functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extrae el número de un bloque a partir de su identificador (ej: "FC5" -> 5)
|
||||||
|
/// </summary>
|
||||||
|
private int ExtractNumber(string blockId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(blockId) || blockId.Length < 3)
|
||||||
|
return 9999; // Valor alto para ordenar al final
|
||||||
|
|
||||||
|
if (int.TryParse(blockId.Substring(2), out int number))
|
||||||
|
return number;
|
||||||
|
|
||||||
|
return 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parsea el tamaño del bloque a partir del valor MC5LEN
|
||||||
|
/// </summary>
|
||||||
|
private int ParseBlockSize(string mc5Len)
|
||||||
|
{
|
||||||
|
if (int.TryParse(mc5Len, out int size))
|
||||||
|
return size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determina el lenguaje de programación a partir del código MC5
|
||||||
|
/// </summary>
|
||||||
|
private string DetermineLanguageFromMC5Code(string mc5Code)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(mc5Code))
|
||||||
|
return "Desconocido";
|
||||||
|
|
||||||
|
// Esta es una lógica simplificada. En una implementación real,
|
||||||
|
// necesitarías analizar patrones específicos en el código MC5
|
||||||
|
if (mc5Code.Contains("STL") || mc5Code.Contains("AWL"))
|
||||||
|
return "AWL";
|
||||||
|
else if (mc5Code.Contains("SCL"))
|
||||||
|
return "SCL";
|
||||||
|
else if (mc5Code.Contains("GRAPH"))
|
||||||
|
return "GRAPH";
|
||||||
|
else if (mc5Code.Contains("KOP") || mc5Code.Contains("LAD"))
|
||||||
|
return "KOP";
|
||||||
|
else if (mc5Code.Contains("FUP") || mc5Code.Contains("FBD"))
|
||||||
|
return "FUP";
|
||||||
|
|
||||||
|
// Por defecto asumimos AWL (lenguaje más común)
|
||||||
|
return "AWL";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determina el tipo de retorno a partir del código MC5
|
||||||
|
/// </summary>
|
||||||
|
private string DetermineReturnTypeFromMC5Code(string mc5Code)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(mc5Code))
|
||||||
|
return "VOID";
|
||||||
|
|
||||||
|
// Esta es una lógica simplificada. En una implementación real,
|
||||||
|
// necesitarías analizar patrones específicos en el código MC5
|
||||||
|
if (mc5Code.Contains("BOOL") && mc5Code.Contains("RET_VAL"))
|
||||||
|
return "BOOL";
|
||||||
|
else if (mc5Code.Contains("INT") && mc5Code.Contains("RET_VAL"))
|
||||||
|
return "INT";
|
||||||
|
else if (mc5Code.Contains("REAL") && mc5Code.Contains("RET_VAL"))
|
||||||
|
return "REAL";
|
||||||
|
else if (mc5Code.Contains("WORD") && mc5Code.Contains("RET_VAL"))
|
||||||
|
return "WORD";
|
||||||
|
else if (mc5Code.Contains("DWORD") && mc5Code.Contains("RET_VAL"))
|
||||||
|
return "DWORD";
|
||||||
|
else if (mc5Code.Contains("TIME") && mc5Code.Contains("RET_VAL"))
|
||||||
|
return "TIME";
|
||||||
|
|
||||||
|
// Buscar cualquier tipo de dato asociado a RET_VAL
|
||||||
|
int retValPos = mc5Code.IndexOf("RET_VAL");
|
||||||
|
if (retValPos > 0)
|
||||||
|
{
|
||||||
|
string[] dataTypes = { "BYTE", "DINT", "CHAR", "STRING", "DATE", "TIME_OF_DAY", "DATE_AND_TIME" };
|
||||||
|
foreach (string type in dataTypes)
|
||||||
|
{
|
||||||
|
if (mc5Code.IndexOf(type, retValPos - 50, 100) > 0)
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "VOID";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extrae información adicional del código MC5
|
||||||
|
/// </summary>
|
||||||
|
private void ExtractAdditionalInfoFromMC5Code(S7Function fc, string mc5Code)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(mc5Code))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Extraer descripción (comentario del bloque)
|
||||||
|
int descStart = mc5Code.IndexOf("//");
|
||||||
|
if (descStart >= 0)
|
||||||
|
{
|
||||||
|
int descEnd = mc5Code.IndexOf('\n', descStart);
|
||||||
|
if (descEnd > descStart)
|
||||||
|
{
|
||||||
|
string comment = mc5Code.Substring(descStart + 2, descEnd - descStart - 2).Trim();
|
||||||
|
if (!string.IsNullOrEmpty(comment))
|
||||||
|
fc.Description = DbfParser.ConvertCP1252ToUtf8(comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Añadir otros metadatos que se puedan extraer
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignorar errores en la extracción para no detener el proceso
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Analiza los datos de interfaz para extraer parámetros de entrada/salida
|
||||||
|
/// </summary>
|
||||||
|
private void AnalyzeInterfaceData(S7Function fc, string mc5Code)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(mc5Code))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Crear lista de parámetros
|
||||||
|
fc.Parameters = new List<FunctionParameter>();
|
||||||
|
|
||||||
|
// Buscar sección de interfaz
|
||||||
|
int varInputPos = mc5Code.IndexOf("VAR_INPUT");
|
||||||
|
int varOutputPos = mc5Code.IndexOf("VAR_OUTPUT");
|
||||||
|
int varInOutPos = mc5Code.IndexOf("VAR_IN_OUT");
|
||||||
|
|
||||||
|
// Procesar parámetros de entrada
|
||||||
|
if (varInputPos >= 0)
|
||||||
|
{
|
||||||
|
int endPos = DetermineNextSectionPos(mc5Code, varInputPos);
|
||||||
|
string inputSection = mc5Code.Substring(varInputPos, endPos - varInputPos);
|
||||||
|
ExtractParameters(inputSection, "IN", fc.Parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Procesar parámetros de salida
|
||||||
|
if (varOutputPos >= 0)
|
||||||
|
{
|
||||||
|
int endPos = DetermineNextSectionPos(mc5Code, varOutputPos);
|
||||||
|
string outputSection = mc5Code.Substring(varOutputPos, endPos - varOutputPos);
|
||||||
|
ExtractParameters(outputSection, "OUT", fc.Parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Procesar parámetros de entrada/salida
|
||||||
|
if (varInOutPos >= 0)
|
||||||
|
{
|
||||||
|
int endPos = DetermineNextSectionPos(mc5Code, varInOutPos);
|
||||||
|
string inOutSection = mc5Code.Substring(varInOutPos, endPos - varInOutPos);
|
||||||
|
ExtractParameters(inOutSection, "IN/OUT", fc.Parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignorar errores en la extracción para no detener el proceso
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determina la posición de la siguiente sección en el código
|
||||||
|
/// </summary>
|
||||||
|
private int DetermineNextSectionPos(string code, int startPos)
|
||||||
|
{
|
||||||
|
string[] sections = { "VAR_INPUT", "VAR_OUTPUT", "VAR_IN_OUT", "VAR_TEMP", "VAR", "BEGIN" };
|
||||||
|
|
||||||
|
int minPos = code.Length;
|
||||||
|
foreach (string section in sections)
|
||||||
|
{
|
||||||
|
int pos = code.IndexOf(section, startPos + 1);
|
||||||
|
if (pos > 0 && pos < minPos)
|
||||||
|
minPos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return minPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extrae parámetros individuales de una sección de interfaz
|
||||||
|
/// </summary>
|
||||||
|
private void ExtractParameters(string section, string direction, List<FunctionParameter> parameters)
|
||||||
|
{
|
||||||
|
// Dividir por líneas
|
||||||
|
string[] lines = section.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
foreach (string line in lines)
|
||||||
|
{
|
||||||
|
string trimmedLine = line.Trim();
|
||||||
|
|
||||||
|
// Ignorar líneas de declaración de sección, comentarios o END_VAR
|
||||||
|
if (trimmedLine.StartsWith("VAR_") || trimmedLine.StartsWith("//") ||
|
||||||
|
trimmedLine.StartsWith("END_VAR") || string.IsNullOrWhiteSpace(trimmedLine))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Procesar línea con declaración de parámetro
|
||||||
|
int colonPos = trimmedLine.IndexOf(':');
|
||||||
|
if (colonPos > 0)
|
||||||
|
{
|
||||||
|
string paramName = trimmedLine.Substring(0, colonPos).Trim();
|
||||||
|
|
||||||
|
// Extraer tipo y comentario
|
||||||
|
string remainder = trimmedLine.Substring(colonPos + 1).Trim();
|
||||||
|
string paramType = remainder;
|
||||||
|
string comment = string.Empty;
|
||||||
|
|
||||||
|
// Verificar si hay comentario en la línea
|
||||||
|
int commentPos = remainder.IndexOf("//");
|
||||||
|
if (commentPos > 0)
|
||||||
|
{
|
||||||
|
paramType = remainder.Substring(0, commentPos).Trim();
|
||||||
|
comment = remainder.Substring(commentPos + 2).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limpiar cualquier punto y coma al final del tipo
|
||||||
|
if (paramType.EndsWith(";"))
|
||||||
|
paramType = paramType.Substring(0, paramType.Length - 1).Trim();
|
||||||
|
|
||||||
|
// Añadir parámetro a la lista
|
||||||
|
parameters.Add(new FunctionParameter
|
||||||
|
{
|
||||||
|
Name = paramName,
|
||||||
|
DataType = paramType,
|
||||||
|
Direction = direction,
|
||||||
|
Description = comment
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Crea bloques FC de ejemplo cuando no se pueden parsear los reales
|
||||||
|
/// </summary>
|
||||||
|
private List<S7Function> CreateSampleFunctions()
|
||||||
|
{
|
||||||
|
return 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),
|
||||||
|
Language = "SCL",
|
||||||
|
Parameters = new List<FunctionParameter>
|
||||||
|
{
|
||||||
|
new FunctionParameter { Name = "Target", DataType = "REAL", Direction = "IN", Description = "Valor objetivo" },
|
||||||
|
new FunctionParameter { Name = "Actual", DataType = "REAL", Direction = "IN", Description = "Valor actual" },
|
||||||
|
new FunctionParameter { Name = "Gain", DataType = "REAL", Direction = "IN", Description = "Ganancia" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new S7Function {
|
||||||
|
Name = "Scale_Analog",
|
||||||
|
Number = "FC2",
|
||||||
|
Size = 68,
|
||||||
|
ReturnType = "REAL",
|
||||||
|
Description = "Escalado de valor analógico",
|
||||||
|
Modified = DateTime.Now.AddDays(-12),
|
||||||
|
Language = "AWL",
|
||||||
|
Parameters = new List<FunctionParameter>
|
||||||
|
{
|
||||||
|
new FunctionParameter { Name = "Raw", DataType = "INT", Direction = "IN", Description = "Valor bruto" },
|
||||||
|
new FunctionParameter { Name = "RawLow", DataType = "INT", Direction = "IN", Description = "Valor mínimo bruto" },
|
||||||
|
new FunctionParameter { Name = "RawHigh", DataType = "INT", Direction = "IN", Description = "Valor máximo bruto" },
|
||||||
|
new FunctionParameter { Name = "ScaleLow", DataType = "REAL", Direction = "IN", Description = "Valor mínimo escalado" },
|
||||||
|
new FunctionParameter { Name = "ScaleHigh", DataType = "REAL", Direction = "IN", Description = "Valor máximo escalado" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new S7Function {
|
||||||
|
Name = "CheckAlarms",
|
||||||
|
Number = "FC3",
|
||||||
|
Size = 154,
|
||||||
|
ReturnType = "BOOL",
|
||||||
|
Description = "Verificación de alarmas",
|
||||||
|
Modified = DateTime.Now.AddDays(-5),
|
||||||
|
Language = "KOP",
|
||||||
|
Parameters = new List<FunctionParameter>
|
||||||
|
{
|
||||||
|
new FunctionParameter { Name = "Value", DataType = "REAL", Direction = "IN", Description = "Valor a comprobar" },
|
||||||
|
new FunctionParameter { Name = "LowLimit", DataType = "REAL", Direction = "IN", Description = "Límite inferior" },
|
||||||
|
new FunctionParameter { Name = "HighLimit", DataType = "REAL", Direction = "IN", Description = "Límite superior" },
|
||||||
|
new FunctionParameter { Name = "AlarmStatus", DataType = "WORD", Direction = "OUT", Description = "Estado de alarmas" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new S7Function {
|
||||||
|
Name = "Timer_Control",
|
||||||
|
Number = "FC4",
|
||||||
|
Size = 86,
|
||||||
|
ReturnType = "VOID",
|
||||||
|
Description = "Control de temporizadores",
|
||||||
|
Modified = DateTime.Now.AddDays(-3),
|
||||||
|
Language = "FUP",
|
||||||
|
Parameters = new List<FunctionParameter>
|
||||||
|
{
|
||||||
|
new FunctionParameter { Name = "Start", DataType = "BOOL", Direction = "IN", Description = "Iniciar temporizador" },
|
||||||
|
new FunctionParameter { Name = "Duration", DataType = "TIME", Direction = "IN", Description = "Duración" },
|
||||||
|
new FunctionParameter { Name = "TimerNo", DataType = "INT", Direction = "IN", Description = "Número de temporizador" },
|
||||||
|
new FunctionParameter { Name = "Status", DataType = "BOOL", Direction = "OUT", Description = "Estado (activo/inactivo)" },
|
||||||
|
new FunctionParameter { Name = "ElapsedTime", DataType = "TIME", Direction = "OUT", Description = "Tiempo transcurrido" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new S7Function {
|
||||||
|
Name = "String_Process",
|
||||||
|
Number = "FC5",
|
||||||
|
Size = 210,
|
||||||
|
ReturnType = "INT",
|
||||||
|
Description = "Procesamiento de cadenas",
|
||||||
|
Modified = DateTime.Now.AddDays(-1),
|
||||||
|
Language = "SCL",
|
||||||
|
Parameters = new List<FunctionParameter>
|
||||||
|
{
|
||||||
|
new FunctionParameter { Name = "InStr", DataType = "STRING", Direction = "IN", Description = "Cadena de entrada" },
|
||||||
|
new FunctionParameter { Name = "Operation", DataType = "INT", Direction = "IN", Description = "Operación a realizar" },
|
||||||
|
new FunctionParameter { Name = "OutStr", DataType = "STRING", Direction = "OUT", Description = "Cadena de salida" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -225,15 +225,57 @@ namespace S7Explorer.Parsers
|
||||||
|
|
||||||
private void ParseBlocks(S7Object dbFolder, S7Object fbFolder, S7Object fcFolder, S7Object obFolder, int subblockListId)
|
private void ParseBlocks(S7Object dbFolder, S7Object fbFolder, S7Object fcFolder, S7Object obFolder, int subblockListId)
|
||||||
{
|
{
|
||||||
// Esta es una implementación de muestra
|
try
|
||||||
// En una versión completa, leerías los archivos SUBBLK.DBF reales
|
{
|
||||||
|
// Usar parser específico para las funciones (FC)
|
||||||
|
var fcParser = new FCParser(_projectDirectory);
|
||||||
|
var functions = fcParser.ParseFunctionBlocks(subblockListId);
|
||||||
|
|
||||||
// Crear algunos bloques de ejemplo
|
// Añadir bloques FC al árbol de proyecto
|
||||||
|
foreach (var fc in functions)
|
||||||
|
{
|
||||||
|
fc.Parent = fcFolder;
|
||||||
|
fcFolder.Children.Add(fc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Para los otros tipos de bloques, seguimos usando los ejemplos por ahora
|
||||||
|
// En una implementación completa, crearíamos parsers específicos para cada tipo
|
||||||
AddSampleDataBlocks(dbFolder);
|
AddSampleDataBlocks(dbFolder);
|
||||||
AddSampleFunctionBlocks(fbFolder);
|
AddSampleFunctionBlocks(fbFolder);
|
||||||
AddSampleFunctions(fcFolder);
|
|
||||||
AddSampleOrgBlocks(obFolder);
|
AddSampleOrgBlocks(obFolder);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// En caso de error, añadimos un objeto de error informativo
|
||||||
|
var errorObject = new S7Object
|
||||||
|
{
|
||||||
|
Name = "Error en parseo de bloques",
|
||||||
|
Description = ex.Message,
|
||||||
|
ObjectType = S7ObjectType.Folder
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lo añadimos a cada carpeta para que sea visible
|
||||||
|
dbFolder.Children.Add(errorObject);
|
||||||
|
fbFolder.Children.Add(new S7Object
|
||||||
|
{
|
||||||
|
Name = errorObject.Name,
|
||||||
|
Description = errorObject.Description,
|
||||||
|
ObjectType = errorObject.ObjectType
|
||||||
|
});
|
||||||
|
fcFolder.Children.Add(new S7Object
|
||||||
|
{
|
||||||
|
Name = errorObject.Name,
|
||||||
|
Description = errorObject.Description,
|
||||||
|
ObjectType = errorObject.ObjectType
|
||||||
|
});
|
||||||
|
obFolder.Children.Add(new S7Object
|
||||||
|
{
|
||||||
|
Name = errorObject.Name,
|
||||||
|
Description = errorObject.Description,
|
||||||
|
ObjectType = errorObject.ObjectType
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void AddSampleDataBlocks(S7Object dbFolder)
|
private void AddSampleDataBlocks(S7Object dbFolder)
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,9 +46,37 @@ namespace S7Explorer.ViewModels
|
||||||
{
|
{
|
||||||
// Actualizar el objeto seleccionado para PropertyGrid
|
// Actualizar el objeto seleccionado para PropertyGrid
|
||||||
SelectedObject = _selectedTreeItem;
|
SelectedObject = _selectedTreeItem;
|
||||||
|
|
||||||
|
// Acciones específicas según el tipo de objeto seleccionado
|
||||||
|
if (_selectedTreeItem is S7Function fc)
|
||||||
|
{
|
||||||
|
// Si el objeto seleccionado es una función, actualizar su interfaz
|
||||||
|
UpdateFunctionInterface(fc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza la información de interfaz de una función seleccionada
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateFunctionInterface(S7Function fc)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Aquí podríamos cargar información adicional específica de la función
|
||||||
|
// Por ejemplo, cargar el código de la función, detalles de parámetros, etc.
|
||||||
|
|
||||||
|
// Actualizar estadísticas o análisis de la función
|
||||||
|
// Por ejemplo, mostrar uso de la función en otros bloques
|
||||||
|
|
||||||
|
// Este método se ejecutará cuando el usuario seleccione una FC en el árbol
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Ignorar errores para no interrumpir la experiencia del usuario
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ObservableCollection<S7Object> ProjectStructure
|
public ObservableCollection<S7Object> ProjectStructure
|
||||||
{
|
{
|
||||||
|
@ -138,8 +166,61 @@ namespace S7Explorer.ViewModels
|
||||||
// Recopilar todos los objetos en el proyecto
|
// Recopilar todos los objetos en el proyecto
|
||||||
var allObjects = GetAllObjects(_currentProject);
|
var allObjects = GetAllObjects(_currentProject);
|
||||||
|
|
||||||
|
// Lista para almacenar coincidencias
|
||||||
|
var matchingObjects = new List<S7Object>();
|
||||||
|
|
||||||
|
// Buscar en función del texto de búsqueda
|
||||||
|
string searchText = SearchText.Trim().ToLowerInvariant();
|
||||||
|
|
||||||
// Buscar objetos que coincidan con el texto
|
// Buscar objetos que coincidan con el texto
|
||||||
var matchingObjects = allObjects.Where(o => o.ContainsText(SearchText)).ToList();
|
foreach (var obj in allObjects)
|
||||||
|
{
|
||||||
|
// Comprobar si el texto de búsqueda parece ser una referencia a FC específica
|
||||||
|
if (searchText.StartsWith("fc") && searchText.Length >= 3)
|
||||||
|
{
|
||||||
|
// Si es una función específica (por ejemplo "fc5")
|
||||||
|
if (obj is S7Function func &&
|
||||||
|
func.Number.ToLowerInvariant() == searchText)
|
||||||
|
{
|
||||||
|
matchingObjects.Add(func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Comprobar si queremos encontrar todas las FCs
|
||||||
|
else if (searchText == "fc" || searchText == "función" || searchText == "funcion")
|
||||||
|
{
|
||||||
|
if (obj is S7Function)
|
||||||
|
{
|
||||||
|
matchingObjects.Add(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Búsqueda en parámetros de función
|
||||||
|
else if (obj is S7Function func)
|
||||||
|
{
|
||||||
|
if (func.ContainsText(searchText))
|
||||||
|
{
|
||||||
|
matchingObjects.Add(func);
|
||||||
|
}
|
||||||
|
else if (func.Parameters != null)
|
||||||
|
{
|
||||||
|
// Buscar en los parámetros de la función
|
||||||
|
foreach (var param in func.Parameters)
|
||||||
|
{
|
||||||
|
if (param.Name.ToLowerInvariant().Contains(searchText) ||
|
||||||
|
param.DataType.ToLowerInvariant().Contains(searchText) ||
|
||||||
|
(param.Description?.ToLowerInvariant().Contains(searchText) ?? false))
|
||||||
|
{
|
||||||
|
matchingObjects.Add(func);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Búsqueda general para otros objetos
|
||||||
|
else if (obj.ContainsText(searchText))
|
||||||
|
{
|
||||||
|
matchingObjects.Add(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (matchingObjects.Count == 0)
|
if (matchingObjects.Count == 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,6 +23,8 @@ namespace S7Explorer.Views
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void SearchBox_KeyDown(object sender, KeyEventArgs e)
|
private void SearchBox_KeyDown(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key == Key.Enter && ViewModel != null)
|
if (e.Key == Key.Enter && ViewModel != null)
|
||||||
|
|
Loading…
Reference in New Issue