Avatar billede microjet Nybegynder
25. september 2009 - 02:21 Der er 13 kommentarer og
1 løsning

Hent data fra comporten med høj hastighed... hvordan

Hejsa

Jeg håber der en eller anden haj der har stor erfaring med at programere til comporten der læser dette.

Jeg har bøvlet i flere uge med at få data fra comporten uden at miste data... men det lykkes bare ikke... Jeg har en test-unit der sender en ASCII streng eks: "G+12345.\r" 2000gange/sec.

Baudraten er 230400baud

Dvs. 9 bytes pr. streng ~ 90bit * 2000 = 180000bit/sec... så der burde være luft i regnskabet når der køres 230400baud

Jeg er nået der til at jeg "kun" mister en byte ud af op til  2.000.000bytes, men hvis jeg bevæger vinduer eller gør noget andet mister jeg bytes efter måske kun 1000 bytes.

Jeg har prøvet at bruge serialportens "Data recieved event"

Jeg har prøvet at hente en byte ad gangen med "readbyte"

Jeg har prøvet at bruge "ReadTo("\r") så der læses til carriage return

Sidstnævnte virker bedst. Desuden har jeg prøvet at sætte prioriteten op på den tråd der henter data, men det afhjælper ikke problemet.

Jeg har hævet buffersize til 16384, og checker om den når over, hvilket den ikke gør... bufferen bliver ikke fyldt op mere.

HyperTerm mister ikke data, men det gør min kode!

Jeg mister flest data når der er aktivitet på computeren.

Så min kode er altså ikke så interessant, men hvis en eller anden kan poste et stykke kode der kan hente data fra comporten uden at miste data så er der 200point at hente

Stil gerne flere spørgsmål hvis ovenstående ikke er fyldestgørende!
Avatar billede js_delphi Nybegynder
25. september 2009 - 09:09 #1
Jeg udvikler selv maaleprogrammer med dataopsamling, dog ikke i C#.
Det er aldrig lykkes mig at faa et program til at koere "real-time" paa en Windows maskine, da Windows ikke er egnet til dette.

Jeg har altid brugt andet hardware (real-time) til at opsamle data i en buffer. Mine programmer, som koerer paa Windows maskiner, toemmer saa denne buffer, naar Windows mener at have tid til det.

Dette er sikkert ikke nogen hjaelp for dig, men jeg lytter lige med her.
Avatar billede bitmatic Nybegynder
25. september 2009 - 09:43 #2
Prøv at lade det her køre i lidt tid, og se om du mister data...

namespace ConsoleApplication5
{
    class Program
    {
        internal static SerialPort sp = new SerialPort("COM1");
        internal static Queue<string> inQueue = new Queue<string>();
        static void Main(string[] args)
        {
            sp.ReceivedBytesThreshold = 100;
            sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
            sp.ErrorReceived += new SerialErrorReceivedEventHandler(sp_ErrorReceived);

            Console.ReadLine();
            System.Diagnostics.Debugger.Break();
        }

        static void sp_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            System.Diagnostics.Debugger.Break();
        }
               
        static void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            inQueue.Enqueue(sp.ReadExisting());
        }
    }
}
Avatar billede bitmatic Nybegynder
25. september 2009 - 09:44 #3
Jeg sidder i øvrigt ikke ved en maskine med en comport lige nu, så jeg har ingen pejling om det overhovedet kan køre - men princippet er der :-)
Avatar billede microjet Nybegynder
25. september 2009 - 12:08 #4
Tusind tak!... jeg forsøger med det samme!
Avatar billede microjet Nybegynder
25. september 2009 - 12:13 #5
js-delphi

Jeg har tidligere prog i Delphi, men vi er gået over til c#... og har bestemt ikke fortrudt, men jeg har samme erfaring som dig at realtime og COMport ikke er en god kombination.

Jeg har heller ikke brug for realtime, men blot at jeg får de data der rent faktisk bliver sendt... og det gør jeg ikke lige nu.

