DotNetSiemensPLCToolBoxLibrary (LibNoDave) Zugriff auf Dual-Port RAM / FB15

In der "doUploadNC" und "endUploadNC" ist pa[4]=1, bei den SPS Funktionen war dieser 0. Weiß jedoch nicht was dieser aussagt.
uc pa[]= {0x1f,0,0,0,1,0,0,1};

Hab die jetzigen Upload Funktionen soweit angepasst, dass ich nen Erfolgreichen Upload durchführen konnte.

Vielleicht besteht die upload ID aus 4 Bytes, und nicht nur aus einem? Dann wären die Funktionen für den Upload zwischen SPS und NC grundsätzlich identisch.
Bei der SPS baust du den Dateinamen aus Sektion, Blocktyp, Blocknummer und Dateisystem (Aktiv/Passiv) zusammen, bei der NC gibst du einfach den Dateinamen an. Gehört bei der NC das "_N_" mit zum Dateinamen, oder ist das auch sowas wie ein Prefix?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Also die UploadID besteht definitiv aus 4 Bytes.

Ich habe es gerade getestet, indem ich die 4 Bytes von der SPS auf dem Weg zum PG (Step7) mit eigenen Werten modifiziert habe. Step7 schickt daraufhin in beim nächsten Upload-Befehl diese 4 Bytes mit genau diesen Werten wieder zurück. D.h. so wie es jetzt in libnodave steht mag es zwar funktionieren, aber ist prinzipiell falsch.
 
@Thomas_v2.1

Der Upload einer kleinen Datei hat funktioniert. Wenn ich nun eine Größere Datei von der Steuerung hochlade funktioniert dieser zwar soweit, dass der Wireshark stimmt, jedoch liefert die Funktion keine oder falsche Rückgabeparameter.

Wenn more = 0 -> alles gut
Wenn more != 0 -> Problem

Laut Wireshark sollte die Länge "len" bis auf das letzte Paket 946 lang sein. Es wird jedoch abwechselnd 942 und 0 geliefert. Wenn 0 geliefert wird ist der Buffer auch leer, jedoch nicht das PDU laut Wireshark.

Die Variable "more" liefert auch komische Werte. 1, 95, 1, 117, 1, 77, 1, 67 wobei sich die Werte != 1 auch ändern.


Wo ist mein Fehler?

Code:
int DECL2 doUploadNC(daveConnection * dc, int * more, uc**buffer, int * len, int uploadID){    PDU p1,p2;
    int res, netLen;
    p1.header=dc->msgOut+dc->PDUstartO;
    _daveConstructDoUploadNC(&p1, uploadID);
    res=_daveExchange(dc, &p1);
    if (daveDebug & daveDebugUpload) {
        LOG2("error:%d\n", res);
        FLUSH;
    }    
    *more=0;
    if(res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc, &p2);
    *more=p2.param[1];
    if(res!=daveResOK) return res;
    //    netLen=p2.data[1] /* +256*p2.data[0]; */ /* for long PDUs, I guess it is so */;
    netLen=p2.data[1]+256*p2.data[0]; /* some user confirmed my guess... */;
    //if (*buffer) {
        memcpy(buffer,p2.data+4,netLen);
        *buffer+=netLen;
        if (daveDebug & daveDebugUpload) {
            LOG2("buffer:%p\n",*buffer);
            FLUSH;
        }
    //}
    *len=netLen;
    return res;
}
 
Das ist bis auf den Dateinamen der bei Startupload eingefügt wird, und der aus 4 Bytes bestehenden Upload-ID doch identisch mit dem SPS Upload.

Du hast ja nur eine Funktion gezeigt, ich würde alle zugehörigen wie folgt anpassen.
Code:
int DECL2 initUploadNC(daveConnection *dc, char *filename, uc *uploadID){
    PDU p1,p2;
    int res;
    if (daveDebug & daveDebugUpload) {
        LOG1("****initUploadNC\n");
    }
    p1.header = dc->msgOut + dc->PDUstartO;
    _daveConstructUploadNC(&p1, filename);
    res = _daveExchange(dc, &p1);
    if (daveDebug & daveDebugUpload) {
        LOG2("error:%d\n", res);
        FLUSH;
    }
    if (res != daveResOK) return res;
    res = _daveSetupReceivedPDU(dc, &p2);
    if (res != daveResOK) return res;
    uploadID[0] = p2.param[4];
    uploadID[1] = p2.param[5];
    uploadID[2] = p2.param[6];
    uploadID[3] = p2.param[7];
    return 0;
}

