496 lines
19 KiB
C#
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";
|
|
}
|
|
}
|
|
} |