You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TCP读取称重秤重量:连续称重求和实现及异常排查

问题分析与解决方案

异常直接原因

System.IndexOutOfRangeException的触发核心是读取到的响应不符合预期格式,导致Split(',')后的数组长度不足3,无法访问索引为2的元素。常见场景:

  • Read方法中netStream.DataAvailable为false时,直接返回空字符串,Trim()后仍为空,Split(',')得到仅含空元素的数组,访问索引2必然报错。
  • TCP是流式传输,单次netStream.Read可能只读取到部分响应,拆分后元素数量不足。

现有实现的核心问题

1. Read方法可靠性不足

  • 依赖DataAvailable判断数据是否到达,网络延迟会导致指令发送后数据未及时到达,直接返回空字符串。
  • 未处理TCP粘包/拆包问题,单次读取无法保证获取完整响应。

2. DoubleWeighing逻辑存在漏洞

  • 无响应格式校验,直接拆分访问数组索引,异常场景下必然崩溃。
  • 两次读取间隔未考虑称重秤的数值稳定时间,可能读取到未稳定的无效值。
  • 时间差判断粗糙,未处理两次读取过于接近(秤数值未更新)的情况。

修复与优化方案

1. 重构Read方法,保证完整响应读取

修改Read方法,通过循环读取直到检测到响应结束符(假设响应以\r\n结尾,与指令格式匹配):

private static string command = "READ\r\n";
private static readonly string responseTerminator = "\r\n";

public static string Read(NetworkStream netStream)
{
    // 发送指令
    byte[] commandBuffer = Encoding.UTF8.GetBytes(command);
    netStream.Write(commandBuffer, 0, commandBuffer.Length);

    StringBuilder receivedString = new StringBuilder();
    byte[] readingBuffer = new byte[256];
    int bytesRead;

    // 循环读取直到获取完整响应
    while (!receivedString.ToString().EndsWith(responseTerminator))
    {
        if (netStream.DataAvailable)
        {
            bytesRead = netStream.Read(readingBuffer, 0, readingBuffer.Length);
            receivedString.Append(Encoding.ASCII.GetString(readingBuffer, 0, bytesRead));
        }
        else
        {
            // 短暂等待避免CPU空转
            Thread.Sleep(10);
        }
    }

    // 返回去除结尾换行的有效内容
    return receivedString.ToString().TrimEnd('\r', '\n');
}

优化点:

  • 用StringBuilder替代字符串拼接,提升性能。
  • 循环读取直到检测到结束符,确保获取完整响应。
  • 空等待时加入短暂休眠,降低CPU占用。

2. 优化DoubleWeighing方法,增加格式校验

public static string DoubleWeighing(NetworkStream netStream)
{
    // 读取第一次重量并校验格式
    string firstResponse = Read(netStream);
    double firstWeight;
    if (!TryParseWeight(firstResponse, out firstWeight))
    {
        return firstResponse;
    }
    DateTime firstTime = DateTime.Now;

    // 等待秤数值稳定(可根据实际设备调整时长)
    Thread.Sleep(1000);

    // 读取第二次重量并校验格式
    string secondResponse = Read(netStream);
    double secondWeight;
    if (!TryParseWeight(secondResponse, out secondWeight))
    {
        return firstResponse;
    }
    DateTime secondTime = DateTime.Now;

    TimeSpan interval = secondTime - firstTime;

    // 优化判断逻辑:考虑秤精度误差,设置合理间隔范围
    if (Math.Abs(secondWeight - firstWeight) > 0.1
        && secondWeight > 0
        && interval.TotalSeconds >= 1 && interval.TotalSeconds <= 60)
    {
        double total = firstWeight + secondWeight;
        // 保持原格式的空格对齐
        string[] parts = secondResponse.Split(',');
        parts[2] = total.ToString("F1").PadLeft(4);
        return string.Join(",", parts);
    }
    else
    {
        return firstResponse;
    }
}

// 辅助方法:统一处理重量解析与格式校验
private static bool TryParseWeight(string response, out double weight)
{
    weight = 0;
    string[] parts = response.Split(',');
    // 预期格式为ST,NT,   0,kg,共4个元素
    if (parts.Length != 4)
    {
        return false;
    }
    // 去除空格后尝试转换
    return double.TryParse(parts[2].Trim(), out weight);
}

优化点:

  • 新增TryParseWeight辅助方法,统一格式校验与重量转换,避免数组越界。
  • 用精度阈值替代直接判等,适配称重秤的微小误差。
  • 增加两次读取的等待时间,确保数值稳定。
  • 替换重量值时保持原格式的空格对齐,输出格式统一。

3. Main方法调用示例

public static void Main(String[] args)
{
    TcpClient tcpClient = Connect.ConnectionSetup(IPAddress.Parse(ipAddress), portNumber);
    NetworkStream netStream = tcpClient.GetStream();

    while (true)
    {
        var timestamp = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss.fff");
        var response = DoubleWeighing(netStream);
        Console.WriteLine($"{timestamp} - {response}");
        Thread.Sleep(1000);
    }
}

总结

原问题的核心是Read方法无法保证获取完整合法的响应,加上DoubleWeighing缺乏格式校验导致数组越界。通过修复Read方法的可靠性,增加格式校验与逻辑优化,可解决异常问题并提升称重逻辑的合理性。

内容的提问来源于stack exchange,提问作者Daniel Gattesco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 10:17:29