# Libnodave 0.8.5



## Zottel (19 Oktober 2013)

Ich habe soeben eine neue Version von Libnodave veröffentlicht. Die wesentlichen Neuerungen sind:
- Routing möglich.
- Auswahl der Kommunikation (PG-, OP-, S7-Basis-Kommunikation) möglich.
- Große PDUs können nun über mehrere ISO- oder IBH-Pakete verteilt und wieder zusammengesetzt werden.
- Hoffentlich ein paar Bugs weniger.


----------



## Matze001 (19 Oktober 2013)

Das klingt super!

Ich dachte an Libnodave wird nicht mehr gearbeitet, freut mich zu hören das es doch nicht stimmt.

Grüße

Marcel


----------



## Jochen Kühner (20 Oktober 2013)

Es sind noch ein paar kleine Bugfixes welche in meiner Version schon behoben sind welche noch rein gehören:

Funktion doUpload, letzte zeile von  *len+=netLen; in  *len=netLen; ändern

Kannst schaun, in meiner Version gibts auch noch ein "davePutProgramBlock" vielleicht willst du das ja auch noch einbauen...

Muss Ich mal schaun wie Ich den stand jetzt wieder mit meinem gleichziehe, da das routing ja anderst gelöst wurde als bei mir (mir gefällt deine Lösung aber besser)...

falls ich noch fixes finde welche Ich eingebaut hatte, werde Ich sie hier posten...


----------



## Jochen Kühner (20 Oktober 2013)

Ich hab noch gesehen, Ich hatte die Funktion davTestresultdataMulti umgebaut, die gibts aber jetzt nicht mehr


```
int DECL2 _daveTestResultDataMulti(PDU * p) {	
	int res; /*=daveResCannotEvaluatePDU;*/
	if ((p->data[0]==255)&&(p->dlen>4))
	{
		res=daveResOK;
		p->udata=p->data+4;
		p->udlen=p->data[2]*0x100+p->data[3];
		if (p->data[1]==4) {
			p->udlen>>=3;	/* len is in bits, adjust */
		} else if (p->data[1]==9) {
			/* len is already in bytes, ok */
		} else if (p->data[1]==3) {
			/* len is in bits, but there is a byte per result bit, ok */
		} else {
			if (daveDebug & daveDebugPDU)
				LOG2("fixme: what to do with data type %d?\n",p->data[1]);
			res = daveResUnknownDataUnitSize;
			//res = 0;
		}	    
	}
	else if (p->data[0]==10 || p->data[0]==5)
	{
		//This Section returns ok, even if nothing was read,
		//because with the multiple read we get the error in (daveUseResult)
		res = daveResOK;		
	}
	else {
		res=p->data[0];
	}
	return res;    
}
```

die hatte glaub sonst wenn ein item nicht gefunden wurde ein fehler ausgegeben. Ich hatte es so umgebaut das trotzdem Ok zurück kam, und bei daveUseResult dann der Fehler kam. (es kann ja sein, man liest mehrere Items, und ein paar davon funktionieren und ein paar nicht!) Geht das bei der neuen Version dann auch so?


----------



## Jochen Kühner (20 Oktober 2013)

Achso, nochwas. Falls du irgendeine meiner Änderungen noch übernehmen willst, hier sind Sie aufgelistet: https://github.com/jogibear9988/Dot...commits/master/externalDlls/libnodave_patches


----------



## Zottel (20 Oktober 2013)

Jochen Kühner schrieb:


> Es sind noch ein paar kleine Bugfixes welche in meiner Version schon behoben sind welche noch rein gehören:
> 
> Funktion doUpload, letzte zeile von  *len+=netLen; in  *len=netLen; ändern


Wenn ich mich noch richtig erinnere ist die Funktion wie folgt:
Beim Laden eines Blocks, der länger als eine PDU ist, wird die Funktion doUpload mehrfach aufgrufen.
Ihr wird als Parameter len ein Zeiger auf einen int übergeban, der vor dem 1. Aufruf 0 sein sollte.
Bei jedem Aufruf addiert nun doUpload die empfangene Teillänge.


