1

私は現在、完全にc#でコーディングした盗難警報ソリューションに取り組んでいます。プログラムは、アラームセンサーが配線されたUSBシリアルポートベースのIOボードと通信します。DataReceivedイベント内からTextLog(終了を参照)というサブを呼び出さない限り、DataReceivedイベントがメインUIを更新できないという問題があります。奇妙なことに、ゾーン1のクエリからのDataReceivedイベントはメインUIを更新できますが、ゾーン2または3は更新できません。さらに、シリアルポートの書き込みが実行される行にブレークポイントを挿入すると、期待どおりに機能します。

これらのグローバル変数に言及する価値があります:

string ioCardRxString = "";

bool[] arrGlobalZoneStatus = new bool[4];

設定ファイルから設定を読み取ってシリアルポートを開きます(すべて正常に機能します)。

private void OpenIOComPort()
    {
        bool error = false;

        else
        {
            // Set the port's settings
            spIOCard.PortName = Settings1.Default.ioComPort;
            spIOCard.BaudRate = int.Parse(Settings1.Default.ioBaudRate);
            spIOCard.DataBits = int.Parse(Settings1.Default.ioDataBits);
            spIOCard.StopBits = (System.IO.Ports.StopBits)Enum.Parse(typeof(System.IO.Ports.StopBits), Settings1.Default.ioStopBits);
            spIOCard.Parity = (System.IO.Ports.Parity)Enum.Parse(typeof(System.IO.Ports.Parity), Settings1.Default.ioParity);
            spIOCard.Handshake = (System.IO.Ports.Handshake)Enum.Parse(typeof(System.IO.Ports.Handshake), Settings1.Default.ioHandshake);

            try
            {
                // Open the port
                spIOCard.Open();
            }
            catch (UnauthorizedAccessException) { error = true; }
            catch (System.IO.IOException) { error = true; }
            catch (ArgumentException) { error = true; }

            //On error, advise the user
            if (error)
            {
                MessageBox.Show("Could not open the I/O Board COM port.");                    
                globalIOCardError = true;
            }

            if (!error)
            {
                globalIOCardError = false;
                // Do Nothing
            }

        }
    }

タイマーは500msごとに実行され、シリアルポートに3つのコマンドを書き込みます。各コマンドは25msで区切られています(ハードウェアの制限)。これらのコマンドは、各アラームセンサーのステータスを判断するためにIOボードにクエリを実行します。

private void tmrAuditSensors_Tick(object sender, EventArgs e) 
    {
        try
        {
            if (globalIOCardError == false && Settings1.Default.disableSensorAudit == false)
            {
                if (Settings1.Default.zone1Armed == true)
                {
                    spIOCard.Write("~in01~");
                    System.Threading.Thread.Sleep(25);;
                }

                if (Settings1.Default.zone2Armed == true)
                {
                    spIOCard.Write("~in02~");
                    System.Threading.Thread.Sleep(25);;
                }

                if (Settings1.Default.zone3Armed == true)
                {
                    spIOCard.Write("~in03~");
                    System.Threading.Thread.Sleep(25);;
                }

                //Applicable results will appear on serial data received event
            }
        }
        catch
        {
            textLog("There was a problem writing to the serial port, check and restart app");
            emergencyHalt();
        }
    }

Serial Port DataReceivedイベントは、返された文字列を読み取り、ブール値のグローバル配列に書き込みます。そのゾーンが開いている場合(= 1)はTrue、閉じている場合(= 0)はFalseです。注:スター付きの線。

private void spIO_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    {
        ioCardRxString = spIOCard.ReadExisting();
        textLog(ioCardRxString); //*Cannot make it work without this

        if (ioCardRxString.Contains("in05=1") == true)
        {
            arrGlobalZoneStatus[1] = true;
        }

        if (ioCardRxString.Contains("in05=0") == true)
        {
            arrGlobalZoneStatus[1] = false;
        }

        if (ioCardRxString.Contains("in01=1") == true)
        {
            arrGlobalZoneStatus[2] = true;
        }

        if (ioCardRxString.Contains("in01=0") == true)
        {
            arrGlobalZoneStatus[2] = false;
        }

        if (ioCardRxString.Contains("in17=1") == true)
        {
            arrGlobalZoneStatus[3] = true;
        }

        if (ioCardRxString.Contains("in17=0") == true)
        {
            arrGlobalZoneStatus[3] = false;
        }

    }