Men hvis jeg logger med det gode gamle program HyperTerm... så mangler der ikke en eneste måling... men det køre sikkert noget direkte windows API
Avatar billede microjet Nybegynder
25. september 2009 - 14:35 #6
Hej bitmatic

Så er jeg tilbage igen... jeg har testet dit udmærkede eks, men jeg må desværre skuffe dig... fejl indtræffer meget hurtigt... nogle gange inden måling nr 100... hvilket jo er meget hurtigt, når der kommer 2000/sec

Her er koden jeg brugte:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;

namespace ConsoleApplication1
{
    class Program
    {
        static int ErrorCount = 0;
        internal static SerialPort sp = new SerialPort("COM5");
        internal static Queue<string> inQueue = new Queue<string>();
        static void Main(string[] args)
        {
           
            sp.BaudRate = 230400;
            sp.ReceivedBytesThreshold = 100;
            sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
            sp.ErrorReceived += new SerialErrorReceivedEventHandler(sp_ErrorReceived);
            sp.Open();
            sp.WriteLine("GG\r");//sender kommando der starter transmission af 2000 datapakker/sec
            Console.ReadLine();
            System.Diagnostics.Debugger.Break();
        }

        static void sp_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            System.Diagnostics.Debugger.Break();
        }

        static void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            //inQueue.Enqueue(sp.ReadExisting());
            string str1 = sp.ReadTo("\r");// læser til carrige return
            inQueue.Enqueue(str1);
            if (!str1.Length.Equals(8))
            {
                ErrorCount++; // checker pakke størelsen!
            }
            if (ErrorCount > 10)
            { }
        }
    }

}
Avatar billede bitmatic Nybegynder
25. september 2009 - 14:50 #7
hehe.... Nu har du jo heller ikke brugt den kode jeg foreslog...

Kunne du prøve at ændre din DataReceived event til:

static void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  inQueue.Enqueue(sp.ReadExisting());
}
Avatar billede microjet Nybegynder
25. september 2009 - 15:21 #8
Hej bitmatic

det har du jo ret i :-)..MEN jeg prøvede det til at starte med og fik stadig fejl, det var bare sværere på den måde at finde fejlene, da der indlæses mange pakker i et hug!

Men jeg har nu testet det lidt mere og det ser ud til at det kun er i første indlæsning der kommer nogle "?"

Jeg vil give det en chance til og teste lidt mere... det kan være at  read existing bruger færre ressourcer... så måske er det ikke en hel dum ide du kom med der ;-)

Du høre nærmere... men pt. stadig fejl i indlæsningen!
Avatar billede microjet Nybegynder
25. september 2009 - 15:46 #9
Et eksempel på data:

Der er lavet 4 læsninger med ReadExisting... dvs bufferen er blevet tømt 4 gange.

herefter læses eks følgende data:

"G+00035.\rG+0003s??G+00037.\rG+00038.\r...osv

men G+0003s?? er jo altså fejldata!... nu checker jeg kun på "?" fordi jeg ofte ser dette tegn i fejl data!

men ofte mangler der også en karakter i "pakken"

Jeg poster lige den nye kode!
Avatar billede microjet Nybegynder
25. september 2009 - 15:47 #10
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;

namespace ConsoleApplication1
{
    class Program
    {
        static int EmptyBuffer = 0;
        static int ErrorCount = 0;
        internal static SerialPort sp = new SerialPort("COM5");
        internal static Queue<string> inQueue = new Queue<string>();
        static void Main(string[] args)
        {
           
            sp.BaudRate = 230400;
            sp.ReceivedBytesThreshold = 100;
            sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
            sp.ErrorReceived += new SerialErrorReceivedEventHandler(sp_ErrorReceived);
            sp.Open();
            sp.WriteLine("GG\r");//sender kommando der starter transmission af 2000 datapakker/sec
            Console.ReadLine();
            System.Diagnostics.Debugger.Break();
        }

