using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; using DotNetSiemensPLCToolBoxLibrary.DataTypes; using DotNetSiemensPLCToolBoxLibrary.DataTypes.Blocks; using DotNetSiemensPLCToolBoxLibrary.DataTypes.Blocks.Step7V5; using DotNetSiemensPLCToolBoxLibrary.DataTypes.Projectfolders; using DotNetSiemensPLCToolBoxLibrary.DataTypes.Projectfolders.Step7V5; using DotNetSiemensPLCToolBoxLibrary.Projectfiles; using S7Explorer.Models; using ProjectFolder = DotNetSiemensPLCToolBoxLibrary.DataTypes.Projectfolders.ProjectFolder; namespace S7Explorer.Services { public class ProjectService { private readonly LogService _logService; public ProjectService() { _logService = LogService.Instance; } public async Task LoadProjectAsync(string projectPath) { return await Task.Run(() => LoadProject(projectPath)); } private ProjectStructure? LoadProject(string projectPath) { try { _logService.LogInfo($"Loading project from: {projectPath}"); if (!File.Exists(projectPath)) { _logService.LogError($"Project file not found: {projectPath}"); return null; } // Load the Step7 project var step7Project = Projects.GetStep7ProjectsFromDirectory(Path.GetDirectoryName(projectPath)).FirstOrDefault(); if (step7Project == null) { _logService.LogError("Could not load project. Make sure it's a valid S7 project file."); return null; } _logService.LogInfo($"Project loaded: {step7Project.ProjectName}"); // Create the project structure ProjectStructure projectStructure = new ProjectStructure { Name = step7Project.ProjectName, ProjectData = step7Project, ProjectPath = projectPath, ProjectVersion = "Unknown", // Not directly available in the API CreationDate = File.GetCreationTime(projectPath), LastModifiedDate = File.GetLastWriteTime(projectPath) }; try { // Process the project structure ProcessProjectStructure(step7Project, projectStructure); } catch (Exception ex) { _logService.LogError($"Error processing project structure: {ex.Message}"); } return projectStructure; } catch (Exception ex) { _logService.LogError(ex); return null; } } private void ProcessProjectStructure(Project project, ProjectStructure projectStructure) { // We need to carefully navigate the project structure if (project.ProjectStructure == null) { _logService.LogWarning("Project structure is null"); return; } // Use reflection to find and process PLC folders ProcessProjectFolder(project.ProjectStructure, projectStructure); } private void ProcessProjectFolder(ProjectFolder folder, ProjectStructure projectStructure) { try { // Check if this is a CPU/PLC folder if (IsProgrammFolder(folder)) { string folderName = GetFolderName(folder); _logService.LogInfo($"Processing PLC folder: {folderName}"); // Process this folder LoadHardwareConfiguration(folder, projectStructure.HardwareFolder); // Try to find blocks folder var blocksFolder = GetBlocksFolder(folder); if (blocksFolder != null) { LoadBlocks(blocksFolder, projectStructure.BlocksFolder); } // Try to find symbol table var symbolTable = GetSymbolTable(folder); if (symbolTable != null) { LoadSymbolTable(symbolTable, projectStructure.SymbolsFolder); } } // Try to process sub-folders ProcessSubFolders(folder, projectStructure); } catch (Exception ex) { _logService.LogError($"Error processing folder: {ex.Message}"); } } private void ProcessSubFolders(ProjectFolder folder, ProjectStructure projectStructure) { try { // Use reflection to find sub-folders var properties = folder.GetType().GetProperties(); foreach (var property in properties) { if (typeof(ProjectFolder).IsAssignableFrom(property.PropertyType)) { // This property is a ProjectFolder var subFolder = property.GetValue(folder) as ProjectFolder; if (subFolder != null) { ProcessProjectFolder(subFolder, projectStructure); } } else if (property.PropertyType.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(property.PropertyType.GetGenericTypeDefinition())) { // This might be a collection of folders var collection = property.GetValue(folder) as System.Collections.IEnumerable; if (collection != null) { foreach (var item in collection) { if (item is ProjectFolder subFolder) { ProcessProjectFolder(subFolder, projectStructure); } } } } } } catch (Exception ex) { _logService.LogError($"Error processing sub-folders: {ex.Message}"); } } private void LoadBlocks(IBlocksFolder blocksFolder, S7Explorer.Models.ProjectItem parentFolder) { try { // IBlocksFolder doesn't have a Name property directly, so let's use a generic name string folderName = "Blocks"; if (blocksFolder is ProjectFolder pf && pf.GetType().GetProperty("Name") != null) folderName = GetFolderName(pf); _logService.LogInfo($"Loading blocks from: {folderName}"); // Create a folder for this block type S7Explorer.Models.ProjectItem blocksFolderItem = new S7Explorer.Models.ProjectItem { Name = folderName, Parent = parentFolder }; parentFolder.Children.Add(blocksFolderItem); // Get all blocks var blockInfos = blocksFolder.BlockInfos; _logService.LogInfo($"Found {blockInfos.Count} blocks"); // Add each block to the tree foreach (var blockInfo in blockInfos) { // Skip system blocks if needed if (blockInfo.BlockType == PLCBlockType.SFC || blockInfo.BlockType == PLCBlockType.SFB) continue; try { // Load full block data S7Block blockData = blocksFolder.GetBlock(blockInfo) as S7Block; if (blockData != null) { // Need to extract block number from blockInfo int blockNumber = 0; // Try to parse block number from ToString() which typically returns something like "DB100" string blockStr = blockInfo.ToString(); string numPart = string.Empty; for (int i = 0; i < blockStr.Length; i++) { if (char.IsDigit(blockStr[i])) numPart += blockStr[i]; } if (!string.IsNullOrEmpty(numPart)) int.TryParse(numPart, out blockNumber); S7Explorer.Models.BlockItem blockItem = new S7Explorer.Models.BlockItem { Name = blockInfo.ToString(), BlockInfo = blockInfo, BlockData = blockData, BlockType = blockInfo.BlockType.ToString(), BlockNumber = blockNumber, BlockComment = blockData.Title ?? string.Empty, Parent = blocksFolderItem }; // Get block source code try { if (blocksFolder is BlocksOfflineFolder bof) { var sourceCode = bof.GetSourceBlock(blockInfo, true); blockItem.BlockContent = sourceCode ?? string.Empty; } else { blockItem.BlockContent = "// Unable to retrieve block source code"; } } catch (Exception) { blockItem.BlockContent = "// Unable to retrieve block source code"; } blocksFolderItem.Children.Add(blockItem); } } catch (Exception ex) { _logService.LogWarning($"Failed to load block {blockInfo}: {ex.Message}"); } } } catch (Exception ex) { _logService.LogError($"Error loading blocks: {ex.Message}"); } } private void LoadSymbolTable(ISymbolTable symbolTable, S7Explorer.Models.ProjectItem parentFolder) { try { string tableName = "Symbols"; if (symbolTable is ProjectFolder pf) tableName = GetFolderName(pf); _logService.LogInfo($"Loading symbols from table: {tableName}"); // Create a folder for this symbol table S7Explorer.Models.ProjectItem symbolTableItem = new S7Explorer.Models.ProjectItem { Name = tableName, Parent = parentFolder }; parentFolder.Children.Add(symbolTableItem); // Try to get symbols using reflection to avoid direct property access int count = LoadSymbolsUsingReflection(symbolTable, symbolTableItem); if (count > 0) { _logService.LogInfo($"Loaded {count} symbols"); } else { _logService.LogWarning("No symbols found or symbol table is empty"); } } catch (Exception ex) { _logService.LogError($"Error loading symbols: {ex.Message}"); } } private int LoadSymbolsUsingReflection(ISymbolTable symbolTable, S7Explorer.Models.ProjectItem parentFolder) { int count = 0; try { // Find property that might contain symbols var properties = symbolTable.GetType().GetProperties(); // Look for collection properties that might be symbol collections foreach (var prop in properties) { // Skip non-collection properties if (!typeof(IEnumerable).IsAssignableFrom(prop.PropertyType) || prop.PropertyType == typeof(string)) continue; // Try to get the collection var collection = prop.GetValue(symbolTable) as IEnumerable; if (collection == null) continue; // Check if this collection contains symbols foreach (var item in collection) { if (item == null) continue; // Check if this looks like a symbol var itemType = item.GetType(); var symbolProp = itemType.GetProperty("Symbol"); var operandProp = itemType.GetProperty("Operand") ?? itemType.GetProperty("Address"); // If it has Symbol and Operand properties, treat it as a symbol if (symbolProp != null && operandProp != null) { var dataTypeProp = itemType.GetProperty("DataType"); var commentProp = itemType.GetProperty("Comment"); S7Explorer.Models.SymbolItem symbolItem = new S7Explorer.Models.SymbolItem { Name = symbolProp.GetValue(item)?.ToString() ?? "Unknown", SymbolAddress = operandProp.GetValue(item)?.ToString() ?? "", SymbolDataType = dataTypeProp?.GetValue(item)?.ToString() ?? "", SymbolComment = commentProp?.GetValue(item)?.ToString() ?? "", Parent = parentFolder }; parentFolder.Children.Add(symbolItem); count++; } } // If we found symbols, stop looking through properties if (count > 0) break; } } catch (Exception ex) { _logService.LogError($"Error loading symbols using reflection: {ex.Message}"); } return count; } private void LoadHardwareConfiguration(object cpuFolder, S7Explorer.Models.ProjectItem parentFolder) { try { string folderName = GetFolderName(cpuFolder); _logService.LogInfo($"Loading hardware configuration for CPU: {folderName}"); // Create a folder for this CPU S7Explorer.Models.ProjectItem cpuItem = new S7Explorer.Models.ProjectItem { Name = folderName, Parent = parentFolder }; parentFolder.Children.Add(cpuItem); // Add CPU information S7Explorer.Models.HardwareItem cpuHardwareItem = new S7Explorer.Models.HardwareItem { Name = $"CPU {folderName}", ModuleType = "CPU", OrderNumber = "Unknown", // Would need to extract from hardware Position = "Unknown", Parent = cpuItem }; cpuItem.Children.Add(cpuHardwareItem); // We would need to analyze the hardware configuration in more detail // to extract modules, but this requires more insight into the library's structure _logService.LogInfo("Hardware configuration loaded"); } catch (Exception ex) { _logService.LogError($"Error loading hardware configuration: {ex.Message}"); } } // Helper methods to safely access project structure private bool IsProgrammFolder(object folder) { return folder is IProgrammFolder || folder.GetType().GetInterfaces().Any(i => i.Name == "IProgrammFolder"); } private IBlocksFolder GetBlocksFolder(object folder) { try { // Try using reflection to access the BlocksOfflineFolder property var property = folder.GetType().GetProperty("BlocksOfflineFolder"); if (property != null) { return property.GetValue(folder) as IBlocksFolder; } // If that fails, look for a property of type IBlocksFolder foreach (var prop in folder.GetType().GetProperties()) { if (typeof(IBlocksFolder).IsAssignableFrom(prop.PropertyType)) { return prop.GetValue(folder) as IBlocksFolder; } } } catch (Exception ex) { _logService.LogError($"Error accessing blocks folder: {ex.Message}"); } return null; } private ISymbolTable GetSymbolTable(object folder) { try { // Try using reflection to access the SymbolTable property var property = folder.GetType().GetProperty("SymbolTable"); if (property != null) { return property.GetValue(folder) as ISymbolTable; } // If that fails, look for a property of type ISymbolTable foreach (var prop in folder.GetType().GetProperties()) { if (typeof(ISymbolTable).IsAssignableFrom(prop.PropertyType)) { return prop.GetValue(folder) as ISymbolTable; } } } catch (Exception ex) { _logService.LogError($"Error accessing symbol table: {ex.Message}"); } return null; } private string GetFolderName(object folder) { try { // Try to access Name property var property = folder.GetType().GetProperty("Name"); if (property != null) { return property.GetValue(folder)?.ToString() ?? "Unknown"; } } catch { // Ignore errors } return "Unknown"; } } }