Avatar billede fiskerendk Nybegynder
14. februar 2013 - 13:15 Der er 16 kommentarer og
1 løsning

Køre Function/Sub flere gange uden at lave longblocking,

Hej,

jeg er ved at lave et program som skal skrive og læse fra en TCP Stream og kontrollere et anlæg via Modbus.

Jeg kan sagtens skrive og læse til TCP Streamen. Men jeg har det problem at det er ikke altid at der bliver svaret på, og jeg vil derfor gerne have den til at forsøge 3 gange indtil den for svar.

Jeg kører med BeginRead i en seperat Sub for at være sikker på at den læser alt den data jeg efterspørger og læser hele tiden, har prøvet med blot Read men resultatet er ikke stabilt.

Jeg gemmer indholdet fra BeginRead Sub'en i en Global variable DataReceived,

Problemet er hvis jeg laver en loop som sender til streamen 3 gange med et lille sleep indtil DataReceived er noget så skriver den aldrig til variablen og stopper efter 3 forsøg. Skriver jeg så igen så virker den blot med det gamle svar som er kommet efter loopen.

Jeg håber det giver mening, ellers må i sige til.
Avatar billede runesoft Nybegynder
14. februar 2013 - 13:28 #1
Jeg kender ikke noget til modbus..  men.  med TCP er du garanteret levering, det burde derfor ikke være nødvendigt at sende beskeden flere gange. Det lyder til gengæld som om du prøver at læse svaret før det er kommet retur. Kan du ikke hooke en event op til at modtage svaret, eller polle efter svaret så du prøver at modtage, indtil du har modtaget noget istedet for at blive ved med at sende.
Avatar billede fiskerendk Nybegynder
14. februar 2013 - 14:32 #2
Ja altså jeg er også sikker på den er leveret for kan se på anlægget at den gør det ting jeg beder den om, som sagt problemet er bare jeg ikke altid får svar på det, og det skal jeg bruge i mit program da det kan være en temperatur den skal sende tilbage.

Har du en ide til hvordan det kan gøres? Da mit nuværende Async NetworkStreamBeginRead jo ikke er så nemt at få til at virke.

Jeg har nemlig før prøvet at lave en NetworkStream.Read, men når jeg gør det og f.eks. har sat buffer(64) eller lign, så venter den jo til der er 64bytes og da svaret kan variaere fra 8-50 bytes er det sq lidt svært at styre syntes jeg. og sætter jeg den til Buffer(0 eller 1) så smider den bare en helvede masse 00 bytes i hoved + lidt data + en masse nuller igen.
Avatar billede runesoft Nybegynder
14. februar 2013 - 17:12 #3
er du sikker på at 00 bytene ikke er del af protokollen der returneres. Jeg antager at du ikke har skrevet koden på den dims du snakker med. så vidt jeg kan se er de første 8 bytes i en modus besked ikke data. stemmer det overens med hvad du får tilbage?
Avatar billede arne_v Ekspert
14. februar 2013 - 18:56 #4
Nej.

En read med en buffer 64 returnerer naar der er nogle data og fortaeller dig hvor mange data der faktisk er. Den venter ikke paa at buffer er fuld.
Avatar billede fiskerendk Nybegynder
15. februar 2013 - 10:00 #5
Hej igen, Jeg har nu brugt aftenen på at lave mit program så simpelt som overhoved muligt og droppet alt det jeg var ude i med Async læsning osv. Less is more eller hvad man siger.

Når jeg sender "1E:10:03:EB:00:01:02:00:03:4E:7A" til min Moxa NPort via følgende Function får jeg følgende svar 1E:10:03:EB:00:01:73:D6:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00

Og det er rigtig find for nu ser det ud som om jeg for alt det text som jeg skal bruge, og ser ud til at være rimelig stabilt, hvis jeg kigger på min Log på Moxa NPorten deler den svaret op i 2 receive linier og jeg tror det har været mit problem.
2013/02/15 10:16:27[T] 1E 10 03 EB 00 01 02 00 03 4E 7A
2013/02/15 10:16:27[R] 1E
2013/02/15 10:16:27[R] 10 03 EB 00 01 73 D6