これとは別に、別のタイマーが定期的に(250msごとに)各配列メンバーの内容をチェックし、それに応じてメインUIを色の変更とテキストの更新で更新します。

 private void tmrCheckZoneStatus_Tick(object sender, EventArgs e)
    {
        if (arrGlobalZoneStatus[1] == true)
        {
            button10.BackColor = System.Drawing.Color.Red;
            textLog(button10.Text + " was activated");

            if (globalFullAlarmSet || globalNightAlarmSet || globalDoorsAlarmSet)
            {
                this.BeginInvoke(new EventHandler(delegate { checkAndActivateRelays(1); }));
            }
        }

        if (arrGlobalZoneStatus[1] == false)
        {
            button10.BackColor = System.Drawing.Color.Gray;
        }

        if (arrGlobalZoneStatus[2] == true)
        {
            button11.BackColor = System.Drawing.Color.Red;
            textLog(button11.Text + " was activated"); ;

            if (globalFullAlarmSet || globalNightAlarmSet || globalDoorsAlarmSet)
            {
                this.BeginInvoke(new EventHandler(delegate { checkAndActivateRelays(2); }));
            }
        }

        if (arrGlobalZoneStatus[2] == false)
        {
            button11.BackColor = System.Drawing.Color.Gray;
        }

        if (arrGlobalZoneStatus[3] == true)
        {
            button12.BackColor = System.Drawing.Color.Red;
            textLog(button12.Text + " was activated");

            if (globalFullAlarmSet || globalNightAlarmSet || globalDoorsAlarmSet)
            {
                this.BeginInvoke(new EventHandler(delegate { checkAndActivateRelays(3); }));
            }
        }

        if (arrGlobalZoneStatus[3] == false)
        {
            button12.BackColor = System.Drawing.Color.Gray;
        }

    }

textLog sub:

public void textLog(string logEntry)
    {
        textLines++;
        try
        {
            if (this.txtLog.InvokeRequired)
            {
                ChangeTextCallback MethodCallback = new ChangeTextCallback(textLog);
                this.Invoke(MethodCallback, new object[] { logEntry });
            }
            else
            {
                if (!logEntry.Contains("?"))
                {
                    txtLog.Text = txtLog.Text + DateTime.Now + " >: " + logEntry + "\r\n";
                    txtLog.SelectionStart = txtLog.Text.Length;
                    txtLog.ScrollToCaret();

                    if (textLines > 3000)
                    {
                        txtLog.Clear();
                        textLines = 0;
                        textLog("Text log cleared");
                    }

                    System.IO.StreamWriter sw = new System.IO.StreamWriter(logFile, true);
                    try
                    {
                        sw.WriteLine(DateTime.Now + " >: " + logEntry);
                    }
                    catch (Exception ex)
                    {
                        //
                    }

                    sw.Close();
                }
            }
        }
        catch
        {
            //
        }


    }

どこかにinvoke/delegateを組み込む必要があると思いますが、初心者として頭を悩ませています。あなたの助けをいただければ幸いです。

ありがとう

4

1 に答える 1

3

すべてを読んだわけではありませんが、これで正しい道に進むことができます。

イベントは別のSerialPort.DataReceivedスレッドで発生します。データの受信を非同期で処理し、GUI で何かを行う必要がある場合は、次のようにします。

private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
    var port = (SerialPort)sender;
    string data = port.ReadExisting();

    UpdateGui(data);
}

private void UpdateGui(string data) {
    if (this.InvokeRequired) {
        this.Invoke(new Action( d => UpdateGui(d) ));
        return;
    }

    this.txtBox1.Text = data;
}

さて、それは言った....あなたは本当に使いたいDataReceivedですか? あなた(ホストコンピュータ)が外部ボードとのすべての通信を開始しているようです。その場合は、代わりに同期 (ブロッキング) 読み取りを使用することをお勧めします。

1. Write the request out the port
2. Call read() with the expected number of bytes
3. Process the reply.
于 2012-09-02T22:27:02.543 に答える