int DECL2 doUploadNC(daveConnection *dc, int *more, uc **buffer, int *len, uc *uploadID){
    PDU p1,p2;
    int res, netLen;
    p1.header = dc->msgOut + dc->PDUstartO;
    _daveConstructDoUploadNC(&p1, uploadID);
    res=_daveExchange(dc, &p1);
    if (daveDebug & daveDebugUpload) {
        LOG2("error:%d\n", res);
        FLUSH;
    }
    *more = 0;
    if (res != daveResOK) return res;
    res = _daveSetupReceivedPDU(dc, &p2);
    *more = p2.param[1];
    if (res != daveResOK) return res;
    netLen = p2.data[1] + 256*p2.data[0];
    if (*buffer) {
        memcpy(*buffer, p2.data+4, netLen);
        *buffer += netLen;
        if (daveDebug & daveDebugUpload) {
            LOG2("buffer:%p\n",*buffer);
            FLUSH;
        }
    }
    *len+=netLen;
    return res;
}

int DECL2 endUploadNC(daveConnection *dc, uc *uploadID){
    PDU p1,p2;
    int res;
    
    p1.header = dc->msgOut + dc->PDUstartO;
    _daveConstructEndUploadNC(&p1, uploadID);
    res = _daveExchange(dc, &p1);
    if (daveDebug & daveDebugUpload) {
        LOG2("error:%d\n", res);
        FLUSH;
    }
    if(res != daveResOK) return res;
    res = _daveSetupReceivedPDU(dc, &p2);
    return res;
}

void DECL2 _daveConstructUploadNC(PDU *p, char *filename) {
    uc pa[260];
    memset(pa, 0, sizeof(pa));
    pa[0] = 0x1d;
    pa[8] = strlen(filename);
    memcpy(&pa[9], filename, strlen(filename));
    _daveInitPDUheader(p, 1);
    _daveAddParam(p, pa, strlen(filename) + 9);
    if (daveDebug & daveDebugPDU) {
        _daveDumpPDU(p);
    }
}

void DECL2 _daveConstructDoUploadNC(PDU *p, uc *uploadID) {
    uc pa[] = {0x1e,0,0,0,0,0,0,1};
    pa[4] = uploadID[0];
    pa[5] = uploadID[1];
    pa[6] = uploadID[2];
    pa[7] = uploadID[3];
    _daveInitPDUheader(p, 1);
    _daveAddParam(p, pa, sizeof(pa));
    if (daveDebug & daveDebugPDU) {
        _daveDumpPDU(p);
    }
}

void DECL2 _daveConstructEndUploadNC(PDU * p, uc *uploadID) {
    uc pa[] = {0x1f,0,0,0,0,0,0,1};
    pa[4] = uploadID[0];
    pa[5] = uploadID[1];
    pa[6] = uploadID[2];
    pa[7] = uploadID[3];
    _daveInitPDUheader(p,1);
    _daveAddParam(p, pa, sizeof(pa));
    if (daveDebug & daveDebugPDU) {
        _daveDumpPDU(p);
    }
}

Für die Upload ID musst du ausserhalb ein unsigned char Array mit 4 Elementen anlegen, quasi so:
Code:
unsigned char uploadID[4];
/* Verbindung aufbauen usw. */
res = initUploadNC(dc, "_N_TEST_UPLOAD_MPF", uploadID);
...
und diese dann in gleicher Weise an die folgenden Funktionen übergeben.
 
Könntest du noch einen Mitschnitt von einem Download einer großen Datei machen, d.h. eine die mehrere PDUs benötigt? Und evtl. auch mal einen fehlerhaften Download erzeugen, wenn das irgendwie möglich ist.
 
Mit dem Upload von großen Dateien hab ich noch Probleme.