Tilgengæld har jeg stadig det problem at jeg modtager ikke et svar hver eneste gang jeg sender en kommando. Nogen gange skal den sendes 3 gange pr gang. Nogen ideer?

Og endt op med følgende kode:

    Public Function SendReceive(ByVal SendCommand As String)
        Dim returnCode As String = Nothing

        '//Write
        Dim outText As String = SendCommand
        Dim outNumBytes As Integer = 0
        Dim outStream As Byte() = New Byte((outText.Length + 1) \ 3 - 1) {}

        Try
            For outNumBytes = 0 To outStream.Length - 1
                outStream(outNumBytes) = Byte.Parse(outText.Substring(3 * outNumBytes, 2), Globalization.NumberStyles.HexNumber)
            Next

            NetworkStream.Write(outStream, 0, outNumBytes)

            txtLastSend.Text = outText
            txtLastReceived.Text = Nothing
        Catch ex As Exception
            returnCode = "Error: " & ex.Message

            Return returnCode
            Exit Function
        End Try

        '//Read
        Dim inStream(32) As Byte
        Dim inBytes As New StringBuilder(inStream.Length * 2)
        Dim inText As String = Nothing

        Do While NetworkStream.CanRead And NetworkStream.DataAvailable

            Try
                NetworkStream.Read(inStream, 0, inStream.Length)

                For Each inByte As Byte In inStream
                    inBytes.AppendFormat("{0:X2}:", inByte)
                Next

            Catch ex As Exception
                returnCode = "Error: " & ex.Message

                Return returnCode
                Exit Function
            End Try

        Loop

        inText = inBytes.ToString()
        returnCode = inText

        Return returnCode
    End Function
Avatar billede arne_v Ekspert
15. februar 2013 - 12:34 #6
And NetworkStream.DataAvailable

er ikke god.

Den betyder at hvis der ikke er modtaget nogle data naar du naar dertil saa opgiver du at laese.

Drop den. Og Read indtil du har faaet det du skal bruge.
Avatar billede fiskerendk Nybegynder
15. februar 2013 - 13:49 #7
Altså hvis jeg fjerner denne så laver den jo Read uendeligt, Hvordan kan jeg bedst lave en løsning der så stopper read'en når den ikke modtager mere data.
Avatar billede arne_v Ekspert
15. februar 2013 - 13:53 #8
Der maa vaere noget i svaret som markerer naar det er slut.

Maaske sende de laengden i foerste byte.

Masske afsluttes der med CR LF.
Avatar billede fiskerendk Nybegynder
15. februar 2013 - 14:28 #9
Den første byte er desværre blot ID'en på den ModBus slave enhed. i det her tilfælde 1E = 30. Og går igen på både write og read. Problemet med CR/LF er desværre at af en eller anden årsag så deler den svaret op i flere linier når jeg kigger i Moxa loggen f.eks.

2013/02/15 15:08:34[T] 1E 10 03 EB 00 01 02 00 03 4E 7A
2013/02/15 15:08:34[R] 1E
2013/02/15 15:08:34[R] 10 03 EB 00 01 73 D6

[T] er transmit og selvfølgelig er den streng jeg sender.
[R] er receive og der skulle optimalt jo komme 1E 10 03 EB 00 01 73 D6 i en linie. men tilsyneladende deles den i to linier. Men det kan jo også være Moxa NPorten eller lign der laver noget pjat for det virkede fint som SerielPort istedet for TCP.
Avatar billede arne_v Ekspert
15. februar 2013 - 14:33 #10
Der er ikke noget overraskende i opsplit.

TCP er stream orienteret, saa det er helt normalt at:
- der sendes 100, 300 og 200 bytes
- der laeses 50, 250, 150 og 150 bytes

men der maa vaere en eller anden maade hvorpaa du kan se i svaret om du har faaet det hele.

Fast laengde?
Avatar billede fiskerendk Nybegynder
15. februar 2013 - 14:46 #11
Der er ligesom 3 typer svar jeg kan få ud af de 3 forskellige typer kommando'er man kan sende. Kort fortalt.