Jochen Kühner schrieb:


> Kannst schaun, in meiner Version gibts auch noch ein "davePutProgramBlock" vielleicht willst du das ja auch noch einbauen...


Die Funktionalität gibt es seit jeher in den Testprogrammen testISO_TCPload.c etc. Sie unterscheidet sich jedoch irgenwie zwischen rotokollarten, so daß sie sich nicht auf einfache Weise in eine Bibliotheksfunktion für alle Protokollarten gießen ließe (wenn ich mich richtig erinnere).




Jochen Kühner schrieb:


> falls ich noch fixes finde welche Ich eingebaut hatte, werde Ich sie hier posten...


Ich würde mich freuen, wenn du als Dot.NET-Experte dir mal die libnodave.net.cs anschauen würdest. Es gab da mal eine Diskussion von Anwendern, die Abstürze beklagten und angebliche memory leaks gefunden und gefixt hatten. Konnte ich aber nicht nachvollziehen. Deren Änderungen sollten aus den Dateien change.cs oder ch.cs im Ordner Dot.NET ersichtlich sein.


----------



## Jochen Kühner (20 Oktober 2013)

Zottel schrieb:


> Wenn ich mich noch richtig erinnere ist die Funktion wie folgt:
> Beim Laden eines Blocks, der länger als eine PDU ist, wird die Funktion doUpload mehrfach aufgrufen.
> Ihr wird als Parameter len ein Zeiger auf einen int übergeban, der vor dem 1. Aufruf 0 sein sollte.
> Bei jedem Aufruf addiert nun doUpload die empfangene Teillänge.



Ja, aber das zusammenzählen machst du ausserhalb von doUpload nochmal: totlen+=len; Deshalb darf doupload nur die aktuelle länge zurückgeben!




Zottel schrieb:


> Die Funktionalität gibt es seit jeher in den Testprogrammen testISO_TCPload.c etc. Sie unterscheidet sich jedoch irgenwie zwischen rotokollarten, so daß sie sich nicht auf einfache Weise in eine Bibliotheksfunktion für alle Protokollarten gießen ließe (wenn ich mich richtig erinnere).


Zwischen was für Protkollen gibts den da unterschiede?

Ich hab auf jeden Fall eine bei mir eingebaut die bei mir funzt (musste glaub auch noch ein paar kleinigkeiten fixen welche in TestIsoTcpLoad falsch waren).

Code.

