using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using TeamAAS.Feeder.Attributes;
using TeamAAS.Feeder.Devices;
using TeamAAS.Feeder.Interfaces;
using TeamAAS.Feeder.Models;
using TeamAAS.Feeder.Enums;
using TeamAAS.Feeder.Models;
namespace TeamAAS.Feeder
{
///
/// 供料器管理器。懒汉单例:FeederManager.Instance。
/// 整合:设备 CRUD + 配方管理 + 反射发现设备类型 + 配置持久化。
/// 管理类按架构规则保留在根命名空间 TeamAAS.Feeder。
///
public class FeederManager : IFeederManager
{
#region 单例
private static readonly Lazy _instance =
new Lazy(() => new FeederManager(), isThreadSafe: true);
///
/// 懒汉单例入口。首次访问时初始化,线程安全。
///
public static FeederManager Instance => _instance.Value;
#endregion
private readonly object _sync = new object();
private readonly Dictionary _feeders = new Dictionary();
private readonly Dictionary _devices = new Dictionary();
private List _recipes = new List();
private List _scannedTypes;
private readonly object _scanLock = new object();
public static string DefaultConfigPath => TeamAAS.PathHelper.FeedersConfigFile;
private static string DefaultRecipeFilePath => TeamAAS.PathHelper.FeederRecipesFile;
public event EventHandler FeederListChanged;
public FeederManager()
{
_recipes = LoadAllRecipesInternal();
}
#region 设备配置 CRUD
public void LoadConfig(string path = null)
{
path = path ?? DefaultConfigPath;
if (!File.Exists(path)) return;
try
{
var list = TeamAAS.JsonFileStore.Load>(path);
if (list == null) return;
lock (_sync)
{
_feeders.Clear();
foreach (var info in list)
{
if (!_feeders.ContainsKey(info.Id))
_feeders.Add(info.Id, info);
}
}
FeederListChanged?.Invoke(this, EventArgs.Empty);
}
catch (Exception ex)
{
Trace.WriteLine($"FeederManager.LoadConfig 失败: {ex.Message}");
}
}
public bool SaveConfig(string path = null)
{
path = path ?? DefaultConfigPath;
try
{
List list;
lock (_sync) { list = _feeders.Values.ToList(); }
return TeamAAS.JsonFileStore.Save(path, list);
}
catch (Exception ex)
{
Trace.WriteLine($"FeederManager.SaveConfig 失败: {ex.Message}");
return false;
}
}
public FeederInfo Add(FeederInfo info)
{
if (info == null) throw new ArgumentNullException(nameof(info));
lock (_sync)
{
if (_feeders.ContainsKey(info.Id))
_feeders[info.Id] = info;
else
_feeders.Add(info.Id, info);
}
FeederListChanged?.Invoke(this, EventArgs.Empty);
return info;
}
public bool Remove(Guid id)
{
bool removed;
lock (_sync)
{
removed = _feeders.Remove(id);
if (_devices.TryGetValue(id, out var dev))
{
dev.Dispose();
_devices.Remove(id);
}
}
if (removed) FeederListChanged?.Invoke(this, EventArgs.Empty);
return removed;
}
public FeederInfo Get(Guid id)
{
lock (_sync) { return _feeders.TryGetValue(id, out var f) ? f : null; }
}
public IReadOnlyList GetAll()
{
lock (_sync) { return _feeders.Values.ToList().AsReadOnly(); }
}
#endregion
#region 设备实例管理
public IFeeder GetDevice(Guid id)
{
lock (_sync) { return _devices.TryGetValue(id, out var d) ? d : null; }
}
public IFeeder GetOrCreateDevice(FeederInfo info)
{
if (info == null) return null;
lock (_sync)
{
if (_devices.TryGetValue(info.Id, out var existing))
{
try { existing.Dispose(); } catch { }
_devices.Remove(info.Id);
}
var device = CreateDeviceInstance(info);
_devices[info.Id] = device;
return device;
}
}
#endregion
#region 配方管理
public IReadOnlyList GetRecipes()
{
lock (_sync) { return _recipes.ToList().AsReadOnly(); }
}
public FeederRecipe GetRecipe(string name)
{
lock (_sync) { return _recipes.FirstOrDefault(r => r.RecipeName == name); }
}
public bool SaveRecipe(FeederRecipe recipe)
{
if (recipe == null || string.IsNullOrWhiteSpace(recipe.RecipeName)) return false;
var ok = SaveRecipeInternal(recipe);
if (ok)
{
lock (_sync)
{
var existing = _recipes.FirstOrDefault(r => r.RecipeName == recipe.RecipeName);
if (existing != null)
_recipes[_recipes.IndexOf(existing)] = recipe;
else
_recipes.Add(recipe);
}
}
return ok;
}
public bool DeleteRecipe(string name)
{
var ok = DeleteRecipeInternal(name);
if (ok)
{
lock (_sync)
{
var r = _recipes.FirstOrDefault(x => x.RecipeName == name);
if (r != null) _recipes.Remove(r);
}
}
return ok;
}
public FeederRecipe LoadRecipe(string path)
{
var recipe = LoadRecipeInternal(path);
if (recipe != null)
{
lock (_sync)
{
var existing = _recipes.FirstOrDefault(r => r.RecipeName == recipe.RecipeName);
if (existing != null)
_recipes[_recipes.IndexOf(existing)] = recipe;
else
_recipes.Add(recipe);
}
}
return recipe;
}
private static List LoadAllRecipesInternal()
{
try
{
if (!File.Exists(DefaultRecipeFilePath)) return new List();
var json = File.ReadAllText(DefaultRecipeFilePath);
var list = JsonConvert.DeserializeObject>(json);
return list ?? new List();
}
catch (Exception ex)
{
Trace.WriteLine($"FeederManager.LoadAllRecipes 失败: {ex.Message}");
return new List();
}
}
private static bool SaveAllRecipesInternal(List recipes)
{
try
{
var json = JsonConvert.SerializeObject(recipes, Formatting.Indented);
File.WriteAllText(DefaultRecipeFilePath, json);
return true;
}
catch (Exception ex)
{
Trace.WriteLine($"FeederManager.SaveAllRecipes 失败: {ex.Message}");
return false;
}
}
private static bool SaveRecipeInternal(FeederRecipe recipe)
{
try
{
recipe.UpdatedAt = DateTime.Now;
var list = LoadAllRecipesInternal();
var idx = list.FindIndex(r => r.RecipeName == recipe.RecipeName);
if (idx >= 0) list[idx] = recipe;
else list.Add(recipe);
return SaveAllRecipesInternal(list);
}
catch (Exception ex)
{
Trace.WriteLine($"FeederManager.SaveRecipe 失败: {ex.Message}");
return false;
}
}
private static bool DeleteRecipeInternal(string recipeName)
{
try
{
var list = LoadAllRecipesInternal();
var removed = list.RemoveAll(r => r.RecipeName == recipeName);
if (removed > 0) return SaveAllRecipesInternal(list);
return false;
}
catch (Exception ex)
{
Trace.WriteLine($"FeederManager.DeleteRecipe 失败: {ex.Message}");
return false;
}
}
private static FeederRecipe LoadRecipeInternal(string path)
{
try
{
if (!File.Exists(path)) return null;
var json = File.ReadAllText(path);
return JsonConvert.DeserializeObject(json);
}
catch (Exception ex)
{
Trace.WriteLine($"FeederManager.LoadRecipe 失败: {ex.Message}");
return null;
}
}
#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();
// 通用模块扫描:收集全部 IFeeder 实现(含 Runtime\Plugins 里的插件 DLL)
foreach (var type in TeamAAS.Modularity.AssemblyScanner.FindImplementations())
{
var attrs = type.GetCustomAttributes();
foreach (var attr in attrs)
{
result.Add(new FeederTypeInfo
{
TypeKey = type.FullName,
DisplayName = attr.DisplayName,
Brand = attr.Brand,
Description = attr.Description,
Type = type
});
}
}
// fallback:如果反射没扫到品牌特性,注册内置 TeamFeeder(兼容旧代码)
if (!result.Any(t => t.Brand == FeederBrand.Team))
{
result.Add(new FeederTypeInfo
{
TypeKey = typeof(TeamFeeder).FullName,
DisplayName = "Team 供料器",
Brand = FeederBrand.Team,
Description = "内置 Team 品牌供料器实现",
Type = typeof(TeamFeeder)
});
}
return result
.GroupBy(t => t.TypeKey + "|" + t.Brand)
.Select(g => g.First())
.OrderBy(t => t.DisplayName)
.ToList();
}
private IFeeder CreateDeviceInstance(FeederInfo info)
{
if (info == null) throw new ArgumentNullException(nameof(info));
var types = GetAvailableTypes();
var match = types.FirstOrDefault(t => t.Brand == info.FeederBrand);
if (match == null)
throw new NotSupportedException($"暂不支持的供料器品牌: {info.FeederBrand}");
return (IFeeder)Activator.CreateInstance(
match.Type,
info.Id, info.FeederNo, info.FeederName, info.IP, info.Port);
}
#endregion
}
public class FeederTypeInfo
{
public string TypeKey { get; set; }
public string DisplayName { get; set; }
public FeederBrand Brand { get; set; }
public string Description { get; set; }
public Type Type { get; set; }
public override string ToString() => DisplayName;
}
}