none
C Code in C# umschreiben RRS feed

  • Frage

  • Hallo Zusammen,

    ich müsste folgenden C code in C# umschreiben. Leider bin ich sehr neu in dem Gebiet.

    Wäre super wenn ihr mir helfen könntet.

     char COM_CHECK_SINGLE (char* befbuf, short len, unsigned short *deviceid, unsigned short *codeid, unsigned short *channelid, unsigned short *wertid)
    	{   //0    1         7     13        19      25
    		//<DIR>,<KENNUNG>,<CODE>,<CHANNEL>,<WERT><ETX>
    		//Die Zahlen sind grundsätzlich 5-stellig (unsigned short)
    		
    		// Generelle Befehlslänge OK ?
    		if (len != COM_LN_SINGLE) return (0);
    		
    		// Erster Befehlscheck: Größe und Rahmen soweit OK ?
    		if  ( (befbuf[1]!= ',')||
    		      (befbuf[7]!= ',')||
    			  (befbuf[13]!= ',')||
    			  (befbuf[19]!= ',')||
    			  (befbuf[25]!= '#')||
    			  ((befbuf[0]!= 'S') && (befbuf[0]!= 'R')))
    			  return (0);  // nein, so nicht, Framefehler
    		 
    		 // Ok, dann die Devicekennung extrahieren
    		 memset(&buf[0],'0',sizeof(buf));
    		 memcpy(&buf[0],&befbuf[2],sizeof(char)*5);
    		 buf[5]= '\n';
    		 *deviceid = atoi( &buf[0]);
    		 
    		 // Ok, dann die Befehlskennung extrahieren
    		 memset(&buf[0],'0',sizeof(buf));
    		 memcpy(&buf[0],&befbuf[8],sizeof(char)*5);
    		 buf[5]= '\n';
    		 *codeid = atoi( &buf[0]);
    		  
    		 // Ok, dann die Kanalkennung extrahieren
    		 memset(&buf[0],'0',sizeof(buf));
    		 memcpy(&buf[0],&befbuf[14],sizeof(char)*5);
    		 buf[5]= '\n';
    		 *channelid = atoi( &buf[0]);
    		 
    		  // Ok, dann die Befehlswertkennung extrahieren
    		  memset(&buf[0],'0',sizeof(buf));
    		  memcpy(&buf[0],&befbuf[20],sizeof(char)*5);
    		  buf[5]= '\n';
    		  *wertid = atoi( &buf[0]);
    		
    		// Rückgabe über die Parameterliste, wir sind fertig hier
    		return (1);
    	}
    
    
    
    char COM_GET_SINGLE (char* befbuf, short len, char befid, unsigned short deviceid, unsigned short codeid,  unsigned short channelid, unsigned short wertid)
    {   //0    1         7     13        19      25
    	//<DIR>,<KENNUNG>,<CODE>,<CHANNEL>,<WERT><ETX>
    	//Die Zahlen sind grundsätzlich 5-stellig (unsigned short)
    	
    	// Generelle Befehlslänge OK ?
    	if (len != COM_LN_SINGLE) return (0);
    	
    	// Gültige Befehlskennung ?
    	if ((befid != 'S') && (befid != 'R')) return (0);
    	
    	// ok, bauen wir zusammen
    	befbuf[0] = befid;
    	sprintf(&befbuf[1],",%05u,%05u,%05u,%05u#",deviceid,codeid,channelid,wertid);
    	
    	return (1);
    	
    }

    Donnerstag, 5. März 2015 09:07

