C# + Modbus TCP

0

Witam.
Dopiero zaczynam programować w C# więc proszę o wyrozumiałość ;)
Próbuję stworzyć prostą wizualizację. Dane wczytuje ze sterownika PLC po Modbusie TCP IP. Generalnie komunikacja działa, znam format ramki - tu problemów nie ma.

  1. Przy rozłączeniu PLC (wypięciu wtyczki ethernet) i próbie połączenia moja aplikacja crashuje. Jak zabezpieczyć program przed taką sytuacją?
  2. Chcę sczytywać wejścia, wyjścia oraz rejestry oraz je zapisywać - powinienem wysyłać zapytania po kolei, zwiększając za każdym razem identyfikator transakcji ramki czy raczej wysyłać zapytanie, czekać na odpowiedź i następnie wysyłać inne zapytanie?
  3. Jak wykonać cykliczne sczytywanie wartości ze sterownika? Dispatcher Timer?

Za wszelkie podpowiedzi dziękuję i pozdrawiam.

0
  1. try cach?? Pokaż kod, który się wywala
  2. zależy od sterownika - niektóre odeślą odpowiedź na wszystkie a niektóre tylko na ostatnie pytanie. Bezpieczniej wysłać jeden i czekać na odpowiedź
  3. w wątku
0
  1. Po wyjęciu wtyczki ze sterownika i próbie połączenia, program wisi przez kilka sekund i wyświetla:
An unhandled exception of type 'System.Net.Sockets.SocketException' occurred in System.dll

Additional information: Próba połączenia nie powiodła się, ponieważ połączona strona nie odpowiedziała poprawnie po ustalonym okresie czasu lub utworzone połączenie nie powiodło się, ponieważ połączony host nie odpowiedział
  1. Możesz jaśniej?

Przy włożonej wtyczce i poprawnym połączeniu i sczytaniu danych i tak pojawia mi się błąd (dopiero teraz zauważyłem):

Error..... 
   w System.Net.Sockets.TcpClient.GetStream()
   w Modbus_WPF.MainWindow.timer_Tick(Object sender, EventArgs e) w c:\Users\Sebastian\Documents\Visual Studio 2013\Projects\Modbus_WPF\Modbus_WPF\MainWindow.xaml.cs:wiersz 58

Generalnie mam to zrobione tak:

public partial class MainWindow : Window
    {
        SolidColorBrush Kolor_czerwony = new SolidColorBrush();
        SolidColorBrush Kolor_zielony = new SolidColorBrush();
        TcpClient klient = new TcpClient();
        DispatcherTimer timer = new DispatcherTimer();



        public MainWindow()
        {
            InitializeComponent();
            Tekst1.Text = "Nie połączono!";

            Kolor_czerwony.Color = Colors.Red;
            Kolor_zielony.Color = Colors.Green;


            timer.Interval = new TimeSpan(0, 0, 0, 0, 100);
            timer.Tick += timer_Tick;

            

        }

        void timer_Tick(object sender, EventArgs e)
        {
            try
            {
                byte[] dane = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x05, 0x00, 0x00, 0x08 };

                NetworkStream serverStream = klient.GetStream();
                serverStream.Write(dane, 0, dane.Length);
                serverStream.Flush();

                byte[] inStream = new byte[10025];
                serverStream.Read(inStream, 0, (int)klient.ReceiveBufferSize);

                int seba = inStream[9];
                Tekst2.Text = Convert.ToString(seba);
                BitArray b = new BitArray(new int[] { seba });

                if (b[0] == true)
                {
                    Kontrolka8.Fill = Kolor_zielony;
                }
                else
                {
                    Kontrolka8.Fill = Kolor_czerwony;
                }
                if (b[1] == true)
                {
                    Kontrolka7.Fill = Kolor_zielony;
                }
                else
                {
                    Kontrolka7.Fill = Kolor_czerwony;
                }
                if (b[2] == true)
                {
                    Kontrolka6.Fill = Kolor_zielony;
                }
                else
                {
                    Kontrolka6.Fill = Kolor_czerwony;
                }
                if (b[3] == true)
                {
                    Kontrolka5.Fill = Kolor_zielony;
                }
                else
                {
                    Kontrolka5.Fill = Kolor_czerwony;
                }
                if (b[4] == true)
                {
                    Kontrolka4.Fill = Kolor_zielony;
                }
                else
                {
                    Kontrolka4.Fill = Kolor_czerwony;
                }
                if (b[5] == true)
                {
                    Kontrolka3.Fill = Kolor_zielony;
                }
                else
                {
                    Kontrolka3.Fill = Kolor_czerwony;
                }
                if (b[6] == true)
                {
                    Kontrolka2.Fill = Kolor_zielony;
                }
                else
                {
                    Kontrolka2.Fill = Kolor_czerwony;
                }
                if (b[7] == true)
                {
                    Kontrolka1.Fill = Kolor_zielony;
                }
                else
                {
                    Kontrolka1.Fill = Kolor_czerwony;
                }
            }

            catch (Exception ex)
            {
                Tekst1.Text = ("Error..... \n" + ex.StackTrace);
            } 

            finally
            {
                klient.Close();
            }

        }



        private void Przycisk1_Click(object sender, RoutedEventArgs e)
        {
            timer.Start();

                Tekst1.Text = "Łączenie...";

                klient.Connect("10.3.0.113", 502);
                if (klient.Connected == true)
                {
                    Tekst1.Text = "Połączono!";
                }

        }
