rllib  1
rleibnetip.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  rleibnetip.cpp - description
3  -------------------
4  begin : WEd Apr 04 2007
5  copyright : (C) 2007 by R. Lehrig
6  email : lehrig@t-online.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This library is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as *
13  * published by the Free Software Foundation *
14  * *
15  ***************************************************************************/
16 #include "rleibnetip.h"
17 #include "rltime.h"
19 #include <stdio.h>
20 #include <string.h>
21 #include <math.h>
22 
23 #define EIB_HEADERSIZE 6
24 #define EIB_VERSION 0x10
25 #define EIB_ADRSIZE 8
26 //not sizeof(struct sockaddr_in)
27 #define EIB_CRICRDSIZE 4
28 #define TUNNEL_CONNECTION 4
29 #define REMLOG_CONNECTION 6
30 #define OBJSVR_CONNECTION 8
31 #define TUNNEL_LINKLAYER 2
32 #define E_NO_ERROR 0
33 #define E_NO_MORE_CONNECTIONS 0x24
34 #define E_SEQUENCE_NUMBER 0x04
35 
36 #define L_Data_Req 0x11
37 #define L_Data_Con 0x2E
38 #define L_Data_Ind 0x29
39 
41 {
42  SEARCH_REQUEST = 0x0201,
43  SEARCH_RESPONSE = 0x0202,
46  CONNECT_REQUEST = 0x0205,
47  CONNECT_RESPONSE = 0x0206,
53  TUNNELLING_ACK = 0x0421
54 };
55 
56 static void *eib_reader(void *arg) // thread
57 {
58  THREAD_PARAM *p = (THREAD_PARAM *) arg;
59  rlEIBnetIP *eib = (rlEIBnetIP *) p->user;
60  rlEIBnetIP::PDU pdu;
61  rlTime now, last, diff;
62  int ret, len;
63  int recseq = 0;
64  int expected_recseq = 0;
65  unsigned char b[4];
66 
67  last.getLocalTime();
68  while(eib->running)
69  {
70  if(eib->isConnected() == 0)
71  {
72  eib->connect();
73  expected_recseq = 0;
74  last.getLocalTime();
75  }
76  ret = eib->recv(&pdu, sizeof(pdu));
77  now.getLocalTime();
78  if(ret > 0)
79  {
80  switch (ntohs(pdu.servicetype))
81  {
82  case DISCONNECT_REQUEST:
83  eib->disconnect();
84  expected_recseq = 0;
85  break;
87  if(eib->debug) ::printf("eib_reader() CONNECTIONSTATE_RESPONSE\n");
88  break;
89  case TUNNELLING_REQUEST:
90  if(eib->debug)
91  ::printf("eib_reader() TUNNELING_REQUEST sequenzecounter=%d\n",recseq);
92  recseq = pdu.data[2]; // sequencecounter
93  b[0] = pdu.data[0]; // remember 4 bytes of data
94  b[1] = pdu.data[1];
95  b[2] = pdu.data[2];
96  b[3] = pdu.data[3];
97  len = ntohs(pdu.totalsize) - 6; // remember len (-headerlength)
98  pdu.headersize = EIB_HEADERSIZE; // fill acknowledge
99  pdu.version = EIB_VERSION;
100  pdu.servicetype = htons(TUNNELLING_ACK);
101  pdu.totalsize = htons(EIB_HEADERSIZE+4);
102  pdu.data[0] = 4; // structlength
103  pdu.data[1] = eib->channelid; // channelid
104  pdu.data[2] = recseq; // sequencecounter
105  pdu.data[3] = E_NO_ERROR; // typespecific
106  if(recseq != expected_recseq)
107  {
108  if(eib->debug)
109  ::printf("eib_reader() recseq=%d expected_recseq=%d\n",recseq,expected_recseq);
110  // we simply ignore the sequencecounter
111  // pdu.data[4] = E_SEQUENCE_NUMBER;
112  }
113  expected_recseq = (expected_recseq+1) & 0x0ff;
114  eib->rlUdpSocket::sendto(&pdu, ntohs(pdu.totalsize), eib->server);
115  pdu.data[0] = b[0]; // restore received data
116  pdu.data[1] = b[1];
117  pdu.data[2] = b[2];
118  pdu.data[3] = b[3];
119  eib->storeBuffer(&pdu.data[4],len-4); // +EIBNETIP_COMMON_CONNECTION_HEADER
120  break;
121  case TUNNELLING_ACK:
122  if(pdu.data[3] == E_NO_ERROR)
123  {
124  eib->tunnel_ack = 1;
125  if(eib->debug) ::printf("eib_reader() TUNNELLING_ACK typespecific=0x%x\n",pdu.data[3]);
126  }
127  else
128  {
129  eib->tunnel_ack = -1;
130  if(eib->debug) ::printf("eib_reader() TUNNELLING_NACK typespecific=0x%x\n",pdu.data[3]);
131  }
132  break;
133  default:
134  ::printf("eib_reader() unknown servicetype=0x%x\n",ntohs(pdu.servicetype));
135  break;
136  }
137  }
138  diff = now - last;
139  if(eib->isConnected() && eib->channelid != -1 && diff.second > 50)
140  { // send heartbeat
141  if(eib->debug) ::printf("send heartbeat\n");
142  pdu.headersize = EIB_HEADERSIZE;
143  pdu.version = EIB_VERSION;
144  pdu.servicetype = htons(CONNECTIONSTATE_REQUEST);
145  pdu.totalsize = htons(EIB_HEADERSIZE+EIB_ADRSIZE*2+EIB_CRICRDSIZE);
146  pdu.data[0] = eib->channelid;
147  pdu.data[1] = 0;
148  eib->rlUdpSocket::sendto(&pdu, ntohs(pdu.totalsize), eib->server);
149  last.getLocalTime();
150  }
151  }
152  return NULL;
153  /*
154  for(int i=0; i<50; i++)
155  {
156  p->thread->lock();
157  //do something critical
158  printf("this is the thread\n");
159  p->thread->unlock();
160  }
161  */
162 }
163 
164 rlEIBnetIP::rlEIBnetIP(int num_signals, int _debug, rlDataAcquisitionProvider *_provider) : rlUdpSocket(_debug)
165 {
166  debug = _debug;
167  provider = _provider;
168  watch_eib = 0;
169  if(debug) ::printf("rlEIBnetIP() constructor\n");
170  char *cptr;
171  mem = NULL;
172  memsize = 0;
173  running = 0;
174  maxvalues = 0;
175  is_connected = 0;
176  channelid = -1;
177  server = NULL;
179  //rlUdpSocket::setSockopt(SO_BROADCAST);
181  if(num_signals <= 0) return;
182  memsize = sizeof(EIB_TEL)*num_signals;
183  maxvalues = num_signals;
184  cptr = new char[memsize];
185  memset(cptr,0,memsize);
186  mem = cptr;
187  setSourceAdr("/0/0/001");
188 }
189 
191 {
192  if(running) stopReading();
193  if(mem != NULL)
194  {
195  char *cptr = (char *) mem;
196  delete [] cptr;
197  }
199 }
200 
201 int rlEIBnetIP::storeBuffer(unsigned char *buf, int len)
202 {
203  EIB_TEL tel;
204  short unsigned int *sptr;
205  int i;
206 
207  if(len <= 0 || mem == NULL) return -1;
208  if(debug)
209  {
210  ::printf("rlEIBnetIP::storeBuffer() buf=[0x%x",buf[0]);
211  for(i=1; i<len; i++) ::printf(",0x%x",buf[i]);
212  ::printf("]\n");
213  }
214 
215  tel.mc = buf[0];
216  tel.addi1 = buf[1];
217  tel.ctrl1 = buf[2];
218  tel.ctrl2 = buf[3];
219  sptr = (unsigned short *) &buf[4];
220  tel.saddr = ntohs(*sptr);
221  sptr = (unsigned short *) &buf[6];
222  tel.daddr = ntohs(*sptr);
223  tel.apci_length = buf[8];
224  tel.apci = buf[9];
225  tel.val[0] = buf[10];
226  tel.val[1] = buf[10+1];
227  tel.val[2] = buf[10+2];
228  tel.val[3] = buf[10+3];
229  for(i=0; i<(len-10); i++) tel.val[i] = buf[10+i];
230  switch (tel.mc)
231  {
232  case L_Data_Req: //0x11:
233  ::printf("rlEIBnetIP::storeBuffer() messagecode=0x%x L_Data_Req\n",tel.mc);
234  break;
235  case L_Data_Ind: //0x29:
236  break;
237  case L_Data_Con: //0x2E:
238  break;
239  default:
240  ::printf("rlEIBnetIP::storeBuffer() unknown messagecode=0x%x L_Data_Con\n",tel.mc);
241  return -1;
242  }
243  if(debug)
244  {
245  ::printf("\nmc=0x%x addi1=0x%x ctrl1=0x%x ctrl2=0x%x saddr=0x%x daddr=0x%x acpi_length=0x%x apci=0x%x",
246  tel.mc,
247  tel.addi1,
248  tel.ctrl1,
249  tel.ctrl2,
250  tel.saddr,
251  tel.daddr,
252  tel.apci_length,
253  tel.apci);
254  for(i=10; i<len; i++) ::printf(" val[%d]=0x%x",i-10,tel.val[i-10]);
255  ::printf("\n");
256  }
257 
258  if(watch_eib) printTelegram(&tel);
259  if(provider != NULL) return storeInProvider(&tel);
260 
261  // store EIB_TEL in *mem
262  EIB_TEL *memptr = (EIB_TEL *) mem;
263  for(i=0; i<maxvalues; i++)
264  {
265  if(memptr[i].mc == 0)
266  {
267  //if(debug) ::printf("rlEIBnetIP::storeBuffer() insert new value\n");
268  thread.lock();
269  memcpy(&memptr[i],&tel,sizeof(tel));
270  thread.unlock();
271  return 0;
272  }
273  //else if(memptr[i].saddr == tel.saddr && memptr[i].daddr == tel.daddr)
274  else if(memptr[i].daddr == tel.daddr)
275  {
276  //if(debug) ::printf("rlEIBnetIP::storeBuffer() update existing value\n");
277  thread.lock();
278  memcpy(&memptr[i],&tel,sizeof(tel));
279  thread.unlock();
280  return 0;
281  }
282  }
283  ::printf("rlEIBnetIP::storeBuffer() maxvalues=%d too small\n",maxvalues);
284  return -1;
285 }
286 
288 {
289  int s1,s2,s3,d1,d2,d3,val,length;
290 
291  s1 = tel->saddr/(8*256);
292  s2 = (tel->saddr/256) & 0x0ff;
293  s3 = tel->saddr & 0x0ff;
294  d1 = tel->daddr/(8*256);
295  d2 = (tel->daddr/256) & 0x0ff;
296  d3 = tel->daddr & 0x0ff;
297  length = tel->apci_length;
298  val = 0;
299  if(length == 1)
300  {
301  val = tel->val[0];
302  }
303  else if(length == 2)
304  {
305  char *cptr = (char *) &tel->val[0];
306  val = (((*cptr)*256)+tel->val[1]);
307  }
308  else if(length == 3)
309  {
310  char *cptr = (char *) &tel->val[0];
311  val = (((*cptr)*256)+tel->val[1])*tel->val[2];
312  }
313  else if(length == 4)
314  {
315  char *cptr = (char *) &tel->val[0];
316  val = ((((*cptr)*256)+tel->val[1])*(tel->val[2]*256) * tel->val[3]);
317  }
318  ::printf("src=/%d/%d/%03d\tdest=/%d/%d/%03d\tval=0x%x\tlen=%d\n",s1,s2,s3,d1,d2,d3,val,length);
319  return 0;
320 }
321 
323 {
324  if(provider == NULL || tel == NULL) return -1;
325  //int s1,s2,s3;
326  int d1,d2,d3,val,length;
327  char name[128], value[128];
328 
329  //s1 = tel->saddr/(8*256);
330  //s2 = (tel->saddr/256) & 0x0ff;
331  //s3 = tel->saddr & 0x0ff;
332  d1 = tel->daddr/(8*256);
333  d2 = (tel->daddr/256) & 0x0ff;
334  d3 = tel->daddr & 0x0ff;
335  length = tel->apci_length;
336  val = 0;
337  if(length == 1)
338  {
339  val = tel->val[0];
340  }
341  else if(length == 2)
342  {
343  char *cptr = (char *) &tel->val[0];
344  val = (((*cptr)*256)+tel->val[1]);
345  }
346  else if(length == 3)
347  {
348  char *cptr = (char *) &tel->val[0];
349  val = (((*cptr)*256)+tel->val[1])*tel->val[2];
350  }
351  else if(length == 4)
352  {
353  char *cptr = (char *) &tel->val[0];
354  val = ((((*cptr)*256)+tel->val[1])*(tel->val[2]*256) * tel->val[3]);
355  }
356  //::printf("/%d/%d/%d\t/%d/%d/%d\t0x%x\t%d\n",s1,s2,s3,d1,d2,d3,val,length);
357  sprintf(name,"/%d/%d/%03d",d1,d2,d3);
358  sprintf(value,"%d",val);
359  if(debug) ::printf("storeInProvicer: name=%s value=%s", name, value);
360  provider->setStringValue(name, value);
361  return 0;
362 }
363 
364 int rlEIBnetIP::dump(FILE *fout)
365 {
366  if(fout == NULL) return -1;
367  if(debug) ::printf("rlEIBnetIP::dump()\n");
368  int s1,s2,s3,d1,d2,d3,val,length;
369  int i = 0;
370  EIB_TEL *tel;
371  EIB_TEL *memptr = (EIB_TEL *) mem;
372 
373  val = 0;
374  thread.lock();
375  while(1)
376  {
377  tel = &memptr[i];
378  if(tel->mc == 0) break;
379  if(i >= maxvalues) break;
380  s1 = tel->saddr/(8*256);
381  s2 = (tel->saddr/256) & 0x0ff;
382  s3 = tel->saddr & 0x0ff;
383  d1 = tel->daddr/(8*256);
384  d2 = (tel->daddr/256) & 0x0ff;
385  d3 = tel->daddr & 0x0ff;
386  length = tel->apci_length;
387  if(length == 1)
388  {
389  val = tel->val[0];
390  }
391  else if(length == 2)
392  {
393  char *cptr = (char *) &tel->val[0];
394  val = (((*cptr)*256)+tel->val[1]);
395  }
396  else if(length == 3)
397  {
398  char *cptr = (char *) &tel->val[0];
399  val = (((*cptr)*256)+tel->val[1])*tel->val[2];
400  }
401  else if(length == 4)
402  {
403  char *cptr = (char *) &tel->val[0];
404  val = ((((*cptr)*256)+tel->val[1])*(tel->val[2]*256) * tel->val[3]);
405  }
406  fprintf(fout,"/%d/%d/%d\t/%d/%d/%d\t0x%x\t%d\n",s1,s2,s3,d1,d2,d3,val,length);
407  i++;
408  }
409  thread.unlock();
410  return i;
411 }
412 
413 int rlEIBnetIP::setValuesFromCSV(const char *filename)
414 {
415  FILE *fin;
416  char line[1024], *cptr;
417  int s1,s2,s3,d1,d2,d3,i,val,len;
418  unsigned int uval;
419 
420  if(filename == NULL) return -1;
421  fin = fopen(filename,"r");
422  if(fin == NULL)
423  {
424  ::printf("rlEIBnetIP::setValuesFromCSV(%s) could not open file\n",filename);
425  return -1;
426  }
427 
428  while(fgets(line,sizeof(line)-1,fin) != NULL)
429  {
430  d1 = d2 = d3 = 0;
431  cptr = strchr(line,'/');
432  if(cptr == NULL) break;
433  sscanf(cptr,"/%d/%d/%d",&s1,&s2,&s3);
434  i = 0;
435  while(line[i] != '\0')
436  {
437  if(line[i] == ' ' || line[i] == '\t')
438  {
439  while(line[i] == ' ' || line[i] == '\t') i++;
440  if(line[i] == '/')
441  {
442  cptr = strstr(&line[i],"0x");
443  if(cptr == NULL)
444  {
445  sscanf(&line[i],"/%d/%d/%d %d %d",&d1,&d2,&d3,&val,&len);
446  }
447  else
448  {
449  sscanf(&line[i],"/%d/%d/%d %x %d",&d1,&d2,&d3,&val,&len);
450  }
451  }
452  break;
453  }
454  i++;
455  }
456  if(line[i] == '\0') return rlEIBnetIP::EIBERROR;
457  cptr = strchr(line,'/');
458  if(cptr != NULL)
459  {
460  sprintf(line,"/%d/%d/%d",d1,d2,d3);
461  for(i=0; i<100; i++) // try setting even if hardware responds very slowly
462  {
463  memcpy(&uval,&val,sizeof(uval));
464  uval = setValueUnsigned(line,uval,len);
465  if(uval != rlEIBnetIP::EIBERROR) break;
466  }
467  }
468  }
469 
470  fclose(fin);
471  return 0;
472 }
473 
475 {
476  if(server == NULL) return rlEIBnetIP::EIBERROR;
477  if(debug) ::printf("rlEIBnetIP()::connect()\n");
478  PDU pdu,response;
479  unsigned char cricrd[EIB_CRICRDSIZE];
480 
481  if(is_connected == 1)
482  {
483  ::printf("rlEIBnetIP::connect() already connected\n");
484  return 1;
485  }
486 
487  channelid = -1;
488  memset(&response,0,sizeof(response));
490  pdu.version = EIB_VERSION;
491  pdu.servicetype = htons(CONNECT_REQUEST);
493  memcpy(&pdu.data[0],&client.address,EIB_ADRSIZE);
494  memcpy(&pdu.data[8],&client.address,EIB_ADRSIZE);
495  cricrd[0] = EIB_CRICRDSIZE; // structlength
496  cricrd[1] = TUNNEL_CONNECTION; // connectiontypecode
497  cricrd[2] = TUNNEL_LINKLAYER; // data1
498  cricrd[3] = 0; // data2
499  memcpy(&pdu.data[8*2],cricrd,EIB_CRICRDSIZE);
500  is_connected = 0;
501 
502  rlUdpSocket::sendto(&pdu, ntohs(pdu.totalsize), server);
503  recv(&response, sizeof(PDU));
504 
505  if(ntohs(response.servicetype) == CONNECT_RESPONSE)
506  {
507  if(debug)
508  {
509  ::printf("response.headersize = 0x%x\n",response.headersize);
510  ::printf("response.version = 0x%x\n",response.version);
511  ::printf("response.servicetype = 0x%x\n",ntohs(response.servicetype));
512  ::printf("response.totalsize = 0x%x\n",ntohs(response.totalsize));
513  ::printf("response.channelid = 0x%x\n",response.data[0]);
514  ::printf("response.status = 0x%x\n",response.data[1]);
515  }
516  if(response.data[1] == E_NO_ERROR)
517  {
518  channelid = response.data[0];
519  if(debug) ::printf("CONNECT_RESPONSE == success\n");
520  is_connected = 1;
522  return 1;
523  }
524  ::printf("response == CONNECT_RESPONSE status=0x%x failed\n",response.data[1]);
525  return -1; //
526  }
527  else
528  {
529  ::printf("response != CONNECT_RESPONSE\n");
530  return -1;
531  }
532 }
533 
535 {
536  if(server == NULL) return rlEIBnetIP::EIBERROR;
537  if(debug) ::printf("rlEIBnetIP()::disconnect()\n");
538  PDU pdu;
539 
541  pdu.version = EIB_VERSION;
542  pdu.servicetype = htons(DISCONNECT_RESPONSE);
543  pdu.totalsize = htons(EIB_HEADERSIZE+2+EIB_ADRSIZE);
544  pdu.data[0] = channelid; // channelid
545  pdu.data[1] = 0; // reserved
546  memcpy(&pdu.data[2],&client.address,EIB_ADRSIZE);
547  rlUdpSocket::sendto(&pdu, ntohs(pdu.totalsize), server);
548  is_connected = 0;
549  channelid = -1;
550  return rlEIBnetIP::SUCCESS;
551 }
552 
554 {
555  if(debug) ::printf("rlEIBnetIP()::sendDisconnectRequest()\n");
556  if(is_connected == 0) return -1;
557  PDU pdu;
558 
560  pdu.version = EIB_VERSION;
561  pdu.servicetype = htons(DISCONNECT_REQUEST);
562  pdu.totalsize = htons(EIB_HEADERSIZE+2+EIB_ADRSIZE);
563  pdu.data[0] = channelid; // channelid
564  pdu.data[1] = 0; // reserved
565  memcpy(&pdu.data[2],&client.address,EIB_ADRSIZE);
566  rlUdpSocket::sendto(&pdu, ntohs(pdu.totalsize), server);
567  is_connected = 0;
568  channelid = -1;
569  return rlEIBnetIP::SUCCESS;
570 }
571 
573 {
574  return is_connected;
575 }
576 
578 {
579  running = 1;
580  is_connected = 0;
581  channelid = -1;
582  thread.create(eib_reader,this);
583  return rlEIBnetIP::SUCCESS;
584 }
585 
587 {
588  running = 0;
589  is_connected = 0;
590  channelid = -1;
591  thread.cancel();
592  return rlEIBnetIP::SUCCESS;
593 }
594 
595 int rlEIBnetIP::setSourceAdr(const char *adr)
596 {
597  const char *cptr;
598  int a1,a2,a3;
599 
600  // interpret name
601  cptr = strchr(adr,'/');
602  if(cptr == NULL)
603  {
604  ::printf("USER_ERROR: rlEIBnetIP::setSourceAdr() wrong adr=%s\n", adr);
605  return -1;
606  }
607  sscanf(cptr,"/%d/%d/%d",&a1,&a2,&a3);
608  saddr = ((a1*8)+a2)*256+a3;
609  return 0;
610 }
611 
612 int rlEIBnetIP::setValue(const char *name, int val, int length, int addi1, int ctrl, int apci)
613 {
614  unsigned int uval;
615  memcpy(&uval,&val,sizeof(uval));
616  return setValueUnsigned(name, uval, length, addi1, ctrl, apci);
617 }
618 
619 int rlEIBnetIP::value(const char *name)
620 {
621  int val;
622  unsigned int uval;
623  uval = valueUnsigned(name);
624  memcpy(&val,&uval,sizeof(val));
625  return val;
626 }
627 
628 int rlEIBnetIP::setValueFloat(const char *name, float val, int length, int addi1, int ctrl, int apci)
629 {
630  unsigned int uval,s,e,m;
631  float fval,eval;
632  if(length == -1 || length == 2)
633  {
634  length = 2;
635  if(val < 0.0f) s = 0x80000000;
636  else s = 0;
637  m = (unsigned int) (val*100.0f);
638  m = m/100;
639  // m*2^e = fabsf(val)
640  // 2^e = fabsf(val)/m;
641  // e = ln(fabsf(val)/m)
642  if(m == 0) e = 0;
643  else
644  {
645  fval = fabsf(val)/(float) m;
646  eval = logf(fval);
647  e = ((unsigned int) eval) * 8* 256;
648  }
649  uval = s | e | m;
650  }
651  else if(length == 4)
652  {
653  memcpy(&uval,&val,sizeof(uval));
654  }
655  else
656  {
657  return rlEIBnetIP::EIBERROR;
658  }
659  return setValueUnsigned(name, uval, length, addi1, ctrl, apci);
660 }
661 
662 float rlEIBnetIP::valueFloat2(const char *name)
663 {
664  float val,sign;
665  unsigned int uval,e,m;
666  uval = valueUnsigned(name);
667  if(uval & 0x080000000) sign = 1.0f;
668  else sign = -1.0f;
669  uval = uval | 0x07fffffff; // eliminate sign
670  m = uval & 0x07ffff;
671  e = uval / (8*256);
672  val = sign*(0.01f*m)*(2^e);
673  return val;
674 }
675 
676 float rlEIBnetIP::valueFloat4(const char *name)
677 {
678  float val;
679  unsigned int uval;
680  uval = valueUnsigned(name);
681  memcpy(&val,&uval,sizeof(float));
682  return val;
683 }
684 
685 unsigned int rlEIBnetIP::valueUnsigned(const char *name)
686 {
687  if(name == NULL || mem == NULL) return rlEIBnetIP::EIBERROR;
688  unsigned int val,a1,a2,a3,length;
689  const char *cptr;
690  EIB_TEL tel;
691  EIB_TEL *memptr = (EIB_TEL *) mem;
692 
693  // interpret name
694  memset(&tel,0xff,sizeof(EIB_TEL));
695  cptr = strchr(name,'/');
696  if(cptr == NULL)
697  {
698  ::printf("USER_ERROR: rlEIBnetIP::value() wrong name=%s\n", name);
699  return rlEIBnetIP::EIBERROR;
700  }
701  sscanf(cptr,"/%d/%d/%d",&a1,&a2,&a3);
702  tel.saddr = (short) saddr;
703  tel.daddr = ((a1*8)+a2)*256+a3;
704 
705  for(int i=0; i<maxvalues; i++)
706  {
707  if(memptr[i].mc == 0) break;
708  //if(memptr[i].saddr == tel.saddr && memptr[i].daddr == tel.daddr)
709  if(memptr[i].daddr == tel.daddr)
710  {
711  thread.lock();
712  val = 0;
713  length = memptr[i].apci_length;
714  if(length == 1)
715  {
716  val = memptr[i].val[0];
717  }
718  else if(length == 2)
719  {
720  char *cptr = (char *) &memptr[i].val[0];
721  val = (((*cptr)*256)+memptr[i].val[1]);
722  }
723  else if(length == 3)
724  {
725  char *cptr = (char *) &memptr[i].val[0];
726  val = (((*cptr)*256)+memptr[i].val[1])*memptr[i].val[2];
727  }
728  else if(length == 4)
729  {
730  char *cptr = (char *) &memptr[i].val[0];
731  val = ((((*cptr)*256)+memptr[i].val[1])*(memptr[i].val[2]*256) * memptr[i].val[3]);
732  }
733  thread.unlock();
734  if(length < 1 || length > 4 || length == 3)
735  {
736  ::printf("rlEIBnetIP::value(0x%x) unknown length=%d\n",val,length);
737  return rlEIBnetIP::EIBERROR;
738  }
739  return val;
740  }
741  }
742  return rlEIBnetIP::EIBERROR;
743 }
744 
745 int rlEIBnetIP::getText(const char *name, char *text, int maxlen)
746 {
747  if(name == NULL || text == NULL || maxlen <= 0) return rlEIBnetIP::EIBERROR;
748  unsigned int a1,a2,a3; //,length;
749  int j;
750  const char *cptr;
751  char buf[16];
752  EIB_TEL tel;
753  EIB_TEL *memptr = (EIB_TEL *) mem;
754 
755  text[0] = '\0';
756  // interpret name
757  memset(&tel,0xff,sizeof(EIB_TEL));
758  cptr = strchr(name,'/');
759  if(cptr == NULL)
760  {
761  ::printf("USER_ERROR: rlEIBnetIP::getText() wrong name=%s\n", name);
762  return rlEIBnetIP::EIBERROR;
763  }
764  sscanf(cptr,"/%d/%d/%d",&a1,&a2,&a3);
765  tel.saddr = (short) saddr;
766  tel.daddr = ((a1*8)+a2)*256+a3;
767 
768  for(int i=0; i<maxvalues; i++)
769  {
770  if(memptr[i].mc == 0) break;
771  //if(memptr[i].saddr == tel.saddr && memptr[i].daddr == tel.daddr)
772  if(memptr[i].daddr == tel.daddr)
773  {
774  thread.lock();
775  //length = memptr[i].apci_length;
776  for(j=0; j<14; j++) buf[j] = memptr[i].val[j];
777  buf[14] = '\0';
778  j = 0;
779  while(j<maxlen && j<14)
780  {
781  text[j] = buf[j];
782  j++;
783  }
784  return j;
785  }
786  }
787  return 0;
788 }
789 
790 int rlEIBnetIP::setText(const char *name, const char *text)
791 {
792  if(name == NULL || text == NULL) return rlEIBnetIP::EIBERROR;
793  char buf[16];
794  int length = strlen(text);
795  unsigned int a1,a2,a3,daddr;
796  int i,retry;
797  const char *cptr;
798  PDU pdu;
799  EIB_TEL tel;
800  EIB_TEL *memptr = (EIB_TEL *) mem;
801 
802  if(length > 14) length = 14;
803  memset(buf,0,sizeof(buf));
804  for(i=0; i<length; i++) buf[i] = text[i];
805 
806  // interpret name
807  cptr = strchr(name,'/');
808  if(cptr == NULL)
809  {
810  ::printf("USER_ERROR: rlEIBnetIP::setText() wrong name=%s text=%s\n", name, text);
811  return rlEIBnetIP::EIBERROR;
812  }
813  sscanf(cptr,"/%d/%d/%d",&a1,&a2,&a3);
814  daddr = ((a1*8)+a2)*256+a3;
815 
816  // fill EIBnet/IP PDU
818  pdu.version = EIB_VERSION;
819  pdu.servicetype = htons(TUNNELLING_REQUEST);
820  pdu.totalsize = htons(EIB_HEADERSIZE+4+10+length);
821  pdu.data[0] = 4; // structlength
822  pdu.data[1] = channelid; // channelid
823  pdu.data[2] = send_sequencecounter; // sequencecounter
824  pdu.data[3] = E_NO_ERROR; // typespecific
825  tel.mc = L_Data_Req; // L_Data_Req=0x11, L_Data_Con=0x2E L_Data_Ind=0x29
826  tel.addi1 = 0x0;
827  tel.ctrl1 = 0xbc;
828  tel.ctrl2 = 0xe0;
829  tel.saddr = (unsigned short) htons((short) saddr); //0x1);
830  tel.daddr = (unsigned short) htons((short) daddr); //(0x100)
831  tel.apci_length = 0x1;
832  tel.apci = 0x0;
833  for(i=0; i<14; i++) tel.val[i] = buf[i];
834  memcpy(&pdu.data[4],&tel,sizeof(EIB_TEL));
835 
836  tunnel_ack = retry = 0;
837  while(1)
838  {
839  // send value over EIBnet/IP
840  rlUdpSocket::sendto(&pdu, ntohs(pdu.totalsize), server);
841  rlsleep(10);
842  if (tunnel_ack == 1) // OK
843  {
845  if(debug) ::printf("rlEIBnetIP::setText(%s) ACK\n",text);
846  break;
847  }
848  else if(tunnel_ack == -1) // failure
849  {
850  if(debug) ::printf("rlEIBnetIP::setText(%s) NACK\n",text);
851  retry++;
852  }
853  else
854  {
855  if(debug) ::printf("rlEIBnetIP::setText(%s) timeout\n",text);
856  retry++;
857  }
858  if(retry >= 2)
859  {
860  is_connected = 0 ;
861  ::printf("rlEIBnetIP::setText() connection lost\n");
862  return rlEIBnetIP::EIBERROR;
863  }
864  }
865  tunnel_ack = 0;
866 
867  tel.saddr = ntohs(tel.saddr); // convert network -> host byte order
868  tel.daddr = ntohs(tel.daddr);
869  // remember value within mem
870  for(i=0; i< maxvalues; i++)
871  {
872  if(memptr[i].mc == 0)
873  {
874  if(debug) ::printf("rlEIBnetIP::setText() insert new value\n");
875  thread.lock();
876  memcpy(&memptr[i],&tel,sizeof(tel));
877  thread.unlock();
878  return length;
879  }
880  //else if(memptr[i].saddr == tel.saddr && memptr[i].daddr == tel.daddr)
881  else if(memptr[i].daddr == tel.daddr)
882  {
883  if(debug) ::printf("rlEIBnetIP::setText() update existing value\n");
884  thread.lock();
885  memcpy(&memptr[i],&tel,sizeof(tel));
886  thread.unlock();
887  return length;
888  }
889  }
890  ::printf("ERROR rlEIBnetIP::setText() not enough signals specified max=%d\n",maxvalues);
891  return rlEIBnetIP::EIBERROR;
892 }
893 
894 int rlEIBnetIP::setValueUnsigned(const char *name, unsigned int val, int length, int addi1, int ctrl, int apci)
895 {
896  if(name == NULL || mem == NULL || channelid == -1) return rlEIBnetIP::EIBERROR;
897  unsigned int i,a1,a2,a3,daddr;
898  int retry;
899  const char *cptr;
900  PDU pdu;
901  EIB_TEL tel;
902  EIB_TEL *memptr = (EIB_TEL *) mem;
903 
904  // interpret name
905  cptr = strchr(name,'/');
906  if(cptr == NULL)
907  {
908  ::printf("USER_ERROR: rlEIBnetIP::setValue() wrong name=%s val=%d\n", name, val);
909  return rlEIBnetIP::EIBERROR;
910  }
911  sscanf(cptr,"/%d/%d/%d",&a1,&a2,&a3);
912  daddr = ((a1*8)+a2)*256+a3;
913 
914  // fill EIBnet/IP PDU
916  pdu.version = EIB_VERSION;
917  pdu.servicetype = htons(TUNNELLING_REQUEST);
918  if(length == -1)
919  pdu.totalsize = htons(EIB_HEADERSIZE+4+10+1);
920  else
921  pdu.totalsize = htons(EIB_HEADERSIZE+4+10+length);
922  pdu.data[0] = 4; // structlength
923  pdu.data[1] = channelid; // channelid
924  pdu.data[2] = send_sequencecounter; // sequencecounter
925  pdu.data[3] = E_NO_ERROR; // typespecific
926  tel.mc = L_Data_Req; // L_Data_Req=0x11, L_Data_Con=0x2E L_Data_Ind=0x29
927 
928  tel.addi1 = 0x0;
929  tel.ctrl1 = 0xbc;
930  tel.ctrl2 = 0xe0;
931  tel.saddr = (unsigned short) htons((short) saddr); //0x1);
932  tel.daddr = (unsigned short) htons((short) daddr); //(0x100)
933  tel.apci_length = 0x1;
934  tel.apci = 0x0;
935 
936  if(addi1 != -1) tel.addi1 = (unsigned char) ((addi1 & 0x0ff));
937  if(ctrl != -1)
938  {
939  tel.ctrl1 = (unsigned char) ((ctrl & 0x0ff00)/256);
940  tel.ctrl2 = (unsigned char) ((ctrl & 0x0ff));
941  }
942  if(length != -1) tel.apci_length = (unsigned char) length;
943  if(apci != -1) tel.apci = (unsigned char) apci;
944 
945  if(length == -1 || length == 1)
946  {
947  tel.val[0] = (unsigned char) val; // 0x80; // 0x81
948  tel.val[1] = (unsigned char) 0x0;
949  tel.val[2] = (unsigned char) 0x0;
950  tel.val[3] = (unsigned char) 0x0;
951  }
952  else if(length == 2)
953  {
954  tel.val[0] = (unsigned char) ((val & 0x0ff00)/256);
955  tel.val[1] = (unsigned char) (val & 0x0ff);
956  tel.val[2] = (unsigned char) 0x0;
957  tel.val[3] = (unsigned char) 0x0;
958  }
959  else if(length == 3)
960  {
961  tel.val[0] = (unsigned char) ((val & 0x0ff000000)/(256*256));
962  tel.val[1] = (unsigned char) (val & 0x0ff0000)/256;
963  tel.val[2] = (unsigned char) (val & 0x0ff00);
964  tel.val[3] = (unsigned char) (val & 0x0ff);
965  }
966  else if(length == 4)
967  {
968  tel.val[0] = (unsigned char) ((val & 0x0ff000000)/(256*256*256));
969  tel.val[1] = (unsigned char) ((val & 0x0ff0000)/(256*256));
970  tel.val[2] = (unsigned char) ((val & 0x0ff00)/(256));
971  tel.val[3] = (unsigned char) (val & 0x0ff);
972  }
973  else
974  {
975  ::printf("rlEIBnetIP::setValue(0x%x) unknown length=%d\n",val,length);
976  return rlEIBnetIP::EIBERROR;
977  }
978  memcpy(&pdu.data[4],&tel,sizeof(EIB_TEL));
979 
980  if(debug)
981  {
982  ::printf("rlEIBnetIP::setValue() mc = 0x%x\n", tel.mc);
983  ::printf("rlEIBnetIP::setValue() addi1 = 0x%x\n", tel.addi1);
984  ::printf("rlEIBnetIP::setValue() ctrl1 = 0x%x\n", tel.ctrl1);
985  ::printf("rlEIBnetIP::setValue() ctrl2 = 0x%x\n", tel.ctrl2);
986  ::printf("rlEIBnetIP::setValue() saddr = 0x%x\n", ntohs(tel.saddr));
987  ::printf("rlEIBnetIP::setValue() daddr = 0x%x\n", ntohs(tel.daddr));
988  ::printf("rlEIBnetIP::setValue() apci_length = 0x%x\n", tel.apci_length);
989  ::printf("rlEIBnetIP::setValue() apci = 0x%x\n", tel.apci);
990  ::printf("rlEIBnetIP::setValue() val[0] = 0x%x\n", tel.val[0]);
991  ::printf("rlEIBnetIP::setValue() val[1] = 0x%x\n", tel.val[1]);
992  ::printf("rlEIBnetIP::setValue() val[2] = 0x%x\n", tel.val[2]);
993  ::printf("rlEIBnetIP::setValue() val[3] = 0x%x\n", tel.val[3]);
994  }
995 
996  tunnel_ack = retry = 0;
997  while(1)
998  {
999  // send value over EIBnet/IP
1000  rlUdpSocket::sendto(&pdu, ntohs(pdu.totalsize), server);
1001  rlsleep(10);
1002  if (tunnel_ack == 1) // OK
1003  {
1005  if(debug) ::printf("rlEIBnetIP::setValue(0x%x) ACK\n",val);
1006  break;
1007  }
1008  else if(tunnel_ack == -1) // failure
1009  {
1010  if(debug) ::printf("rlEIBnetIP::setValue(0x%x) NACK\n",val);
1011  retry++;
1012  }
1013  else
1014  {
1015  if(debug) ::printf("rlEIBnetIP::setValue(0x%x) timeout\n",val);
1016  retry++;
1017  }
1018  if(retry >= 2)
1019  {
1020  is_connected = 0 ;
1021  ::printf("rlEIBnetIP::setValue() connection lost\n");
1022  return rlEIBnetIP::EIBERROR;
1023  }
1024  }
1025  tunnel_ack = 0;
1026 
1027  tel.saddr = ntohs(tel.saddr); // convert network -> host byte order
1028  tel.daddr = ntohs(tel.daddr);
1029 
1030  if(provider != NULL) return 0;
1031 
1032  // remember value within mem
1033  for(i=0; i< (unsigned int) maxvalues; i++)
1034  {
1035  if(memptr[i].mc == 0)
1036  {
1037  if(debug) ::printf("rlEIBnetIP::setValue(0x%x) insert new value\n",val);
1038  thread.lock();
1039  memcpy(&memptr[i],&tel,sizeof(tel));
1040  thread.unlock();
1041  return 0;
1042  }
1043  //else if(memptr[i].saddr == tel.saddr && memptr[i].daddr == tel.daddr)
1044  else if(memptr[i].daddr == tel.daddr)
1045  {
1046  if(debug) ::printf("rlEIBnetIP::setValue(0x%x) update existing value\n",val);
1047  thread.lock();
1048  memcpy(&memptr[i],&tel,sizeof(tel));
1049  thread.unlock();
1050  return 0;
1051  }
1052  }
1053  ::printf("ERROR rlEIBnetIP::setValue() not enough signals specified max=%d\n",maxvalues);
1054  return rlEIBnetIP::EIBERROR;
1055 }
1056 
1058 {
1059  if(_server == NULL) return rlEIBnetIP::EIBERROR;
1060  server = _server;
1061  PDU pdu;
1062  int ret;
1063 
1064  ret = rlEIBnetIP::getDescription(&pdu);
1065  if(ret > 0)
1066  {
1067  if(debug) ::printf("rlEIBnetIP::setServer() found server\n");
1068  memcpy(&server->address,&from.address,sizeof(server->address));
1069  return 0;
1070  }
1071  else
1072  {
1073  ::printf("rlEIBnetIP::setServer() could not find server\n");
1074  return -1;
1075  }
1076 }
1077 
1079 {
1080  // eib murx
1081  unsigned char murx1[8], murx2[8];
1082  memcpy(murx1,&_client->address,sizeof(murx1));
1083  murx2[0] = murx1[0]; // sin_len
1084  murx2[1] = murx1[1]; // sin_family
1085  murx2[6] = murx1[2]; // sin_port
1086  murx2[7] = murx1[3]; // sin_port
1087  murx2[2] = murx1[4]; // sin_addr
1088  murx2[3] = murx1[5]; // sin_addr
1089  murx2[4] = murx1[6]; // sin_addr
1090  murx2[5] = murx1[7]; // sin_addr
1091  memcpy(&client.address,murx2,sizeof(murx2));
1092  return 0;
1093 }
1094 
1095 int rlEIBnetIP::recv(void *buf, int maxlen)
1096 {
1097  int ret;
1098  while(1)
1099  {
1100  ret = rlUdpSocket::recvfrom(buf, maxlen, &from, 1000);
1101  if(ret < 0) return ret;
1102  if(*server == from) return ret;
1103  }
1104 }
1105 
1107 {
1108  if(server == NULL) return rlEIBnetIP::EIBERROR;
1109  if(debug) ::printf("rlEIBnetIP()::getDescription()\n");
1110  PDU pdu;
1111  int ret;
1112 
1113  pdu.headersize = EIB_HEADERSIZE;
1114  pdu.version = EIB_VERSION;
1115  pdu.servicetype = htons(DESCRIPTION_REQUEST);
1116  pdu.totalsize = htons(EIB_HEADERSIZE+EIB_ADRSIZE*2);
1117  memcpy(pdu.data,&client.address,EIB_ADRSIZE*2);
1118 
1119  rlUdpSocket::sendto(&pdu, ntohs(pdu.totalsize), server);
1120  while(1)
1121  {
1122  ret = rlUdpSocket::recvfrom(buf, sizeof(pdu), &from, 1000);
1123  if(ret < 0)
1124  {
1125  if(debug) ::printf("rlEIBnetIP()::getDescription() timeout\n");
1126  return ret;
1127  }
1128  if(ntohs(buf->servicetype) == DESCRIPTION_RESPONSE)
1129  {
1130  if(debug) ::printf("rlEIBnetIP()::getDescription() got description\n");
1131  return ret;
1132  }
1133  }
1134 }
1135 
1136 //#define TESTING
1137 #ifdef TESTING
1138 
1139 int main()
1140 {
1141  int ret, val;
1142  rlEIBnetIP eib(1000);
1143  rlIpAdr client;
1144  rlIpAdr server;
1145  char line[1024];
1146 
1147  client.setAdr("nb3lehrig",rlEIBnetIP::PORT);
1148  //server.setAdr("192.168.1.30",rlEIBnetIP::PORT);
1149  server.setAdr("eibnet1",rlEIBnetIP::PORT);
1150  ret = eib.setClient(&client);
1151  if(ret < 0) return -1;
1152  ret = eib.setServer(&server);
1153  if(ret < 0) return -1;
1154  eib.debug = 1;
1155  eib.startReading();
1156 
1157  line[0] = '\0';
1158  while(1)
1159  {
1160  scanf("%s",line);
1161  if(line[0] == 'x') break;
1162  if(line[0] == 'd')
1163  {
1164  eib.dump(stdout);
1165  }
1166  else if(line[0] == 's')
1167  {
1168  eib.setValuesFromCSV("test.csv");
1169  }
1170  else if(line[0] == 't')
1171  {
1172  eib.setText("/0/1/000","test");
1173  }
1174  else
1175  {
1176  sscanf(line,"%x",&val);
1177  eib.setValue("/0/1/000", val);
1178  printf("eib.value(/0/1/000)=0x%x\n",eib.value("/0/1/000"));
1179  }
1180  printf("x=exit d=dump s=set t=text value=\n");
1181  }
1182  return 0;
1183 }
1184 
1185 #endif
void getLocalTime()
Definition: rltime.cpp:342
#define EIB_VERSION
Definition: rleibnetip.cpp:24
unsigned short saddr
Definition: rleibnetip.h:60
void * mem
Definition: rleibnetip.h:101
int channelid
Definition: rleibnetip.h:104
#define E_NO_ERROR
Definition: rleibnetip.cpp:32
static void * eib_reader(void *arg)
Definition: rleibnetip.cpp:56
int setValue(const char *name, int val, int length=-1, int addi1=-1, int ctrl=-1, int apci=-1)
Definition: rleibnetip.cpp:612
int maxvalues
Definition: rleibnetip.h:116
int printTelegram(EIB_TEL *tel)
Definition: rleibnetip.cpp:287
int sendDisconnectRequest()
Definition: rleibnetip.cpp:553
int cancel()
Definition: rlthread.cpp:78
#define EIB_HEADERSIZE
Definition: rleibnetip.cpp:23
void * user
Definition: rlthread.h:30
#define TUNNEL_CONNECTION
Definition: rleibnetip.cpp:28
int setSourceAdr(const char *adr)
Definition: rleibnetip.cpp:595
int storeInProvider(EIB_TEL *tel)
Definition: rleibnetip.cpp:322
unsigned short servicetype
Definition: rleibnetip.h:49
#define EIB_ADRSIZE
Definition: rleibnetip.cpp:25
int watch_eib
Definition: rleibnetip.h:88
int value(const char *name)
Definition: rleibnetip.cpp:619
int unlock()
Definition: rlthread.cpp:52
unsigned int valueUnsigned(const char *name)
Definition: rleibnetip.cpp:685
int isConnected()
Definition: rleibnetip.cpp:572
int recvfrom(void *buf, int maxlen, rlIpAdr *source, int timeout=-1)
int startReading()
Definition: rleibnetip.cpp:577
int recv(void *buf, int maxlen)
unsigned char mc
Definition: rleibnetip.h:56
float valueFloat2(const char *name)
Definition: rleibnetip.cpp:662
unsigned char apci
Definition: rleibnetip.h:63
int create(void *(*func)(void *), void *argument)
Definition: rlthread.cpp:35
int getDescription(PDU *pdu)
int printf(rlIpAdr *dest, const char *format,...)
unsigned char data[128-6]
Definition: rleibnetip.h:51
unsigned char headersize
Definition: rleibnetip.h:47
int getText(const char *name, char *text, int maxlen)
Definition: rleibnetip.cpp:745
int setAdr(const char *adr, int port)
Definition: rludpsocket.cpp:31
int send_sequencecounter
Definition: rleibnetip.h:107
int disconnect()
Definition: rleibnetip.cpp:534
ServiceType
Definition: rleibnetip.cpp:40
int bind(int port)
#define EIB_CRICRDSIZE
Definition: rleibnetip.cpp:27
int setServer(rlIpAdr *server)
rlIpAdr client
Definition: rleibnetip.h:114
unsigned char val[14]
Definition: rleibnetip.h:64
#define L_Data_Ind
Definition: rleibnetip.cpp:38
short saddr
Definition: rleibnetip.h:118
#define L_Data_Req
Definition: rleibnetip.cpp:36
int main()
Definition: rlcorba.cpp:18
#define TUNNEL_LINKLAYER
Definition: rleibnetip.cpp:31
unsigned char apci_length
Definition: rleibnetip.h:62
void rlsleep(long msec)
Definition: rlwthread.cpp:396
float valueFloat4(const char *name)
Definition: rleibnetip.cpp:676
rlThread thread
Definition: rleibnetip.h:105
int is_connected
Definition: rleibnetip.h:117
unsigned char addi1
Definition: rleibnetip.h:57
unsigned short daddr
Definition: rleibnetip.h:61
rlEIBnetIP(int num_signals=1000, int debug=0, rlDataAcquisitionProvider *provider=NULL)
Definition: rleibnetip.cpp:164
int setValueFloat(const char *name, float val, int length=-1, int addi1=-1, int ctrl=-1, int apci=-1)
Definition: rleibnetip.cpp:628
int stopReading()
Definition: rleibnetip.cpp:586
unsigned char ctrl1
Definition: rleibnetip.h:58
virtual ~rlEIBnetIP()
Definition: rleibnetip.cpp:190
int storeBuffer(unsigned char *buf, int len)
Definition: rleibnetip.cpp:201
int setClient(rlIpAdr *client)
int sendto(const void *buf, int len, rlIpAdr *dest)
int setValuesFromCSV(const char *filename)
Definition: rleibnetip.cpp:413
int setText(const char *name, const char *text)
Definition: rleibnetip.cpp:790
int setValueUnsigned(const char *name, unsigned int val, int length=-1, int addi1=-1, int ctrl=-1, int apci=-1)
Definition: rleibnetip.cpp:894
int setStringValue(const char *variable, const char *value)
int tunnel_ack
Definition: rleibnetip.h:108
rlIpAdr from
Definition: rleibnetip.h:115
struct sockaddr_in address
Definition: rludpsocket.h:47
int lock()
Definition: rlthread.cpp:47
unsigned char version
Definition: rleibnetip.h:48
int connect()
Definition: rleibnetip.cpp:474
int dump(FILE *fout)
Definition: rleibnetip.cpp:364
Definition: rltime.h:25
unsigned char ctrl2
Definition: rleibnetip.h:59
rlDataAcquisitionProvider * provider
Definition: rleibnetip.h:109
#define L_Data_Con
Definition: rleibnetip.cpp:37
rlIpAdr * server
Definition: rleibnetip.h:106
unsigned short totalsize
Definition: rleibnetip.h:50