1

我用 C# 制作了一个数据记录应用程序。它通过 SerialPort 类连接到 4 个 USB 传感器。我在每个字节上触发了数据接收事件阈值。当接收到数据时,程序会检查该字节是否是行尾。如果不是,则将输入添加到缓冲区。如果是行尾,则程序添加时间戳并将数据和时间戳写入文件(每个输入源获得一个专用文件)。

使用多个 COM 端口输入时会出现问题。我在输出文件中看到的是:

4 个文件中的任何一个:

...
timestamp1 value1
timestamp2 value2
timestamp3 value3
value4
value5
value6
timestamp7 value7
...

因此,看起来计算机速度不够快,无法在下一个值到达之前到达所有 4 个中断。我有充分的理由相信这是罪魁祸首,因为有时我会看到这样的输出:

...
timestamp value
timestamp value
value
val
timestamp ue
timestamp value
...

这可能是因为我将处理器亲和性更改为仅在 Core 2 上运行。我这样做是因为我使用的时间戳是用处理器周期计算的,所以我不能有多个时间引用,具体取决于哪个核心是跑步。我在下面放了一些代码片段;任何可能有助于删除时间戳的建议将不胜感激!

    public mainLoggerIO()
    {
        //bind to one cpu
        Process proc = Process.GetCurrentProcess();
        long AffinityMask = (long)proc.ProcessorAffinity;
        AffinityMask &= 0x0002; //use only the 2nd processor
        proc.ProcessorAffinity = (IntPtr)AffinityMask;

        //prevent any other threads from using core 2
        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
        Thread.CurrentThread.Priority = ThreadPriority.Highest;

        long frequency = Stopwatch.Frequency;
        Console.WriteLine("  Timer frequency in ticks per second = {0}",
            frequency);
        long nanosecPerTick = (1000L * 1000L * 1000L) / frequency;
        Console.WriteLine("  Timer is accurate within {0} nanoseconds",
            nanosecPerTick);

        if (Stopwatch.IsHighResolution)
            MessageBox.Show("High Resolution Timer Available");
        else
            MessageBox.Show("No High Resolution Timer Available on this Machine");

        InitializeComponent();
    }

等等。每个数据返回中断如下所示:

    private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    {
        //serialPort1.DataReceived = serialPort1_DataReceived;
        rawPort1Data = "";
        rawPort1Data = serialPort1.ReadExisting();
        this.Invoke((MethodInvoker)delegate { returnTextPort1(); });
    }

方法 returnTextPort#() 是:

    private void returnTextPort1()
    {
        if (string.Compare(saveFileName, "") == 0)//no savefile specified
            return;
        bufferedString += rawPort1Data;
        if(bufferedString.Contains('\r')){
            long timeStamp = DateTime.Now.Ticks;
            textBox2.AppendText(nicknames[0] + " " + timeStamp / 10000 + ", " + rawPort1Data);
            //write to file
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@saveFileName, true))
            {
                 file.WriteLine(nicknames[0] + " " + timeStamp / 10000 + ", " + rawPort1Data);//in Ms
            }
            bufferedString = "";
        }

    }
4

1 回答 1

1

一种更简洁的方法是ConcurrentQueue<T>在接收到的数据事件处理程序和将处理结果数据的单独线程之间使用一个。这样,事件处理程序可以立即返回,而不rawPort1是以完全非线程安全的方式修改数据,您可以转向线程安全的解决方案。

创建一个从并发队列中读取、写入文件并调用 UI 更改的线程。请注意,写入文件不应在 UI 线程上。

ConcurrentQueue<T>可以在您将实现的类T中捕获:端口号、接收到的数据以及接收到的时间戳。

另请注意,这DateTime.Now很少是正确的答案,对于大多数地方来说,当夏令时开始或结束时,它每年两次跳跃一个小时,而不是DateTime.UtcNow. 但是请注意,您似乎试图通过StopWatch代码获得的准确性都没有。

您不需要操纵进程或线程优先级来执行此操作:串行端口有一个缓冲区,只要您有效地处理它,您就不会错过数据。

于 2013-09-26T04:00:09.367 回答