FUNC 3 Byte 1 = Slave ID, Byte 2 = Function 3, Byte 3 = antal bytes at læse, Byte 4+5 6+7 osv = Data, Sidste 2 bytes er CRC.

FUNC 4: Byte 1 = Slave ID, Byte 2 = Function 4, Byte 3 = Antal bytes for at læse, Byte 4+5 6+7 8+9 osv osv = Data, Sidste to bytes CRC.

FUNC 16: Den ovenfra den er rimelig nem for den har et fast svar på 8 bytes og de er altid Byte 1 = Slave ID, Byte 2 = Function 10(16 decimalt), Byte 3 = (mængde af data som kommer her 3 bytes), Byte 4-6 = Affald :), Byte 7+8 = CRC.
Avatar billede arne_v Ekspert
15. februar 2013 - 15:06 #12
Saa du skal altsaa foerst laese 3 bytes. Saa tager du vaerdien af den 3. byte og bestemmer hvor mange bytes du skal laese. Og du venter indtil du har faaet dem alle. Og naar du har det saa koerer du videre.
Avatar billede fiskerendk Nybegynder
18. februar 2013 - 16:30 #13
Det er super Arne, du har jo fuldstændig ret, jeg skal blot lige lave en kode der passer til det, midlertidigt er jeg dog kommet ud over det ved blot at lave 64bytes også sortere alle de sidste fra. Men når jeg skal kigge lidt på den del af koden igen så fixer jeg det da sådan at den læser 3 bytes og så hvor mange der er nødvendigt derefter.

En anden ting arne, Jeg har et andet problem også som du måske kan give mig et praj til. Jeg har en TcpListener hvor jeg modtager en Stream, jeg sender teksten 1E:03:03:EC:00:01:47:D4 som en ASCII som så bliver lavet om til byte som så bliver lavet om til ASCII igen på "server" siden. Men når denne bliver lavet om med Dim modtaget as string = Encoding.ASCII.GetString(ByteString) og jeg skal bruge modtaget videre så kan jeg godt skrive teksten til console eller eventviewer og få samme tekst ud altså "1E:03:03:EC:00:01:47:D4" som tekst. men bruger jeg strengen til en funktion så laver den input strengen var ikke korrekt eller lign. Men tager jeg og sætter modtaget = "1E:03:03:EC:00:01:47:D4" så kan jeg godt bruge strengen. Hvad går der galt i encodningen der?

Og smid lige et svar til det andet så du kan få nogle points.


mvh
Thomas Nissen
Avatar billede arne_v Ekspert
18. februar 2013 - 16:36 #14
svar
Avatar billede arne_v Ekspert
18. februar 2013 - 16:37 #15
Er du sikker paa at der ikke er nogle non-printable tegn sidst i strengen?
Avatar billede fiskerendk Nybegynder
18. februar 2013 - 16:42 #16
Hold da kæft du er hurtig :) Jeg tror jeg har fixet det jeg har ændret følgende nedenunder. Problemet var nok som du siger at i ASCII konverteringen var der mere data i form af ingen ting eller lign. Nu ASCII jeg selvf samme data som jeg modtager og det ser ud til at virke. Tak Endnu engang.

Fra:
---------------------
Dim TcpReceiveBytes(TcpClient.ReceiveBufferSize) As Byte
TcpStream.Read(TcpReceiveBytes, 0, CInt(TcpClient.ReceiveBufferSize))

Dim TcpReceive As String = Encoding.ASCII.GetString(TcpReceiveBytes)


Til:
---------------------
Dim TcpReceiveBytes(TcpClient.ReceiveBufferSize) As Byte
TcpStream.Read(TcpReceiveBytes, 0, CInt(TcpClient.ReceiveBufferSize))

Dim TcpReceive As String = Encoding.ASCII.GetString(TcpReceiveBytes, 0, TcpClient.ReceiveBufferSize)
Avatar billede arne_v Ekspert
18. februar 2013 - 16:51 #17
Ja - du har nok haft nogle null bytes efter de egentlige data.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester