[ bigguy @ 17.01.2010. 17:06 ] @
Imam malo cudan problem. Mislim da je po sredi moje ne poznavanje rada sa nitima i njihovo koriscenje, pa sam se zato obratio za pomoc. O ovoj aplikaciji nesto sam ranije vec postavljao pitanje, ali ipak cu ponoviti ukratko sta radim. Rec je o sledecem: radim jednu aplikaciju vezanu za prijem i slanje podataka putem SerialPort-a. Sve ono sto pristize i salje se sa porta treba da bude vidljivo, odnosno da se ispisuje u okviru jednog RichTextBox-a. Takodje, kad god se neki podatak posalje preko porta to korisnik moze vizualno da vidi, osim ispisivanja teksta u okviru RichTexBox-a, i na osnovu promene male ikonice iz crvene boje u zelenu. Simuliram posojanje neke vrste diodice i to traje 1/3 sekunde. Postoje dve ikonice pbTX za slanje podataka i pbRX za prijem podataka. Njih sam postavio kao Timer-e koji se ukljucuju prilikom pokretanja funkcije za slanje podataka timerTX i prilikom prijema podataka timerRX. Ovi timer-i imaju event timer_Tick.
Radi po meni lakseg organizovanja odvojio sam GUI korisnika od class PortProperties gde se nalaze i podesavaju osnovni parametri port-a, a slanje i prijem funkcije zbog svoje slozenosti odvojio sam u posebne klase class Send i class Received. Ove klase pozivam iz class PortProperties putem funkcija. Ceo program testiram preko virtualnog COM3 porta, to jest preko istog porta se i primaju i salju podaci. Problem mi se javio upravo u ovim segmentima. Dacu najpre ukratko skraceni kod, a potom cu objasniti i problem:

Code:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        PortProperties portProperties = new PortProperties();

        private void Form1_Load(object sender, EventArgs e)
        {
            frmForm1 = this;
            LoadValues();
            SetDefaults();
            SetControlState();
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            portProperties.ClosePort();
        }

        private void SetDefaults()
        {
            ...//podesavanje osnovnih parametara
        }

        private void LoadValues()
        {
            ...//ucitavanje vrednosti parametara SerialPort-a
        }

        private void SetControlState()
        {
            pictureBox1.Image = ...//postavljanje na odgovarajucu ikonicu
            pictureBox2.Image = ..//postavljanje na odgovarajucu ikonicu
        }
        private void btnOpen_Click(object sender, EventArgs e)
        {
            portProperties._pbRX = pictureBox1;
            portProperties._pbTX = pictureBox2;
            portProperties._rtbDisplay = rtbDisplay;
            ...//ostala objekti na formi
            portProperties.OpenPort();
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            string message = "Hello World!";
            byte[] sendMessage = Encoding.ASCII.GetBytes(message);
            portProperties.SendByte(sendMessage);
        }
    }


Code:

   class PortProperties
    {
        Send send = new Send();
        Received received = new Received();

        public RichTextBox _rtbDisplay { get; set; }
        public PictureBox _pbRX { get; set; }
        public PictureBox _pbTX { get; set; }
        ...//ostali parametri SerialPort-a i MainForm-e
        private SerialPort comPort = new SerialPort();


        public PortProperties()
        {
            _rtbDisplay = null;
            _pbRX = null;
            _pbTX = null;
            ... 
            comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
        }

        public PortProperties( PictureBox pb1, PictureBox pb2, RichTextBox RTBDISPLAY,...)
        {
            _pbRX = pb1;
            _pbTX = pb2;
            _rtbDisplay = RTBDISPLAY;
            ...
            comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
        }

        public bool OpenPort()
        {
            ...//postavljanje propertiza SerialPort-a
            RTBDisplay(true);
        }      

        public bool ClosePort()
        {
            ...//zatvaranje SerialPort-a
            RTBDisplay(false);
        }      

        private void RTBDisplay(bool state)
        {
            _rtbDisplay.Invoke(new EventHandler(delegate
                {
                    ...//Ispisivanje teksta prilikom otvaranja ili zatvaranja SerialPort-a
        }

        void comPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            received._rtbDisplay = _rtbDisplay;
            received._pbRX = _pbRX;
                received.ReceivedData(comPort);
        }

        public void SendByte(byte[] msg)
        {
            send._rtbDisplay = _rtbDisplay;
            send._pbTX = _pbTX;
            send.WriteData(comPort, msg);
        }
    }


Code:

    class  Send
    {
        private delegate void SetDisplyDelegate(byte[] data);
        private int timer;
        private System.Windows.Forms.Timer timerTX;

        public PictureBox _pbTX { get; set; }
        public RichTextBox _rtbDisplay { get; set; }
        
        public Send()
        {
            _rtbDisplay = null;
        }

        public Send(RichTextBox RTBDISPLAY)
        {
            _rtbDisplay = RTBDISPLAY;
        }

        public void WriteData(SerialPort comPort, byte[] msg)
        {
            timerTX = new System.Windows.Forms.Timer();
            timer = 150;
            timerTX.Interval = 25;
            timerTX.Tick += new EventHandler(this.timerTX_Tick);
            try
            {
                timerTX.Start();
                comPort.Write(msg, 0, msg.Length);
                _rtbDisplay.BeginInvoke(new SetDisplyDelegate(RTBDisplay), new object[] { msg });
                RTBDisplay(msg);
            }
            catch
            {
                MessageBox.Show("Error! Data isn't send.", "Critical Warning", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void RTBDisplay(byte[] msg)
        {
             _rtbDisplay.AppendText(System.Text.Encoding.ASCII.GetString(msg);
        }

        private void timerTX_Tick(object sender, EventArgs e)
        {
            timer=timer-timerTX.Interval*2;
            _pbTX.Image = global::Architect_ci8200.Properties.Resources.XSIMPLE_GREEN;
            if (timer == 0)
            {
                _pbTX.Image = global::Architect_ci8200.Properties.Resources.XSIMPLE_CRIMSON;
                timerTX.Dispose();
            }
            else
            {
                if (timer < 0)
                {
                    _pbTX.Image = global::Architect_ci8200.Properties.Resources.XSIMPLE_CRIMSON;
                    timerTX.Dispose();
                }

            }
        }
    }


Code:

    class Received
    {
        private delegate void SetDisplyDelegate(byte[] data);
        private int timer;
        private System.Windows.Forms.Timer timerRX;
        
        public PictureBox _pbRX { get; set; }
        public RichTextBox _rtbDisplay { get; set; }
        
        public Received()
        {
            _rtbDisplay = null;
        }
        
        public Received(RichTextBox RTBDISPLAY)
        {
            _rtbDisplay = RTBDISPLAY;
        }

        public void ReceivedData(SerialPort comPort)
        {
            int bytes = comPort.BytesToRead;
            byte[] msg = new byte[bytes];
            comPort.Read(msg, 0, bytes);

            timerRX = new System.Windows.Forms.Timer();
            timer = 150;
            timerRX.Interval = 25;
            timerRX.Tick += new EventHandler(this.timerRX_Tick);
            timerRX.Start();

            _rtbDisplay.BeginInvoke(new SetDisplyDelegate(RTBDisplay), new object[] { msg });
        }

        private void RTBDisplay(byte[] msg)
        {
             _rtbDisplay.AppendText(System.Text.Encoding.ASCII.GetString(msg);
        }

        private void timerRX_Tick(object sender, EventArgs e)
        {
            timer = timer - timerRX.Interval * 2;
            _pbRX.Image = global::Architect_ci8200.Properties.Resources.XSIMPLE_GREEN;
            if (timer == 0)
            {
                _pbRX.Image = global::Architect_ci8200.Properties.Resources.XSIMPLE_CRIMSON;
                timerRX.Dispose();
            }
            else
            {
                if (timer < 0)
                {
                    _pbRX.Image = global::Architect_ci8200.Properties.Resources.XSIMPLE_CRIMSON;
                    timerRX.Dispose();
                }

            }
        }
    }


E sad konacno i problem
Kada posaljem podatke kod kada dodje do funkcije RTBDisplay(byte[] msg) najpre prodje linije koda RTBDisplay funkcije u klasi class Received a atim tek onda prodje linije koda RTBDisplay() klase class Send. Pritom mi timerTX ispravno radi, a timerRX ne. Naime, kada pratim liniju koda prilikom prijema podataka on prodje funkciju DataReceived() klase class Received ali ne ucitava funkciju RTBDisply() iste funkcije i ne pokrece event timerRX_Tick. Kako bi mogao ovaj problem resiti? Da li je mozda jednood resenja ubaciti, mada je meni nejasno kao to izvesti, da se prijem podataka vrsi kroz jednu nit a slanje kroz drugu.... Imali neko nekakvu ideju?
Malo sam bio opsiran.....ali problem je takve prirode

Ranije na ovom forumu sam imao savet da u aplikaciju ubacim BackGraundWorker i da preko njega probam prijem i slanje podataka ali mi nije jasno kako bi mogao njega uklopiti u ovaj kod?


[Ovu poruku je menjao bigguy dana 17.01.2010. u 19:15 GMT+1]
[ bigguy @ 18.01.2010. 11:31 ] @
Mozeli mi neko na prostom primeru objasniti kako da napravim da mi se podaci primaju na jednoj niti, podaci salju na drugoj niti a na trecoj niti da mi se ispisuju ono sto je poslato i primljeno?
I da li se event serialPort_DataReceived ustvari ponasa kao zasebna nit?
[ malo_nj @ 18.01.2010. 11:35 ] @
ne trebaju ti 3 niti dosta su 2 jedna za slanje/prijem druga za prikaz. Ako stignem danas cu ti okaciti. Ili posalji mi svoj citav kod pa cu pogledati.
[ bigguy @ 18.01.2010. 13:11 ] @
Aplikaciju sam okacio na ovoj link:

Project

Molim te obrati paznju zbog cega se event timerRX_Tick uopste ne pokrece? Ono sto jos mogu da primetim je da je prijem podataka relativno spor, pedpostavljam iz razloga ne postojanja niti, jer jednu relativno kratku oruku on cepka na prijemu kao 2,3, ili nekad i daleko vise. U ostalom videces kada budes probao kod. Hvala unapred na pomoci, izludeh pokusavajuci svasta ali mi nista nije uspevalo....
PS-inace videces na Main formi aktivna su button NUL, ACK i LF.
[ bigguy @ 19.01.2010. 18:14 ] @
Malo_nj imali pomoci tom kodu?
Imali jos neko predlog kako ovo resiti?!
[ malo_nj @ 19.01.2010. 19:28 ] @
evo tek sad gledam ali vec ti nemam sta dobro reci. jos cu veceras malo caprkati pa ako sta bude javim ti. pokusaj ostaviti post na MSDN forumu mozda tamo budes imao vise srece
[ malo_nj @ 19.01.2010. 20:36 ] @
Uspio sam ti izvuci alternativno resenje koliko je ono dobro neam pojma al barem radi. Iz nekog razloga timer event se ne poziva nakon poziva comPort_DataReceived (neki problem sa eventovima tredovima ko ce ga znati) e sad ti ga pokrenes u konstruktoru Receive() koji se pravi negdje na pocetku rada aplikacije i taj timerRX ti sad non stop tuce al sa if-om mu das do znanja jel se vrsi prijem pa da on onda odradi svoje.


Code:

  public Receive()
        {
            _rtbDisplay = null;

             timerRX = new System.Windows.Forms.Timer();
             timer = 150;
             timerRX.Interval = 25;
            timerRX.Tick += new EventHandler(timerRX_Tick);
            timerRX.Start();
        }



Code:

  public static bool receiving = false;
        private void timerRX_Tick(object sender, EventArgs e)
        {
            if (receiving)
            {
                timer = timer - timerRX.Interval * 2;
                _pbRX.Image = global::Architect_ci8200.Properties.Resources.XSIMPLE_GREEN;
                if (timer <= 0)
                {
                    _pbRX.Image = global::Architect_ci8200.Properties.Resources.XSIMPLE_CRIMSON;
                    receiving = false;
                }
            }
            
        }



Code:

  public void ReceivedData(SerialPort comPort)
        {
          
           
            try
            {
                timer = 150;
                timerRX.Interval = 25;

                receiving = true;
                int bytes = comPort.BytesToRead;
                byte[] msg = new byte[bytes];
                comPort.Read(msg, 0, bytes);
                _rtbDisplay.BeginInvoke(new SetDisplyDelegate(RTBDisplay), new object[] { msg });

        }
            catch { }
        }


[ bigguy @ 19.01.2010. 21:44 ] @
Hvala na ovom postu mislim da imas pravo za MSDN, a tvoje resenje cu proveriti vec sutra. Mozesli mi samo jos na nekom prostom primeru pokazati kako jos da samo napravim one dve niti, jenu za slanje/prijem i jednu za prikaz? Ovo mi je ne ophodno kako bi mogao obezbediti da prilikom prijema podataka se ne moze desiti da se pokusa slanje podataka u istom momentu i kako bi obezbedio efikasniji i brzi prijem. Tada bi trebalo niti za prijem i slanje dati veci prioritet nego niti za prikaz, jel to tacno?!
[ bigguy @ 20.01.2010. 14:08 ] @
Imali neko predlog gde bi trebalo da udenem niti i kako to da izvedem? Nije valjda da sam ubo sveti gral programiranja?!
[ malo_nj @ 20.01.2010. 16:48 ] @
bogam jeste gral. sto se tice niti vec ih imas prikaz ti se odvija na zasebnom thredu
[ bigguy @ 20.01.2010. 18:04 ] @
To je ok ali kako da realizujem thread za prijem/slanje podataka? Pleaseeeeeeeeee.....
[ malo_nj @ 20.01.2010. 18:50 ] @
Code:

Thread thr = new Thread(new ThreadStart(Send));
thr.Start();


et i funkcija Send() ti se odvija na zasebnoj niti.
[ bigguy @ 20.01.2010. 19:07 ] @
Da, ali kako da ja to uklopim u ovaj moj kod? Meni Send funkcija u klasi PortProperties ima jednu promenjivu, a ja takvu funkciju ne mogu da pozivam za pokretanje thread-a. Takodje, kako da obezbedim da mi se tokom prijema ne moze desiti da se salju podaci?
[ malo_nj @ 20.01.2010. 19:39 ] @
Da ti se ne salju i primaju podatci u isto vrijeme ti je vec osigurano, a thread mozes pokrenuti i proslijediti mu neki objekat koristi ParameterizedThreadStart
[ bigguy @ 25.01.2010. 18:33 ] @
Svi odgovori na pitanja problema aktivacije Windows.Form.Timer-a unutar dogadjaja serialPort_DataReceive() mogu se naci na ovom forumu:
Timer
a ovaj link ce u velikoj meri resiti vase dalje probleme:
MSDN

Hvala netu i njegovim carolijama