Forum: PC Hard- und Software Daten loggen mit PCANBasicExample in Visual Studio 2008


von CANorNotCAN (Gast)


Lesenswert?

Hallo zusammen,

ich möchte CAN-Messages loggen. Als Grundgerüst verwende ich das Example 
von PCAN-Basic API in Visual Studio 2008. Die ReadMessages-Prozedur will 
ich so verändern, dass die Messwerte nicht nur gelesen, sondern auch in 
einer csv-Datei gespeichert werden:
1
#include <fstream>
2
#include <string>
3
#include <sstream>
4
#include<iostream>
5
using namespace std;
6
7
[...]
8
9
TPCANMsg ^CANMsg;
10
    TPCANTimestamp ^CANTimeStamp;
11
    TPCANStatus ^stsResult;
12
13
    /// added code
14
    String ^filename, ^head;
15
    ofstream logfile;
16
    ostringstream ssFilename;
17
    ///
18
19
    // Initializes Variables
20
    //
21
    CANTimeStamp = gcnew TPCANTimestamp();
22
    CANMsg = gcnew TPCANMsg();
23
    filename = fileName->Text;
24
    //
25
26
    ssFilename.str("");
27
    ssFilename.clear();
28
    ssFilename << &filename << "_" << &CANTimeStamp << ".csv";
29
    filename=ssFilename.str();
30
    logfile.open(filename.c_str(),ios_base::out | ios_base::app);
31
    
32
    head="Time";
33
    for(int i=1;i<13;i++)
34
    {
35
    head=strcat_s(head,", ");
36
    head=strcat_s(head,"Cell",num2str(i));
37
    }
38
39
    logfile << head  << endl << "Start date: " << CANTimeStamp << endl;
40
    /// end of added code
41
42
    // We read at least one time the queue looking for messages.
43
    // If a message is found, we look again trying to find more.
44
    // If the queue is empty or an error occurr, we get out from
45
    // the dowhile statement.
46
    //      
47
    do
48
    {
49
      // We execute the "Read" function of the PCANBasic                
50
      //
51
      stsResult = PCANBasic::Read(*m_PcanHandle, *CANMsg, *CANTimeStamp);
52
53
      // A message was received
54
      // We process the message(s)
55
      //
56
      if (*stsResult == TPCANStatus::PCAN_ERROR_OK)
57
        ProcessMessage(CANMsg, CANTimeStamp);
58
59
      /// added code
60
      if (activateLogging->Checked)
61
        for(int i=0;i<12;i++)    // hier ggf. anzahl der angeschlossenen zellen automatisiert einlesen und einfügen
62
        {
63
          logfile << CANMsg << ", ";
64
        }
65
        logfile << endl;
66
        //LogMessages(filename,CANMsg,logfile);
67
      /// end of added code
68
69
    } while (btnRelease->Enabled && (!Convert::ToBoolean(*stsResult & TPCANStatus::PCAN_ERROR_QRCVEMPTY)));
70
    
71
  logfile.close();
Allerdings funktioniert dieser Code nicht. Es liegt wohl (in erster 
Linie) an den ^strings (Fehlermeldung: error C2440: '=': 
'std::basic_string<_Elem,_Traits,_Ax>' kann nicht in 'System::String ^' 
konvertiert werden).

Kann mir da jemand helfen? Wenn es eine andere funktionstüchtige 
Log-Prozedur für dieses Beispiel gibt, nur her damit. Ich konnte leider 
nichts Passendes finden...

Danke im Voraus!

von auch PCAN-user (Gast)


Lesenswert?

convertiere doch erst einmal die empfangen Daten von ihrem jeweiligen 
Format in das format string!

Die CANmsg ist eine structur die verschiedene Datentypen beinhaltet, 
also z.b. CANmsg.ID ist ein int, CANmsg.Data[0] bis CANmsg.Data[7] sind 
chars.

Das alles kann man nicht in einen string schreiben, du musst jeden Typ 
explizit und einzeln nach sting wandeln!

Habe damals auch das example als Grundgerüst für meine Software 
hergenommen, allerdings habe ich meine routinen damals in C# 
geschrieben, kann dir also leider kein Beispiel in C geben!

von CANorNotCAN (Gast)


Lesenswert?