0

dużo czytelniej wg mnie będzie tak
Kontrolka8.Fill = b[0] ? Kolor_zielony : Kolor_czerwony;

ale to szczegół. Blok `try catch` dawaj na instrukcjach, które mogą powodować problem.

Najpierw uruchamiasz wysyłanie/odbieranie danych (timera) a dopiero potem łączysz się z modułem.

Co do pokazywania się błędu to jeśli robisz to spod VS to błąd się pokaże w samym VS ale program powinien iść dalej
0

Tak wiem, doczytałem o tej konstrukcji już po umieszczeniu posta :)

Uruchomiłem najpierw timera i pojawia się jakiś wyjątek, ale program idzie dalej. Gdy podłączam się do PLC wyjątek znika i komunikacja działa. Krok do przodu :)

Jednak dalej występuje problem z tym, że gdy w trakcie połączenia wyjmę wtyczkę z PLC - program wisi i nie mogę nic kliknąć.
Może jakiś timeout rozwiązałby problem?

0
sebad13 napisał(a):

Jednak dalej występuje problem z tym, że gdy w trakcie połączenia wyjmę wtyczkę z PLC - program wisi i nie mogę nic kliknąć.
Może jakiś timeout rozwiązałby problem?

Nie - rozwiązaniem jest komunikacja z urządzeniem w innym wątku

0

Ok, poczytam o wątkach.

Jeszcze jedno co zauważyłem.

Łączę się z PLC, jest ok. Następnie rozłączam się (zamykam połączenie komendą klient.Close) i próbuję się znów połączyć - crash:

An unhandled exception of type 'System.ObjectDisposedException' occurred in System.dll

Additional information: Nie można uzyskać dostępu do usuniętego obiektu.
0

Bo zamknięcie połączenia powoduje dispose socket'a i nie można go już użyć. Musisz utworzyć obiekt TcpClient na nowo

0

Panowie zerknijcie bo pewnie nie o to chodziło :)

  1. Wątki - coś czuje że robię to źle :D
  2. TcpClienta mam stworzonego na samej górze, a powinienem go tworzyć po kliknięciu przycisku połączenia. Jednak gdy tak robię to krzyczy mi, że w timer_tick nie wie co to "klient".
  3. Wszelkie inne uwagi mile widziane.