Hab die Funktionen jetzt mal so eingebaut, wie du sie geschrieben hast. Problem ist aber das selbe. Unterscheiden sich ja auch nur darin, das die "uploadID" als uc[4] übergibst.

Hier mal die Funktion für den Upload.

//C#
Code:
int length = 0;
byte[] buffer = new byte[size];
res = _dc.daveGetNCProgram(filename, buffer, ref length);
Code:
protected static extern int daveGetNCProgram64(IntPtr dc, string filename, byte[] buffer, ref int length);


//nodave
Code:
int DECL2 daveGetNCProgram(daveConnection *dc, const char *filename, uc *buffer, int *length) {
   int res, len, more, totlen;
   unsigned char uploadID[4];
   uc *bb=(uc*)buffer;
   len=0;    totlen=0;
   //if (dc->iface->protocol==daveProtoAS511) {
   //    return daveGetS5ProgramBlock(dc, blockType, number, buffer, length);
   //}
   res=initUploadNC(dc, filename, uploadID);
    if (res!=0) return res;
   do {
       res=doUploadNC(dc, &more, &bb, &len, uploadID);
       totlen+=len;
       if (res!=0) return res;
   } while (more);
   res=endUploadNC(dc, uploadID);
   *length=totlen;
   return res;}
Anhang anzeigen NcBigUpload_Fehler_3.rar

Problem:
- jedes zweite Paket wird nicht richtig in den Buffer geschrieben.
- Ist der Upload zu Groß oder wird der Code nicht per Einzelschritt ausgeführt, kommt es zum Fehler: "Ein Ausnahmefehler des Typs "System.AccessViolationException" ist in DotNetSiemensPLCToolBoxLibrary.dll aufgetreten."

//######################################################

Möglichkeit 2:
initUploadNC, doSingleUploadNC, endUploadNC werden vom C# Pogramm separat aufgerufen.

Dazu hab ich die "doUploadNC" so angepasst, dass der Pointer für den Buffer nicht mehr weiter gestellt wird.
Die Idee ist, die Funktion wird solange vom C# Programm aufgerufen, bis "more == 0". Der Buffer wird jedesmal anhand der "len" wegkopiert.
Somit theoretisch unendliche Größe für Uploads möglich.

Hier besteht aber ebenfalls das Problem, das bei jedem zweiten Aufruf die "len == 0" und für "more" irgend ein Wert != 0 zurückkommt.
Es werden auch scheinbar doppelt so viele Jobs erzeugt.
-> Upload ist laut Wireshark schon fertig, da werden immer noch Jobs an nodave geschickt. Die Daten im Buffer sind auch erst bei der hälfte der Datei.
-> Auf diese zusätzlichen Jobs antwortet die NC mit Fehler, nodave liefert aber noch (jedes zweite mal) die fehlenden Daten.
-> Das ret der Funktion ist über diesen Zeitraum immer 0.

Jobs, die in ein PDU passen funktionieren!

Wireshark und Ausgabe des Buffers im Anhang: Anhang anzeigen NcBigUpload_Fehler_2.rar

//C#
Code:
do{
   System.Threading.Thread.Sleep(100);
   res = _dc.doUploadNC(out more, buffer, out len, id);
   if (res != 0)
   {
       break;
   }
   try
   {
       ret = System.Text.Encoding.Default.GetString(buffer, 0, len);                                                                                         //
       lstRetStr.Add(System.Text.Encoding.Default.GetString(buffer, 0, len));                                                                            // Siehe Anhang
       lstRet.Add(new stTest() { value = System.Text.Encoding.Default.GetString(buffer, 0, len), more = more, len = len });     //
   }    catch (Exception ex)
   {    }
   System.Threading.Thread.Sleep(100);}
while (more != 0);

