using NPOI.SS.Formula.Functions; using NPOI.Util; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using TeamAAS_VP.Interfaces; using TeamAAS_VP.Models; using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel; namespace TeamAAS_VP.Data { /// /// 系统数据库服务,基于 提供对应用数据的访问。 /// 包含用户增删改查、用户身份验证以及生产记录的写入与统计功能。 /// public class SystemDatabaseService : ISystemDatabaseService { private readonly ISqlSugarClient _db; private User CurrentUser { get; set; } /// /// 使用指定的 SqlSugar 客户端构造数据库服务实例。 /// /// 已配置的 实例。 public SystemDatabaseService(ISqlSugarClient db) { _db = db; //GenerateTestDataAsync(); } #region 用户 // User CRUD /// /// 添加新用户到数据库。 /// 对于 SqlSugar,User.Id 为数据库自增值,因此在此方法中不应设定 Id。 /// /// 要添加的用户对象,不能为空。 /// 返回传入的用户对象(Id 由数据库生成后会回写到该对象)。 /// 为 null 时抛出。 public async Task AddUserAsync(User user) { if (user == null) throw new ArgumentNullException(nameof(user)); // for SqlSugar User.Id is int identity, so do not set it here await _db.Insertable(user).ExecuteCommandAsync(); return user; } /// /// 根据用户 Id 删除用户。 /// /// 要删除的用户 Id。 /// 如果删除了至少一条记录则返回 true,否则返回 false。 public async Task DeleteUserAsync(int userId) { var r = await _db.Deleteable().In(userId).ExecuteCommandAsync(); return r > 0; } /// /// 获取所有用户。 /// /// 用户列表的异步枚举。 public async Task> GetAllUsersAsync() { return await _db.Queryable().ToListAsync(); } /// /// 根据用户 Id 查询单个用户。 /// /// 用户 Id。 /// 匹配的用户,如果未找到则返回 null(取决于 SqlSugar 的行为)。 public async Task GetUserByIdAsync(int userId) { return await _db.Queryable().InSingleAsync(userId); } /// /// 更新用户信息并返回更新后的对象。 /// /// 要更新的用户对象,不能为空。 /// 返回传入的用户对象。 /// 为 null 时抛出。 public async Task UpdateUserAsync(User user) { if (user == null) throw new ArgumentNullException(nameof(user)); var r = await _db.Updateable(user).ExecuteCommandAsync(); return user; } // User authentication /// /// 验证用户名和密码并返回匹配的用户。 /// 注意:当前实现直接以明文密码比较,请根据需要在调用侧或更高层添加哈希/盐策略。 /// /// 用户名,不能为空或空字符串。 /// 密码,不能为空或空字符串。 /// 匹配的用户对象,如果未找到则可能抛出或返回 null(取决于 SqlSugar 的 FirstAsync 行为)。 /// 为空时抛出。 public async Task AuthenticateUserAsync(string username, string password) { if (string.IsNullOrEmpty(username)) throw new ArgumentException("username is required", nameof(username)); if (string.IsNullOrEmpty(password)) throw new ArgumentException("password is required", nameof(password)); return await _db.Queryable().Where(u => u.UserName == username && u.UserPassword == password).FirstAsync(); } // User login / current user helpers /// /// 将指定用户设为当前用户(写入一条成功的登录记录)。 /// /// 要设为当前用户的用户 Id。 public async Task SetCurrentUserAsync(int userId) { // 验证用户存在 var user = await GetUserByIdAsync(userId); if (user == null) { throw new ArgumentException($"User with id {userId} not found", nameof(userId)); } CurrentUser = user; // 记录一条成功的登录记录 var rec = new UserLoginRecord { UserId = userId, Success = true, Time = DateTime.Now, Message = "Set as current user" }; await RecordUserLoginAsync(rec); } /// /// 将指定用户设为当前用户(会写入一条登录记录,标记为成功) /// /// /// public async Task SetCurrentUserAsync(User user) { CurrentUser = user; // 记录一条成功的登录记录 var rec = new UserLoginRecord { UserId = user.Id, Success = true, Time = DateTime.Now, Message = user.UserName }; await RecordUserLoginAsync(rec); } /// /// 获取最近一次成功登录的用户(视为当前用户)。 /// /// 若存在成功登录记录则返回对应用户,否则返回 null。 public Task GetCurrentUserAsync() { return Task.FromResult(GetCurrentUser()); } /// /// 获取当前用户 /// /// public User GetCurrentUser() { //var recs = _db.Queryable() // .Where(r => r.Success == true) // .OrderBy(r => r.Time, OrderByType.Desc) // .Take(1) // .ToList(); //var rec = recs.FirstOrDefault(); ////如果没有登录记录则返回一个默认的用户,即第一个用户 //if (rec == null) return _db.Queryable().First(); //return _db.Queryable().InSingle(rec.UserId); if (CurrentUser == null) { CurrentUser = _db.Queryable().ToList().FirstOrDefault(); } return CurrentUser; } /// /// 记录一条用户登录记录(成功或失败)。 /// /// 登录记录对象,不能为空。 public async Task RecordUserLoginAsync(UserLoginRecord record) { if (record == null) throw new ArgumentNullException(nameof(record)); if (record.Time == default(DateTime)) record.Time = DateTime.Now; await _db.Insertable(record).ExecuteCommandAsync(); } /// /// 获取指定用户最近的一条登录记录(无论是否成功)。 /// /// 用户 Id。 /// 最近的一条登录记录或 null。 public async Task GetLastUserLoginRecordAsync(int userId) { var recs = await _db.Queryable() .Where(r => r.UserId == userId) .OrderBy(r => r.Time, OrderByType.Desc) .Take(1) .ToListAsync(); return recs.FirstOrDefault(); } /// /// 将指定用户名标记为“记住”,并取消其他用户的记住标志。 /// /// 要记住的用户名。 public async Task SetRememberUserAsync(string username) { if (string.IsNullOrEmpty(username)) throw new ArgumentException("username is required", nameof(username)); // 使用事务先清除所有记住标志,再为指定用户设置记住 try { _db.Ado.BeginTran(); // 清除所有 await _db.Updateable().SetColumns(u => new User { IsRemember = false }).Where("1=1").ExecuteCommandAsync(); // 设置指定用户为记住 await _db.Updateable().SetColumns(u => new User { IsRemember = true }).Where(u => u.UserName == username).ExecuteCommandAsync(); _db.Ado.CommitTran(); } catch { try { _db.Ado.RollbackTran(); } catch { } throw; } } /// /// 清除所有用户的记住标志。 /// public async Task ClearRememberUserAsync() { await _db.Updateable().SetColumns(u => new User { IsRemember = false }).ExecuteCommandAsync(); } /// /// 获取当前被标记为记住的用户(如果存在)。 /// /// 被记住的用户或 null。 public async Task GetRememberedUserAsync() { return await _db.Queryable().Where(u => u.IsRemember == true).FirstAsync(); } #endregion #region 生产记录 // Production records /// /// 记录一次生产事件。如果 /// 则会自动生成新的 Guid。 /// /// 生产记录对象,不能为空。 /// 异步任务。 /// 为 null 时抛出。 public async Task RecordProductionAsync(ProductionRecord record) { if (record == null) throw new ArgumentNullException(nameof(record)); // 如果未指定产生该记录的用户名,尝试使用当前登录用户 if (string.IsNullOrEmpty(record.UserName)) { try { var current = await GetCurrentUserAsync(); if (current != null) { record.UserName = current.UserName; } } catch { // 忽略获取当前用户时的错误,继续保存记录(保守策略) } } record.Id = record.Id == Guid.Empty ? Guid.NewGuid() : record.Id; await _db.Insertable(record).ExecuteCommandAsync(); } /// /// 重置生产记录。若未传入 /// 则清空所有生产记录;否则仅删除指定产品名的记录。 /// /// 可选的产品名过滤。 /// 异步任务。 public async Task ResetProductionAsync(string productName = null) { if (string.IsNullOrEmpty(productName)) { await _db.Deleteable().Where("1=1").ExecuteCommandAsync(); } else { await _db.Deleteable().Where(r => r.ProductName == productName).ExecuteCommandAsync(); } } /// /// 获取指定产品(和可选类别)在数据库中累计的产量总和(仅统计 Quantity > 0 的记录)。 /// /// 产品名,不能为空或空字符串。 /// 可选的类别集合,用于过滤。 /// 指定条件下的产量总和(整数)。 /// 为空时抛出。 public async Task GetTotalProductionAsync(string productName, IEnumerable categories = null) { if (string.IsNullOrEmpty(productName)) throw new ArgumentException("productName is required", nameof(productName)); var q = _db.Queryable().Where(r => r.ProductName == productName && r.Quantity > 0); if (categories != null && categories.Any()) { q = q.Where(r => categories.Contains(r.Category)); } var sum = await q.SumAsync(r => r.Quantity); return sum; } /// /// 获取生产统计结果,按指定时间范围、产品名和可选类别筛选, /// 并根据 (hour/day/week/month/total)进行分组聚合。 /// /// 生产统计查询对象,不能为空,必须包含 ProductName/Start/End 等。 /// 按指定分组粒度返回的统计结果集合。 /// 为 null 时抛出。 public async Task> GetProductionStatsAsync(ProductionStatQuery query) { if (query == null) throw new ArgumentNullException(nameof(query)); if (string.IsNullOrEmpty(query.ProductName)) throw new ArgumentException("ProductName is required in query", nameof(query)); var q = _db.Queryable().Where(r => r.ProductName == query.ProductName && r.Timestamp >= query.Start && r.Timestamp < query.End); if (query.Categories != null && query.Categories.Any()) { q = q.Where(r => query.Categories.Contains(r.Category)); } var list = await q.ToListAsync(); var results = new List(); switch ((query.GroupBy ?? "total").ToLower()) { case "hour": // group by hour var hours = list.GroupBy(r => new DateTime(r.Timestamp.Year, r.Timestamp.Month, r.Timestamp.Day, r.Timestamp.Hour, 0, 0)) .OrderBy(g => g.Key); foreach (var g in hours) { results.Add(new ProductionStatResult { PeriodStart = g.Key, PeriodEnd = g.Key.AddHours(1), Quantity = g.Sum(x => x.Quantity), ProductName = query.ProductName }); } break; case "day": var days = list.GroupBy(r => r.Timestamp.Date).OrderBy(g => g.Key); foreach (var g in days) { results.Add(new ProductionStatResult { PeriodStart = g.Key, PeriodEnd = g.Key.AddDays(1), Quantity = g.Sum(x => x.Quantity), ProductName = query.ProductName }); } break; case "week": // ISO week grouping by week start (Monday) var weeks = list.GroupBy(r => FirstDayOfWeek(r.Timestamp)).OrderBy(g => g.Key); foreach (var g in weeks) { results.Add(new ProductionStatResult { PeriodStart = g.Key, PeriodEnd = g.Key.AddDays(7), Quantity = g.Sum(x => x.Quantity), ProductName = query.ProductName }); } break; case "month": var months = list.GroupBy(r => new DateTime(r.Timestamp.Year, r.Timestamp.Month, 1)).OrderBy(g => g.Key); foreach (var g in months) { results.Add(new ProductionStatResult { PeriodStart = g.Key, PeriodEnd = g.Key.AddMonths(1), Quantity = g.Sum(x => x.Quantity), ProductName = query.ProductName }); } break; default: results.Add(new ProductionStatResult { PeriodStart = query.Start, PeriodEnd = query.End, Quantity = list.Sum(x => x.Quantity), ProductName = query.ProductName }); break; } return results; } /// /// 获取所有已知分类相加起来的今天的产能 /// /// /// public async Task GetTodayProductionAsync(string productName) { //今天的起始时间 var todayStart = DateTime.Now.Date; //今天的结束时间 var todayEnd = DateTime.Now.Date; todayEnd = todayEnd.AddDays(1); //按照产品名称和时间范围查询生产记录的总数 var total = await _db.Queryable() .Where(r => r.ProductName == productName && r.Timestamp >= todayStart && r.Timestamp < todayEnd) .SumAsync(r => r.Quantity); return total; } /// /// 获取所有已知分类相加起来的当前时间往前的一个小时的产能 /// /// /// public async Task GetLastHourProductionAsync(string productName) { var lastHourStart = DateTime.Now.AddHours(-1); var lastHourEnd = DateTime.Now; //按照产品名称和时间范围查询生产记录的总数 var total = await _db.Queryable() .Where(r => r.ProductName == productName && r.Timestamp >= lastHourStart && r.Timestamp < lastHourEnd) .SumAsync(r => r.Quantity); return total; } /// /// 获取今天ng的数量 /// /// /// public async Task GetNGProductionAsync(string productName) { //今天的起始时间 var todayStart = DateTime.Now.Date; //今天的结束时间 var todayEnd = DateTime.Now.Date; todayEnd = todayEnd.AddDays(1); //按照产品名称和时间范围查询生产记录的总数 var total = await _db.Queryable() .Where(r => r.ProductName == productName && r.Timestamp >= todayStart && r.Timestamp < todayEnd && r.Category == "NG") .SumAsync(r => r.Quantity); return total; } /// /// 获取当前月的产能 /// /// /// public Task GetCurrentMonthProductionAsync(string productName) { var now = DateTime.Now; var monthStart = new DateTime(now.Year, now.Month, 1); var monthEnd = monthStart.AddMonths(1); //按照产品名称和时间范围查询生产记录的总数 return _db.Queryable() .Where(r => r.ProductName == productName && r.Timestamp >= monthStart && r.Timestamp < monthEnd) .SumAsync(r => r.Quantity); } /// /// 获取当前周的产能 /// /// /// public Task GetCurrentWeekProductionAsync(string productName) { var now = DateTime.Now; var weekStart = FirstDayOfWeek(now); var weekEnd = weekStart.AddDays(7); //按照产品名称和时间范围查询生产记录的总数 return _db.Queryable() .Where(r => r.ProductName == productName && r.Timestamp >= weekStart && r.Timestamp < weekEnd) .SumAsync(r => r.Quantity); } /// /// 计算给定日期所属周的第一天(以星期一为一周的第一天)。 /// 返回结果为日期的 Date 部分(时间为 00:00:00)。 /// /// 输入日期。 /// 该日期对应周的周一日期(Date 部分)。 private DateTime FirstDayOfWeek(DateTime dt) { var diff = (7 + (dt.DayOfWeek - DayOfWeek.Monday)) % 7; return dt.Date.AddDays(-1 * diff); } /// /// 获取指定产品名称的总产能 /// /// /// public Task GetOverallProductionAsync(string productName) { return _db.Queryable() .Where(r => r.ProductName == productName) .SumAsync(r => r.Quantity); } /// /// 获取指定产品的所有已知分类。 /// /// 要查询分类的产品名称。 /// /// 一个字符串集合,包含该产品的所有分类名称;如果没有分类,返回空集合或 null(建议返回空集合)。 /// public async Task> GetProductCategoriesAsync(string productName) { var categories = await _db.Queryable() .Where(r => r.ProductName == productName) .Select(r => r.Category) .Distinct() .ToListAsync(); return categories; } /// /// 获取指定产品名称的所有记录按照分类的产能统计 /// /// /// public async Task> GetProductionByCategoriesAsync(string productName) { var list = await _db.Queryable() .Where(r => r.ProductName == productName) .ToListAsync(); var dict = list.GroupBy(r => r.Category) .ToDictionary(g => g.Key, g => g.Sum(r => r.Quantity)); return dict; } /// /// 获取指定产品名称的一个月内按照每天统计对应的类别和数量 /// /// /// public async Task>> GetMonthlyProductionByDayAndCategoryAsync(string productName) { var now = DateTime.Now; var monthStart = new DateTime(now.Year, now.Month, 1); var monthEnd = monthStart.AddMonths(1); var list = await _db.Queryable() .Where(r => r.ProductName == productName && (r.Timestamp >= monthStart && r.Timestamp < monthEnd)) .ToListAsync(); var dict = new Dictionary>(); var days = list.GroupBy(r => r.Timestamp.Date).OrderBy(g => g.Key); foreach (var dayGroup in days) { var categoryDict = dayGroup.GroupBy(r => r.Category) .ToDictionary(g => g.Key, g => g.Sum(r => r.Quantity)); dict[dayGroup.Key] = categoryDict; } return dict; } /// /// 获取指定产品名称的一个星期内按照每天统计对应的类别和数量 /// /// /// public async Task>> GetWeeklyProductionByDayAndCategoryAsync(string productName) { var now = DateTime.Now; var weekStart = FirstDayOfWeek(now); var weekEnd = weekStart.AddDays(7); var list = await _db.Queryable() .Where(r => r.ProductName == productName && r.Timestamp >= weekStart && r.Timestamp < weekEnd) .ToListAsync(); var dict = new Dictionary>(); var days = list.GroupBy(r => r.Timestamp.DayOfWeek).OrderBy(g => g.Key); foreach (var dayGroup in days) { var categoryDict = dayGroup.GroupBy(r => r.Category) .ToDictionary(g => g.Key, g => g.Sum(r => r.Quantity)); dict[(int)dayGroup.Key] = categoryDict; } return dict; } /// /// 获取指定产品名称的当天内按照每小时统计对应的类别和数量 /// /// /// public async Task>> GetDailyProductionByHourAndCategoryAsync(string productName) { var now = DateTime.Now; var dayStart = now.Date; var dayEnd = dayStart.AddDays(1); var list = await _db.Queryable() .Where(r => r.ProductName == productName && r.Timestamp >= dayStart && r.Timestamp < dayEnd) .ToListAsync(); var dict = new Dictionary>(); var hours = list.GroupBy(r => r.Timestamp.Hour).OrderBy(g => g.Key); foreach (var hourGroup in hours) { var categoryDict = hourGroup.GroupBy(r => r.Category) .ToDictionary(g => g.Key, g => g.Sum(r => r.Quantity)); dict[hourGroup.Key] = categoryDict; } return dict; } /// /// 分页查询生产记录 /// public async Task<(IEnumerable Items, int TotalCount)> QueryProductionRecordsPagedAsync(DateTime start, DateTime end, int pageIndex, int pageSize, string productName = null, string category = null) { if (pageIndex < 1) pageIndex = 1; if (pageSize < 1) pageSize = 20; var q = _db.Queryable().Where(r => r.Timestamp >= start && r.Timestamp < end); if (!string.IsNullOrEmpty(productName)) q = q.Where(r => r.ProductName == productName); if (!string.IsNullOrEmpty(category)) q = q.Where(r => r.Category == category); var total = await q.CountAsync(); var list = await q.OrderBy(r => r.Timestamp, OrderByType.Desc).ToPageListAsync(pageIndex, pageSize); return (list, total); } #endregion #region 报警记录 // Alarm Records /// /// 记录一条报警记录。 /// /// 报警记录对象,不能为空。 public async Task RecordAlarmAsync(AlarmRecord alarm) { if (alarm == null) throw new ArgumentNullException(nameof(alarm)); if (alarm.Time == default(DateTime)) alarm.Time = DateTime.Now; await _db.Insertable(alarm).ExecuteCommandAsync(); } /// /// 按时间范围和可选条件查询报警记录。 /// /// 起始时间(含)。 /// 结束时间(不含)。 /// 可选的报警编号过滤。 /// 可选的报警来源过滤。 /// 满足条件的报警记录集合。 public async Task> QueryAlarmsAsync(DateTime start, DateTime end, string code = null, string source = null) { var q = _db.Queryable().Where(a => a.Time >= start && a.Time < end); if (!string.IsNullOrEmpty(code)) q = q.Where(a => a.Code == code); if (!string.IsNullOrEmpty(source)) q = q.Where(a => a.Source == source); return await q.OrderBy(a => a.Time, OrderByType.Desc).ToListAsync(); } #endregion #region 锁付结果记录 // Lock result methods /// /// 记录单次锁付结果(WaveDatas 不写入数据库) /// /// public async Task RecordLockResultAsync(LockResult result) { try { if (result == null) throw new ArgumentNullException(nameof(result)); if (result.Timestamp == default(DateTime)) result.Timestamp = DateTime.Now; await _db.Insertable(result).ExecuteCommandAsync(); } catch (Exception) { } } /// /// 分页查询锁付结果 /// public async Task<(IEnumerable Items, int TotalCount)> QueryLockResultsAsync(DateTime start, DateTime end, int pageIndex, int pageSize, string productNumber = null, string productName = null, int? screwNumber = null, bool? lockPassed = null) { if (pageIndex < 1) pageIndex = 1; if (pageSize < 1) pageSize = 20; var q = _db.Queryable().Where(r => r.Timestamp >= start && r.Timestamp < end); if (!string.IsNullOrEmpty(productNumber)) q = q.Where(r => r.ProductNumber == productNumber); if (screwNumber.HasValue) q = q.Where(r => r.Number == screwNumber.Value); if (lockPassed.HasValue) q = q.Where(r => r.LockPassed == lockPassed.Value); if (!string.IsNullOrEmpty(productName)) q = q.Where(r => r.ProductName == productName); var total = await q.CountAsync(); var list = await q.OrderBy(r => r.Timestamp, OrderByType.Desc).ToPageListAsync(pageIndex, pageSize); return (list, total); } /// /// 查询所有符合条件的锁付结果(不分页)。 /// public async Task> QueryAllLockResultsAsync(DateTime start, DateTime end, string productNumber = null, string productName = null, int? screwNumber = null, bool? lockPassed = null) { var q = _db.Queryable().Where(r => r.Timestamp >= start && r.Timestamp < end); if (!string.IsNullOrEmpty(productNumber)) q = q.Where(r => r.ProductNumber == productNumber); if (screwNumber.HasValue) q = q.Where(r => r.Number == screwNumber.Value); if (lockPassed.HasValue) q = q.Where(r => r.LockPassed == lockPassed.Value); if (!string.IsNullOrEmpty(productName)) q = q.Where(r => r.ProductName == productName); var list = await q.OrderBy(r => r.Timestamp, OrderByType.Desc).ToListAsync(); return list; } #endregion #region 螺丝供料器批次记录 /// /// 记录一次螺丝供料器批次更换记录 /// /// /// public async Task RecordScrewFeederBatchAsync(ScrewFeederBatchRecord record) { if (record == null) throw new ArgumentNullException(nameof(record)); if (record.ChangeTime == default(DateTime)) record.ChangeTime = DateTime.Now; record.Id = record.Id == Guid.Empty ? Guid.NewGuid() : record.Id; await _db.Insertable(record).ExecuteCommandAsync(); } /// /// 查询螺丝供料器批次记录。 /// 可按时间段、批次号精确匹配及供料器编号过滤。所有参数均为可选,传入 null 表示不作为过滤条件。 /// /// 可选:起始时间(包含) /// 可选:结束时间(包含) /// 可选:批次号(精确匹配) /// 可选:供料器编号 /// 满足条件的记录集合(按 ChangeTime 降序)。 public async Task> QueryScrewFeederBatchRecordsAsync(DateTime? start = null, DateTime? end = null, string batchNumber = null, int? feederNumber = null) { var q = _db.Queryable(); if (start.HasValue) q = q.Where(r => r.ChangeTime >= start.Value); if (end.HasValue) q = q.Where(r => r.ChangeTime <= end.Value); if (!string.IsNullOrEmpty(batchNumber)) q = q.Where(r => r.BatchNumber == batchNumber); if (feederNumber.HasValue) q = q.Where(r => r.FeederNumber == feederNumber.Value); var list = await q.OrderBy(r => r.ChangeTime, OrderByType.Desc).ToListAsync(); return list; } /// /// 分页查询螺丝供料器批次记录 /// public async Task<(IEnumerable Items, int TotalCount)> QueryScrewFeederBatchRecordsPagedAsync(DateTime? start = null, DateTime? end = null, string batchNumber = null, int? feederNumber = null, int pageIndex = 1, int pageSize = 20) { if (pageIndex < 1) pageIndex = 1; if (pageSize < 1) pageSize = 20; var q = _db.Queryable(); if (start.HasValue) q = q.Where(r => r.ChangeTime >= start.Value); if (end.HasValue) q = q.Where(r => r.ChangeTime <= end.Value); if (!string.IsNullOrEmpty(batchNumber)) q = q.Where(r => r.BatchNumber == batchNumber); if (feederNumber.HasValue) q = q.Where(r => r.FeederNumber == feederNumber.Value); var total = await q.CountAsync(); var list = await q.OrderBy(r => r.ChangeTime, OrderByType.Desc).ToPageListAsync(pageIndex, pageSize); return (list, total); } /// /// 获取指定供料器编号的最新一条批次更换记录 /// /// /// public async Task GetLatestScrewFeederBatchRecordAsync(int feederNumber) { var recs = await _db.Queryable() .Where(r => r.FeederNumber == feederNumber) .OrderBy(r => r.ChangeTime, OrderByType.Desc) .Take(1) .ToListAsync(); return recs.FirstOrDefault(); } #endregion #region 拍照记录 /// /// 记录产品拍照结果 /// /// /// /// public async Task RecordPhotoCaptureAsync(PhotoCaptureRecord record) { if (record == null) throw new ArgumentNullException(nameof(record)); if (record.CaptureTime == default(DateTime)) record.CaptureTime = DateTime.Now; record.Id = record.Id == Guid.Empty ? Guid.NewGuid() : record.Id; await _db.Insertable(record).ExecuteCommandAsync(); } /// /// 查询拍照记录,支持按配方名、产品SN和时间段过滤,参数均为可选。 /// /// /// /// /// /// /// public async Task> QueryPhotoCapturesAsync(string recipeName = null, string cameraName = null, string productSN = null, DateTime? start = null, DateTime? end = null) { var q = _db.Queryable(); if (!string.IsNullOrEmpty(recipeName)) q = q.Where(r => r.RecipeName == recipeName); if (!string.IsNullOrEmpty(productSN)) q = q.Where(r => r.ProductSN == productSN); if (start.HasValue) q = q.Where(r => r.CaptureTime >= start.Value); if (end.HasValue) q = q.Where(r => r.CaptureTime <= end.Value); var list = await q.OrderBy(r => r.CaptureTime, OrderByType.Desc).ToListAsync(); return list; } /// /// 分页查询拍照记录 /// public async Task<(IEnumerable Items, int TotalCount)> QueryPhotoCapturesPagedAsync(string recipeName = null, string cameraName = null, string productSN = null, DateTime? start = null, DateTime? end = null, int pageIndex = 1, int pageSize = 20) { if (pageIndex < 1) pageIndex = 1; if (pageSize < 1) pageSize = 20; var q = _db.Queryable(); if (!string.IsNullOrEmpty(recipeName)) q = q.Where(r => r.RecipeName == recipeName); if (!string.IsNullOrEmpty(cameraName)) q = q.Where(r => r.CameraName == cameraName); if (!string.IsNullOrEmpty(productSN)) q = q.Where(r => r.ProductSN == productSN); if (start.HasValue) q = q.Where(r => r.CaptureTime >= start.Value); if (end.HasValue) q = q.Where(r => r.CaptureTime <= end.Value); var total = await q.CountAsync(); var list = await q.OrderBy(r => r.CaptureTime, OrderByType.Desc).ToPageListAsync(pageIndex, pageSize); return (list, total); } #endregion #region 披头更换记录 /// /// 记录披头更换 /// /// /// /// public async Task RecordHeadChangeAsync(HeadChangeRecord record) { if (record == null) throw new ArgumentNullException(nameof(record)); if (record.ChangeTime == default(DateTime)) record.ChangeTime = DateTime.Now; record.Id = record.Id == Guid.Empty ? Guid.NewGuid() : record.Id; await _db.Insertable(record).ExecuteCommandAsync(); } /// /// 查询披头更换记录 /// /// /// /// /// public async Task> QueryHeadChangeRecordsAsync(DateTime? start = null, DateTime? end = null, string headBatchNumber = null) { var q = _db.Queryable(); if (start.HasValue) q = q.Where(r => r.ChangeTime >= start.Value); if (end.HasValue) q = q.Where(r => r.ChangeTime <= end.Value); if (!string.IsNullOrEmpty(headBatchNumber)) q = q.Where(r => r.HeadBatchNumber == headBatchNumber); var list = await q.OrderBy(r => r.ChangeTime, OrderByType.Desc).ToListAsync(); return list; } /// /// 分页查询披头更换记录 /// public async Task<(IEnumerable Items, int TotalCount)> QueryHeadChangeRecordsPagedAsync(DateTime? start = null, DateTime? end = null, string headBatchNumber = null, int pageIndex = 1, int pageSize = 20) { if (pageIndex < 1) pageIndex = 1; if (pageSize < 1) pageSize = 20; var q = _db.Queryable(); if (start.HasValue) q = q.Where(r => r.ChangeTime >= start.Value); if (end.HasValue) q = q.Where(r => r.ChangeTime <= end.Value); if (!string.IsNullOrEmpty(headBatchNumber)) q = q.Where(r => r.HeadBatchNumber == headBatchNumber); var total = await q.CountAsync(); var list = await q.OrderBy(r => r.ChangeTime, OrderByType.Desc).ToPageListAsync(pageIndex, pageSize); return (list, total); } /// /// 获取最新的一条披头更换记录 /// /// public async Task GetLatestHeadChangeRecordAsync() { var recs = await _db.Queryable().OrderBy(r => r.ChangeTime, OrderByType.Desc).Take(1).ToListAsync(); return recs.FirstOrDefault(); } /// /// 对最新披头的锁付次数进行递增并返回更新后的次数(原子操作)。 /// 若不存在记录则创建一条新记录并返回 1。 /// public async Task IncrementLatestHeadLockCountAsync(string operatorName = null) { // 使用事务保证原子性 try { _db.Ado.BeginTran(); // 获取最新记录(排他查询) var latest = _db.Queryable().OrderBy(r => r.ChangeTime, OrderByType.Desc).Take(1).First(); if (latest == null) { var newRec = new HeadChangeRecord { ChangeTime = DateTime.Now, HeadBatchNumber = string.Empty, CurrentLockCount = 1, OperatorName = operatorName }; await _db.Insertable(newRec).ExecuteCommandAsync(); _db.Ado.CommitTran(); return 1; } latest.CurrentLockCount += 1; if (!string.IsNullOrEmpty(operatorName)) latest.OperatorName = operatorName; await _db.Updateable(latest).ExecuteCommandAsync(); _db.Ado.CommitTran(); return latest.CurrentLockCount; } catch { try { _db.Ado.RollbackTran(); } catch { } throw; } } #endregion #region 吸嘴更换记录 /// /// 记录喷嘴更换记录。 /// /// /// public async Task RecordNozzleChangeAsync(NozzleChangeRecord record) { if (record == null) throw new ArgumentNullException(nameof(record)); if (record.ChangeTime == default(DateTime)) record.ChangeTime = DateTime.Now; record.Id = record.Id == Guid.Empty ? Guid.NewGuid() : record.Id; await _db.Insertable(record).ExecuteCommandAsync(); } /// /// 查询喷嘴更换记录,可按时间段或喷嘴批号过滤(所有参数可选)。 /// /// /// /// /// public async Task> QueryNozzleChangeRecordsAsync(DateTime? start = null, DateTime? end = null, string nozzleBatchNumber = null) { var q = _db.Queryable(); if (start.HasValue) q = q.Where(r => r.ChangeTime >= start.Value); if (end.HasValue) q = q.Where(r => r.ChangeTime <= end.Value); if (!string.IsNullOrEmpty(nozzleBatchNumber)) q = q.Where(r => r.NozzleBatchNumber == nozzleBatchNumber); var list = await q.OrderBy(r => r.ChangeTime, OrderByType.Desc).ToListAsync(); return list; } /// /// 获取最新的一条喷嘴更换记录(按 ChangeTime 降序)。 /// /// public async Task GetLatestNozzleChangeRecordAsync() { var recs = await _db.Queryable().OrderBy(r => r.ChangeTime, OrderByType.Desc).Take(1).ToListAsync(); return recs.FirstOrDefault(); } /// /// 吸嘴的锁付次数进行递增并返回更新后的次数(如果不存在最新记录,则创建一条新的记录并返回 1)。 /// /// /// public async Task IncrementLatestNozzleLockCountAsync(string operatorName = null) { try { _db.Ado.BeginTran(); var latest = _db.Queryable().OrderBy(r => r.ChangeTime, OrderByType.Desc).Take(1).First(); if (latest == null) { var newRec = new NozzleChangeRecord { ChangeTime = DateTime.Now, NozzleBatchNumber = string.Empty, CurrentLockCount = 1, OperatorName = operatorName }; await _db.Insertable(newRec).ExecuteCommandAsync(); _db.Ado.CommitTran(); return 1; } latest.CurrentLockCount += 1; if (!string.IsNullOrEmpty(operatorName)) latest.OperatorName = operatorName; await _db.Updateable(latest).ExecuteCommandAsync(); _db.Ado.CommitTran(); return latest.CurrentLockCount; } catch { try { _db.Ado.RollbackTran(); } catch { } throw; } } /// /// 分页查询喷嘴更换记录 /// public async Task<(IEnumerable Items, int TotalCount)> QueryNozzleChangeRecordsPagedAsync(DateTime? start = null, DateTime? end = null, string nozzleBatchNumber = null, int pageIndex = 1, int pageSize = 20) { if (pageIndex < 1) pageIndex = 1; if (pageSize < 1) pageSize = 20; var q = _db.Queryable(); if (start.HasValue) q = q.Where(r => r.ChangeTime >= start.Value); if (end.HasValue) q = q.Where(r => r.ChangeTime <= end.Value); if (!string.IsNullOrEmpty(nozzleBatchNumber)) q = q.Where(r => r.NozzleBatchNumber == nozzleBatchNumber); var total = await q.CountAsync(); var list = await q.OrderBy(r => r.ChangeTime, OrderByType.Desc).ToPageListAsync(pageIndex, pageSize); return (list, total); } #endregion #region 生成测试数据 //定义一个方法来生成一些测试的数据,测试数据库写入和查询功能 public async Task GenerateTestDataAsync() { //生成一些喷嘴更换记录的测试数据 for (int i = 0; i < 10; i++) { var record = new NozzleChangeRecord { ChangeTime = DateTime.Now.AddMinutes(-i * 10), NozzleBatchNumber = $"Batch-{i}", CurrentLockCount = i, OperatorName = $"Operator-{i}" }; await RecordNozzleChangeAsync(record); } //生成一些披头更换记录的测试数据 for (int i = 0; i < 10; i++) { var record = new HeadChangeRecord { ChangeTime = DateTime.Now.AddMinutes(-i * 15), HeadBatchNumber = $"HeadBatch-{i}", CurrentLockCount = i, OperatorName = $"HeadOperator-{i}" }; await RecordHeadChangeAsync(record); } //生成一些螺丝供料器批次更换记录的测试数据 for (int i = 0; i < 10; i++) { var record = new ScrewFeederBatchRecord { ChangeTime = DateTime.Now.AddMinutes(-i * 20), BatchNumber = $"ScrewBatch-{i}", FeederNumber = i + 1 }; await RecordScrewFeederBatchAsync(record); } //生成一些生产记录的测试数据 for (int i = 0; i < 50; i++) { var record = new ProductionRecord { Id = Guid.NewGuid(), ProductName = "产品1", Category = (i % 2 == 0) ? "OK" : "NG", Quantity = 1, Timestamp = DateTime.Now.AddMinutes(-i * 5), UserName = "TestUser", Remark = $"TotalSeconds:1.10" }; await RecordProductionAsync(record); } //生成一些锁付结果的测试数据 for (int i = 0; i < 200; i++) { var result = new LockResult(i % 4, $"PN-{i % 5}", 10.5f, 0.3f, (i % 3 != 0)) { ProductName = "产品1", Timestamp = DateTime.Now.AddMinutes(-i * 3), }; await RecordLockResultAsync(result); } //生成一些相机拍照记录的测试数据 for (int i = 0; i < 20; i++) { try { var record = new PhotoCaptureRecord { Id = Guid.NewGuid(), RecipeName = "产品1", CameraName = $"下相机", ProductSN = $"SN-{i}", IsSuccess = (i % 2 == 0), CaptureTime = DateTime.Now.AddMinutes(-i * 7), ImagePath = $"/images/photo_{i}.jpg" }; await RecordPhotoCaptureAsync(record); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } } #endregion #region 抛料 /// /// 将一条抛料信息记录到数据库中 /// /// /// /// public async Task RecordThrowNumberAsync(ThrowRecord record) { if (record == null) throw new ArgumentNullException(nameof(record)); record.Id = record.Id == Guid.Empty ? Guid.NewGuid() : record.Id; await _db.Insertable(record).ExecuteCommandAsync(); } /// /// 获取抛料数量 /// /// public async Task GetThrowNumberAsync() { //今天的起始时间 var todayStart = DateTime.Now.Date; //今天的结束时间 var todayEnd = DateTime.Now.Date; todayEnd = todayEnd.AddDays(1); //按照时间范围查询抛料的总数 var total = await _db.Queryable() .Where(r => r.DayTime >= todayStart && r.DayTime < todayEnd) .SumAsync(r => r.ThrowCount); return total; } /// /// 重置抛料计数 /// /// public async Task ResetThrowNumberAsync() { //今天的起始时间 var todayStart = DateTime.Now.Date; //今天的结束时间 var todayEnd = DateTime.Now.Date; todayEnd = todayEnd.AddDays(1); await _db.Deleteable().Where(r => r.DayTime >= todayStart && r.DayTime < todayEnd).ExecuteCommandAsync(); } #endregion #region 产品和mes的绑定,自动切换产品 /// /// 添加绑定信息 /// /// /// /// /// public async Task InsertProWithMesAsync(ProductWithMes record, ProductModel product) { //record.Id = product.ID; record.ProductName = product.Name; await _db.Insertable(record).ExecuteCommandAsync(); } /// /// 删除绑定信息 /// /// /// /// public async Task DeleteProWithMesAsync(ProductModel product) { var v = await _db.Deleteable().Where(r => r.ProductName == product.Name).ExecuteCommandAsync(); return v; } /// /// 更新绑定信息 /// /// /// /// /// public async Task UpdateProductWithMesAsync(ProductWithMes record, ProductModel product) { if (record == null) throw new ArgumentNullException(nameof(record)); var v = await _db.Updateable(record).Where(r => r.ProductName == product.Name).ExecuteCommandAsync(); return v; } /// /// 查询绑定信息 /// /// /// public async Task QueryProductWithMesAsync(ProductModel product) { var total = await _db.Queryable().Where(r => r.ProductName == product.Name).FirstAsync(); return total; } /// /// 查询所有绑定信息 /// /// /// public async Task> QueryProductWithMesALLAsync() { var total = await _db.Queryable().ToListAsync(); return total; } /// /// 通过产品的编码和颜色查询产品的名称 /// /// /// public async Task QueryProductFromMesAsync(string code, string color) { var v = await _db.Queryable().Where(r => r.ProductCode == code && r.ProductColor == color).FirstAsync(); if (v == null || v.ProductName == null) { return ""; } return v.ProductName; } #endregion } }