Kod:

    public partial class MainWindow : Window
    {
        SolidColorBrush Kolor_czerwony = new SolidColorBrush();
        SolidColorBrush Kolor_zielony = new SolidColorBrush();
        TcpClient klient = new TcpClient();
        DispatcherTimer timer = new DispatcherTimer();


        public MainWindow()
        {
            InitializeComponent();
            Tekst1.Text = "Nie połączono!";

            Kolor_czerwony.Color = Colors.Red;
            Kolor_zielony.Color = Colors.Green;

            timer.Interval = new TimeSpan(0, 0, 0, 0, 100);


            
        }

        public void timer_Tick(object sender, EventArgs e)
        {
            try
            {
                if (klient.Connected == true)
                {
                    Tekst1.Text = ("Połączono!");
                    byte[] dane = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x05, 0x00, 0x00, 0x08 };
                    
                    NetworkStream serverStream = klient.GetStream();
                    serverStream.Write(dane, 0, dane.Length);
                    serverStream.Flush();

                    byte[] inStream = new byte[10025];
                    serverStream.Read(inStream, 0, (int)klient.ReceiveBufferSize);

                    int seba = inStream[9];
                    Tekst2.Text = Convert.ToString(seba);
                    BitArray b = new BitArray(new int[] { seba });

                    Kontrolka8.Fill = b[0] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka7.Fill = b[1] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka6.Fill = b[2] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka5.Fill = b[3] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka4.Fill = b[4] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka3.Fill = b[5] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka2.Fill = b[6] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka1.Fill = b[7] ? Kolor_zielony : Kolor_czerwony;
                }
            }

            catch (Exception ex)
            {
                Tekst1.Text = ("Error..... \n" + ex.StackTrace);
            } 

        }

        private void Przycisk1_Click(object sender, RoutedEventArgs e)
        {
            Thread thread = new Thread(new ThreadStart(Watek1));
            thread.Start();
            Thread thread1 = new Thread(new ThreadStart(Watek2));
            thread1.Start();
        }

        public void Watek1()
        {
            klient.Connect("10.3.0.98", 502);

        }

        public void Watek2()
        {
            timer.Start();
            timer.Tick += timer_Tick;
        }

        private void Przycisk2_Click(object sender, RoutedEventArgs e)
        {
            Tekst3.Text = "Test 1";
        }

        private void Przycisk3_Click(object sender, RoutedEventArgs e)
        {
            Tekst3.Text = "Test 2";
        }

        private void Przycisk4_Click(object sender, RoutedEventArgs e)
        {
            klient.Close();
        }
0

"Na górze" zostaw samo TcpClient klient, a w Watek1 dodaj klient = new TcpClient(). Warunek klient.Connected == true zmień na klient != null && klient.Connected

0

Okej, dzięki za pomoc.

Polecicie jakiś dobry kurs C# lub książkę? Najlepiej razem z opisem WPF.

0

Sry, że post pod postem, ale program nadal wisi po wyjęciu wtyczki przez kilkanaście sekund, a następnie wyrzuca kilka wyjątków (exception).

0

Pomoże ktoś?

