using Model; using Model.Dto.Config; using Model.Entity.Config; using Model.Mapper; using ORM; using Service.Interface.Config; using SqlSugar; using System.IO.Ports; using System.Net; using System.Net.Sockets; namespace Service.Implement.Config { /// /// 网关管理 服务实现 /// public class GatewayService : IGatewayService { public async Task>> GetPagedAsync(int pageIndex, int pageSize, RefAsync total, string? keyword = null, int? protocolType = null) { try { var list = await SqlSugarContext.DbContext.Queryable() .Where(x => x.IsDel == 0) .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.Code.Contains(keyword!) || x.Name.Contains(keyword!)) .WhereIF(protocolType.HasValue, x => x.ProtocolType == (IotDeviceProtocolEnum)protocolType!.Value) .OrderBy(x => x.CreateTime, OrderByType.Desc) .ToPageListAsync(pageIndex, pageSize, total); return Result>.Success(list.ToDtoList()); } catch (Exception ex) { return Result>.Error("查询网关列表失败", ex); } } public async Task> GetByIdAsync(long id) { try { var entity = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); if (entity == null) return Result.Error("网关不存在"); return Result.Success(entity.ToDto()); } catch (Exception ex) { return Result.Error("查询网关详情失败", ex); } } public async Task> AddAsync(GatewayDto dto) { try { // 编码唯一校验 var exists = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Code == dto.Code && x.IsDel == 0).AnyAsync(); if (exists) return Result.Error($"网关编码「{dto.Code}」已存在"); var entity = dto.ToEntity(); entity.Id = 0; // 雪花ID自动生成 var id = await SqlSugarContext.DbContext.Insertable(entity).ExecuteReturnSnowflakeIdAsync(); entity.Id = id; return Result.Success(entity.ToDto()); } catch (Exception ex) { return Result.Error("新增网关失败", ex); } } public async Task> UpdateAsync(GatewayDto dto) { try { if (!long.TryParse(dto.Id, out var id) || id <= 0) return Result.Error("网关Id无效"); var entity = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); if (entity == null) return Result.Error("网关不存在"); // 编码唯一校验(排除自身) var codeExists = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Code == dto.Code && x.Id != id && x.IsDel == 0).AnyAsync(); if (codeExists) return Result.Error($"网关编码「{dto.Code}」已存在"); var newEntity = dto.ToEntity(); newEntity.Id = id; await SqlSugarContext.DbContext.Updateable(newEntity) .IgnoreColumns(x => new { x.CreateTime, x.IsDel }) .ExecuteCommandAsync(); return Result.Success(newEntity.ToDto()); } catch (Exception ex) { return Result.Error("修改网关失败", ex); } } public async Task DeleteAsync(long id) { try { var entity = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); if (entity == null) return Result.Error("网关不存在"); // 软删除网关 await SqlSugarContext.DbContext.Updateable() .SetColumns(x => x.IsDel == 1) .Where(x => x.Id == id).ExecuteCommandAsync(); // 该网关下设备的 GatewayId 置 0(不级联删除设备) await SqlSugarContext.DbContext.Updateable() .SetColumns(x => x.GatewayId == 0) .Where(x => x.GatewayId == id).ExecuteCommandAsync(); return Result.Success(); } catch (Exception ex) { return Result.Error("删除网关失败", ex); } } /// /// 测试网关连接:TCP 尝试 TcpClient.ConnectAsync 3s 超时,串口尝试 SerialPort.Open, /// 成功则 OnlineStatus=Online+更新 LastConnectedTime,失败则 OnlineStatus=Fault+记录 LastError /// public async Task> TestConnectionAsync(long id) { try { var entity = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); if (entity == null) return Result.Error("网关不存在"); bool success = false; string message = ""; var now = DateTime.Now; switch (entity.ProtocolType) { case IotDeviceProtocolEnum.ModbusTcp: case IotDeviceProtocolEnum.Tcp: case IotDeviceProtocolEnum.S7: // TCP 类:尝试连接 IP:Port if (string.IsNullOrWhiteSpace(entity.Host) || !entity.Port.HasValue) { message = "未配置 IP 地址或端口号"; } else { (success, message) = await TestTcpAsync(entity.Host, entity.Port!.Value, 3000); } break; case IotDeviceProtocolEnum.ModbusRtu: case IotDeviceProtocolEnum.Serial: // 串口类:尝试打开串口 if (string.IsNullOrWhiteSpace(entity.ComPort)) { message = "未配置串口号"; } else { (success, message) = TestSerial(entity.ComPort, entity.BaudRate ?? 9600); } break; default: message = $"暂不支持 {entity.ProtocolType} 协议的连接测试"; break; } // 更新网关状态 await SqlSugarContext.DbContext.Updateable() .SetColumns(x => new GatewayEntity { OnlineStatus = success ? (byte)1 : (byte)3, LastConnectedTime = success ? now : x.LastConnectedTime, LastError = success ? null : message }) .Where(x => x.Id == id).ExecuteCommandAsync(); return Result.Success(new GatewayTestResultDto { Success = success, Message = success ? $"连接成功({entity.Host}:{entity.Port ?? 0})" : message }); } catch (Exception ex) { return Result.Error("测试连接失败", ex); } } public async Task>> GetOptionsAsync() { try { var list = await SqlSugarContext.DbContext.Queryable() .Where(x => x.IsDel == 0 && x.IsEnabled) .OrderBy(x => x.Code) .ToListAsync(); return Result>.Success(list.ToOptionDtoList()); } catch (Exception ex) { return Result>.Error("查询网关选项失败", ex); } } #region 私有:连接测试方法 private static async Task<(bool success, string message)> TestTcpAsync(string host, int port, int timeoutMs) { try { using var client = new TcpClient(); var cts = new System.Threading.CancellationTokenSource(timeoutMs); await client.ConnectAsync(host, port, cts.Token); return (true, ""); } catch (OperationCanceledException) { return (false, $"连接超时({timeoutMs / 1000}s):{host}:{port}"); } catch (SocketException ex) { return (false, $"连接失败:{host}:{port}({ex.Message})"); } catch (Exception ex) { return (false, $"连接异常:{ex.Message}"); } } private static (bool success, string message) TestSerial(string comPort, int baudRate) { try { using var sp = new SerialPort(comPort, baudRate); sp.Open(); sp.Close(); return (true, ""); } catch (UnauthorizedAccessException) { return (false, $"串口 {comPort} 被占用"); } catch (Exception ex) { return (false, $"打开串口 {comPort} 失败:{ex.Message}"); } } #endregion } }