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
}
}