Wklejam kod:

 
public partial class MainWindow : Window
    {
        SolidColorBrush Kolor_czerwony = new SolidColorBrush();
        SolidColorBrush Kolor_zielony = new SolidColorBrush();
        TcpClient klient;
        NetworkStream serverStream;
        DispatcherTimer timer = new DispatcherTimer();


        public MainWindow()
        {
            InitializeComponent();
            Tekst1.Text = "Nie połączono!";

            Kolor_czerwony.Color = Colors.Red;
            Kolor_zielony.Color = Colors.Green;

            timer.Interval = new TimeSpan(0, 0, 0, 0, 100);


            
        }

        public void wyslij_zapytanie(byte[] tablica)
        {
            serverStream = klient.GetStream();
            serverStream.Write(tablica, 0, tablica.Length);
            serverStream.Flush();
        }

        public void odbierz(byte[] tablica)
        {
            serverStream.Read(tablica, 0, (int)klient.ReceiveBufferSize);
        }

        // metoda do odczytu wyjsc bitowych - funkcja 01h
        private int odczyt_wyjsc(byte adres1, byte adres2)
        {
            byte[] do_wyslania = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, adres1, adres2, 0x00, 0x08 };
            byte[] odebrane = new byte[10025];
            wyslij_zapytanie(do_wyslania);
            odbierz(odebrane);
            int wynik = odebrane[9];
            return wynik;
        }

        // metoda do odczytu n rejestrow - funkcja 03h
        private int odczyt_rejestrow (byte adres1, byte adres2)
        {
            byte[] do_wyslania = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x03, adres1, adres2, 0x00, 0x01 };
            byte[] odebrane = new byte[10025];
            wyslij_zapytanie(do_wyslania);
            odbierz(odebrane);
            int wynik = (odebrane[9] << 8) + (odebrane[10]);
            return wynik;
        }


        // metoda do zapisu 1 bitu - funkcja 05h
        private int zapis_wyjscia (byte adres1, byte adres2)
        {
            byte[] do_wyslania = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, adres1, adres2, 0xFF, 0x00 };
            byte[] odebrane = new byte[10025];
            wyslij_zapytanie(do_wyslania);
            odbierz(odebrane);
            int wynik = odebrane[9];
            return wynik;
        }

        public void timer_Tick(object sender, EventArgs e)
        {
            try
            {
                if (klient != null && klient.Connected == true)
                {
                    Tekst1.Text = ("Połączono!");


                    int seba1;
                    seba1 = odczyt_wyjsc(0x05, 0x00);


                    Tekst2.Text = Convert.ToString(seba1);
                    BitArray b = new BitArray(new int[] { seba1 });

                    Kontrolka8.Fill = b[0] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka7.Fill = b[1] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka6.Fill = b[2] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka5.Fill = b[3] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka4.Fill = b[4] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka3.Fill = b[5] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka2.Fill = b[6] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka1.Fill = b[7] ? Kolor_zielony : Kolor_czerwony;

                    int seba2;
                    seba2 = odczyt_rejestrow(0x10, 0x00);
                    Tekst4.Text = Convert.ToString(seba2);
                    
                }
                else
                {
                    klient.Close();
                }
            }

            catch (Exception ex)
            {
                Tekst1.Text = ("Error..... \n" + ex.StackTrace);
            } 

        }

        private void Przycisk1_Click(object sender, RoutedEventArgs e)
        {
            Thread thread = new Thread(new ThreadStart(Watek1));
            thread.Start();
            Thread thread1 = new Thread(new ThreadStart(Watek2));
            thread1.Start();
        }

        public void Watek1()
        {
            klient = new TcpClient();
            klient.Connect("10.3.0.119", 502);
            if (klient == null && klient.Connected == false)
            {
                MessageBox.Show("Ups!");
            }
        }

        public void Watek2()
        {
            timer.Start();
            timer.Tick += timer_Tick;
        }

        private void Przycisk2_Click(object sender, RoutedEventArgs e)
        {
            Tekst3.Text = "Test 1";
        }

        private void Przycisk3_Click(object sender, RoutedEventArgs e)
        {
            Tekst3.Text = "Test 2";
        }

        private void Przycisk4_Click(object sender, RoutedEventArgs e)
        {
            klient.Close();
        }
0

Najpierw jedna rzecz do poprawienia:

        public void Watek1()
        {
            klient = new TcpClient();
            klient.Connect("10.3.0.119", 502);
            if (klient == null && klient.Connected == false)
            {
                MessageBox.Show("Ups!");
            }
        }

Jeżeli klient == null to drugi warunek rzuci wyjątkiem chyba chciałeś napisać: if (klient == null || !klient.Connected)

Co do problemu, to rozumiem, że chcesz aby program próbował się ponownie połączyć po zerwaniu połączenia? No tak nie będzie działać, bo tylko raz wywołujesz funkcję połączenia. W timer_Tick musisz sprawdzać czy jest dostępne połączenie.

Tego ifa zrób tak:

if (klient == null || !klient.Connected)
{
   klient = new TcpClient();
   klient.Connect("10.3.0.119", 502);
}

Dalej cała reszta kodu poza tym else. Nie wiem po co tam ten klient.Close()

