using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Seyounth.Auto.Hs.Runtime.Balances { public class SilkBalance : IBalance { public int Id => 1; //扫到码后的业务逻辑 //扫到码后的业务逻辑 private readonly static object _lock = new object(); private List _weights = new List(); public event Action OnScanned; private DeviceConnectConfig DeviceConnectConfig; private TcpClient _tcp; private NetworkStream _stream; private readonly IServiceProvider serviceProvider; private readonly IEnumerable balanceEventHandles; private readonly ILogger logger; private CancellationTokenSource _receiveDataCancellationToken; private bool tag; public SilkBalance(IConfiguration configuration, IServiceProvider serviceProvider, IEnumerable balanceEventHandles, ILogger logger) { /* * 从配置文件获取扫码枪IP/端口 */ var configs = configuration.GetSection("Balance").Get(); if (configs is not null && configs.Length > 0) { DeviceConnectConfig = configs.FirstOrDefault(e => e.Id == Id); } this.serviceProvider = serviceProvider; this.balanceEventHandles = balanceEventHandles; this.logger = logger; } public event Func OnWeightChanged; /// /// 连接体重秤 /// /// /// public async Task ConnectAsync() { while (true) { if (_tcp is not null) { try { _tcp.Dispose(); } catch (Exception ex) { /* * 忽略释放异常 */ } } _tcp = new TcpClient(); try { await _tcp.ConnectAsync(new IPEndPoint(IPAddress.Parse(DeviceConnectConfig.IP), DeviceConnectConfig.Port)); if (_stream is not null) _stream.Dispose(); _stream = _tcp.GetStream(); logger.LogInformation("体重秤连接成功"); _receiveDataCancellationToken?.Cancel(); _receiveDataCancellationToken = new CancellationTokenSource(); ReceiveData(_receiveDataCancellationToken.Token); break; } catch (Exception ex) when (ex.GetBaseException() is OperationCanceledException oce || ex.GetBaseException() is SocketException socketException) { await Task.Delay(1000 * 5); logger.LogError(ex.GetBaseException(), "体重秤连接失败,尝试重连中"); continue; } catch (Exception ex) { logger.LogError(ex, "体重秤连接失败"); } } } /// /// 接收扫码枪数据 /// /// private async Task ReceiveData(CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { //一次最大读取1M byte[] buffer = new byte[1024]; /* readCount :实际读取字节数*/ var readCount = _stream.Read(buffer, 0, buffer.Length); if (readCount <= 0) throw new SocketException(); /* UTF-8 编码获取字符串*/ var result = Encoding.UTF8.GetString(buffer, 0, readCount); /* 异步执行所有事件避免事件中报错未能执行完成所有事件而阻塞下次接收 */ var matchs = Regex.Match(result, "-?\\d+(.?\\d+)?"); var weight = 0m; if (!string.IsNullOrWhiteSpace(matchs.Value)) weight = Convert.ToDecimal(matchs.Value); ; //logger.LogInformation("YYYYYYYYYYYYYYYYYYYY"); //1.刚开机,重量为 0 if (weight <= 0) { tag = true; continue; } ; if (MonitorWeightStability(_weights, weight)) { if (tag) { //logger.LogInformation($"丝锭称接收数据:{result},字节数:{readCount}"); using (var scope = serviceProvider.CreateAsyncScope()) { var handles = scope.ServiceProvider.GetService>(); foreach (var item in handles) { await item.ExecAsync(weight, Id); } } tag = false; } } } } catch (Exception ex) when (ex.GetBaseException() is OperationCanceledException oce || ex.GetBaseException() is SocketException socketException) { logger.LogError(ex.GetBaseException(), "体重秤连接失败,尝试重连中"); await ConnectAsync(); } catch (Exception ex) { logger.LogError(ex, "体重秤连接失败"); } } /// /// 断开连接 /// /// /// public Task DisconnectAsync() { _receiveDataCancellationToken?.Cancel(); _tcp?.Dispose(); return Task.CompletedTask; } /// /// 由于物体刚上称时重量浮动的原因,体重称上传10次的重量一样视为稳定 /// /// /// /// public bool MonitorWeightStability(List weightList, decimal weight) { //由于电子秤上传重量频率较高故加锁保证 lock (_lock) { weightList.Add(weight); if (weightList.Count > 10) { for (int i = weightList.Count - 1; i > 9; i--) weightList.RemoveAt(i); } if (weightList.Count == 10) { var result = weightList.Distinct().Count() == 1; weightList.Clear(); return result; } return false; } } } }