using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using TeamAAS.Robot.Core.Robots; using TeamAAS.Robot.Interfaces; using TeamAAS.Robot.Models; using System.Reflection; using TeamAAS.Robot.Attributes; using TeamAAS.Robot.Enums; namespace TeamAAS.Robot { /// /// 机器人生命周期管理器。懒汉单例:RobotManager.Instance。 /// 整合:设备创建/连接/运动/配置持久化 + 反射发现品牌类型 + IDisposable。 /// 管理类按架构规则保留在根命名空间 TeamAAS.Robot。 /// public class RobotManager : IRobotManager, IDisposable { #region 单例 private static readonly Lazy _instance = new Lazy(() => new RobotManager(), isThreadSafe: true); /// /// 懒汉单例入口。首次访问时初始化,线程安全。 /// public static RobotManager Instance => _instance.Value; #endregion private readonly Dictionary _robotCollection; private readonly object _sync = new object(); private List _scannedTypes; private readonly object _scanLock = new object(); public RobotManager() { _robotCollection = new Dictionary(); } #region 机器人 CRUD public bool CreateRobot(Guid id, RobotInfo robotInfo) { if (robotInfo == null) throw new ArgumentNullException(nameof(robotInfo)); var robot = CreateRobotInstance(robotInfo); robot.Id = id; IRobot replaced = null; lock (_sync) { _robotCollection.TryGetValue(id, out replaced); _robotCollection[id] = robot; } if (replaced != null && !ReferenceEquals(replaced, robot)) { try { replaced.Dispose(); } catch { } } return true; } public Task CreateRobotAsync(Guid id, RobotInfo robotInfo) { return Task.Run(() => CreateRobot(id, robotInfo)); } public IRobot GetRobot(Guid id) { lock (_sync) { return _robotCollection.TryGetValue(id, out var robot) ? robot : null; } } public Task GetRobotAsync(Guid id) { return Task.Run(() => GetRobot(id)); } public void UnRegisterRobot(Guid id) { IRobot robot = null; lock (_sync) { if (_robotCollection.TryGetValue(id, out robot)) _robotCollection.Remove(id); } if (robot != null) { try { robot.Dispose(); } catch { } } } public Task UnRegisterRobotAsync(Guid id) { return Task.Run(() => UnRegisterRobot(id)); } public IReadOnlyCollection GetAllRobots() { lock (_sync) { return _robotCollection.Values.ToList().AsReadOnly(); } } public Task> GetAllRobotsAsync() { return Task.Run(() => GetAllRobots()); } public bool TryGetRobot(Guid id, out IRobot robot) { lock (_sync) { return _robotCollection.TryGetValue(id, out robot); } } public Task<(bool found, IRobot robot)> TryGetRobotAsync(Guid id) { return Task.Run(() => { IRobot robot; bool found; lock (_sync) { found = _robotCollection.TryGetValue(id, out robot); } return (found, robot); }); } public System.Collections.Generic.IReadOnlyList Devices { get { lock (_sync) { return _robotCollection.Values.ToList().AsReadOnly(); } } } public bool ContainsRobot(Guid id) { lock (_sync) { return _robotCollection.ContainsKey(id); } } public Task ContainsRobotAsync(Guid id) { return Task.Run(() => ContainsRobot(id)); } public bool RemoveRobot(Guid id) { IRobot robot = null; lock (_sync) { if (_robotCollection.TryGetValue(id, out robot)) _robotCollection.Remove(id); } if (robot != null) { try { robot.Dispose(); } catch { } return true; } return false; } public Task RemoveRobotAsync(Guid id) { return Task.Run(() => RemoveRobot(id)); } public IRobot GetRobotByNumber(int robotNo) { return TryFindRobotByNumber(robotNo, out _, out var robot) ? robot : null; } public Task GetRobotByNumberAsync(int robotNo) { return Task.Run(() => GetRobotByNumber(robotNo)); } public Task UnRegisterRobotByNumberAsync(int robotNo) { return Task.Run(() => UnRegisterRobotByNumber(robotNo)); } public void UnRegisterRobotByNumber(int robotNo) { if (TryFindRobotByNumber(robotNo, out var id, out _)) UnRegisterRobot(id); } public void RemoveAllRobots() { List robotIds; lock (_sync) { robotIds = _robotCollection.Keys.ToList(); } foreach (var id in robotIds) { RemoveRobot(id); } } public (bool IsSucceed, string Message) InitializeAllRobots(RobotInfo[] robots) { RemoveAllRobots(); foreach (var robotInfo in robots) { var robot = CreateRobotInstance(robotInfo); lock (_sync) { _robotCollection[robot.Id] = robot; } } bool allConnected = true; StringBuilder errorMessages = new StringBuilder(); var allRobots = GetAllRobots(); foreach (var robot in allRobots) { try { robot.Connect(); } catch { } if (!robot.IsConnected) { allConnected = false; errorMessages.AppendLine($"机器人[{robot.Name}]连接失败。"); } } return allConnected ? (true, "所有机器人初始化并连接成功。") : (false, errorMessages.ToString()); } public Task<(bool IsSucceed, string Message)> InitializeAllRobotsAsync(RobotInfo[] robots) { return Task.Run(() => InitializeAllRobots(robots)); } public void UpdateRobotNumber(Guid id, int newNumber) { lock (_sync) { if (_robotCollection.TryGetValue(id, out var robot)) robot.RobotNo = newNumber; } } private bool TryFindRobotByNumber(int robotNo, out Guid id, out IRobot robot) { lock (_sync) { foreach (var kvp in _robotCollection) { if (kvp.Value == null) continue; if (kvp.Value.RobotNo == robotNo) { id = kvp.Key; robot = kvp.Value; return true; } } } id = Guid.Empty; robot = null; return false; } #endregion #region 配置持久化 public static string DefaultConfigPath { get { return Path.Combine(TeamAAS.PathHelper.ConfigDirectory, "Robots.json"); } } public bool SaveConfig(string path = null) { path = path ?? DefaultConfigPath; var infos = new List(); lock (_sync) { foreach (var robot in _robotCollection.Values) { infos.Add(new RobotInfo { Id = robot.Id, RobotNo = robot.RobotNo, RobotName = robot.Name, RobotBrand = robot.Brand, IP = robot.RobotIp, Port = robot.RobotPort, ConnectType = robot.ConnectType, Terminator = robot.Terminator, DataEncoding = robot.DataEncoding, }); } } return TeamAAS.JsonFileStore.Save(path, infos); } public IReadOnlyList GetAllRobotInfos() { var infos = new List(); lock (_sync) { foreach (var robot in _robotCollection.Values) { infos.Add(new RobotInfo { Id = robot.Id, RobotNo = robot.RobotNo, RobotName = robot.Name, RobotBrand = robot.Brand, IP = robot.RobotIp, Port = robot.RobotPort, ConnectType = robot.ConnectType, Terminator = robot.Terminator, DataEncoding = robot.DataEncoding, }); } } return infos.AsReadOnly(); } public void LoadConfig(string path = null) { path = path ?? DefaultConfigPath; if (!File.Exists(path)) return; var infos = TeamAAS.JsonFileStore.Load>(path); if (infos == null || infos.Count == 0) return; InitializeAllRobots(infos.ToArray()); } #endregion #region 反射扫描 + 工厂 public IReadOnlyList GetAvailableTypes() { if (_scannedTypes != null) return _scannedTypes.AsReadOnly(); lock (_scanLock) { if (_scannedTypes != null) return _scannedTypes.AsReadOnly(); _scannedTypes = ScanTypes(); return _scannedTypes.AsReadOnly(); } } private List ScanTypes() { var result = new List(); // 通用模块扫描:收集全部 IRobot 实现(含 Runtime\Plugins 里的插件 DLL) foreach (var type in TeamAAS.Modularity.AssemblyScanner.FindImplementations()) { var attrs = type.GetCustomAttributes(); foreach (var attr in attrs) { result.Add(new RobotTypeInfo { TypeKey = type.FullName, DisplayName = attr.DisplayName, Brand = attr.Brand, Description = attr.Description, Type = type }); } } return result .GroupBy(t => t.TypeKey + "|" + t.Brand) .Select(g => g.First()) .OrderBy(t => t.DisplayName) .ToList(); } private IRobot CreateRobotInstance(RobotInfo robotInfo) { if (robotInfo == null) throw new ArgumentNullException(nameof(robotInfo)); var types = GetAvailableTypes(); var match = types.FirstOrDefault(t => t.Brand == robotInfo.RobotBrand); if (match == null) { // fallback 到 EPSON 实现(兼容未注册的品牌占位) return new EpsonRobot(robotInfo); } return (IRobot)Activator.CreateInstance(match.Type, robotInfo); } #endregion #region IDisposable public void Dispose() { List robots; lock (_sync) { robots = _robotCollection.Values.ToList(); _robotCollection.Clear(); } foreach (var r in robots) { try { r.Dispose(); } catch { } } GC.SuppressFinalize(this); } #endregion } }