C# + Modbus TCP

Odpowiedz Nowy wątek
2014-12-17 11:37
sebad13
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.

Pozostało 580 znaków

2014-12-17 12:09
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

Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.

Pozostało 580 znaków

2014-12-17 13:48
sebad13
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ł


3. 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!";
            }

    }

Pozostało 580 znaków

2014-12-17 15:02
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

Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.

Pozostało 580 znaków

2014-12-18 14:24
sebad13
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?

Pozostało 580 znaków

2014-12-18 14:31
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


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.

Pozostało 580 znaków

2014-12-18 14:38
sebad13
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.

Pozostało 580 znaków

2014-12-18 15:27
0

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

Pozostało 580 znaków

2014-12-18 15:47
sebad13
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();
        }

Pozostało 580 znaków

2014-12-18 20:08
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

Pozostało 580 znaków

2015-01-14 10:10
sebad13
0

Okej, dzięki za pomoc.

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

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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