```
int DECL2 davePutProgramBlock(daveConnection * dc, int blockType, int blknumber, char* buffer, int * length) {
#define maxPBlockLen 0xDe	// real maximum 222 bytes

	int res=0;
	int cnt=0;
	int size=0;
	int blockNumber,rawLen,netLen,blockCont;
	int number=0;

	uc pup[]= {			// Load request
		0x1A,0,1,0,0,0,0,0,9,
		0x5F,0x30,0x42,0x30,0x30,0x30,0x30,0x34,0x50, // block type code and number
		//     _    0    B   0     0    0    0    4    P
		//		SDB		
		0x0D,
		0x31,0x30,0x30,0x30,0x32,0x30,0x38,0x30,0x30,0x30,0x31,0x31,0x30,0	// file length and netto length
		//     1   0     0    0    2    0    8    0    0    0    1    1    0
	};

	PDU p,p2;

	uc pablock[]= {	// parameters for parts of a block
		0x1B,0
	};
	
	uc progBlock[maxPBlockLen + 4]= {
		0,maxPBlockLen,0,0xFB,	// This seems to be a fix prefix for program blocks
	};

	pup[11] = blockType;
	paInsert[13] = blockType;
	/*pup[12] = number / (10*10*10*10);
	pup[13] = (number - (pup[12] * 10*10*10*10 )) / (10*10*10);
	pup[14] = (number - (pup[13] * 10*10*10)) / (10*10);
	pup[15] = (number - (pup[14] * 10*10)) / (10);
	pup[16] = (number - (pup[15] * 10));
	
	pup[12] = pup[12] + 0x30;
	pup[13] = pup[13] + 0x30;
	pup[14] = pup[14] + 0x30;
	pup[15] = pup[15] + 0x30;
	pup[16] = pup[16] + 0x30;*/
	
	memcpy(progBlock+4,buffer,maxPBlockLen);

	progBlock[9] = (blockType + 0x0A - 'A'); //Convert 'A' to 0x0A
	if (blockType == '8') progBlock[9] = 0x08;
	
	progBlock[10] = blknumber / 0x100;
	progBlock[11] = blknumber - (progBlock[10] * 0x100);
		
		
	rawLen=daveGetU16from(progBlock+14);
	netLen=daveGetU16from(progBlock+38);

	sprintf((char*)pup+19,"1%06d%06d",rawLen,netLen);

	sprintf((char*)pup+12,"%05d",blknumber);
	sprintf((char*)paInsert+14,"%05d",blknumber);
	
	pup[17]='P';
	paInsert[19]='P';
	
	p.header=dc->msgOut+dc->PDUstartO;
	_daveInitPDUheader(&p, 1);
	_daveAddParam(&p, pup, sizeof(pup)-1);

	res=_daveExchange(dc, &p);
	if (res==daveResOK) {
		res=_daveSetupReceivedPDU(dc, &p2);
		if (daveGetDebug() & daveDebugPDU) {
			_daveDumpPDU(&p2);
		}
		res=daveGetPDUerror(&p2);

		if (res==0) {
			blockCont=1;
			res=daveGetResponse(dc);
			res=_daveSetupReceivedPDU(dc, &p2);

			cnt = 0;

			do {
				res=0;
				res=_daveSetupReceivedPDU(dc, &p2);

				number=((PDUHeader*)p2.header)->number;
				if (p2.param[0]==0x1B) {
					//READFILE
					memcpy(progBlock+4,buffer+(cnt*maxPBlockLen),maxPBlockLen);
					
					if (cnt == 0)
					{
						progBlock[9] = (blockType + 0x0A - 'A'); //Convert 'A' to 0x0A
						if (blockType == '8') progBlock[9] = 0x08;
	
						progBlock[10] = blknumber / 0x100;
						progBlock[11] = blknumber - (progBlock[10] * 0x100);						
					}

					p.header=dc->msgOut+dc->PDUstartO;
					_daveInitPDUheader(&p, 3);
					size = maxPBlockLen;

					if (*length > ((cnt+1) * maxPBlockLen))  
						pablock[1]=1;
					else
					{
						size = *length - (cnt * maxPBlockLen);
						pablock[1]=0;	//last block
						blockCont=0;
					}
					   
					progBlock[1]=size;
					_daveAddParam(&p, pablock, sizeof(pablock));
					_daveAddData(&p, progBlock, size + 4 /* size of block) */);
					((PDUHeader*)p.header)->number=number;
					if (daveGetDebug() & daveDebugPDU) {
						_daveDumpPDU(&p);
					}
					_daveExchange(dc,&p);
				}
				cnt++;
			} while (blockCont);  

			res=_daveSetupReceivedPDU(dc, &p2);
			if (daveGetDebug() & daveDebugPDU) {
				_daveDumpPDU(&p2);
			}
			number=((PDUHeader*)p2.header)->number;
			if (p2.param[0]==0x1C) {
				p.header=dc->msgOut+dc->PDUstartO;

				_daveInitPDUheader(&p, 3);
				_daveAddParam(&p, p2.param,1);
				((PDUHeader*)p.header)->number=number;
				_daveExchange(dc,&p);

				p.header=dc->msgOut+dc->PDUstartO;
				_daveInitPDUheader(&p, 1);
				_daveAddParam(&p, paInsert, sizeof(paInsert));
				res=_daveExchange(dc, &p);
				res=_daveSetupReceivedPDU(dc, &p2);
				res=daveGetPDUerror(&p2);
			}
		} else {
			printf("CPU doesn't accept load request:%04X\n",res);
		}	
		return res;
	}
	return res;
}
```



Zottel schrieb:


> Ich würde mich freuen, wenn du als Dot.NET-Experte dir mal die libnodave.net.cs anschauen würdest. Es gab da mal eine Diskussion von Anwendern, die Abstürze beklagten und angebliche memory leaks gefunden und gefixt hatten. Konnte ich aber nicht nachvollziehen. Deren Änderungen sollten aus den Dateien change.cs oder ch.cs im Ordner Dot.NET ersichtlich sein.



Ich wüsste jetzt nicht das es da Probleme mit memleaks gab/gibt... Aber die sieht bei mir ja ganz anderst aus, da Ich da 32/64 Bit verzweigungen und auch mappings für monotouch eingebaut habe...


----------



## Jochen Kühner (20 Oktober 2013)

Was in CSharp noch falsch sein könnte ist die Freigabe von den unmanged Resourcen im Destruktor. Ich denke da sollte eher IDisposable implementiert werden... Und das Dispose dann von der Finalize Methode aufgerufen werden...

http://msdn.microsoft.com/de-de/library/vstudio/b1yfkh5e(v=vs.100).aspx


----------



## Zottel (20 Oktober 2013)

Jochen Kühner schrieb:


> Ja, aber das zusammenzählen machst du ausserhalb von doUpload nochmal: totlen+=len; Deshalb darf doupload nur die aktuelle länge zurückgeben!


Wo? Ich habe jetzt mal in testMPI.C und testISO_TCP.c geschaut und dort steht:

if (0==initUpload(dc, blockType, dbe_.number, &uploadID)) {
    	    do {
		doUpload(dc,&more,&bb,&len,uploadID);
	    } while (more);_


----------



## Jochen Kühner (20 Oktober 2013)

nodave.c


```
int DECL2 daveGetProgramBlock(daveConnection * dc, int blockType, int number, char* buffer, int * length) {
    int res, uploadID, len, more, totlen;
    uc *bb=(uc*)buffer;	//cs: is this right?
    len=0;
    totlen=0;
    if (dc->iface->protocol==daveProtoAS511) {	    
	return daveGetS5ProgramBlock(dc, blockType, number, buffer, length);
    }
    
    res=initUpload(dc, blockType, number, &uploadID); 
    if (res!=0) return res;
    do {
	res=doUpload(dc,&more,&bb,&len,uploadID);
	totlen+=len;
	if (res!=0) return res;
    } while (more);
    res=endUpload(dc,uploadID);
    *length=totlen;
    return res;
}
```


----------



## Zottel (21 Oktober 2013)

Danke. getProgramBlock hatte ich jetzt nicht mehr auf dem Schirm...
Nun dazu, wie es zu lösen ist, damit bestehende Anwendungen möglichst wenig beeinträchtigt werden: 
Wenn ich doUpload verändere, stünden alle Benutzer auf dem Schlauch, die sich an die Beispiele in testMPI oder testISO_TCP etc gehalten haben. 
getProgramBlock hingegegen kann noch nie funktioniert haben, wie es soll.
Insofern scheint es mir richtig, getProgramBlock anzupassen...


----------



## Jochen Kühner (22 Oktober 2013)

Zottel schrieb:


> - Große PDUs können nun über mehrere ISO- oder IBH-Pakete verteilt und wieder zusammengesetzt werden.



Was bedeutet das? Heist das Ich kann nun mehr Variablen Anfragen, so das mehr als 240 Bytes zurückgegeben werden? Wie groß ist die Grenze?


----------



## Zottel (23 Oktober 2013)

Jochen Kühner schrieb:


> Was bedeutet das? Heist das Ich kann nun mehr Variablen Anfragen, so das mehr als 240 Bytes zurückgegeben werden?


Eventuell ja. Hängt von der CPU ab.


Jochen Kühner schrieb:


> Wie groß ist die Grenze?


Die Grenze ist die von der CPU zurückgegebene Größe der PDU. Ich habe es mit einer VIPA-CPU getestet, die 960 Byte große PDUs verwenden kann. Dann können auch 960-18 Bytes mit einer Anfrage gelesen werden. Die Antworten auf solch eine Anfrage werden aber auf mehrere Profibus- (oder MPI-) Pakete verteilt, da die Profibus-PDU auf 240 Byte beschränkt ist. Bei Verwendung von IBH-Link müssen sie immer PC-seitig zusammengesetzt werden. Bei ISO over TCP dann, wenn die ausgehandelte Größe der TPDU kleiner ist als die PDU.


----------



## Thomas_v2.1 (23 Oktober 2013)

Zottel schrieb:


> Bei ISO over TCP dann, wenn die ausgehandelte Größe der TPDU kleiner ist als die PDU.


Kommt das denn mal vor?
In meinen Aufzeichnungen zu diversen S7 wird immer eine TPDU Größe von 1024 ausgehandelt.
Was man evtl. mal versuchen könnte ist, ob man zwei PDUs, bzw. die Anzahl die man bei Verbindungsaufbau mitgeteilt bekommt, in eine TPDU packen kann wenn die TPDU entsprechend größer ist. Das könnte bei kleinen PDU-Größen wirklich einen Schub in der Kommunikationsleistung bringen, wobei bei TCP ja die meiste Zeit in der SPS vertrödelt wird.


----------



## Zottel (23 Oktober 2013)

Thomas_v2.1 schrieb:


> Kommt das denn mal vor?


Das weiß ich jetzt nicht mehr. Es kann sein, daß ich zum Testen bei der VIPA die Verbindung mit TPDU-Größe 256 oder 512 initiiert habe. Darüber hinaus meine ich mich dunkel zu erinnern, daß es auch ein Profibus-Ethernet-Gateway gab, wo bei ISO over TCP jede Profibus-PDU in ein eigenes ISO-Paket gepackt wurde.


----------



## Jochen (21 November 2013)

Hast Du nich Lust, libnodave von sourceforge zu Github zu transferieren, so dass im forked repository model daran mitgearbeitet werden kann? Dann könnte Jochen seine Änderungen mit pull requests einfügen und der Administrationsaufwand sinkt. Es gäbe dann auch einen vernünftigen Issue-Tracker usw.
Es wäre dann zentral. Ich habe schon so viele zerpflückte gepatchte libnodave-versionen heumfliegen gesehen, die auf github der irgendwelchen Privathomepages rumlagen, die aber keinen wirklichen Maintainer hatten. Wäre toll, wenn Dein geniales Projekt noch lange besteht.
Gruß

Jochen


----------



## juergi (25 September 2014)

*libnodave und DotNetSiemensPLCToolBoxLibrary*

Hallo,



Jochen schrieb:


> Hast Du nich Lust, libnodave von sourceforge zu Github zu transferieren, so dass im forked repository model daran mitgearbeitet werden kann? Dann könnte Jochen seine Änderungen mit pull requests einfügen und der Administrationsaufwand sinkt. Es gäbe dann auch einen vernünftigen Issue-Tracker usw.
> Es wäre dann zentral. Ich habe schon so viele zerpflückte gepatchte libnodave-versionen heumfliegen gesehen, die auf github der irgendwelchen Privathomepages rumlagen, die aber keinen wirklichen Maintainer hatten. Wäre toll, wenn Dein geniales Projekt noch lange besteht.



es hat schon jemand libnodave nach Github gebracht: https://github.com/netdata/libnodave
So wie es aussieht, ist es aber "nur" die Kopie aus einem älteren Tar-Archiv.

Schön wäre es, wenn sich dort die aktuelle Version befinden würde. (Evtl. mit kompletter Historie mittels git cvsimport oder git svn clone ...)
Dann könnte man die Änderungen aus https://github.com/jogibear9988/Dot...ry/tree/master/externalDlls/libnodave_patches einfliessen lassen (falls nötig in einem eigenen Branch).
Und dann könnte libnodave als gitsubmodule in DotNetSiemensPLCToolBoxLibrary/tree/master/externalDlls eingebunden werden.

Im Moment ist es für mich nicht zu verstehen, welche Version von libnodave mit den Patches aus libnodave_patches überschrieben werden soll.
Wenn libnodave als gitsubmodule geladen würde, dann würde git automatisch die richtige "Version" nehmen.

Gruß
  Jürgen


----------



## Jochen Kühner (29 September 2014)

Ich hab auch ein git repo von libnoadve angelegt: https://github.com/jogibear9988/libnodave

Da ich das Routing aber anderst gelöst habe, kann man die Versionen im Moment nicht mergen. Ich ziehe bei mir aber immer die fixes aus libnodave nach! Deshalb habe Ich auch dieses Repo erstellt um die Unterschiede in den Versionen zu sehen...


----------



## Jochen Kühner (29 September 2014)

Im Moment gibt's mit libnodave auch noch einen Bug mit langen Telegrammen bei alten CPUs: https://github.com/jogibear9988/DotNetSiemensPLCToolBoxLibrary/issues/6

Vielleicht hat jemand mal Zeit sich das anzuschauen?


----------



## Zottel (29 September 2014)

Wo ist da der Bug in Libnodave?


----------



## Jochen Kühner (29 September 2014)

Gab da irgendein problem mit gesplitteten TCP Paketen... Hab gesehen, das in der neusten libnodave da was gefixt wurde, die fixes hab ich noch nicht übernommen, aber der Thread ersteller hat gepostet das er auch mit der originalen libnodave nicht lesen konnte...

Werd deine Änderungen demnächst mal einpflegen....


----------



## johnsnow (13 Januar 2016)

Hi
I want to write a program in my PLC, and I used the code you written here. I couldn't debug your code. Could you help me to debug this code?
regards


----------



## johnsnow (13 Januar 2016)

the code is this:
int DECL2 davePutProgramBlock(int blockType, int blknumber, char* buffer, int * length)
{
	#define maxPBlockLen 0xDe;
	int res = 0;
	int cnt = 0;
	int size = 0;
	int blockNumber, rawLen, netLen, blockCont;
	int number = 0;
	uc pup[] = { 0x1A, 0, 1, 0, 0, 0, 0, 0, 9, 0x5F, 0x30, 0x42, 0x30, 0x30, 0x30, 0x30, 0x34, 0x50, 0x0D, 0x31, 0x30, 0x30, 0x30, 0x32, 0x30, 0x38, 0x30, 0x30, 0x30, 0x31, 0x31, 0x30, 0 };
	PDU p, p2;


	uc pablock[] = {0x1B, 0};


	uc progBlock[maxPBlockLen + 4] = {0, maxPBlockLen, 0, 0xFB};
	pup[11] = blockType;
	paInsert[13] = blockType;
	/*pup[12] = number / (10*10*10*10);
	pup[13] = (number - (pup[12] * 10*10*10*10 )) / (10*10*10);
	pup[14] = (number - (pup[13] * 10*10*10)) / (10*10);
	pup[15] = (number - (pup[14] * 10*10)) / (10);
	pup[16] = (number - (pup[15] * 10));


	pup[12] = pup[12] + 0x30;
	pup[13] = pup[13] + 0x30;
	pup[14] = pup[14] + 0x30;
	pup[15] = pup[15] + 0x30;
	pup[16] = pup[16] + 0x30;*/
	uc buffer[maxPBlockLen + 4] = { 0, maxPBlockLen, 0, 0xFB };


	memcpy(progBlock , buffer, maxPBlockLen+4);


	progBlock[9] = (blockType + 0x0A - 'A'); //Convert 'A' to 0x0A
	if (blockType == '8') progBlock[9] = 0x08;


	progBlock[10] = blknumber / 0x100;
	progBlock[11] = blknumber - (progBlock[10] * 0x100);




	rawLen = daveGetU16from(progBlock + 14);
	netLen = daveGetU16from(progBlock + 38);
	sprintf((char*)pup + 19, "1%06d%06d", rawLen, netLen);


	sprintf((char*)pup + 12, "%05d", blknumber);
	sprintf((char*)paInsert + 14, "%05d", blknumber);


	pup[17] = 'P';
	paInsert[19] = 'P';


	p.header = dc->msgOut + dc->PDUstartO;
	_daveInitPDUheader(&p, 1);
	_daveAddParam(&p, pup, sizeof(pup)-1);


	res = _daveExchange(dc, &p);


	return res;
};


----------