//nodave
Code:
int DECL2 doSingleUploadNC(daveConnection *dc, int *more, uc *buffer, int *len, uc *uploadID){
   PDU p1,p2;
   int res, netLen;
   p1.header = dc->msgOut + dc->PDUstartO;
   _daveConstructDoUploadNC(&p1, uploadID);
   res=_daveExchange(dc, &p1);
   if (daveDebug & daveDebugUpload) {
       LOG2("error:%d\n", res);
       FLUSH;    }
   *more = 0;
   if (res != daveResOK) return res;
   res = _daveSetupReceivedPDU(dc, &p2);
   *more = p2.param[1];
   if (res != daveResOK) return res;
   netLen = p2.data[1] + 256*p2.data[0];
   if (netLen > 1024) return -1;
   //if (*buffer) {
       memcpy(buffer, p2.data+4, netLen);
       //*buffer += netLen;
       if (daveDebug & daveDebugUpload) {
           LOG2("buffer:%p\n",*buffer);
           FLUSH;        }
   //}
   *len=netLen;
   return res;
}
 
Zuletzt bearbeitet:
Da läuft was auf ISO-Ebene schief.

Bei Verbindungsaufbau auf ISO-Ebene wird von libnodave eine TPDU-Größe von 512 Bytes vorgeschlagen.
Später beim Verbindungsaufbau auf S7-Kommunikationsebene wird aber eine 960 Bytes große PDU vorgeschlagen.
Wenn der Partner diese Größe wirklich akzeptiert (was bei der NC der Fall ist), muss eine PDU >512 Bytes fragmentiert übertragen werden.
Bei libnodave ist zwar eine Zusammensetzen von fragmentiert übertragenen Paketen realisiert, aber irgendwas stimmt da nicht.

Da du ja die lib von Jochen benutzt, schau mal nach ob bei ihm in der _daveReadISOPacket() ein Unterschied zur letzten libnodave ist.
Wenn nicht, dann ist da wohl ein Fehler in libnodave bei zusammensetzen.

Am saubersten wäre es aber, dass libnodave eine TPDU von 1024 vorschlägt, wenn er später auch eine 960er S7-PDU übertragen will.
Das ist in der _daveConnectPLCTCP() die Zeile mit:
0xC0,1,0x9,

die 0x9 wäre auf 0xa zu ändern. Du musst aber prüfen ob dann die ganzen internen Puffer auch groß genug sind. Oder du änderst die angeforderte S7-PDU Größe von 960 auf 480 Bytes, dann passt es auch zusammen, und es sollten keine TPDUs fragmentiert werden.

Wireshark kann die ISO-TPDUs übrigens wieder zusammensetzen (reassemblieren). Du musst es aber für das Protokoll COPT über das Menü Bearbeiten->Einstellungen aktivieren.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich glaubeirgendwas mit dem zusammensetzen von iso paketen ist erst später in libnodave dazugekommen, weiß aber nicht ob ich das zu 100% richtig übernommen habe! kanns mir auch die nächsten tage mal anschauen! hier kams glaub dazu: https://github.com/jogibear9988/libnodave/commit/1a6f3b45a67f77c17f810c630702c0b19bbd648a
ich nutze ja aber einen fork, wo ich ein paar andere dinge gefixt habe, und routing auch über seriell/netlink/etc ermögliche
 
Am saubersten wäre es aber, dass libnodave eine TPDU von 1024 vorschlägt, wenn er später auch eine 960er S7-PDU übertragen will.
Das ist in der _daveConnectPLCTCP() die Zeile mit:
0xC0,1,0x9,

die 0x9 wäre auf 0xa zu ändern. Du musst aber prüfen ob dann die ganzen internen Puffer auch groß genug sind. Oder du änderst die angeforderte S7-PDU Größe von 960 auf 480 Bytes, dann passt es auch zusammen, und es sollten keine TPDUs fragmentiert werden.

Hab jetzt mal einfach 0xa anstelle von 0x9 bei "uc b4[]", sowie "uc b4R[]" eingetragen.
Damit bekomme ich das richtige Ergebnis.

@Jochen: Kannst du dir das trotzdem noch ansehen, ob das so passt?

@Thomas: Bietet Wireshark die Möglichkeit zwei Aufzeichnungen zu vergleichen?

@Allgemein: Gibt es eine Möglichkeit den Speicher des "Buffer" dynamisch zu vergeben?
 
