S7Explorer/Services/ProjectService.cs

496 lines
19 KiB
C#

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<ProjectStructure?> 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";
}
}
}