diff --git a/RIZO.Admin.WebApi/PLC/Service/PF6ScrewGunService.cs b/RIZO.Admin.WebApi/PLC/Service/PF6ScrewGunService.cs new file mode 100644 index 0000000..e493c88 --- /dev/null +++ b/RIZO.Admin.WebApi/PLC/Service/PF6ScrewGunService.cs @@ -0,0 +1,537 @@ +using System; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; + +namespace RIZO.Admin.WebApi.PLC.Service +{ + /// + /// PF6螺丝枪基础通讯服务(核心功能:TCP连接、心跳、全报文发收测试) + /// 适配PF6开放协议V2.5版本,支持所有已定义报文的批量发送与调试 + /// + public class PF6ScrewGunService : BackgroundService + { + // TCP通讯核心对象 + private TcpClient _tcpClient; + private NetworkStream _networkStream; + + // 螺丝枪配置(与协议文档一致) + private readonly string _screwGunIp = "192.168.1.145"; // 控制器IP(可配置) + private readonly int _screwGunPort = 4545; // 协议默认端口(文档明确) + private readonly TimeSpan _heartbeatInterval = TimeSpan.FromSeconds(10); // 心跳间隔(文档要求10秒) + private readonly TimeSpan _reconnectInterval = TimeSpan.FromSeconds(8); // 重连间隔(延长避免频繁重试) + private readonly TimeSpan _testCommandInterval = TimeSpan.FromSeconds(15); // 测试指令间隔(降低频率) + private readonly TimeSpan _batchTestInterval = TimeSpan.FromSeconds(30); // 全报文批量测试间隔 + + // 协议基础常量(严格遵循PF6开放协议V2.5文档) + private const string MessageEndFlag = "00"; // 报文结束标识(文档第1页明确) + private const int MessageTotalLength = 20; // 发送报文固定长度(文档"字符总长20字节") + private const string HeartbeatMid = "9999"; // 心跳指令MID(文档测试页面确认) + private const string TestTightenMid = "0610"; // 测试拧紧指令MID(文档功能按钮确认) + private const string HandshakeMid = "0001"; // 握手指令MID(文档初始连接流程) + private const string PsetQueryMid = "0005"; // 参数组查询MID(文档PSET查询功能) + private const string TightenResultMid = "0620"; // 拧紧结果查询MID(文档明确) + private const string ProtocolVersion = "3030"; // 协议版本号(文档报文示例) + private const string MessagePrefix = "303020"; // 报文固定前缀(文档2017-12-1交互示例) + private const string FillChar = "20"; // 填充字符(文档"20填满字符总长") + private const int LinkTimeout = 1000; // 链路超时(文档配置页面1000ms) + private const int AppCommandTimeout = 1000; // 应用指令超时(文档配置页面1000ms) + private const int MaxRetries = 3; // 最大重试次数(文档配置页面3次) + private const int ReceiveBufferSize = 4096; // 接收缓冲区扩大(文档响应报文可能超过1024字节) + + /// + /// 服务核心执行逻辑 + /// + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] PF6螺丝枪通讯服务开始启动..."); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 服务支持报文:握手(0001)、心跳(9999)、测试拧紧(0610)、参数组查询(0005)、拧紧结果查询(0620)"); + _tcpClient = new TcpClient(); + + // 服务主循环:断线自动重连(响应停止指令) + while (!stoppingToken.IsCancellationRequested) + { + try + { + // 1. 建立TCP连接(带重试) + bool connectSuccess = await ConnectWithRetryAsync(stoppingToken); + if (!connectSuccess) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 连接重试失败,等待下一轮重连"); + await Task.Delay(_reconnectInterval, stoppingToken); + continue; + } + + // 2. 发送握手报文并确认(协议必须步骤) + bool handshakeSuccess = await PerformHandshakeAsync(stoppingToken); + if (!handshakeSuccess) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 握手失败,关闭连接准备重连"); + DisconnectFromScrewGun(); + await Task.Delay(_reconnectInterval, stoppingToken); + continue; + } + + // 3. 并行执行:心跳维持 + 全报文批量测试 + 常规测试指令 + var taskArray = new[] + { + KeepHeartbeatContinuousAsync(stoppingToken), + SendBatchAllMessagesTestAsync(stoppingToken), + SendTestCommandAndReceiveResultAsync(stoppingToken) + }; + + await Task.WhenAny(taskArray); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 核心任务退出,准备重连"); + } + catch (OperationCanceledException) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] PF6螺丝枪通讯服务收到停止指令,正在退出..."); + break; + } + catch (Exception ex) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] PF6螺丝枪通讯服务运行异常,准备重连:{ex.Message}"); + } + finally + { + DisconnectFromScrewGun(); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 当前连接已断开,等待重连..."); + if (!stoppingToken.IsCancellationRequested) + { + await Task.Delay(_reconnectInterval, stoppingToken); + } + } + } + + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] PF6螺丝枪通讯服务已停止"); + } + + /// + /// 带重试机制的TCP连接(遵循协议重试次数配置) + /// + private async Task ConnectWithRetryAsync(CancellationToken stoppingToken) + { + int retryCount = 0; + while (retryCount < MaxRetries && !stoppingToken.IsCancellationRequested) + { + try + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 第{retryCount + 1}次尝试连接螺丝枪:{_screwGunIp}:{_screwGunPort}"); + + // 重置TcpClient状态 + if (_tcpClient.Connected) + { + _tcpClient.Close(); + } + _tcpClient?.Dispose(); + _tcpClient = new TcpClient(); + _tcpClient.ReceiveTimeout = LinkTimeout; + _tcpClient.SendTimeout = LinkTimeout; + + // 建立连接(设置连接超时,避免无限等待) + var connectTask = _tcpClient.ConnectAsync(_screwGunIp, _screwGunPort); + var timeoutTask = Task.Delay(LinkTimeout * 2, stoppingToken); + var completedTask = await Task.WhenAny(connectTask, timeoutTask); + + if (completedTask == timeoutTask) + { + throw new TimeoutException("连接超时(超过2000ms)"); + } + + // 初始化网络流(配置超时参数与协议一致) + _networkStream = _tcpClient.GetStream(); + _networkStream.ReadTimeout = AppCommandTimeout; + _networkStream.WriteTimeout = AppCommandTimeout; + + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 螺丝枪TCP连接成功!(链路超时:{LinkTimeout}ms,指令超时:{AppCommandTimeout}ms)"); + return true; + } + catch (Exception ex) + { + retryCount++; + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 第{retryCount}次连接失败:{ex.Message}"); + if (retryCount < MaxRetries) + { + await Task.Delay(1000 * retryCount, stoppingToken); // 重试间隔递增(1s→2s→3s) + } + } + } + return false; + } + + /// + /// 协议握手流程(发送MID=0001,等待控制器确认) + /// + private async Task PerformHandshakeAsync(CancellationToken stoppingToken) + { + try + { + // 构建握手报文(严格遵循文档格式) + var handshakeMessage = BuildStandardMessage(HandshakeMid); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 准备发送握手报文(MID=0001),报文内容:{handshakeMessage}"); + + // 发送握手报文(带重试) + bool sendSuccess = await SendMessageWithRetryAsync(handshakeMessage, stoppingToken, "握手"); + if (!sendSuccess) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 握手报文发送失败(已重试{MaxRetries}次)"); + return false; + } + + // 接收握手响应(文档响应报文可能较长,需完整接收) + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 等待握手响应(超时:{AppCommandTimeout}ms)"); + var handshakeResponse = await ReceiveMessageAsync(stoppingToken, "握手"); + + // 校验响应有效性 + if (string.IsNullOrEmpty(handshakeResponse)) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 握手响应为空,无效"); + return false; + } + if (!handshakeResponse.EndsWith(MessageEndFlag)) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 握手响应未包含结束标识({MessageEndFlag}),响应内容:{handshakeResponse}"); + return false; + } + + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 握手成功!响应报文:{handshakeResponse}(长度:{handshakeResponse.Length})"); + return true; + } + catch (Exception ex) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 握手过程异常:{ex.Message}"); + return false; + } + } + + /// + /// 持续发送心跳(协议要求10秒间隔,无响应需重连) + /// + private async Task KeepHeartbeatContinuousAsync(CancellationToken stoppingToken) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 心跳任务启动(间隔:{_heartbeatInterval.TotalSeconds}秒),仅确保发送成功,不强制要求响应"); + while (!stoppingToken.IsCancellationRequested && _tcpClient.Connected) + { + try + { + var heartbeatMessage = BuildStandardMessage(HeartbeatMid); + bool sendSuccess = await SendMessageWithRetryAsync(heartbeatMessage, stoppingToken, "心跳"); + if (sendSuccess) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 心跳报文发送成功(MID=9999),报文内容:{heartbeatMessage}"); + } + else + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 心跳报文发送失败(已重试{MaxRetries}次),终止心跳任务"); + break; + } + + await Task.Delay(_heartbeatInterval, stoppingToken); + } + catch (Exception ex) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 心跳交互异常,终止心跳任务:{ex.Message}"); + break; + } + } + } + + /// + /// 常规测试指令发送与响应接收(MID=0610) + /// + private async Task SendTestCommandAndReceiveResultAsync(CancellationToken stoppingToken) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 常规测试任务启动(间隔:{_testCommandInterval.TotalSeconds}秒,MID=0610)"); + while (!stoppingToken.IsCancellationRequested && _tcpClient.Connected) + { + try + { + // 构建测试拧紧指令(额外数据"0101"为测试参数) + var testMessage = BuildStandardMessage(TestTightenMid, "0101"); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 准备发送常规测试指令(MID=0610),报文内容:{testMessage}"); + + // 发送指令(带重试) + bool sendSuccess = await SendMessageWithRetryAsync(testMessage, stoppingToken, "常规测试拧紧"); + if (!sendSuccess) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 常规测试指令发送失败(已重试{MaxRetries}次),延迟5秒重试"); + await Task.Delay(5000, stoppingToken); + continue; + } + + // 接收响应(文档响应可能包含多个字段,需完整读取) + var testResponse = await ReceiveMessageAsync(stoppingToken, "常规测试拧紧"); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 常规测试指令响应成功!响应内容:{testResponse}(长度:{testResponse.Length})"); + + await Task.Delay(_testCommandInterval, stoppingToken); + } + catch (Exception ex) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 常规测试指令交互异常,延迟后重试:{ex.Message}"); + await Task.Delay(3000, stoppingToken); + } + } + } + + /// + /// 批量发送所有已定义报文(用于完整调试,包含所有功能) + /// + private async Task SendBatchAllMessagesTestAsync(CancellationToken stoppingToken) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 全报文批量测试任务启动(间隔:{_batchTestInterval.TotalSeconds}秒)"); + while (!stoppingToken.IsCancellationRequested && _tcpClient.Connected) + { + try + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] ============== 开始全报文批量测试 =============="); + + // 1. 参数组查询(MID=0005,查询全部参数组,额外数据为空) + await SendSingleMessageWithResponseAsync(PsetQueryMid, "", "参数组查询(MID=0005)", stoppingToken); + await Task.Delay(2000, stoppingToken); // 间隔2秒,避免报文拥堵 + + // 2. 参数组查询(MID=0005,查询指定参数组ID=01) + await SendSingleMessageWithResponseAsync(PsetQueryMid, "01", "参数组查询(MID=0005,指定ID=01)", stoppingToken); + await Task.Delay(2000, stoppingToken); + + // 3. 拧紧结果查询(MID=0620,查询全部结果) + await SendSingleMessageWithResponseAsync(TightenResultMid, "", "拧紧结果查询(MID=0620)", stoppingToken); + await Task.Delay(2000, stoppingToken); + + // 4. 拧紧结果查询(MID=0620,查询指定SN=0001) + await SendSingleMessageWithResponseAsync(TightenResultMid, "0001", "拧紧结果查询(MID=0620,指定SN=0001)", stoppingToken); + await Task.Delay(2000, stoppingToken); + + // 5. 测试拧紧(MID=0610,额外参数=0202) + await SendSingleMessageWithResponseAsync(TestTightenMid, "0202", "测试拧紧(MID=0610,参数=0202)", stoppingToken); + + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] ============== 全报文批量测试结束 ==============\n"); + await Task.Delay(_batchTestInterval, stoppingToken); + } + catch (Exception ex) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 全报文批量测试异常:{ex.Message}\n"); + await Task.Delay(10000, stoppingToken); + } + } + } + + /// + /// 发送单个报文并接收响应(封装通用逻辑,简化批量测试) + /// + private async Task SendSingleMessageWithResponseAsync(string mid, string extraData, string messageDesc, CancellationToken stoppingToken) + { + try + { + // 构建报文 + var message = BuildStandardMessage(mid, extraData); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 准备发送 {messageDesc},报文内容:{message}"); + + // 发送报文 + bool sendSuccess = await SendMessageWithRetryAsync(message, stoppingToken, messageDesc); + if (!sendSuccess) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {messageDesc} 发送失败(已重试{MaxRetries}次)"); + return; + } + + // 接收响应 + var response = await ReceiveMessageAsync(stoppingToken, messageDesc); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {messageDesc} 响应成功!响应内容:{response}(长度:{response.Length})"); + } + catch (Exception ex) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {messageDesc} 交互异常:{ex.Message}"); + } + } + + /// + /// 构建标准PF6协议报文(严格遵循文档格式要求,增加参数校验) + /// + private string BuildStandardMessage(string mid, string extraData = "") + { + // 前置严格校验,避免无效报文 + if (string.IsNullOrEmpty(mid)) + { + throw new ArgumentNullException(nameof(mid), "指令MID不能为空(PF6协议要求4位字符)"); + } + if (mid.Length != 4) + { + throw new ArgumentException($"MID必须为4位字符(当前MID:{mid},长度:{mid.Length})", nameof(mid)); + } + // 额外数据避免过长,防止填充后超出长度限制 + if (!string.IsNullOrEmpty(extraData) && extraData.Length > 8) + { + throw new ArgumentException("额外数据长度不能超过8位(避免超出报文总长度限制)", nameof(extraData)); + } + + var messageBuilder = new StringBuilder(); + // 拼接格式:固定前缀 + MID + 版本号 + 额外数据 + 填充符 + 结束标识 + messageBuilder.Append(MessagePrefix) + .Append(mid) + .Append(ProtocolVersion) + .Append(extraData); + + // 补全填充字符(确保总长达到20-2=18字节,预留结束标识位置) + while (messageBuilder.Length < MessageTotalLength - MessageEndFlag.Length) + { + messageBuilder.Append(FillChar); + } + + // 追加结束标识 + messageBuilder.Append(MessageEndFlag); + + // 长度校验(严格保证20字节,协议强制要求) + if (messageBuilder.Length != MessageTotalLength) + { + throw new InvalidOperationException($"构建的报文长度不符合PF6协议要求,预期{MessageTotalLength}位,实际{messageBuilder.Length}位,报文内容:{messageBuilder.ToString()}"); + } + + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 报文构建成功(MID={mid}),长度:{messageBuilder.Length}位,内容:{messageBuilder.ToString()}"); + return messageBuilder.ToString(); + } + + /// + /// 发送报文(带重试机制,遵循协议重试次数,增加报文描述日志) + /// + private async Task SendMessageWithRetryAsync(string message, CancellationToken stoppingToken, string messageDesc) + { + int retryCount = 0; + while (retryCount < MaxRetries && !stoppingToken.IsCancellationRequested && _tcpClient.Connected) + { + try + { + var sendData = Encoding.ASCII.GetBytes(message); + await _networkStream.WriteAsync(sendData, 0, sendData.Length, stoppingToken); + await _networkStream.FlushAsync(stoppingToken); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {messageDesc} 报文发送成功(字节数:{sendData.Length})"); + return true; + } + catch (Exception ex) + { + retryCount++; + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {messageDesc} 报文发送重试{retryCount}次:{ex.Message}"); + if (retryCount < MaxRetries) + { + await Task.Delay(500, stoppingToken); // 每次重试间隔500ms + } + } + } + return false; + } + + /// + /// 接收报文(支持动态长度,处理协议响应格式,增加报文描述日志) + /// + private async Task ReceiveMessageAsync(CancellationToken stoppingToken, string messageDesc) + { + if (!_tcpClient.Connected || _networkStream == null) + { + throw new InvalidOperationException("未与螺丝枪建立有效TCP连接,无法接收报文"); + } + + var receiveBuffer = new byte[ReceiveBufferSize]; + var totalBytesRead = 0; + var responseBuilder = new StringBuilder(); + + try + { + // 循环读取直到收到结束标识或超时 + while (!stoppingToken.IsCancellationRequested) + { + var bytesRead = await _networkStream.ReadAsync(receiveBuffer, 0, receiveBuffer.Length, stoppingToken); + if (bytesRead == 0) + { + throw new SocketException((int)SocketError.ConnectionReset); + } + + totalBytesRead += bytesRead; + var segment = Encoding.ASCII.GetString(receiveBuffer, 0, bytesRead).TrimEnd('\0', ' '); + responseBuilder.Append(segment); + + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 接收{messageDesc}响应片段,读取字节数:{bytesRead},累计字节数:{totalBytesRead},片段内容:{segment}"); + + // 检查是否包含结束标识(协议结束标志"00") + if (responseBuilder.ToString().EndsWith(MessageEndFlag)) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 接收{messageDesc}响应完成(已检测到结束标识{MessageEndFlag})"); + break; + } + + // 防止缓冲区溢出 + if (totalBytesRead >= ReceiveBufferSize) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 接收{messageDesc}响应超过缓冲区上限({ReceiveBufferSize}字节),停止读取"); + break; + } + } + + var finalResponse = responseBuilder.ToString().TrimEnd('\0', ' '); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {messageDesc} 响应处理完成,最终内容长度:{finalResponse.Length}"); + return finalResponse; + } + catch (TimeoutException) + { + // 心跳响应可能超时,返回已接收数据 + var partialData = responseBuilder.ToString().TrimEnd('\0', ' '); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 接收{messageDesc}响应超时,已接收部分数据:{partialData}(长度:{partialData.Length})"); + return partialData; + } + } + + /// + /// 构建PF6协议参数组查询报文(MID=0005,封装专用方法,提高可读性) + /// + private string BuildPsetQueryMessage(string psetId = "") + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 构建参数组查询报文,参数组ID:{(string.IsNullOrEmpty(psetId) ? "全部" : psetId)}"); + return BuildStandardMessage(PsetQueryMid, psetId); + } + + /// + /// 构建PF6协议拧紧结果查询报文(MID=0620,封装专用方法,提高可读性) + /// + private string BuildTightenResultQueryMessage(string snCode = "") + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 构建拧紧结果查询报文,产品SN:{(string.IsNullOrEmpty(snCode) ? "全部" : snCode)}"); + return BuildStandardMessage(TightenResultMid, snCode); // 绑定正确MID=0620,支持自定义SN码 + } + + /// + /// 断开连接并释放资源(增加详细日志,避免资源泄漏) + /// + private void DisconnectFromScrewGun() + { + try + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 准备断开与螺丝枪的连接,释放相关资源"); + + _networkStream?.Dispose(); + _networkStream = null; + + if (_tcpClient.Connected) + { + _tcpClient.Close(); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] TCP连接已主动关闭"); + } + _tcpClient?.Dispose(); + + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 资源释放完成"); + } + catch (Exception ex) + { + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 断开连接时发生异常:{ex.Message}"); + } + } + + /// + /// 释放资源(重写BackgroundService的Dispose方法) + /// + public override void Dispose() + { + DisconnectFromScrewGun(); + base.Dispose(); + } + } +} \ No newline at end of file diff --git a/RIZO.Admin.WebApi/Program.cs b/RIZO.Admin.WebApi/Program.cs index 0b46292..4f5212e 100644 --- a/RIZO.Admin.WebApi/Program.cs +++ b/RIZO.Admin.WebApi/Program.cs @@ -115,6 +115,9 @@ builder.Services.AddLocalization(options => options.ResourcesPath = ""); //builder.Services.AddHostedService(); //builder.Services.AddHostedService(); +//螺丝枪服务注册 +//builder.Services.AddHostedService(); + // 在应用程序启动的最开始处调用 var app = builder.Build(); InternalApp.ServiceProvider = app.Services;