Zuletzt bearbeitet:
@Thomas: Bietet Wireshark die Möglichkeit zwei Aufzeichnungen zu vergleichen?
Welche Werte möchtest du denn vergleichen?
Wenn komplett alle Bytes, dann könntest du einen Hexdump über Wireshark erstellen, und die Dateien dann mit diff o.Ä. vergleichen.

Heydump geht über Wireshark, aber ich nehme dafür ganz gerne tshark, das ist im Wireshark Verzeichnis vorhanden, und kann über die Eingabeaufforderung aufgerufen werden. Ein Hexdump von allen s7comm-Paketen in einer Aufzeichnung erhältst du mit:
Code:
tshark -r C:\meincaputure.pcap -x -Y s7comm > file1hexdump.txt
Das Ergebnis landet dann in file1hexdump.txt.

In dem Hexdump sind aber die Bytes des gesamten Telegramms, d.h. nicht nur die Nutzlast der ISO-Protokolls (hier s7comm). Das geht leider nur wenn du in Wireshark für ein einzelnes Telegramm und den Baum "S7 Communication" klickst, und dann "Paketbytes anzeigen" wählst.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Du kannst dir auch nur die Nutzlast des ISO-Protokolls exportieren. Dazu musst du aber vorher Wireshark starten, und das Protokoll S7COMM deaktivieren. Dann heißt das Nutzdatenfeld von cotp: data.data. Das kann man sich dann mit tshark anzeigen lassen:
Code:
tshark -r C:\meincapture.pcap -Y cotp -T fields -e data.data
Zusätzliche zu exportierende Felder kannst du mit -e feldname anhängen. Z.B. die Quell-IP-Adresse mit -e ip.src
 
@Thomas_v2.1: Kannst du für den NC Programm Download auch noch die dave Funktion anpassen? ("davePutProgramBlock" -> "davePutNCProgram")

Vielen Dank schon mal für deine Hilfe!

PS: Deine aktuelle Wireshark Beta hab ich momentan im Einsatz. Bis jetzt sieht alles gut aus.
 
Ich guck mir das mal an.
Wichtiger wäre wohl, dass Jochen sein "Fork" von libnodave mal auf den aktuellen Stand bringt. Features hinzufügen ist schön und gut, Fehlerbehebungen nachpflegen ist mMn aber wichtiger.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Problem ist da eher das libnodave nicht mit quellcodeverwaltung entwickelt wird und man nich gescheit sieht was verändert wird! daher gibts von mir auch das libnodave repo auf github um den unterschied der versionen zu zeigen! ja, hab damals aber nicht alles übernommen, lag aber eher daran das das routing komplett anderst implementiert wurde als bei mir (mit weniger funktionen).
ist das denn im aktuellen libnodave korrekt?
 
Keine Ahnung obs funktioniert, zumindest ist es vorhanden.

Du kannst dir doch in ein paar Sekunden dein diff selber bilden, ist doch nicht so dass ohne github überhaupt nichts mehr funktioniert. Und zur Not kann man sich ein lokales git-repo für libnodave anlegen, in dem man die einzelnen Versionen seit der Erstveröffentlichung nacheinander eincheckt. Dann gibt es eine vollständige Änderungs-Historie. Und das schreibe ich als git-Hasser ;-)
 
@Thomas_v2.1: Kannst du für den NC Programm Download auch noch die dave Funktion anpassen? ("davePutProgramBlock" -> "davePutNCProgram")
Wie liegt die Datei denn vor welche du downloaden möchtest? Ist das eine Textdatei auf der Festplatte? Und wenn ja, kann deren Inhalt 1:1 Byte/Zeichenweise hochgeladen werden, oder gibt es in der Datei noch eine Art Kopf?

Der noch zu erstellenden Funktion könnte man entweder den Dateinamen(inkl. Pfad) übergeben und diese liest diese selber ein, oder es wird die Datei extern eingelesen und der Funktion wird nur ein Zeiger auf den Dateiinhalt übergeben,

davePutProgramBlock kenne ich überhaupt nicht. In libnodave (der dll) ist ein Download für die SPS überhaupt nicht enthalten. Das gibt es nur in einem test-Programm.
 
Zurück
Oben