using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace MainShell.DeviceMaintance.Model { public static class CylinderDefinitionLoader { private const string CylinderConfigFileName = "CylinderDefinitions.csv"; public static IReadOnlyList LoadDefinitions() { var definitions = new List(); var path = ResolveConfigPath(); if (string.IsNullOrWhiteSpace(path) || !File.Exists(path)) { return definitions; } var lines = File.ReadAllLines(path, Encoding.UTF8); foreach (var raw in lines) { if (string.IsNullOrWhiteSpace(raw)) { continue; } var line = raw.Trim(); if (line.StartsWith("#") || line.StartsWith("Name", StringComparison.OrdinalIgnoreCase)) { continue; } var cols = line.Split(','); if (cols.Length < 7) { continue; } var extendOutputPoints = ParsePointReferences(cols[3]); if (extendOutputPoints.Count == 0) { continue; } var retractOutputPoints = ParsePointReferences(cols[4]); var hasConditionColumns = cols.Length >= 10; var extendConditionIndex = hasConditionColumns ? 7 : -1; var retractConditionIndex = hasConditionColumns ? 8 : -1; var descriptionIndex = hasConditionColumns ? 9 : 7; definitions.Add(new CylinderDefinition { Name = cols[0].Trim(), Module = cols[1].Trim(), ControlType = ParseControlType(cols[2]), ExtendOutputPoints = extendOutputPoints, RetractOutputPoints = retractOutputPoints, ExtendedFeedbackPoints = ParsePointReferences(cols[5]), RetractedFeedbackPoints = ParsePointReferences(cols[6]), ExtendConditions = extendConditionIndex >= 0 ? ParseConditions(cols[extendConditionIndex]) : new List(), RetractConditions = retractConditionIndex >= 0 ? ParseConditions(cols[retractConditionIndex]) : new List(), Description = cols.Length > descriptionIndex ? string.Join(",", cols.Skip(descriptionIndex)).Trim() : string.Empty }); } return definitions; } private static CylinderControlType ParseControlType(string raw) { if (string.Equals(raw, "DualOutput", StringComparison.OrdinalIgnoreCase) || string.Equals(raw, "Dual", StringComparison.OrdinalIgnoreCase)) { return CylinderControlType.DualOutput; } if (string.Equals(raw, "MultiOutput", StringComparison.OrdinalIgnoreCase) || string.Equals(raw, "Multi", StringComparison.OrdinalIgnoreCase)) { return CylinderControlType.MultiOutput; } return CylinderControlType.SingleOutput; } private static List ParsePointReferences(string raw) { return (raw ?? string.Empty) .Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()) .Where(x => !string.IsNullOrWhiteSpace(x)) .ToList(); } private static List ParseConditions(string raw) { return (raw ?? string.Empty) .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) .Select(ParseCondition) .Where(x => x != null) .ToList(); } private static CylinderActionConditionDefinition ParseCondition(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return null; } var parts = raw.Split(new[] { ':' }, 3); if (parts.Length < 2) { return null; } var pointReference = parts[1].Trim(); if (string.IsNullOrWhiteSpace(pointReference)) { return null; } CylinderConditionType conditionType; switch ((parts[0] ?? string.Empty).Trim().ToUpperInvariant()) { case "POINTON": case "ON": conditionType = CylinderConditionType.PointOn; break; case "POINTOFF": case "OFF": conditionType = CylinderConditionType.PointOff; break; default: return null; } return new CylinderActionConditionDefinition { ConditionType = conditionType, PointReference = pointReference, Message = parts.Length > 2 ? parts[2].Trim() : string.Empty }; } private static string ResolveConfigPath() { var baseDir = AppDomain.CurrentDomain.BaseDirectory; var localPath = Path.Combine(baseDir, "Configuration", CylinderConfigFileName); var sourcePath = ResolveRootConfigPath(baseDir); if (TrySyncConfigToLocal(sourcePath, localPath)) { return localPath; } return File.Exists(localPath) ? localPath : sourcePath; } private static string ResolveRootConfigPath(string baseDir) { var current = new DirectoryInfo(baseDir); while (current != null) { var rootConfigDir = Path.Combine(current.FullName, "Configuration"); if (Directory.Exists(rootConfigDir) && Directory.Exists(Path.Combine(current.FullName, "MainShell"))) { var path = Path.Combine(rootConfigDir, CylinderConfigFileName); if (File.Exists(path)) { return path; } } current = current.Parent; } return null; } private static bool TrySyncConfigToLocal(string sourcePath, string localPath) { if (string.IsNullOrWhiteSpace(sourcePath) || !File.Exists(sourcePath)) { return false; } if (!string.Equals(Path.GetFullPath(sourcePath), Path.GetFullPath(localPath), StringComparison.OrdinalIgnoreCase)) { var localDir = Path.GetDirectoryName(localPath); if (!string.IsNullOrWhiteSpace(localDir)) { Directory.CreateDirectory(localDir); } if (!File.Exists(localPath) || File.GetLastWriteTimeUtc(localPath) < File.GetLastWriteTimeUtc(sourcePath)) { File.Copy(sourcePath, localPath, true); } } return true; } } }