Connect jest synchroniczne więc zawiesi działanie wątku. Problem pozostaje taki, że w międzyczasie timer może odpalić tą metodę jeszcze raz. Nie wiem z jakiego timera korzystasz. Ja używałem do takich rzeczy System.Threading.Timer

Tworzysz go tak:

timer = new Timer(timer_Tick, null, new TimeSpan(0, 0, 0, 0, 100), Timeout.Infinite);

Co powoduje że uruchomi się tylko raz!
Natomiast na końcu metody timer_Tick umieszczasz ręcznie ustawienie kolejnego uruchomienia:

timer.Change(new TimeSpan(0, 0, 0, 0, 100), Timeout.Infinite);

I jeżeli czegoś nie pokręciłem to program będzie się próbował połączyć do skutku.
Jeszcze mała uwaga, że timer_Tick w tym przypadku musi przyjmować jeden parametr typu object.

0

Chodzi mi o to, aby stworzyć program, który będzie odporny na wypięcie kabla, utratę połączenia itp.
Generalnie teraz jest tak:

  1. Jeśli uruchomię program i nacisnę połącz bez wpiętego kabla to okno główne wisi przez kilkanaście sekund i wyświetla:
 
An unhandled exception of type 'System.Net.Sockets.SocketException' occurred in System.dll

Additional information: Próba połączenia nie powiodła się, ponieważ połączona strona nie odpowiedziała poprawnie po ustalonym okresie czasu lub utworzone połączenie nie powiodło się, ponieważ połączony host nie odpowiedział
  1. Jeśli uruchomię program, nacisnę połącz z wpiętym kablem - program działa i sczytuje dane. Gdy w momencie połączenia wypnę kabel, znów okno główne wisi. Po kilku sekundach na moment się odświeży, a po jeszcze kilku kolejnych sekundach znów wyświetla komunikat, który wkleiłem powyżej.

Poza tym ramka tekstowa "Tekst0" w żadnym wypadku nie pokazuje stanu "Nie połączono!".

Wklejam kod jeszcze raz po ostatnich poprawkach:

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Collections;
using System.Windows.Threading;
using System.Threading;

namespace Modbus_WPF
{
    /// <summary>
    /// Logika interakcji dla klasy MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        SolidColorBrush Kolor_czerwony = new SolidColorBrush();
        SolidColorBrush Kolor_zielony = new SolidColorBrush();
        TcpClient klient;
        NetworkStream serverStream;
        DispatcherTimer timer = new DispatcherTimer();
        //Timer timerek = new Timer(timer_Tick, null, 100, Timeout.Infinite);

        public MainWindow()
        {
            InitializeComponent();
            
            Kolor_czerwony.Color = Colors.Red;
            Kolor_zielony.Color = Colors.Green;

            timer.Interval = new TimeSpan(0, 0, 0, 0, 100);

        }

        public void wyslij_zapytanie(byte[] tablica)
        {
            serverStream = klient.GetStream();
            serverStream.Write(tablica, 0, tablica.Length);
            serverStream.Flush();
        }

        public void odbierz(byte[] tablica)
        {
            serverStream.Read(tablica, 0, (int)klient.ReceiveBufferSize);
        }