Danke für den Hinweis. Allerdings haben sich jetzt die Fehlermeldungen 
erhöht. Das Problem liegt wohl im Unterschied zwischen C# und C++ (und 
meinem mangelnden Wissen bzgl. der Übersetzung zwischen den Sprachen). 
Ich habe folgende Konvertierung versucht:
1
// string coversion of CAN messages
2
    for (int i=0;i<2;i++)
3
    {
4
      stringCAN[i] = gcnew String(CANMsg->Data(i));
5
      stringCANTimeStamp = gcnew String(CANTimeStamp);
6
    }
7
//
Da meckert der Compiler:
1
 error C2882: 'Data': Unzulässige Verwendung eines Namespace-Bezeichners in einem Ausdruck
Vorher hatte ich es versucht es über ToString zu konvertieren. Da waren 
die Fehler aber noch schlimmer...

Grüße

von auch PCAN-user (Gast)


Lesenswert?

versuch mal die einzelnen datenbytes seperat zu konvertieren

..bin allerdings kein c++ spezialist
1
void datenschreiben (void)
2
{
3
TPCANMsg CANMsg;
4
string text;
5
6
7
   text = "";
8
   text  = Convert.ToString(CANMsg.DATA[0]);
9
   text |= Convert.ToString(CANMsg.DATA[1]);
10
 ...
11
}

von ... (Gast)


Lesenswert?

auch PCAN-user schrieb:
> ..bin allerdings kein c++ spezialist

Macht nichts, das was der TO da macht ist ja auch kein C++.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

CANorNotCAN schrieb:
> das Problem liegt wohl im Unterschied zwischen C# und C++

Was Du da nutzt, ist kein C++, sondern eine Microsoft-Perversion 
namens "Managed C++" bzw. "C++/CLI". Sieht zwar so ähnlich aus, heißt 
auch so ähnlich, ist aber kein C++, sondern eine Mutation für den 
.Net-Unterbau.

Das hast Du bei Deinen Projekteinstellungen berücksichtigt?

von CANorNotCAN (Gast)


Lesenswert?

@ auch PCAN-user:

an welcher Stelle (Prozedur) hast du denn geloggt?

@...:

... schrieb:
> Macht nichts, das was der TO da macht ist ja auch kein C++.

"der TO"?!

von ... (Gast)


Lesenswert?

CANorNotCAN schrieb:
> ... schrieb:
>> Macht nichts, das was der TO da macht ist ja auch kein C++.
>
> "der TO"?!

TO = Threadopener
Genau Du warst damit gemeint :)

von PCAN-user (Gast)


Lesenswert?

Das example für C# hat die Funktion tmrRead_Tick welche eben zyklisch 
den PCAN-Dongel auf empfangene Zeichen prüft, dort wird die Funktion
"ProcessMessage(TCLightMsg MyMsg)" aufgerufen.

Hier wird bereits die Struktur TCLightMsg (Deine TPCANMsg) übergeben, 
und hier habe ich dann die CAN-Telegramme weiter verwendet und in ein 
.csv gespeichert.
1
        private void tmrRead_Tick(object sender, EventArgs e)
2
        {
3
            TCLightMsg MyMsg;
4
            CANResult Res;
5
6
            // We read at least one time the queue looking for messages.
7
            // If a message is found, we look again trying to find more.
8
            // If the queue is empty or an error occurr, we get out from
9
            // the dowhile statement.
10
            //      
11
            do
12
            {
13
                // We read the queue looking for messages.
14
   --->        Res = CANLight.Read(ActiveHardware, out MyMsg);
15
16
                // A message was received
17
                // We process the message(s)
18
                if (Res == CANResult.ERR_OK)
19
   --->            ProcessMessage(MyMsg);
20
            } while (!Convert.ToBoolean(Res & CANResult.ERR_QRCVEMPTY));
21
        }
22
23
24
....................................................
25
26
27
        private void ProcessMessage(TCLightMsg MyMsg)
28
        {
29
            InsertMsgEntry(MyMsg);
30
31
            if (JitterMessungenSoll != 0)
32
            {
33
                if (MyMsg.ID == NodeID)
34
                {
35
                    UpdatePosition(MyMsg);
36
                }
37
            }
38
39
40
            if (tmrSync.Enabled == true)
41
            {
42
                if ((MyMsg.ID == FromHex(tbAllgmCob1.Text)) ||
43
                   (MyMsg.ID == FromHex(tbAllgmCob2.Text)) ||
44
                   (MyMsg.ID == FromHex(tbAllgmCob1.Text)))
45
                {
46
                   UpdateTabAllgm(MyMsg);
47
                }
48
            }
49
        }
