Avatar
Michaal.K
Člen
Avatar
Michaal.K:

Ahoj, tak bych zase potřeboval poradit :-)
Potřeboval bych vytvořit nějaký timeout při sériové komunikaci. Mělo by to fungovat, tak že já něco vyšlu a protějšek by měl odpovědět. Pokud do nějaké doby neodpoví, vyhodím chybu komunikace. Problém je v tom, že mám na příjem vytvořenou událost, která je vyvolána pokud se něco zapíše do přijímacího stringu. Viz. kód níže kde je celá třída communication.

public List<SerialPort> serialPorts = new List<SerialPort>();                       // List vsech dostupnych seriovych portu i s nastavenim (rychlost, partita, ...)
        private bool connected;                                                             // Info o otevreni nebo zavreni nejakeho serioveho portu

        public delegate void DataReceivedEventHandler(SerialPort comPort, string data);     // Deklarace delagata, ktery bude slouzit jako predpis metody predstavujici reakci na udalost (handler)
        public event DataReceivedEventHandler OnSerialDataReceived;                         // Deklarace udalosti

        // Trida Communication jako Singleton, abych mohl sdilet jeden objekt pro celou aplikaci
        // Data, ktera jsou v Communication budou sdilena mezi formy
        private static Communication instance;
        public static Communication Instance
        {
            get
            {
                if (instance == null)
                    instance = new Communication();
                return instance;
            }
        }

        // Konstruktor tridy Communication
        private Communication()
        {
            // Defaultni nastaveni vsech seriovych portu, ktere jsou v pocitaci
            // BaudRate: 19200, Parity: none, DataBits = 8, StopBits = 1
            var portNames = SerialPort.GetPortNames();
            foreach (var port in portNames)
            {
                /*
                comPorts.Add(new Port() { PortName = port, BaudRate = 19200, Parity = Parity.None, DataBits = 8, StopBits = StopBits.One });
                SerialPort sp = new SerialPort(comPorts[i].PortName, comPorts[i].BaudRate, comPorts[i].Parity, comPorts[i].DataBits, comPorts[i].StopBits);
                */

                SerialPort comPort = new SerialPort(port, 19200, Parity.None, 8, StopBits.One);

                comPort.Handshake = Handshake.None;
                comPort.ReadTimeout = 500;
                comPort.WriteTimeout = 500;
                comPort.DtrEnable = true;
                comPort.RtsEnable = true;

                comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);       // prida udalost, ktera je vyvolana pokud dojde k zapisu do prijimaciho stringu

                serialPorts.Add(comPort);           // Pridani parametru com portu do listu serialPorts

                //MessageBox.Show(comPorts[i].PortName.ToString() + " " + comPorts[i].BaudRate.ToString() + " " + comPorts[i].DataBits.ToString());
            }
            // Serazeni com portu v listu podle cisla portu
            serialPorts.Sort(delegate(SerialPort x, SerialPort y)
            {
                string[] data2 = x.PortName.Split('M');         // Rozdelim zadany retezec na cislo com portu a zbytek textu
                int cisPortuX = Convert.ToInt32(data2[1]);      // Cislo com portu v int
                data2 = y.PortName.Split('M');                  // Rozdelim zadany retezec na cislo com portu a zbytek textu
                int cisPortuY = Convert.ToInt32(data2[1]);      // Cislo com portu v int
                return cisPortuX.CompareTo(cisPortuY);
            });
        }

        /// <summary>
        /// Otevre seriovou linku
        /// </summary>
                public void Connected()
                {
            foreach (var comPort in serialPorts)            // Otevru vsechny dostupne porty
            {
                try
                {
                    if (!comPort.IsOpen)
                        comPort.Open();

                    MessageBox.Show(comPort.PortName + " otevren!");
                    //sp.Write("Test");
                    this.connected = true;                  // Nastavim na true pokud je nejaky seriovy port otevren
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Port nelze otevřít " + ex.Message, MainForm.appName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
                }

        /// <summary>
                /// Zavre seriovou linku
                /// </summary>
        public void Disconnect()
        {
            // Po ukonceni aplikace pokud je otevreny seriovy port tak ho uzavru
            foreach (var comPort in serialPorts)
            {
                if (comPort.IsOpen)
                {
                    try
                    {
                        comPort.Close();
                        this.connected = false;             // Nastavim na false pokud je nejaky seriovy port uzavren
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message, MainForm.appName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }

        }

        public void WriteData(int adrPortuList, string data)
        {
            //Thread.Sleep(500);
            SerialPort comPort = serialPorts[adrPortuList];         // Nacteni danych parametru com portu kam se maji data vyslat
            comPort.Write(data + "\n");                             // Pred vyslanim pridam LF(line feed) znak \n vyzaduje protokol SCPI, ktery komunikuje s mericimi pristroji
        }

        void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            Thread.Sleep(300);
            SerialPort comPort = (SerialPort)sender;                // <- Obtain the serial port
            string data = comPort.ReadExisting();

            // Zkontroluju jestli je event nekde z venci teto tridy pouzit, kdyby nebyl nebudu ho volat, protoze je null = exception
            if (OnSerialDataReceived != null)
            {
                OnSerialDataReceived(comPort, data);
            }
        }

Nevím jak to vymyslet a nechce se mi za nějakou dobou pořád dokola testovat zda se něco neobjevilo v přijímacím stringu.... Nexistuje nějaká událost nebo jiná možnost?
Díky za rady

 
Odpovědět 21. března 14:37
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na Michaal.K
ostrozan:

Při odeslání spusť časovač.
Při příjmu ho zastav.
Při přetečení vyhoď chybu.

Jinak kolik dat přijímáš, že čekáš 300 ms?
Při 19200 bd příjmeš za tu dobu cca 500 bajtů.

Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
 
Nahoru Odpovědět 21. března 15:40
Avatar
Michaal.K
Člen
Avatar
Odpovídá na ostrozan
Michaal.K:

Ahoj,
díky za rady s timeoutem, vytvořil jsem ho pomocí timeru.
Objevil se mi jiný problém a nechci zakládat nové téma. Teď mi program hází chybu:

Nelze volat funkci Invoke nebo BeginInvoke pro ovládací prvek, dokud není vytvořen popisovač okna.

A nevím co stím. Děje se to v tříde settingsform(je to form s nastavením), když formulář otevřu podruhé a stisknu tlač. Test připojení (metoda buttonTest_Click). Chyba nastane v comm_OnTimedEvent. Zkrácený kód třídy settingsform:

Communication comm = Communication.Instance;

      public SettingsForm(MainForm form)
      {
         InitializeComponent();

            this.FormBorderStyle = FormBorderStyle.FixedDialog;             // Zakaz zmeny velikosti

            comm.OnSerialDataReceived += new Communication.DataReceivedEventHandler(comm_OnSerialDataReceived);
            comm.OnTimedEvent +=new Communication.TimedEventEventHandler(comm_OnTimedEvent);
      }

        void buttonTest_Click(object sender, EventArgs e)
        {
            Button buttonAkt = (Button)sender;                              // Zjisteni na ktery button se kliklo
            var tabConParent = buttonAkt.Parent.Parent;                     // Zjisteni rodice(zalozka tabControl) buttonu na ktery se kliklo

            List<ComboBox> allCombos = new List<ComboBox>();                // Ulozeni vsech comboboxu
            allCombos.AddRange(GetComboBoxes((Control)tabConParent));       // Ulozi vsechny comboboxy v dane zalozce tabControl

            comm.serialPorts[tabConParent.TabIndex].BaudRate = Convert.ToInt32(allCombos[0].SelectedItem);                  // Do listu serialPorts ulozim aktualne nastavene hodnoty z comboBoxu
            comm.serialPorts[tabConParent.TabIndex].Parity = (Parity)Enum.Parse(typeof(Parity), allCombos[1].Text);
            comm.serialPorts[tabConParent.TabIndex].StopBits = (StopBits)Enum.Parse(typeof(StopBits), allCombos[2].Text);
            comm.ConnectionTest(comm.serialPorts[tabConParent.TabIndex]);   // Zavolam test pripojeni
        }

        void comm_OnSerialDataReceived(SerialPort comPort, string data)
        {
            this.Invoke(new EventHandler(delegate
            {
                Form frm = Application.OpenForms["SettingsForm"];
                if (frm == null)                                                            // Zjistim jestli je aktivni SettingsForm pokud ano pokracuji dal
                    return;                                                                 // Ukoncim metodu pokud neni aktivni form, abych nezapisoval do data do prvku ktery neni vytvoren!!

                int indexPortu = comm.serialPorts.IndexOf(comPort);                         // Index portu v tabControl a listu serialPorts
                var tabConParent = tabControl1.TabPages[indexPortu];                        // Zalozka tabPages kde bylo stisknuty button Test pripojeni
                var gbTestAkt = tabConParent.GetContainerControl().ActiveControl.Parent;    // Aktualni groupBox v dane zalozce tabPages

                TextBox textBoxAkt = new TextBox();                                         // Ulozeni TextBoxu
                textBoxAkt = GetTextBox((Control)gbTestAkt);                                // Ulozi TextBox v dane zalozce tabControl a groubBoxu

                textBoxAkt.Font = new Font("Microsoft Sans Serif", 8);
                textBoxAkt.Text = data;                                                     // Zapsani prijatych dat
                textBoxAkt.TextAlign = HorizontalAlignment.Center;                          // Zarovnani textu na stred
                textBoxAkt.BackColor = Color.GreenYellow;
            }));
        }

        // Vyvola se tato metoda pokud dojde k preteceni casovace -> Timeout komunikace
        void comm_OnTimedEvent()
        {
            this.Invoke(new EventHandler(delegate
            {
                int indexTabPage = tabControl1.SelectedIndex;                               // Index aktualni zalozkt v tabControl a listu serialPorts
                var tabConParent = tabControl1.TabPages[indexTabPage];                      // Zalozka tabPages kde bylo stisknuty button Test pripojeni
                var gbTestAkt = tabConParent.GetContainerControl().ActiveControl.Parent;    // Aktualni groupBox v dane zalozce tabPages

                TextBox textBoxAkt = new TextBox();                                         // Ulozeni TextBoxu
                textBoxAkt = GetTextBox((Control)gbTestAkt);                                // Ulozi TextBox v dane zalozce tabControl a groubBoxu

                textBoxAkt.Font = new Font("Microsoft Sans Serif", 8, FontStyle.Bold);
                textBoxAkt.Text = "Časový limit operace vypršel!";
                textBoxAkt.TextAlign = HorizontalAlignment.Center;
                textBoxAkt.BackColor = Color.DarkRed;
            }));
        }

a tady jsou úpravy třídy communication:

public delegate void TimedEventEventHandler();                                      // Deklarace delagata, ktery bude slouzit jako predpis metody predstavujici reakci na udalost (handler)
        public event TimedEventEventHandler OnTimedEvent;                                   // Deklarace udalosti

        private System.Timers.Timer timerTimeout;


// Metoda, ktera provede nastaveni timeru pro timeout komunikace
        private void SetTimerTimeout()
        {
            timerTimeout = new System.Timers.Timer(500);
            timerTimeout.Elapsed += new System.Timers.ElapsedEventHandler(timerTimeout_Elapsed);        // Pridani udalosti, ktera je vyvolana pokud dojde k preteceni casovace
        }

        // Metoda, ktera se vykona pri preteceni casovace
        void timerTimeout_Elapsed(object sender, EventArgs e)
        {
            System.Timers.Timer timerTimeout = (System.Timers.Timer)sender;
            timerTimeout.Enabled = false;                       // Zastavim timer

            Form frm = Application.OpenForms["SettingsForm"];
            if (frm != null)                                    // Zjistim jestli je aktivni SettingsForm pokud ano zavolam metodu
                OnTimedEvent();
            else
                MessageBox.Show("Časový limit operace vypršel!", MainForm.appName, MessageBoxButtons.OK, MessageBoxIcon.Error);

            Disconnect();                                       // Zavru vsechny porty
        }

        public void ConnectionTest(SerialPort comPort)
        {
            SetTimerTimeout();                                  // Nastaveni parametru timeru
            ConnectedPort(comPort);                             // Otevru dany seriovy port
            int adrPortuList = serialPorts.IndexOf(comPort);    // Zjistim index daneho serioveho portu v listu serialPorts
            WriteData(adrPortuList, "Test");                    // Vyslu testovaci retezec
            timerTimeout.Enabled = true;                        // Zapnu timer
        }

Díky za pomoc

 
Nahoru Odpovědět 22. března 13:56
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 3 zpráv z 3.