Antworten

  • Hallo,

    grundsätzlich musst Du beachten, das .NET mit Unicode Zeichenketten arbeitest, Dein Modem vermutlich aber mit ANSI (oder ASCII). Bei Verwendung der SerialPort legt die Encoding-Eigenschaft die Konvertierung fest.

    Da ein .NET String sein Länge immer kennt und unveränderlich ist, benötigt man bei der ersten Funktion den len Parameter nicht mehr. Anstatt mit suspekten Fehlercodes (die gerne ignoriert werden), verwendet man in .NET generell Ausnahmen. Und so könnte die erste Funktion in etwa lauten:

            // len kann entfallen, ist durch befbuf festgelegt
            // anstatt der vielen refs wäre eine Struktur (als Rückgabe) sinnvoller
            void ComCheckSingle(string befbuf, /* short len, */ 
                ref ushort deviceid, 
                ref ushort codeid, 
                ref ushort channelid, 
                ref ushort wertid)
            {   //0    1         7     13        19      25
                //<DIR>,<KENNUNG>,<CODE>,<CHANNEL>,<WERT><ETX>
                //Die Zahlen sind grundsätzlich 5-stellig (unsigned short)
    
                if (befbuf == null)
                    throw new ArgumentNullException("befbuf");
                // Generelle Befehlslänge OK?
                if (befbuf.Length != COM_LN_SINGLE)
                    throw new ArgumentOutOfRangeException(befbuf, 
                        String.Format("Ungültige Länge, erhalten {0}, erwartet {1}.", befbuf.Length, COM_LN_SINGLE)) ;
    
                // Erster Befehlscheck: Größe und Rahmen soweit OK ?
                if ((befbuf[1] != ',') ||
                      (befbuf[7] != ',') ||
                      (befbuf[13] != ',') ||
                      (befbuf[19] != ',') ||
                      (befbuf[25] != '#') ||
                      ((befbuf[0] != 'S') && (befbuf[0] != 'R')))
                    throw new ArgumentOutOfRangeException(befbuf, "Ungültiger Aufbau.");
    
                // Wenn in der Folge keine Zahl wird eine Ausnahme ausgelöst!!!
                // Ok, dann die Devicekennung extrahieren
                deviceid = ushort.Parse(befbuf.Substring(2, 5));
                // Ok, dann die Befehlskennung extrahieren
                codeid = ushort.Parse(befbuf.Substring(8, 5));
                // Ok, dann die Kanalkennung extrahieren
                channelid = ushort.Parse(befbuf.Substring(14, 5));
                // Ok, dann die Befehlswertkennung extrahieren
                channelid = ushort.Parse(befbuf.Substring(20, 5));
            }

    Beachte, dass die Parse Methoden ebenfalls eine Ausnahme auslösen, sollte da keine Zahl stehen. Wobei es insgesamt übersichtlicher und praktischer wäre, die Methode würde eine Struktur / Klasse zurückgeben, die die Informationen enthält.

    Damit wäre dann auch Nr. 2 leichter umzusetzen:

        // wenn über eine Struktur (für ComCheckSingle) ginge es als ToString() Überladung 
            string ComGetSingle (char befid, ushort deviceid, ushort codeid, ushort channelid, ushort wertid)
            {   
                //0    1         7     13        19      25
    	        //<DIR>,<KENNUNG>,<CODE>,<CHANNEL>,<WERT><ETX>
    	        //Die Zahlen sind grundsätzlich 5-stellig (unsigned short)
    	
                // Gültige Befehlskennung ?
            	if ((befid != 'S') && (befid != 'R'))
                    throw new ArgumentOutOfRangeException("befid", 
                        String.Format("Erlaubt sind 'R' oder 'S', erhalten {0}.", befid));
    	
                // Rückgabe der Befehlszeichenfolge
                return String.Format("{0}{1:5},{2:5},{3:5},{4:5}", befid, deviceid,codeid,channelid,wertid);
            }
    

    Hier wird die Rückgabe der Methode für den Befehl verwendet, denn da man Strings nie verändern kann, muss man einen neuen erzeugen. Auch hier gibt es eine Ausnahme, sollte befid nicht wie erwartet sein.

    Siehe auch Kombinierte Formatierung zu den Format Möglichkeiten die .NET bietet.

    Insgesamt hübscher wäre es, eine Klasse zu erstellen, die über eine Parse Methode (Nr. 1) verfügt, um die Daten zu extrahieren und über ToString (Nr. 2) den Befehl zurück liefert. Das wäre insgesamt mehr .NET / OOP Stil.

    Gruß Elmar

    • Als Antwort markiert gsonur Donnerstag, 5. März 2015 12:09
    Donnerstag, 5. März 2015 09:43
    Beantworter