        static void sp_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            System.Diagnostics.Debugger.Break();
        }

        static void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            string str1 = sp.ReadExisting();
            EmptyBuffer++;

            if(str1.Contains("?"))// checker på kendt fejl data!
            { Console.WriteLine(str1); }

            inQueue.Enqueue(str1);

        }
    }

}
Avatar billede bitmatic Nybegynder
25. september 2009 - 19:06 #11
Hep hey !! Nu kom der lidt kød på.

Hvis den streng du modtager indeholder spørgsmålstegn. Så er det nok ikke fordi du har mistet data. Det er snarere fordi string ikke har kunnet parse det modtagne byte[] med den angivne encoding.

Du skal sætte din SerialPort.Encoding op til at passe til encodingen af de bytes der bliver sendt ud på porten. Hvis du ikke kender encodingen af bytes i den dims der sprøjter dataene ud, så prøv hele lortet igennem, der er ikke så frygteligt mange. Start med ASCII og UTF-8 - Det er de mest brugte.

Prøv det først.

Du kan evt. også checke dine data ved at læse dem som byte[] i stedet for string. ReadExisting laver en konvertering. Prøv at læse med en af de funktioner der læser dataene ned i en byte buffer i stedet.
Avatar billede microjet Nybegynder
25. september 2009 - 21:23 #12
Det kan godt være du er inde på noget!

Hvilke encoding står den mon til default?

Nu checker jeg kun for lige den fejl jeg kunne se med det blotte øje da jeg skimmede bufferen.

Det sker også at der mangler en karakter eller den er dobbelt.
min test unit sender ren ASCII tekst, så ingen special tegn

Der køres et loop som følgende med 2000 resultater i sek. så det tager lidt over 32sek før tælleren starter på nul.

\r = 0D hex eller carrige return

G+00000.\r
G+00001.\r
G+00002.\r
...
...
G+65534.\r
G+65535.\r (tæller ruller over og starter forfra)
G+00000.\r

Men vile det ikke være lidt mærkeligt hvis det kun virkede nogle gange?

... men tak fordi du hænger på tråden :-)
Avatar billede microjet Nybegynder
28. september 2009 - 13:58 #13
Jeg har prøvet det meste efterhånden...

læse enkelte byte
læse existing
bruge ReadTo

Jeg har sågar brugt en win32 dll der bruger direkte API kald til at hente bytes, men det fejler også.

Ens for alle metoder er at så snart computeren belastes ved at rykke  vinduer rundt eller starte en anden comport, så fejler det helt vildt.

**PROBLEMET** er at ligemeget hvor stor input-bufferen er så bliver den fyldt... der går bare længere tid hvis bufferen er stor. Det kan bare ikke lade sig gøre at tømme bufferen hurtigt nok. Det virker som om det prioriteres så lavt at alle andre processer kan forsinke læsningen af COMporten.

Jeg håber virkelig en eller anden har et godt forslag!
Avatar billede microjet Nybegynder
29. september 2009 - 16:45 #14
Jeg tror mit problem er løst:-)

Det viser sig at SerialPort i .NET 3.5 simpelt hen er så ringe at den nærmest er ubrugelig... Det viser sig åbenbart at der er flere end mig der har haft de mest mystiske problemer... bla. med data-tab.

http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvb/thread/3e64f562-e74b-4213-8019-e69f54fc0d74

Nåå men for at gøre en lang historie kort, så løste problemet sig da jeg hentede OpenNETCF's comport dll... det spiller bare. Der har været et par småfejl, hvis jeg presser maskineriet ved at starte virusscan og hente data på 2 comporte samtidig, men der er tale om så lidt så jeg kan leve med det og måske endda forbedre koden, så det slet ikke sker.

** OpenNETCF kan downloades her: http://serial.codeplex.com/  **

