using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; using Haoliang.Core.Services; using Haoliang.Models.Device; using Haoliang.Models.Production; using Haoliang.Models.System; namespace Haoliang.Core.Services { public interface ICacheService { /// /// Get cached value or execute factory if not exists /// Task GetOrSetAsync(string key, Func> factory, MemoryCacheEntryOptions options = null); /// /// Get cached value synchronously /// T Get(string key); /// /// Set cache value /// void Set(string key, T value, MemoryCacheEntryOptions options = null); /// /// Remove cached value /// bool Remove(string key); /// /// Check if key exists in cache /// bool Exists(string key); /// /// Clear all cache /// void Clear(); /// /// Get cache statistics /// CacheStatistics GetStatistics(); /// /// Get cache keys matching pattern /// IEnumerable GetKeys(string pattern); /// /// Refresh cached value /// bool Refresh(string key); } public class CacheStatistics { public long TotalItems { get; set; } public long Hits { get; set; } public long Misses { get; set; } public double HitRate => Hits + Misses > 0 ? (double)Hits / (Hits + Misses) : 0; public long MemoryUsageBytes { get; set; } public DateTime LastCleared { get; set; } public Dictionary ItemsByType { get; set; } } public class MemoryCacheService : ICacheService { private readonly IMemoryCache _memoryCache; private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); private long _hits = 0; private long _misses = 0; private long _memoryUsage = 0; public MemoryCacheService(IMemoryCache memoryCache) { _memoryCache = memoryCache; } public Task GetOrSetAsync(string key, Func> factory, MemoryCacheEntryOptions options = null) { return Task.Run(async () => { // Try to get from cache first if (_memoryCache.TryGetValue(key, out T value)) { Interlocked.Increment(ref _hits); return value; } Interlocked.Increment(ref _misses); // Create new value value = await factory(); if (value != null) { // Set cache options if not provided options = options ?? new MemoryCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(30), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2), Size = 1024 // 1KB }; // Set cache with lock _lock.EnterWriteLock(); try { _memoryCache.Set(key, value, options); UpdateMemoryUsage(options.Size.GetValueOrDefault(1024)); } finally { _lock.ExitWriteLock(); } } return value; }); } public T Get(string key) { if (_memoryCache.TryGetValue(key, out T value)) { Interlocked.Increment(ref _hits); return value; } Interlocked.Increment(ref _misses); return default(T); } public void Set(string key, T value, MemoryCacheEntryOptions options = null) { if (value == null) return; options = options ?? new MemoryCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(30), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2), Size = 1024 // 1KB }; _lock.EnterWriteLock(); try { _memoryCache.Set(key, value, options); UpdateMemoryUsage(options.Size.GetValueOrDefault(1024)); } finally { _lock.ExitWriteLock(); } } public bool Remove(string key) { _lock.EnterWriteLock(); try { if (_memoryCache.TryGetValue(key, out object value)) { _memoryCache.Remove(key); UpdateMemoryUsage(-(GetEstimatedSize(value))); return true; } return false; } finally { _lock.ExitWriteLock(); } } public bool Exists(string key) { return _memoryCache.TryGetValue(key, out _); } public void Clear() { _lock.EnterWriteLock(); try { // This is a simplified implementation // In a real scenario, you might need a more sophisticated way to clear the cache _memoryCache.Compact(1.0); // Remove all entries _memoryUsage = 0; Interlocked.Exchange(ref _hits, 0); Interlocked.Exchange(ref _misses, 0); } finally { _lock.ExitWriteLock(); } } public CacheStatistics GetStatistics() { _lock.EnterReadLock(); try { var stats = new CacheStatistics { TotalItems = GetCacheSize(), Hits = _hits, Misses = _misses, MemoryUsageBytes = _memoryUsage, LastCleared = DateTime.Now, ItemsByType = new Dictionary() }; // Count items by type (simplified) var deviceCacheCount = GetKeys("device:*").Count(); var productionCacheCount = GetKeys("production:*").Count(); var configCacheCount = GetKeys("config:*").Count(); var templateCacheCount = GetKeys("template:*").Count(); stats.ItemsByType["device"] = deviceCacheCount; stats.ItemsByType["production"] = productionCacheCount; stats.ItemsByType["config"] = configCacheCount; stats.ItemsByType["template"] = templateCacheCount; return stats; } finally { _lock.ExitReadLock(); } } public IEnumerable GetKeys(string pattern) { // This is a simplified implementation // In a real scenario, you might need a more sophisticated key pattern matching return _memoryCache.Keys .Cast() .Where(key => key.StartsWith(pattern.Replace("*", ""))); } public bool Refresh(string key) { if (!_memoryCache.TryGetValue(key, out T value)) return false; // Remove and re-add to refresh _lock.EnterWriteLock(); try { _memoryCache.Remove(key); _memoryCache.Set(key, value, new MemoryCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(30), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2) }); return true; } finally { _lock.ExitWriteLock(); } } #region Private Methods private int GetCacheSize() { // This is an approximation return _memoryCache.Count; } private void UpdateMemoryUsage(long delta) { Interlocked.Add(ref _memoryUsage, delta); } private int GetEstimatedSize(object obj) { // Simplified size estimation if (obj == null) return 0; var type = obj.GetType(); if (type.IsValueType || type == typeof(string)) { return System.Text.Json.JsonSerializer.Serialize(obj).Length; } // For complex objects, estimate based on type return 1024; // Default 1KB for complex objects } #endregion } // Extension methods for common caching patterns public static class CacheServiceExtensions { /// /// Cache device information /// public static Task GetOrSetDeviceAsync(this ICacheService cache, int deviceId, Func> factory, TimeSpan? expiration = null) { var options = new MemoryCacheEntryOptions(); if (expiration.HasValue) { options.SlidingExpiration = expiration.Value; options.AbsoluteExpirationRelativeToNow = expiration.Value + TimeSpan.FromMinutes(30); } else { options.SlidingExpiration = TimeSpan.FromMinutes(30); options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2); } return cache.GetOrSetAsync($"device:{deviceId}", factory, options); } /// /// Cache device list /// public static Task> GetOrSetAllDevicesAsync(this ICacheService cache, Func>> factory, TimeSpan? expiration = null) { var options = new MemoryCacheEntryOptions { SlidingExpiration = expiration ?? TimeSpan.FromMinutes(15), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1) }; return cache.GetOrSetAsync("devices:all", factory, options); } /// /// Cache device status /// public static Task GetOrSetDeviceStatusAsync(this ICacheService cache, int deviceId, Func> factory, TimeSpan? expiration = null) { var options = new MemoryCacheEntryOptions { SlidingExpiration = expiration ?? TimeSpan.FromMinutes(5), AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) }; return cache.GetOrSetAsync($"device:status:{deviceId}", factory, options); } /// /// Cache production records /// public static Task> GetOrSetProductionRecordsAsync(this ICacheService cache, int deviceId, DateTime date, Func>> factory, TimeSpan? expiration = null) { var options = new MemoryCacheEntryOptions { SlidingExpiration = expiration ?? TimeSpan.FromMinutes(10), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1) }; return cache.GetOrSetAsync($"production:records:{deviceId}:{date:yyyy-MM-dd}", factory, options); } /// /// Cache production summary /// public static Task GetOrSetProductionSummaryAsync(this ICacheService cache, int deviceId, string programName, Func> factory, TimeSpan? expiration = null) { var options = new MemoryCacheEntryOptions { SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2) }; return cache.GetOrSetAsync($"production:summary:{deviceId}:{programName}", factory, options); } /// /// Cache system configuration /// public static Task GetOrSetSystemConfigurationAsync(this ICacheService cache, Func> factory, TimeSpan? expiration = null) { var options = new MemoryCacheEntryOptions { SlidingExpiration = expiration ?? TimeSpan.FromHours(1), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6) }; return cache.GetOrSetAsync("config:system", factory, options); } /// /// Cache template /// public static Task GetOrSetTemplateAsync(this ICacheService cache, int templateId, Func> factory, TimeSpan? expiration = null) { var options = new MemoryCacheEntryOptions { SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2) }; return cache.GetOrSetAsync($"template:{templateId}", factory, options); } /// /// Cache template list /// public static Task> GetOrSetAllTemplatesAsync(this ICacheService cache, Func>> factory, TimeSpan? expiration = null) { var options = new MemoryCacheEntryOptions { SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2) }; return cache.GetOrSetAsync("templates:all", factory, options); } /// /// Cache alert configuration /// public static Task GetOrSetAlertConfigurationAsync(this ICacheService cache, Func> factory, TimeSpan? expiration = null) { var options = new MemoryCacheEntryOptions { SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1) }; return cache.GetOrSetAsync("config:alerts", factory, options); } /// /// Cache dashboard summary /// public static Task GetOrSetDashboardSummaryAsync(this ICacheService cache, DateTime date, Func> factory, TimeSpan? expiration = null) { var options = new MemoryCacheEntryOptions { SlidingExpiration = expiration ?? TimeSpan.FromMinutes(10), AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) }; return cache.GetOrSetAsync($"dashboard:summary:{date:yyyy-MM-dd}", factory, options); } /// /// Invalidate device-related cache /// public static void InvalidateDeviceCache(this ICacheService cache, int deviceId, params string[] additionalKeys) { var keys = new[] { $"device:{deviceId}", $"device:status:{deviceId}", $"production:records:{deviceId}", $"production:summary:{deviceId}" }; foreach (var key in keys.Concat(additionalKeys)) { cache.Remove(key); } } /// /// Invalidate production-related cache /// public static void InvalidateProductionCache(this ICacheService cache, int deviceId, string programName, DateTime date) { cache.Remove($"production:records:{deviceId}:{date:yyyy-MM-dd}"); cache.Remove($"production:summary:{deviceId}:{programName}"); } /// /// Invalidate template-related cache /// public static void InvalidateTemplateCache(this ICacheService cache, int templateId) { cache.Remove($"template:{templateId}"); cache.Remove("templates:all"); } /// /// Invalidate system configuration cache /// public static void InvalidateSystemConfigCache(this ICacheService cache) { cache.Remove("config:system"); cache.Remove("config:alerts"); cache.Remove("dashboard:summary"); } /// /// Invalidate dashboard cache /// public static void InvalidateDashboardCache(this ICacheService cache, DateTime date) { cache.Remove($"dashboard:summary:{date:yyyy-MM-dd}"); } /// /// Get or set with sliding expiration for frequently accessed data /// public static Task GetOrSetWithSlidingExpiration(this ICacheService cache, string key, Func> factory, TimeSpan slidingExpiration) { var options = new MemoryCacheEntryOptions { SlidingExpiration = slidingExpiration }; return cache.GetOrSetAsync(key, factory, options); } /// /// Get or set with absolute expiration for time-sensitive data /// public static Task GetOrSetWithAbsoluteExpiration(this ICacheService cache, string key, Func> factory, TimeSpan absoluteExpiration) { var options = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = absoluteExpiration }; return cache.GetOrSetAsync(key, factory, options); } /// /// Get or set with priority for important data /// public static Task GetOrSetWithPriority(this ICacheService cache, string key, Func> factory, CacheItemPriority priority) { var options = new MemoryCacheEntryOptions { Priority = priority }; return cache.GetOrSetAsync(key, factory, options); } } /// /// Cache service for distributed caching /// public interface IDistributedCacheService : ICacheService { /// /// Get distributed lock /// Task AcquireLockAsync(string key, TimeSpan timeout); /// /// Refresh distributed cache /// Task RefreshAsync(string key); /// /// Get distributed cache statistics /// Task GetDistributedStatisticsAsync(); } public interface IDistributedLock : IDisposable { bool IsAcquired { get; } void Release(); } public class DistributedCacheStatistics : CacheStatistics { public int ConnectedNodes { get; set; } public long NetworkCalls { get; set; } public long SynchronizationErrors { get; set; } } /// /// Cache manager for managing multiple cache instances /// public interface ICacheManager { ICacheService LocalCache { get; } IDistributedCacheService DistributedCache { get; } void ConfigureCacheSettings(CacheSettings settings); void InitializeCaches(); } public class CacheSettings { public bool EnableLocalCache { get; set; } = true; public bool EnableDistributedCache { get; set; } = false; public TimeSpan DefaultSlidingExpiration { get; set; } = TimeSpan.FromMinutes(30); public TimeSpan DefaultAbsoluteExpiration { get; set; } = TimeSpan.FromHours(2); public long MaxMemorySizeBytes { get; set; } = 1024 * 1024 * 100; // 100MB public CacheEvictionPolicy EvictionPolicy { get; set; } = CacheEvictionPolicy.LRU; public bool EnableCacheLogging { get; set; } = false; public TimeSpan CacheRefreshInterval { get; set; } = TimeSpan.FromMinutes(5); } public enum CacheEvictionPolicy { LRU, // Least Recently Used LFU, // Least Frequently Used FIFO, // First In First Out Random } public class CacheManager : ICacheManager { public ICacheService LocalCache { get; private set; } public IDistributedCacheService DistributedCache { get; private set; } public CacheManager(ICacheService localCache, IDistributedCacheService distributedCache) { LocalCache = localCache; DistributedCache = distributedCache; } public void ConfigureCacheSettings(CacheSettings settings) { // Configure cache settings based on provided configuration // This would involve setting up the cache instances with the specified settings } public void InitializeCaches() { // Initialize caches with default settings ClearAllCaches(); } public void ClearAllCaches() { LocalCache?.Clear(); DistributedCache?.Clear(); } } }