50
..............................................................

Viel Erfolg!

von CANorNotCAN (Gast)


Lesenswert?

Danke PCAN-User!

Mal etwas anderes: bzgl. stringcat_s kommt die Fehlermeldung:

"error C2660: 'strcat_s': Funktion akzeptiert keine 2 Argumente"

Was soll das?! Das "muss" mit den komischen ^ bei der Initialisierung 
des Strings <head> zusammenhängen. Jetzt verstehe ich, inwiefern diese 
Sprache > eine Microsoft-Perversion ist, wobei ich vermute, dass das 
etwas CAN-spezifisches sein muss, da man im Internet nichts Gescheits 
dazu findet :(.

von CANorNotCAN (Gast)


Lesenswert?

achso ja,
das eigentlich speichern der Datei mache ich natürlich nicht im 
tmrRead_Tick, in der Unterfunktion InsertMsgEntry befülle ich ein 
array und in die Datei schreibe ich dann wenn ich mal Zeit habe, z.B. 
wenn meine Messung beendet ist oder aber wenn ich den close-button 
anklicke!!!

Das sollte aber klar sein!?

von CANorNotCAN (Gast)


Lesenswert?

>Was soll das?! Das "muss" mit den komischen ^ bei der Initialisierung
>des Strings <head> zusammenhängen. Jetzt verstehe ich, inwiefern diese
>Sprache > eine Microsoft-Perversion ist, wobei ich vermute, dass das
>etwas CAN-spezifisches sein muss, da man im Internet nichts Gescheits
>dazu findet :(.

Ne sorry, hab leider keine Ahnung was das soll, die komischen "Häckchen" 
sind mir gestern schon in Deinem code aufgefallen, keine Ahung was die 
bedeuten!

von PCAN-user (Gast)


Lesenswert?

ups sorry, da habe ich den falschen user-name kopiert,
die letzten beiden postings sind von mir!

von ... (Gast)


Lesenswert?

Wobei 'strcat_s' noch ne weitere Eigenheit ist, die bei M$ auch in C++ 
(ja, richtiges C++ geht mit VisualStudio auch) auftaucht. Das normale 
'strcat' gibt es zwar noch, schmeißt aber eine Warning (es sei denn man 
stellt sie via #define wieder ab). Das 'strcat_s' will als zusätzlichen 
Parameter noch die Puffergröße haben.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

CANorNotCAN schrieb:
> Ne sorry, hab leider keine Ahnung was das soll, die komischen "Häckchen"
> sind mir gestern schon in Deinem code aufgefallen, keine Ahung was die
> bedeuten!

Diese Häkchen (mit nur einem 'c') sind ein klares Zeichen dafür, daß es 
sich hierbei nicht um C++ handelt, sondern um die .Net-Sprache 
"C++/CLI". Sieht aus wie C++, riecht auch so ähnlich, ist aber kein C++.

Das ist ein Objektzeiger, bei dem Objekte mit gcnew statt new 
angefordert werden müssen, und deren Speicher nicht von delete, 
sondern vom garbage collector abgeräumt wird.

Wie gesagt, C++ ist das nicht.

von ... (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Sieht aus wie C++
Aber nur auf den ersten Blick.
> riecht auch so ähnlich
Nö, riecht eher nach Müllkippe, 'garbage collector' halt :)

von CANorNotCAN (Gast)


Lesenswert?

Ok, nachdem ich mich ein bissl orientiert habe (MC++-Syntax etc.) gab es 
einige Fortschritte, aber jetzt hängt es wieder. Es ist ähnlich wie bei 
Schrödingers Katze, sobald ich meinen Code wieder (teilweise) 
einkommentiere, gibt es Fehlermeldungen, obwohl (!) das Programm 
eigentlich bis zu dieser Stelle ordnungsgemäß laufen sollte. Habe mich 
an dem msdn-Beispiel zu FileStream orientiert (poste es als C-Code, auch 
wenn es das nicht ist ;) ):
1
private: void ReadMessages()
2
  { 
3
    TPCANMsg ^CANMsg;
4
    TPCANTimestamp ^CANTimeStamp;
5
    TPCANStatus ^stsResult;
6
7
    String ^head,^path;
8
    FileStream ^logfile;
9
    StreamWriter ^streamWriter;
10
11
    // Initializes Variables
12
    //
13
    CANTimeStamp = gcnew TPCANTimestamp();
14
    CANMsg = gcnew TPCANMsg();
15
16
    
17
    path = "C:...\\test.txt"; // In dem richtigen Programmcode ist hier der richtige Pfad angegeben. Da der aber niemanden was angeht, habe ich ihn "zensiert" ;). Hier ist aber sicherlich kein Fehler!
18
    head="string1,string 2,string3;"; // auch hier habe ich zensiert
19
    if (!File::Exists(path))
20
    {
21
      logfile = gcnew FileStream(path,FileMode::Append,FileAccess::Write,FileShare::Write);
22
      logfile->Close();
23
      streamWriter = gcnew StreamWriter(path,true,Encoding::ASCII);
24
      streamWriter->Write(head);
25
      streamWriter->Write("\n");
26
      streamWriter->Close();
27
    }
28
    else
29
      File::Open(path,FileMode::Append,FileAccess::Write,FileShare::Write);
30
31
    // We read at least one time the queue looking for messages.
32
    // If a message is found, we look again trying to find more.
33
    // If the queue is empty or an error occurr, we get out from
34
    // the dowhile statement.
35
    //      
36
    do
37
    {
38
      // We execute the "Read" function of the PCANBasic                
39
      //
40
      stsResult = PCANBasic::Read(*m_PcanHandle, *CANMsg, *CANTimeStamp);
41
42
      // A message was received
43
      // We process the message(s)
44
      //
45
      if (*stsResult == TPCANStatus::PCAN_ERROR_OK)
46
        ProcessMessage(CANMsg,CANTimeStamp);
47
      else
48
        if ((*stsResult == TPCANStatus::PCAN_ERROR_OK) && (activateLogging->Checked))
49
          ProcessMessageLog(CANMsg,CANTimeStamp,path);
50
51
    } while (btnRelease->Enabled && (!Convert::ToBoolean(*stsResult & TPCANStatus::PCAN_ERROR_QRCVEMPTY)));
52
  
53
    streamWriter->Close();
54
        
55
}
Die Funktion "ProcessMessageLog" ist eine Kopie von der bestehenden 
ProcessMessage, welche ich um die Byte-konvertierung erweitert habe und 
diese dann in die datei schreibe:
1
...
2
for (int i = 0; i<8; i++)
3
      {
4
        bMessageData[i] = theMsg->DATA[i];
5
      }
6
      msgId = theMsg->ID;
7
      time = itsTimeStamp->millis + 0xFFFF * itsTimeStamp->millis_overflow;
8
9
...
10
11
if (msgId==0xD7)
12
          {
13
            Number = (bMessageData[0] >> 4) & 0xF;
14
            Vo = ((bMessageData[0] & 0xF) << 12)^ bMessageData[1];
15
            streamWriter->Write(time.ToString());
16
            streamWriter->Write(msgId.ToString());
17
            streamWriter-> Write(Number.ToString());
18
            streamWriter-> Write(Vo.ToString());
19
            streamWriter->Write("\n");
20
          }
Typische Fehlermeldung (wird bei "streamWriter->Close();" am Ende der 
ReadMessages-Prozedur angezeigt, wobei ich vermute, dass das eher 
irrelevant ist):
"Eine nicht behandelte Ausnahme des Typs "System.NullReferenceException" 
ist in PCANBasicExample.exe aufgetreten.
Zusätzliche Informationen: Der Objektverweis wurde nicht auf eine 
Objektinstanz festgelegt."

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

In Deinem "ReadMessages wird "streamWriter" mehrfach nacheinander 
geschlossen.

Ob das so sein soll?

von CANorNotCAN (Gast)


Lesenswert?

das problem war, dass es mehrfach nacheinander aufgerufen wurde. habe es 
bissl umgestellt (bspw. wird der filestream jetzt zu beginn bei der 
globalen objekt-initialisierung angelegt, sodass ich diesen weiter unten 
in ProcessMessages mehrfach aufrufen kann). es klappt jetzt erstmal 
hinreichend gut, sodass ich erste tests machen kann. dem realen 
härtetests wird der code wohl nicht standhalten können, da bin ich mir 
jetzt schon zimelich sicher ;).
danke für die hilfe...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.
Lade...