Alle Antworten

  • Hallo,

    grundsätzlich musst Du beachten, das .NET mit Unicode Zeichenketten arbeitest, Dein Modem vermutlich aber mit ANSI (oder ASCII). Bei Verwendung der SerialPort legt die Encoding-Eigenschaft die Konvertierung fest.

    Da ein .NET String sein Länge immer kennt und unveränderlich ist, benötigt man bei der ersten Funktion den len Parameter nicht mehr. Anstatt mit suspekten Fehlercodes (die gerne ignoriert werden), verwendet man in .NET generell Ausnahmen. Und so könnte die erste Funktion in etwa lauten:

            // len kann entfallen, ist durch befbuf festgelegt
            // anstatt der vielen refs wäre eine Struktur (als Rückgabe) sinnvoller
            void ComCheckSingle(string befbuf, /* short len, */ 
                ref ushort deviceid, 
                ref ushort codeid, 
                ref ushort channelid, 
                ref ushort wertid)
            {   //0    1         7     13        19      25
                //<DIR>,<KENNUNG>,<CODE>,<CHANNEL>,<WERT><ETX>
                //Die Zahlen sind grundsätzlich 5-stellig (unsigned short)
    
                if (befbuf == null)
                    throw new ArgumentNullException("befbuf");
                // Generelle Befehlslänge OK?
                if (befbuf.Length != COM_LN_SINGLE)
                    throw new ArgumentOutOfRangeException(befbuf, 
                        String.Format("Ungültige Länge, erhalten {0}, erwartet {1}.", befbuf.Length, COM_LN_SINGLE)) ;
    
                // Erster Befehlscheck: Größe und Rahmen soweit OK ?
                if ((befbuf[1] != ',') ||
                      (befbuf[7] != ',') ||
                      (befbuf[13] != ',') ||
                      (befbuf[19] != ',') ||
                      (befbuf[25] != '#') ||
                      ((befbuf[0] != 'S') && (befbuf[0] != 'R')))
                    throw new ArgumentOutOfRangeException(befbuf, "Ungültiger Aufbau.");
    
                // Wenn in der Folge keine Zahl wird eine Ausnahme ausgelöst!!!
                // Ok, dann die Devicekennung extrahieren
                deviceid = ushort.Parse(befbuf.Substring(2, 5));
                // Ok, dann die Befehlskennung extrahieren
                codeid = ushort.Parse(befbuf.Substring(8, 5));
                // Ok, dann die Kanalkennung extrahieren
                channelid = ushort.Parse(befbuf.Substring(14, 5));
                // Ok, dann die Befehlswertkennung extrahieren
                channelid = ushort.Parse(befbuf.Substring(20, 5));
            }

    Beachte, dass die Parse Methoden ebenfalls eine Ausnahme auslösen, sollte da keine Zahl stehen. Wobei es insgesamt übersichtlicher und praktischer wäre, die Methode würde eine Struktur / Klasse zurückgeben, die die Informationen enthält.

    Damit wäre dann auch Nr. 2 leichter umzusetzen:

        // wenn über eine Struktur (für ComCheckSingle) ginge es als ToString() Überladung 
            string ComGetSingle (char befid, ushort deviceid, ushort codeid, ushort channelid, ushort wertid)
            {   
                //0    1         7     13        19      25
    	        //<DIR>,<KENNUNG>,<CODE>,<CHANNEL>,<WERT><ETX>
    	        //Die Zahlen sind grundsätzlich 5-stellig (unsigned short)
    	
                // Gültige Befehlskennung ?
            	if ((befid != 'S') && (befid != 'R'))
                    throw new ArgumentOutOfRangeException("befid", 
                        String.Format("Erlaubt sind 'R' oder 'S', erhalten {0}.", befid));
    	
                // Rückgabe der Befehlszeichenfolge
                return String.Format("{0}{1:5},{2:5},{3:5},{4:5}", befid, deviceid,codeid,channelid,wertid);
            }
    

    Hier wird die Rückgabe der Methode für den Befehl verwendet, denn da man Strings nie verändern kann, muss man einen neuen erzeugen. Auch hier gibt es eine Ausnahme, sollte befid nicht wie erwartet sein.

    Siehe auch Kombinierte Formatierung zu den Format Möglichkeiten die .NET bietet.

    Insgesamt hübscher wäre es, eine Klasse zu erstellen, die über eine Parse Methode (Nr. 1) verfügt, um die Daten zu extrahieren und über ToString (Nr. 2) den Befehl zurück liefert. Das wäre insgesamt mehr .NET / OOP Stil.

    Gruß Elmar

    • Als Antwort markiert gsonur Donnerstag, 5. März 2015 12:09
    Donnerstag, 5. März 2015 09:43
    Beantworter
  • Hi Elmar,

    kannst du mir folgenden Code bitte auch noch umschreiben. Wäre dir echt Dankbar :)

    char CHECK_COMMUNICATION (void)
    	{
    	unsigned short devid = (unsigned short) HW_DEFID;
    	unsigned short channel = 0;
    	unsigned short befid = 0;
    	unsigned short wert = 0;
    	unsigned short blk_lang = 0;
    	
    	unsigned short befid_block[ COM_LN_BLCOMMAND] = {0}; 
    	unsigned short channel_block[ COM_LN_BLCOMMAND] = {0};  
    	unsigned short wert_block[ COM_LN_BLCOMMAND] = {0}; 
    	
    	short rec_bytes = 0;	
    		
    	char tbuf[COM_LN_SINGLE] = {0};
    		
    	
    
    	
    	rec_bytes = RS_RECEIVE();
    	
    	
    	
    	//Blockbefehl empfangen? (das sieht man an der Länge)
    	if ((rec_bytes <COM_LN_BLOCK ) && (rec_bytes >COM_LN_SINGLE))
    	{
    		//Block kann variable Länge haben, deshalb an der empfangenen Anzahl Zeichen orientieren
    		memcpy(&tbuf[0],&bef_readybuf[0],sizeof(char)*rec_bytes);
    		tbuf[rec_bytes] = '\0';
    		
    		
    		blk_lang = COM_CHECK_BLOCK(&tbuf[0],&bef_readybuf[0],&devid, &befid_block[0], &channel_block[0], &wert_block[0]);
    	}
    	
    	
    	
    	if ((rec_bytes <COM_LN_SINGLE ) && (rec_bytes >0))
    		{
    		// wir müssen aber das Stringendezeichen wieder hinpfrimeln, sonst geht unser COM-Modul nicht
    		memcpy(&tbuf[0],&bef_readybuf[0],sizeof(char)*COM_LN_SINGLE);
    		tbuf[COM_LN_SINGLE-1] = '\0';
    	
    		devid=0;
    		channel = 0;
    		befid=0;
    		wert=0;
    	
    	// Überprüfen, ob gültiger Befehl vorliegt
    	// wenn ja dann bekommen wir auch gültige Werte für devid, befid, etc.
    	rec_bytes = COM_CHECK_SINGLE (&tbuf[0], sizeof(tbuf), &devid, &befid, &channel, &wert);
    	
    	// ja
    	if (rec_bytes)
    		{
    			// Schicken wir das ganze mit Kennung 'R' wieder zurück als "Receive-Bestätigung"
    			COM_GET_SINGLE (&tbuf[0], sizeof(tbuf), 'R', devid, befid, channel, wert);
    			
    			// Receive-Bestätigung senden
    			sprintf(&rs_sendbuf[0],"%s\r\n",&tbuf[0]);
    			RS_SEND(strlen(&rs_sendbuf[0]));
    			
    			// Auf zur Auswertung
    			
    			//extern char COM_CHECK_SINGLE (char* befbuf, short len, unsigned short *deviceid, unsigned short *codeid,  unsigned short *channelid, unsigned short *wertid);
    		    COM_CHECK_SINGLE (&tbuf[0], strlen(tbuf), &devid, &befid,  &channel, &wert);
    				
    			if (devid == (unsigned short)HW_DEFID) // wir fühlen uns angesprochen 
    				{
    					if (befid == (unsigned short)COMMAND_READ_CHANNEL) // Befehl zum Auslesen eines Kanals erkannt
    						{
    							if ( (channel >0) && (channel <8)) // die ersten 8 sind die Drehzahlen
    								{
    								COM_GET_SINGLE (&tbuf[0], sizeof(tbuf), 'S', (unsigned short)HW_DEFID, (unsigned short)COMMAND_READ_CHANNEL, channel, iw_fanspeed[channel-1]);	
    								sprintf(&rs_sendbuf[0],"%s\r\n",&tbuf[0]);
    								RS_SEND(strlen(&rs_sendbuf[0]));
    								}
    								
    							if ( (channel >7) && (channel <16)) // 8-16 sind die PWM-Werte
    								{
    								COM_GET_SINGLE (&tbuf[0], sizeof(tbuf), 'S', (unsigned short)HW_DEFID, (unsigned short)COMMAND_READ_CHANNEL, channel, iw_pwm[channel-8]);
    								sprintf(&rs_sendbuf[0],"%s\r\n",&tbuf[0]);
    								RS_SEND(strlen(&rs_sendbuf[0]));
    								}
    						}
    						else // Kein Befehl den wir abarbeiten können - Fehler melden
    						{
    						COM_GET_SINGLE (&tbuf[0], sizeof(tbuf), 'R', (unsigned short)HW_DEFID, 65535, 65535, (unsigned short)ERROR_COMMAND); // Unbekannter Befehl
    						sprintf(&rs_sendbuf[0],"%s\r\n",&tbuf[0]);
    						RS_SEND(strlen(&rs_sendbuf[0]));	
    						}
    				}
    				else // falsches Device
    				{
    				COM_GET_SINGLE (&tbuf[0], sizeof(tbuf), 'R', (unsigned short)HW_DEFID, 65535, 65535, (unsigned short)ERROR_DEVICE); // Falsches Device
    				sprintf(&rs_sendbuf[0],"%s\r\n",&tbuf[0]);
    				RS_SEND(strlen(&rs_sendbuf[0]));	
    				}
    			
    		}
    		else // Framefehler
    		{   
    			COM_GET_SINGLE (&tbuf[0], sizeof(tbuf), 'R', (unsigned short)HW_DEFID, 65535, 65535, (unsigned short)ERROR_FRAME); // Framefehler
    			sprintf(&rs_sendbuf[0],"%s\r\n",&tbuf[0]);
    			RS_SEND(strlen(&rs_sendbuf[0]));
    		}
    		
    		}
    	
    	return (1);
    		
    	}

    Montag, 16. März 2015 09:03
  • Hallo,

    kann ich machen, doch wären vorab weitere Informationen hilfreich. Denn wenn man den .NET SerialPort einsetzt, wovon ich ausgehe, so ist der erste Schritt ausreichend Daten zur Verfügung zu haben. Denn DataReceived kann bereits früher ausgelöst werden, siehe dazu u. a. Top 5 SerialPort Tips [Kim Hamilton] - insbes. Tip 2.

    Deswegen müsste der erste Teil der Methode - RS_RECEIVE - anders gelöst werden. Hilfreich wäre dazu entweder die Länge oder ein definitives Ende-Byte für einen Block. Auch wäre nützlich zu wissen, ob die Daten immer als Zeichenkette kommen oder ob auch binäre Daten darunter sind.

    Gruß Elmar



    Montag, 16. März 2015 09:43
    Beantworter
  • Ein Block wird immer mit # beendet.

    Die Daten kommen immer als Zeichenkette.

    LG Onur

    Montag, 16. März 2015 09:59
  • Hallo Onur,

    bei genauerer Betrachtung (unter Berücksichtigung der vorherigen Funktionen) wird ein 1 : 1 umschreiben ziemlich sinnfrei.

    Da unter .NET der Serialport bereits einen String liefert und man jeweils neue Zeichenketten erzeugt (erzeugen muss) würde der Kopierteil komplett entfallen. So könnten z. B. die RS_SEND Strecken direkt implementiert werden (wie z. B. RsSendError, RsSendFrameError)

    Die Auswertung könnte, wie schon oben angedeutet, davon profizierten, dass man eine Klasse daraus macht, damit würden befid und Co. auf Membervariablen abgebildet und müssten nicht mehr in der Funktion verwaltet und auseinander gefriemelt werden.

    Was mir für den Nachweis der Praktikabilität fehlt, wäre die Behandlung der Blockbefehle, die oben nur als COM_CHECK_BLOCK auftauchen.

    Gruß Elmar

    Montag, 16. März 2015 15:24
    Beantworter
  • Hallo Elmar,

    wie du schon gemerkt hast versuch ich eine Serielle Schnittstelle zu programmieren. Ich habe jetzt das Problem das ich Senden kann aber nicht Empfangen. Der Code oben ist der Code von der Platine.

    Jetzt versuch ich in C# Programm zu schreiben mit dem ich Daten schicken und empfangen kann. Das senden funktioniert soweit aber leider kann ich nichts empfangen.

    Ist der Code mit dem ich versusch zu empfangen. Kannst du mir wieder mal weiterhelfen?

     private void bCreateSP_Click(object sender, EventArgs e)
            {
    
                //Hier erstellen wir unseren Serialport und legen die Einstellungen fest
                serialPort = new SerialPort(cbPort.Text, Convert.ToInt32(cbBaudRate.Text), (Parity)Enum.Parse(typeof(Parity), cbParity.Text), Convert.ToInt16(cbDataBits.Text), (StopBits)Enum.Parse(typeof(StopBits), cbStopbits.Text));
                serialPort.Handshake = (Handshake)Enum.Parse(typeof(Handshake), cbHandshake.Text);
                serialPort.RtsEnable = Boolean.Parse(cbRtsEnable.Text);
                serialPort.DtrEnable = Boolean.Parse(cbDtrEnable.Text);
    
                if (!serialPort.IsOpen)
                {
                    serialPort.Open(); //Serialport öffnen
                }
                lbRecievedDelegate = new InvokeLB(InvokeLBRecieved);
                serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived); //DataRecieved Event abonnieren
            }
    
            void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                string RecievedLine = " ";
                while (RecievedLine != "")
                {
                   RecievedLine = serialPort.ReadLine();
                   lbRecieved.Invoke(lbRecievedDelegate,new object[]{RecievedLine});
                }
                
            }

    Mittwoch, 25. März 2015 09:09
  • Hallo,

    so wie der Code oben steht, kannst Du nichts verarbeiten, weil ReceivedLines mit einem Leerzeichen initialisiert wird, aber auf eine leere Zeichenfolge testest, wodurch die Schleife nie durchlaufen wird.

    Im übrigen Du solltest die Eingabe solange sammeln bis das Endezeichen ("#") enthalten ist oder aber eine maximale Länge überschritten wurde (für den Fall, dass die Empfangsdaten kaputt sind).

    Gruß Elmar

    Freitag, 27. März 2015 07:39
    Beantworter
  • Hi Elmar,

    ich habe es einfach nicht hinbekommen. Kannst du mir vielleicht den Code so überarbeiten, dass es funktionieren könnte.

    Wärst mir eine sehr sehr große Hilfe.

    Gruß Onur

    Mittwoch, 1. April 2015 08:43