OpenNETCF.IO.Serial er næsten identisk med den der ligger i .NET 3.5, Så det kræver ikke de store ændringer i ens kode. Der er ændret lidt på navnene til eventhandleren, men i det store hele er metoderne identiske.

Så jeg ville da foreslå alle der har problemer med serialporten hurtigst muligt får downloadet og erstattet Microsoft's SKOD SERIEL DRIVER!!!... Ahhh så kom det ud ;-)


Her min nye test kode:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
//using System.IO.Ports;
using OpenNETCF.IO.Ports;

namespace ConsoleApplication1
{
    class Program
    {
        static int EmptyBuffer = 0;
        static int ErrorCount = 0;
        internal static SerialPort sp = new SerialPort("COM5");
        internal static Queue<string> inQueue = new Queue<string>();
        internal static Queue<string> PacketQueue = new Queue<string>();
        static void Main(string[] args)
        {
            Console.WriteLine("Serial test for 2000 samples/sec");
            System.Threading.Thread deQ = new System.Threading.Thread(DeQue);
            deQ.Start();
       
            sp.BaudRate = 230400;
            sp.ReadBufferSize = 180000;//har leget lidt med buffersize... under normal brug kommer der ikke megert mere end 4-5000 bytes i  ad gangen, men det kan sagtens stige til over 10000 bytes
            //sp.ReadBufferSize = 16384;
            sp.DataBits = 8;
            sp.StopBits = StopBits.One;
            sp.Handshake = Handshake.None;
            sp.Parity = Parity.None;
            sp.Encoding = Encoding.ASCII;
            //sp.ReceivedBytesThreshold = 100;
            sp.ReceivedEvent += new SerialReceivedEventHandler(sp_DataReceived);
            sp.ErrorEvent += new SerialErrorEventHandler(sp_ErrorReceived);
            sp.Open();
            sp.DiscardInBuffer();
            sp.WriteLine("SG");//sender kommando der starter transmission af 2000 datapakker/sec
            Console.ReadLine();
            System.Diagnostics.Debugger.Break();
        }


        static void DeQue()
        {
            int CycleCounter = 0;
            int PacketCounter = 0;
            string str2 = "";
            string str1 = "";
            int error = 0;
            while (true)
            {
               
                int t = 0;
                if (inQueue.Count > 0)
                {
                   
                  str1 = inQueue.Dequeue();
                    t++;
                    for (int i = 0; i <= str1.Length-1; i++) //henter resultater ud af strengene i buffer/kø
                    {
                        str2 += str1[i];
                        if (str1[i] == Convert.ToChar("\r"))
                        {
                            PacketQueue.Enqueue(str2);//lægger resultaterne i ny buffer/kø!
                            string Pnum = ("G+" + PacketCounter.ToString("d5")+".\r");
                            if (!(str2 == Pnum))
                            {
                                error++;
                                Console.WriteLine(error + "  " + str2+" PacketNr: "+PacketCounter);
                            }

                            if (error > 10)
                            { }

                            if (str2 == "G+65535.\r")
                            {
                                CycleCounter++;
                                Console.WriteLine("Cycle nr: " + CycleCounter+"    PacketCounter: "+PacketCounter);
                                PacketCounter = -1;
                            }

                            PacketCounter++;

                            str2 = "";
                        }

                    }
               
                }
            }

        }



        static void sp_ErrorReceived(object sender, SerialErrorEventArgs e)
        {
            System.Diagnostics.Debugger.Break();
        }



        static void sp_DataReceived(object sender, SerialReceivedEventArgs e)
        {

            if (sp.BytesToRead > 3000)
            {
                Console.WriteLine("Bytes in buffer = " + sp.BytesToRead);
            }
            string str1 = sp.ReadExisting(); //Alle bytes i bufferen læses ind i en streng
            EmptyBuffer++;
            if (EmptyBuffer == 1)
            { Console.WriteLine("Started"); }
            inQueue.Enqueue(str1);          //Strengen sættes i en kø/buffer
        }
    }
}
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
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

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