        // metoda do odczytu wyjsc bitowych - funkcja 01h
        private int odczyt_wyjsc(byte adres1, byte adres2)
        {
            byte[] do_wyslania = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, adres1, adres2, 0x00, 0x08 };
            byte[] odebrane = new byte[10025];
            wyslij_zapytanie(do_wyslania);
            odbierz(odebrane);
            int wynik = odebrane[9];
            return wynik;
        }

        // metoda do odczytu n rejestrow - funkcja 03h
        private int odczyt_rejestrow (byte adres1, byte adres2)
        {
            byte[] do_wyslania = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x03, adres1, adres2, 0x00, 0x01 };
            byte[] odebrane = new byte[10025];
            wyslij_zapytanie(do_wyslania);
            odbierz(odebrane);
            int wynik = (odebrane[9] << 8) + (odebrane[10]);
            return wynik;
        }


        // metoda do zapisu 1 bitu - funkcja 05h
        private int zapis_wyjscia (byte adres1, byte adres2)
        {
            byte[] do_wyslania = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, adres1, adres2, 0xFF, 0x00 };
            byte[] odebrane = new byte[10025];
            wyslij_zapytanie(do_wyslania);
            odbierz(odebrane);
            int wynik = odebrane[9];
            return wynik;
        }

        //metoda do zapisu 1 rejestru - funkcja 06h
        private int zapis_rejestru (byte adres1, byte adres2)
        {
            byte[] do_wyslania = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, adres1, adres2, 0x00, 0x03 };
            byte[] odebrane = new byte[10025];
            wyslij_zapytanie(do_wyslania);
            odbierz(odebrane);
            int wynik = odebrane[9];
            return wynik;
        }

        public void timer_Tick(object sender, EventArgs e)
        {
            if (klient == null || !klient.Connected)
            {
                klient = new TcpClient();
                klient.Connect("10.3.0.95", 502);
            }
            
            try
            {
                if (klient != null && klient.Connected)
                {
                    Tekst0.Text = ("Połączono!");


                    int seba1;
                    seba1 = odczyt_wyjsc(0x05, 0x00);


                    Tekst2.Text = Convert.ToString(seba1);
                    BitArray b = new BitArray(new int[] { seba1 });

                    Kontrolka8.Fill = b[0] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka7.Fill = b[1] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka6.Fill = b[2] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka5.Fill = b[3] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka4.Fill = b[4] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka3.Fill = b[5] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka2.Fill = b[6] ? Kolor_zielony : Kolor_czerwony;
                    Kontrolka1.Fill = b[7] ? Kolor_zielony : Kolor_czerwony;

                    int seba2;
                    seba2 = odczyt_rejestrow(0x10, 0x00);
                    Tekst4.Text = Convert.ToString(seba2);
                    
                }
                else
                {
                    Tekst0.Text = "Nie połączono!";
                }
            }

            catch (Exception ex)
            {
                Tekst1.Text = ("Error..... \n" + ex.StackTrace);
            } 

        }

        private void Przycisk1_Click(object sender, RoutedEventArgs e)
        {
            //Thread thread = new Thread(new ThreadStart(Watek1));
            //thread.Start();
            Thread thread1 = new Thread(new ThreadStart(Watek2));
            thread1.Start();
        }

        //public void Watek1()
        //{
        //    klient = new TcpClient();
        //    klient.Connect("10.3.0.119", 502);
        //}

        public void Watek2()
        {
            
            timer.Start();
            timer.Tick += timer_Tick;
        }

        private void Przycisk2_Click(object sender, RoutedEventArgs e)
        {
            Tekst3.Text = "Test 1";
        }

        private void Przycisk3_Click(object sender, RoutedEventArgs e)
        {
            Tekst3.Text = "Test 2";
        }

        private void Przycisk4_Click(object sender, RoutedEventArgs e)
        {
            klient.Close();
        }



    }
}

0

musisz WSZYSTKIE elementy, które mogą rzucić wyjątkiem np. z powodu wypięcia kabla opakować w try catch - nie ma innej możliwości

0

Obejmij również Connect w try...catch. Jedna rzecz którą warto wiedzieć, to to, że właściwość Connected ustawi się na false tylko w przypadku gdy nie uda się próba zapisu lub odczytu z socketa, więc jak wypniesz kabel najpierw będzie wyjątek, który złapiesz w catch, a próba połączenia nastąpi w kolejnym odpaleniu metody timer_Tick

0

Okej, rozumiem o co Wam chodzi tylko jak obsłużyć taki wyjątek i skąd wiedzieć, który wyjątek wybrać?
Dodałem 2 (nr 3 już był):

 
            catch (TimeoutException ex2)
            {
                klient.Close();
            }

            catch (SocketException ex1)
            {

                klient.Close();
            }
            catch (Exception ex)
            {
                Tekst1.Text = ("Error..... " + ex.StackTrace);
            } 

Wg mnie powinienem w bloku catch uwzględnić wyjątek dotyczący braku połączenia i w tym momencie przestać wysyłać i odbierać dane, a jedynie co jakiś czas próbować się ponownie połączyć. Tylko jak to zrobić?

0

Przecież przed próbą odbierania sprawdzasz czy jest połączenie i wtedy próbujesz połączyć. To będzie działać, nie wiem po co chcesz to rozbijać. Oczywiście - możesz pokombinować i wydzielić kod połączenia oraz zatrzymać timer w przypadku braku połączenia i gdzieś w pętli próbować połączyć. Droga wolna. Jeden problem, który możesz mieć, to wywołanie metody ponownie przez timer zanim jedno wywołanie się skończy, ale o tym już pisałem (nie sprawdzałem czy timer którego używasz temu zapobiega)

0

Żeby nie walczyć z kilkoma rzeczami na raz - przerzuciłem fragment kodu dotyczący łączenia i sczytywania danych do wykonania przy naciśnięciu przycisku (każdorazowo). Gdy kabel jest wypięty, a naciskam przycisk - całe okno znów wisi kilka sekund, a następnie dopiero obsługiwany jest wyjątek SocketException.

Prawdopodobnie mam więc problem z wątkami tak?
Jak "wrzucić" do jednego wątku obsługę reszty okna, a do drugiego wątku obsługę przycisku odpowiadającego za łączenie i wczytywanie danych?

0

Nie rozumiem, bo z tego co widzę, to wiesz jak odpalić próbę połączenia w osobnym wątku. Użyj tego co miałeś - Watek1() tylko dodaj tam try..catch. Jeżeli chodzi o odbieranie danych, to timer odpala zdarzenie asynchronicznie, czyli w osobnym wątku.

0

Ok kończę z propozycjami własnych rozwiązań bo się nie dogadamy :)

Problem jest tego typu, że mam teraz próbę połączenia i wczytania danych na przycisku. Jednak, gdy program nie może się skomunikować (wypięty kabel), okno zawiesza się na kilkanaście sekund, a następnie pojawia mi się okienko z napisem "Problem". Dopiero wtedy okno programu się odwiesza. Chcę uniknąć tych kilkunastu sekund zwieszenie programu.

private void Przycisk1_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                klient = new TcpClient();
                klient.Connect("10.3.0.95", 502);
                


                int seba1;
                seba1 = odczyt_wyjsc(0x05, 0x00);


                Tekst2.Text = Convert.ToString(seba1);
                BitArray b = new BitArray(new int[] { seba1 });

                Kontrolka8.Fill = b[0] ? Kolor_zielony : Kolor_czerwony;
                Kontrolka7.Fill = b[1] ? Kolor_zielony : Kolor_czerwony;
                Kontrolka6.Fill = b[2] ? Kolor_zielony : Kolor_czerwony;
                Kontrolka5.Fill = b[3] ? Kolor_zielony : Kolor_czerwony;
                Kontrolka4.Fill = b[4] ? Kolor_zielony : Kolor_czerwony;
                Kontrolka3.Fill = b[5] ? Kolor_zielony : Kolor_czerwony;
                Kontrolka2.Fill = b[6] ? Kolor_zielony : Kolor_czerwony;
                Kontrolka1.Fill = b[7] ? Kolor_zielony : Kolor_czerwony;

                int seba2;
                seba2 = odczyt_rejestrow(0x10, 0x00);
                Tekst4.Text = Convert.ToString(seba2);
            }

            catch (SocketException ex11)
            {
                MessageBox.Show("Problem!");
                klient.Close();
                
            }
}
 
0

Wybacz ale nie mam tyle czasu, żeby tobie pisać program. Musisz do wielu rzeczy dojść sam. Staram się jak mogę, żeby tobie pomóc rozwiązać konkretne problemy. Teraz masz łączenie się w wątku UI stąd te zawieszenie. Najprościej zrobić tak:

private void Przycisk1_Click(object sender, RoutedEventArgs e)
        {
            var thread = new Thread(() =>
            {
               try
               {
                   klient = new TcpClient();
                   klient.Connect("10.3.0.95", 502);
                   
                   int seba1;
                   seba1 = odczyt_wyjsc(0x05, 0x00);
  
                   Tekst2.Text = Convert.ToString(seba1);
                   BitArray b = new BitArray(new int[] { seba1 });
  
                   Kontrolka8.Fill = b[0] ? Kolor_zielony : Kolor_czerwony;
                   Kontrolka7.Fill = b[1] ? Kolor_zielony : Kolor_czerwony;
                  Kontrolka6.Fill = b[2] ? Kolor_zielony : Kolor_czerwony;
                  Kontrolka5.Fill = b[3] ? Kolor_zielony : Kolor_czerwony;
                  Kontrolka4.Fill = b[4] ? Kolor_zielony : Kolor_czerwony;
                  Kontrolka3.Fill = b[5] ? Kolor_zielony : Kolor_czerwony;
                  Kontrolka2.Fill = b[6] ? Kolor_zielony : Kolor_czerwony;
                  Kontrolka1.Fill = b[7] ? Kolor_zielony : Kolor_czerwony;
 
                  int seba2;
                  seba2 = odczyt_rejestrow(0x10, 0x00);
                  Tekst4.Text = Convert.ToString(seba2);
              }
              catch (SocketException ex11)
              {
                  MessageBox.Show("Problem!");
                  klient.Close();
                  
              }
            });
            thread.Start();
}
 
0
sebad13 napisał(a):

Problem jest tego typu, że mam teraz próbę połączenia i wczytania danych na przycisku. Jednak, gdy program nie może się skomunikować (wypięty kabel), okno zawiesza się na kilkanaście sekund, a następnie pojawia mi się okienko z napisem "Problem". Dopiero wtedy okno programu się odwiesza. Chcę uniknąć tych kilkunastu sekund zwieszenie programu.

żeby było po twojemu to przede wszystkim trzeba to napisać tak aby oddzielić obsługę połączenia od interakcji z userem. Pierwszym krokiem do tego będzie napisanie klasy, która przyjmie parametry do połączenia, będzie miała metody w stylu połącz, rozłącz, wyślij, właściwość połączony oraz zdarzenia odebrano, błąd. Jak to sobie napiszesz i będzie Ci działało bez zarzutów to wkładasz to w wątek (jak wyżej pokazane) i już

0

Sarrus
Oczywiście nie chodzi mi o gotowca, a jedynie na podprowadzenie do tematu. Widać, że jeszcze długa droga przede mną. Może polecisz jakąś książkę lub kurs? Zastanawia mnie też czemu w WPF zrezygnowali z kilku kontrolek - np. timer. Dużo łatwiej jest zrobić timera w WF poprzez zwykłe przeciągnięcie go, zamiast kodowania całości..

abrakadaber
Piszesz, że żeby było po mojemu.. Spróbuję Twojego sposobu, ale to niekoniecznie ma być po mojemu. Napisałem tutaj żeby dostać porady, którą drogę wybrać, a kod chcę oczywiście napisać sam. W takim razie napisz jak Ty byś to zrobił? Zależy mi na tym żeby połączyć się z konkretnym IP i co pewien okres czasu wymieniać z nim dane, ale w taki sposób aby zabezpieczyć program przed wypadkami typu utrata połączenia itp. Być może idę złą droga, być może jest lepsza, a może w ogóle zrezygnować z C# i WPF na rzecz innego języka bo da się łatwiej?
Znajomy korzysta z Delphi i tam dużo rzeczy może zrobić za pomocą przeciągnięcia kontrolek na okno główne (np. Socket do połączenia lub timer). Szczerze to wszedłem w C# i WPF z tego względu, że jest to nowsze i bardziej przyszłościowe rozwiązanie niż korzystanie ze starego Delphi, tylko nie rozumiem dlaczego w "nowszym" języku trzeba się więcej napracować żeby uzyskać ten sam efekt.

0 użytkowników online, w tym zalogowanych: 0, gości: 0