GuruxDLMS.c - GuruxDLMSClientExample - SRC - Communication.c at 2d9b9846 Gurux - GuruxDLMS.c GitHub
GuruxDLMS.c - GuruxDLMSClientExample - SRC - Communication.c at 2d9b9846 Gurux - GuruxDLMS.c GitHub
c Public
1 //
2 // --------------------------------------------------------------------------
3 // Gurux Ltd
4 //
5 //
6 //
7 // Filename: $HeadURL: $
8 //
9 // Version: $Revision: $,
10 // $Date: $
11 // $Author: $
12 //
13 // Copyright (c) Gurux Ltd
14 //
15 //---------------------------------------------------------------------------
16 #if defined(_WIN32) || defined(_WIN64)//Windows includes
17 #include <ws2tcpip.h>
18 #include <Windows.h> //Add support for serial port functions.
19 #else
20 #define INVALID_HANDLE_VALUE -1
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <errno.h> //Add support for sockets
24 #include <netdb.h> //Add support for sockets
25 #include <sys/types.h> //Add support for sockets
26 #include <sys/socket.h> //Add support for sockets
27 #include <netinet/in.h> //Add support for sockets
28 #include <arpa/inet.h> //Add support for sockets
29 #include <termios.h>
30 #include <sys/ioctl.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #endif
34
35 #include "../include/communication.h"
36 #include "../../development/include/gxkey.h"
37 #include "../../development/include/converters.h"
38 #include "../../development/include/cosem.h"
39 #include "../../development/include/gxserializer.h"
40
41 //Returns current time.
42 //If you are not using operating system you have to implement this by yourself.
43 //Reason for this is that all compilers's or HWs don't support time at all.
44 void time_now(gxtime* value)
45 {
46 time_initUnix(value, (unsigned long)time(NULL));
47 }
48
49 //Check is IP address IPv6 or IPv4 address.
50 unsigned char com_isIPv6Address(const char* pAddress)
51 {
52 return strstr(pAddress, ":") != NULL;
53 }
54
55 //Make connection using TCP/IP connection.
56 int com_makeConnect(connection* connection, const char* address, int port, int waitTime)
57 {
58 int ret;
59 //create socket.
60 int family = com_isIPv6Address(address) ? AF_INET6 : AF_INET;
61 connection->socket = socket(family, SOCK_STREAM, IPPROTO_IP);
62 if (connection->socket == -1)
63 {
64 #if defined(_WIN32) || defined(_WIN64)//Windows includes
65 int err = WSAGetLastError();
66 printf("Socket creation failed: %d\r\n", err);
67 #else
68 int err = errno;
69 printf("Socket creation failed: %d, %s\r\n", err, strerror(err));
70 #endif
71 return DLMS_ERROR_CODE_INVALID_PARAMETER;
72 }
73
74 struct sockaddr* add;
75 int addSize;
76 struct sockaddr_in6 addrIP6;
77 struct sockaddr_in addIP4;
78 if (family == AF_INET)
79 {
80 addIP4.sin_port = htons(port);
81 addIP4.sin_family = AF_INET;
82 addIP4.sin_addr.s_addr = inet_addr(address);
83 addSize = sizeof(struct sockaddr_in);
84 add = (struct sockaddr*)&addIP4;
85 //If address is give as name
86 if (addIP4.sin_addr.s_addr == INADDR_NONE)
87 {
88 struct hostent* Hostent = gethostbyname(address);
89 if (Hostent == NULL)
90 {
91 #if defined(_WIN32) || defined(_WIN64)//Windows includes
92 ret = WSAGetLastError();
93 closesocket(connection->socket);
94 #else
95 ret = errno;
96 close(connection->socket);
97 #endif
98 connection->socket = -1;
99 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
100 };
101 addIP4.sin_addr = *(struct in_addr*)(void*)Hostent->h_addr_list[0];
102 };
103 }
104 else
105 {
106 memset(&addrIP6, 0, sizeof(struct sockaddr_in6));
107 addrIP6.sin6_port = htons(port);
108 addrIP6.sin6_family = AF_INET6;
109 ret = inet_pton(family, address, &(addrIP6.sin6_addr));
110 if (ret == -1)
111 {
112 #if defined(_WIN32) || defined(_WIN64)//Windows includes
113 ret = WSAGetLastError();
114 closesocket(connection->socket);
115 #else
116 ret = errno;
117 close(connection->socket);
118 #endif
119 connection->socket = -1;
120 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
121 };
122 add = (struct sockaddr*)&addrIP6;
123 addSize = sizeof(struct sockaddr_in6);
124 }
125
126 //Set timeout.
127 #if defined(_WIN32) || defined(_WIN64)//If Windows
128 setsockopt(connection->socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&waitTime, sizeof(wai
129 #else
130 struct timeval tv;
131 tv.tv_sec = waitTime / 1000;
132 tv.tv_usec = 0;
133 setsockopt(connection->socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
134 #endif //
135 //Connect to the meter.
136 ret = connect(connection->socket, add, addSize);
137 if (ret == -1)
138 {
139 #if defined(_WIN32) || defined(_WIN64)//Windows includes
140 ret = WSAGetLastError();
141 closesocket(connection->socket);
142 printf("Connection failed: %d\n", ret);
143 #else
144 ret = errno;
145 close(connection->socket);
146 printf("Connection failed: %d, %s\n", ret, strerror(ret));
147 #endif
148 connection->socket = -1;
149 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
150 };
151 return DLMS_ERROR_CODE_OK;
152 }
153
154 #if defined(_WIN32) || defined(_WIN64)//If Windows
155 int com_setCommState(HANDLE hWnd, LPDCB DCB)
156 {
157 if (!SetCommState(hWnd, DCB))
158 {
159 DWORD err = GetLastError(); //Save occured error.
160 if (err == 995)
161 {
162 COMSTAT comstat;
163 unsigned long RecieveErrors;
164 if (!ClearCommError(hWnd, &RecieveErrors, &comstat))
165 {
166 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | GetLastError();
167 }
168 if (!SetCommState(hWnd, DCB))
169 {
170 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | GetLastError();
171 }
172 }
173 else
174 {
175 //If USB to serial port converters do not implement this.
176 if (err != ERROR_INVALID_FUNCTION)
177 {
178 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | err;
179 }
180 }
181 }
182 return DLMS_ERROR_CODE_OK;
183 }
184 #endif
185
186 int com_readSerialPort(
187 connection* connection,
188 unsigned char eop)
189 {
190 //Read reply data.
191 int ret, cnt = 1, pos;
192 unsigned char eopFound = 0;
193 int lastReadIndex = 0;
194 #if defined(_WIN32) || defined(_WIN64)//Windows
195 unsigned long RecieveErrors;
196 COMSTAT comstat;
197 DWORD bytesRead = 0;
198 #else
199 unsigned short bytesRead = 0;
200 unsigned short readTime = 0;
201 #endif
202 do
203 {
204 #if defined(_WIN32) || defined(_WIN64)//Windows
205 //We do not want to read byte at the time.
206 if (!ClearCommError(connection->comPort, &RecieveErrors, &comstat))
207 {
208 ret = GetLastError();
209 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
210 }
211 bytesRead = 0;
212 cnt = 1;
213 //Try to read at least one byte.
214 if (comstat.cbInQue > 0)
215 {
216 cnt = comstat.cbInQue;
217 }
218 if (!ReadFile(connection->comPort, connection->data.data + connection->data.size, cnt
219 {
220 ret = GetLastError();
221 if (ret != ERROR_IO_PENDING)
222 {
223 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
224 }
225 //Wait until data is actually read
226 if (WaitForSingleObject(connection->osReader.hEvent, connection->waitTime) != WAIT
227 {
228 return DLMS_ERROR_CODE_RECEIVE_FAILED;
229 }
( (
230 if (!GetOverlappedResult(connection->comPort, &connection->osReader, &bytesRead, T
231 {
232 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | GetLastError();
233 }
234 }
235 #else
236 //Get bytes available.
237 ret = ioctl(connection->comPort, FIONREAD, &cnt);
238 //If driver is not supporting this functionality.
239 if (ret < 0)
240 {
241 cnt = RECEIVE_BUFFER_SIZE;
242 }
243 else if (cnt == 0)
244 {
245 //Try to read at least one byte.
246 cnt = 1;
247 }
248 //If there is more data than can fit to buffer.
249 if (cnt > RECEIVE_BUFFER_SIZE)
250 {
251 cnt = RECEIVE_BUFFER_SIZE;
252 }
253 bytesRead = read(connection->comPort, connection->data.data + connection->data.size, c
254 if (bytesRead == 0xFFFF)
255 {
256 //If there is no data on the read buffer.
257 if (errno == EAGAIN)
258 {
259 if (readTime > connection->waitTime)
260 {
261 return DLMS_ERROR_CODE_RECEIVE_FAILED;
262 }
263 readTime += 100;
264 bytesRead = 0;
265 }
266 else
267 {
268 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | errno;
269 }
270 }
271 #endif
272 connection->data.size += (unsigned short)bytesRead;
273 //Note! Some USB converters can return true for ReadFile and Zero as bytesRead.
274 //In that case wait for a while and read again.
275 if (bytesRead == 0)
276 {
277 #if defined(_WIN32) || defined(_WIN64)//Windows
278 Sleep(100);
279 # l
279 #else
280 usleep(100000);
281 #endif
282 continue;
283 }
284 //Search eop.
285 if (connection->data.size > 5)
286 {
287 //Some optical strobes can return extra bytes.
288 for (pos = connection->data.size - 1; pos != lastReadIndex; --pos)
289 {
290 if (connection->data.data[pos] == eop)
291 {
292 eopFound = 1;
293 break;
294 }
295 }
296 lastReadIndex = pos;
297 }
298 } while (eopFound == 0);
299 return DLMS_ERROR_CODE_OK;
300 }
301
302 int com_initializeOpticalHead(
303 connection* connection)
304 {
305 unsigned short baudRate;
306 int ret = 0, len, pos;
307 unsigned char ch;
308 //In Linux serial port name might be very long.
309 char buff[50];
310 #if defined(_WIN32) || defined(_WIN64)
311 DCB dcb = { 0 };
312 unsigned long sendSize = 0;
313 if (connection->comPort == INVALID_HANDLE_VALUE)
314 {
315 return DLMS_ERROR_CODE_INVALID_PARAMETER;
316 }
317 dcb.DCBlength = sizeof(DCB);
318 if (!GetCommState(connection->comPort, &dcb))
319 {
320 ret = GetLastError();
321 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
322 }
323 dcb.fBinary = 1;
324 dcb.fOutX = dcb.fInX = 0;
325 //Abort all reads and writes on Error.
326 dcb.fAbortOnError = 1;
327 if (connection->settings.interfaceType == DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E)
328 {
328 {
329 dcb.BaudRate = 300;
330 dcb.ByteSize = 7;
331 dcb.StopBits = ONESTOPBIT;
332 dcb.Parity = EVENPARITY;
333 }
334 else
335 {
336 dcb.BaudRate = 9600;
337 dcb.ByteSize = 8;
338 dcb.StopBits = ONESTOPBIT;
339 dcb.Parity = NOPARITY;
340 }
341 if ((ret = com_setCommState(connection->comPort, &dcb)) != 0)
342 {
343 return ret;
344 }
345 #else
346 struct termios options;
347 memset(&options, 0, sizeof(options));
348 options.c_iflag = 0;
349 options.c_oflag = 0;
350 if (connection->settings.interfaceType == DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E)
351 {
352 options.c_cflag |= PARENB;
353 options.c_cflag &= ~PARODD;
354 options.c_cflag &= ~CSTOPB;
355 options.c_cflag &= ~CSIZE;
356 options.c_cflag |= CS7;
357 //Set Baud Rates
358 cfsetospeed(&options, B300);
359 cfsetispeed(&options, B300);
360 }
361 else
362 {
363 // 8n1, see termios.h for more information
364 options.c_cflag = CS8 | CREAD | CLOCAL;
365 /*
366 options.c_cflag &= ~PARENB
367 options.c_cflag &= ~CSTOPB
368 options.c_cflag &= ~CSIZE;
369 options.c_cflag |= CS8;
370 */
371 //Set Baud Rates
372 cfsetospeed(&options, B9600);
373 cfsetispeed(&options, B9600);
374 }
375 options.c_lflag = 0;
376 options.c_cc[VMIN] = 1;
377 //How long we are waiting reply charachter from serial port
377 //How long we are waiting reply charachter from serial port.
378 options.c_cc[VTIME] = 5;
379
380 //hardware flow control is used as default.
381 //options.c_cflag |= CRTSCTS;
382 if (tcsetattr(connection->comPort, TCSAFLUSH, &options) != 0)
383 {
384 printf("Failed to Open port. tcsetattr failed.\r");
385 return DLMS_ERROR_CODE_INVALID_PARAMETER;
386 }
387 #endif
388 if (connection->settings.interfaceType == DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E)
389 {
390 #if _MSC_VER > 1000
391 strcpy_s(buff, 50, "/?!\r\n");
392 #else
393 strcpy(buff, "/?!\r\n");
394 #endif
395 len = strlen(buff);
396 if (connection->trace > GX_TRACE_LEVEL_WARNING)
397 {
398 printf("\nTX:\t");
399 for (pos = 0; pos != len; ++pos)
400 {
401 printf("%.2X ", buff[pos]);
402 }
403 printf("\n");
404 }
405 #if defined(_WIN32) || defined(_WIN64)
406 ret = WriteFile(connection->comPort, buff, len, &sendSize, &connection->osWrite);
407 if (ret == 0)
408 {
409 DWORD err = GetLastError();
410 //If error occurs...
411 if (err != ERROR_IO_PENDING)
412 {
413 return DLMS_ERROR_CODE_SEND_FAILED;
414 }
415 //Wait until data is actually sent
416 WaitForSingleObject(connection->osWrite.hEvent, INFINITE);
417 }
418 #else //#if defined(__LINUX__)
419 ret = write(connection->comPort, buff, len);
420 if (ret != len)
421 {
422 return DLMS_ERROR_CODE_SEND_FAILED;
423 }
424 #endif
425 if (connection->trace > GX_TRACE_LEVEL_WARNING)
426 {
426 {
427 printf("\nRX:\t");
428 }
429 //Read reply data.
430 if (com_readSerialPort(connection, '\n') != 0)
431 {
432 return DLMS_ERROR_CODE_INVALID_PARAMETER;
433 }
434 if (connection->trace > GX_TRACE_LEVEL_WARNING)
435 {
436 printf("\r\n");
437 }
438 if (bb_getUInt8(&connection->data, &ch) != 0 || ch != '/')
439 {
440 return DLMS_ERROR_CODE_SEND_FAILED;
441 }
442 //Get used baud rate.
443 if ((ret = bb_getUInt8ByIndex(&connection->data, 4, &ch)) != 0)
444 {
445 return DLMS_ERROR_CODE_SEND_FAILED;
446 }
447 switch (ch)
448 {
449 case '0':
450 #if defined(_WIN32) || defined(_WIN64)
451 baudRate = 300;
452 #else
453 baudRate = B300;
454 #endif
455 break;
456 case '1':
457 #if defined(_WIN32) || defined(_WIN64)
458 baudRate = 600;
459 #else
460 baudRate = B600;
461 #endif
462 break;
463 case '2':
464 #if defined(_WIN32) || defined(_WIN64)
465 baudRate = 1200;
466 #else
467 baudRate = B1200;
468 #endif
469 break;
470 case '3':
471 #if defined(_WIN32) || defined(_WIN64)
472 baudRate = 2400;
473 #else
474 baudRate = B2400;
475 #endif
475 #endif
476 break;
477 case '4':
478 #if defined(_WIN32) || defined(_WIN64)
479 baudRate = 4800;
480 #else
481 baudRate = B4800;
482 #endif
483 break;
484 case '5':
485 #if defined(_WIN32) || defined(_WIN64)
486 baudRate = 9600;
487 #else
488 baudRate = B9600;
489 #endif
490 break;
491 case '6':
492 #if defined(_WIN32) || defined(_WIN64)
493 baudRate = 19200;
494 #else
495 baudRate = B19200;
496 #endif
497 break;
498 default:
499 return DLMS_ERROR_CODE_INVALID_PARAMETER;
500 }
501 //Send ACK
502 buff[0] = 0x06;
503 //Send Protocol control character
504 buff[1] = '2';// "2" HDLC protocol procedure (Mode E)
505 buff[2] = (char)ch;
506 buff[3] = '2';
507 buff[4] = (char)0x0D;
508 buff[5] = 0x0A;
509 len = 6;
510 if (connection->trace > GX_TRACE_LEVEL_WARNING)
511 {
512 printf("\nTX:\t");
513 for (pos = 0; pos != len; ++pos)
514 {
515 printf("%.2X ", buff[pos]);
516 }
517 printf("\n");
518 }
519 #if defined(_WIN32) || defined(_WIN64)//Windows
520 ret = WriteFile(connection->comPort, buff, len, &sendSize, &connection->osWrite);
521 if (ret == 0)
522 {
523 int err = GetLastError();
524 //If error occurs
524 //If error occurs...
525 if (err != ERROR_IO_PENDING)
526 {
527 printf("WriteFile %d\r\n", err);
528 return DLMS_ERROR_CODE_SEND_FAILED;
529 }
530 //Wait until data is actually sent
531 WaitForSingleObject(connection->osWrite.hEvent, INFINITE);
532 }
533 #else //#if defined(__LINUX__)
534 ret = write(connection->comPort, buff, len);
535 if (ret != len)
536 {
537 return DLMS_ERROR_CODE_SEND_FAILED;
538 }
539 #endif
540 #if defined(_WIN32) || defined(_WIN64)//Windows
541 //This sleep is in standard. Do not remove.
542 Sleep(1000);
543 dcb.BaudRate = baudRate;
544 if ((ret = com_setCommState(connection->comPort, &dcb)) != 0)
545 {
546 return ret;
547 }
548 printf("New baudrate %d\r\n", dcb.BaudRate);
549 len = 6;
550 if ((ret = com_readSerialPort(connection, '\n')) != 0)
551 {
552 printf("Read %d\r\n", ret);
553 return ret;
554 }
555 dcb.ByteSize = 8;
556 dcb.StopBits = ONESTOPBIT;
557 dcb.Parity = NOPARITY;
558 if ((ret = com_setCommState(connection->comPort, &dcb)) != 0)
559 {
560 return ret;
561 }
562 #else
563 //This sleep is in standard. Do not remove.
564 usleep(1000000);
565 // 8n1, see termios.h for more information
566 options.c_cflag = CS8 | CREAD | CLOCAL;
567 //Set Baud Rates
568 cfsetospeed(&options, baudRate);
569 cfsetispeed(&options, baudRate);
570 if (tcsetattr(connection->comPort, TCSAFLUSH, &options) != 0)
571 {
572 printf("Failed to Open port. tcsetattr failed.\r");
573 return DLMS ERROR CODE INVALID PARAMETER;
573 return DLMS_ERROR_CODE_INVALID_PARAMETER;
574 }
575 #endif
576 }
577 return ret;
578 }
579
580 int com_open(
581 connection* connection,
582 const char* port)
583 {
584 int ret;
585 //In Linux serial port name might be very long.
586 #if defined(_WIN32) || defined(_WIN64)
587 char buff[50];
588 #if _MSC_VER > 1000
589 sprintf_s(buff, 50, "\\\\.\\%s", port);
590 #else
591 sprintf(buff, "\\\\.\\%s", port);
592 #endif
593 //Open serial port for read / write. Port can't share.
594 connection->comPort = CreateFileA(buff,
595 GENERIC_READ | GENERIC_WRITE, 0, NULL,
596 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
597 if (connection->comPort == INVALID_HANDLE_VALUE)
598 {
599 ret = GetLastError();
600 printf("Failed to open serial port: \"%s\"\n", buff);
601 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
602 }
603 #else //#if defined(__LINUX__)
604 // read/write | not controlling term | don't wait for DCD line signal.
605 connection->comPort = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK);
606 if (connection->comPort == -1) // if open is unsuccessful.
607 {
608 ret = errno;
609 printf("Failed to open serial port: %s\n", port);
610 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
611 }
612 if (!isatty(connection->comPort))
613 {
614 ret = errno;
615 printf("Failed to Open port %s. This is not a serial port.\n", port);
616 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
617 }
618 #endif
619 return com_initializeOpticalHead(connection);
620 }
621
622 int sendData(connection* connection, gxByteBuffer* data)
6 t se d ata(co ect o co ect o , g yte u e data)
623 {
624 int ret = 0;
625 #if defined(_WIN32) || defined(_WIN64)//Windows
626 unsigned long sendSize = 0;
627 #endif
628 if (connection->comPort != INVALID_HANDLE_VALUE)
629 {
630 #if defined(_WIN32) || defined(_WIN64)//Windows
631 ret = WriteFile(connection->comPort, data->data, data->size, &sendSize, &connection->o
632 if (ret == 0)
633 {
634 COMSTAT comstat;
635 unsigned long RecieveErrors;
636 DWORD err = GetLastError();
637 //If error occurs...
638 if (err != ERROR_IO_PENDING)
639 {
640 ret = GetLastError();
641 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
642 }
643 //Wait until data is actually sent
644 ret = WaitForSingleObject(connection->osWrite.hEvent, connection->waitTime);
645 if (ret != 0)
646 {
647 ret = GetLastError();
648 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
649 }
650 //Read bytes in output buffer. Some USB converts require this.
651 if (!ClearCommError(connection->comPort, &RecieveErrors, &comstat))
652 {
653 ret = GetLastError();
654 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
655 }
656 }
657 #else
658 ret = write(connection->comPort, data->data, data->size);
659 if (ret != data->size)
660 {
661 ret = errno;
662 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
663 }
664 #endif
665 }
666 else
667 {
668 if (send(connection->socket, (const char*)data->data, data->size, 0) == -1)
669 {
670 #if defined(_WIN32) || defined(_WIN64)//If Windows
671 ret = WSAGetLastError();
();
672 #else
673 ret = errno;
674 #endif
675 return DLMS_ERROR_TYPE_COMMUNICATION_ERROR | ret;
676 }
677 }
678 return 0;
679 }
680
681 int readData(connection* connection, gxByteBuffer* data, int* index)
682 {
683 int ret = 0;
684 if (connection->comPort != INVALID_HANDLE_VALUE)
685 {
686 if ((ret = com_readSerialPort(connection, 0x7E)) != 0)
687 {
688 return ret;
689 }
690 }
691 else
692 {
693 uint32_t cnt = connection->data.capacity - connection->data.size;
694 if (cnt < 1)
695 {
696 return DLMS_ERROR_CODE_OUTOFMEMORY;
697 }
698 if ((ret = recv(connection->socket, (char*)connection->data.data + connection->data.si
699 {
700 return DLMS_ERROR_CODE_RECEIVE_FAILED;
701 }
702 connection->data.size += ret;
703 }
704 if (connection->trace > GX_TRACE_LEVEL_INFO)
705 {
706 char* hex = hlp_bytesToHex(connection->data.data + *index, connection->data.size - *in
707 if (*index == 0)
708 {
709 printf("\nRX:\t %s", hex);
710 }
711 else
712 {
713 printf(" %s", hex);
714 }
715 free(hex);
716 *index = connection->data.size;
717 }
718 return 0;
719 }
720
721 // Read DLMS Data frame from the device.
722 int readDLMSPacket(
723 connection* connection,
724 gxByteBuffer* data,
725 gxReplyData* reply)
726 {
727 char* hex;
728 int index = 0, ret;
729 if (data->size == 0)
730 {
731 return DLMS_ERROR_CODE_OK;
732 }
733 reply->complete = 0;
734 connection->data.size = 0;
735 connection->data.position = 0;
736 if (connection->trace == GX_TRACE_LEVEL_VERBOSE)
737 {
738 hex = bb_toHexString(data);
739 printf("\nTX:\t%s\n", hex);
740 free(hex);
741 }
742 if ((ret = sendData(connection, data)) != 0)
743 {
744 return ret;
745 }
746 //Loop until packet is complete.
747 unsigned char pos = 0;
748 unsigned char isNotify;
749 gxReplyData notify;
750 reply_init(¬ify);
751 do
752 {
753 if ((ret = readData(connection, &connection->data, &index)) != 0)
754 {
755 if (ret != DLMS_ERROR_CODE_RECEIVE_FAILED || pos == 3)
756 {
757 break;
758 }
759 ++pos;
760 printf("\nData send failed. Try to resend %d/3\n", pos);
761 if ((ret = sendData(connection, data)) != 0)
762 {
763 break;
764 }
765 }
766 else
767 {
768 ret = cl_getData2(&connection->settings, &connection->data, reply, ¬ify, &isNot
769 if (ret != 0 && ret != DLMS_ERROR_CODE_FALSE)
770 {
771 break;
772 }
773 if (isNotify)
774 {
775 gxByteBuffer bb;
776 bb_init(&bb);
777 var_toString(¬ify.dataValue, &bb);
778 char* tmp = bb_toString(&bb);
779 printf("Notification received: %s", tmp);
780 free(tmp);
781 bb_clear(&bb);
782 }
783 }
784 } while (reply->complete == 0);
785 reply_clear(¬ify);
786 if (connection->trace == GX_TRACE_LEVEL_VERBOSE)
787 {
788 printf("\n");
789 }
790 return ret;
791 }
792
793 int com_readDataBlock(
794 connection* connection,
795 message* messages,
796 gxReplyData* reply)
797 {
798 gxByteBuffer rr;
799 int pos, ret = DLMS_ERROR_CODE_OK;
800 //If there is no data to send.
801 if (messages->size == 0)
802 {
803 return DLMS_ERROR_CODE_OK;
804 }
805 bb_init(&rr);
806 //Send data.
807 for (pos = 0; pos != messages->size; ++pos)
808 {
809 //Send data.
810 if ((ret = readDLMSPacket(connection, messages->data[pos], reply)) != DLMS_ERROR_CODE_
811 {
812 return ret;
813 }
814 //Check is there errors or more data from server
815 while (reply_isMoreData(reply))
816 {
817 if ((ret = cl_receiverReady(&connection->settings, reply->moreData, &rr)) != DLMS_
818 {
819 bb_clear(&rr);
820 return ret;
821 }
822 if ((ret = readDLMSPacket(connection, &rr, reply)) != DLMS_ERROR_CODE_OK)
823 {
824 bb_clear(&rr);
825 return ret;
826 }
827 bb_clear(&rr);
828 }
829 }
830 return ret;
831 }
832
833
834 //Close connection to the meter.
835 int com_disconnect(
836 connection* connection)
837 {
838 int ret = DLMS_ERROR_CODE_OK;
839 gxReplyData reply;
840 message msg;
841 reply_init(&reply);
842 mes_init(&msg);
843 if ((ret = cl_disconnectRequest(&connection->settings, &msg)) != 0 ||
844 (ret = com_readDataBlock(connection, &msg, &reply)) != 0)
845 {
846 //Show error but continue close.
847 printf("Close failed.");
848 }
849 reply_clear(&reply);
850 mes_clear(&msg);
851 return ret;
852 }
853
854 //Close connection to the meter.
855 int com_close(
856 connection* connection)
857 {
858 int ret = DLMS_ERROR_CODE_OK;
859 gxReplyData reply;
860 message msg;
861 //If client is closed.
862 if (!connection->settings.server)
863 {
864 reply_init(&reply);
865 mes_init(&msg);
866 if ((ret = cl_releaseRequest(&connection->settings, &msg)) != 0 ||
867 (ret = com_readDataBlock(connection, &msg, &reply)) != 0)
868 {
869 //Show error but continue close.
870 printf("Release failed.");
871 }
872 reply_clear(&reply);
873 mes_clear(&msg);
874
875 if ((ret = cl_disconnectRequest(&connection->settings, &msg)) != 0 ||
876 (ret = com_readDataBlock(connection, &msg, &reply)) != 0)
877 {
878 //Show error but continue close.
879 printf("Close failed.");
880 }
881 reply_clear(&reply);
882 mes_clear(&msg);
883 }
884 if (connection->socket != -1)
885 {
886 connection->closing = 1;
887 #if defined(_WIN32) || defined(_WIN64)//Windows includes
888 closesocket(connection->socket);
889 #else
890 close(connection->socket);
891 #endif
892 connection->socket = -1;
893 }
894 else if (connection->comPort != INVALID_HANDLE_VALUE)
895 {
896 #if defined(_WIN32) || defined(_WIN64)//Windows includes
897 CloseHandle(connection->comPort);
898 connection->comPort = INVALID_HANDLE_VALUE;
899 CloseHandle(connection->osReader.hEvent);
900 CloseHandle(connection->osWrite.hEvent);
901 #else
902 close(connection->comPort);
903 #endif
904 connection->comPort = INVALID_HANDLE_VALUE;
905 }
906 cl_clear(&connection->settings);
907 return ret;
908 }
909
910 //Read Invocation counter (frame counter) from the meter and update it.
911 int com_updateInvocationCounter(
912 connection* connection,
913 const char* invocationCounter)
914 {
915 int ret = DLMS_ERROR_CODE_OK;
916 //Read frame counter if GeneralProtection is used.
917 if (invocationCounter != NULL && connection->settings.cipher.security != DLMS_SECURITY_NON
918 {
919 message messages;
920 gxReplyData reply;
921 unsigned short add = connection->settings.clientAddress;
922 DLMS_AUTHENTICATION auth = connection->settings.authentication;
923 DLMS_SECURITY security = connection->settings.cipher.security;
924 gxByteBuffer challenge;
925 bb_init(&challenge);
926 bb_set(&challenge, connection->settings.ctoSChallenge.data, connection->settings.ctoSC
927 connection->settings.clientAddress = 16;
928 connection->settings.authentication = DLMS_AUTHENTICATION_NONE;
929 connection->settings.cipher.security = DLMS_SECURITY_NONE;
930 if (connection->trace > GX_TRACE_LEVEL_WARNING)
931 {
932 printf("updateInvocationCounter\r\n");
933 }
934 mes_init(&messages);
935 reply_init(&reply);
936 //Get meter's send and receive buffers size.
937 if ((ret = cl_snrmRequest(&connection->settings, &messages)) != 0 ||
938 (ret = com_readDataBlock(connection, &messages, &reply)) != 0 ||
939 (ret = cl_parseUAResponse(&connection->settings, &reply.data)) != 0)
940 {
941 bb_clear(&challenge);
942 mes_clear(&messages);
943 reply_clear(&reply);
944 if (connection->trace > GX_TRACE_LEVEL_OFF)
945 {
946 printf("SNRMRequest failed %s\r\n", hlp_getErrorMessage(ret));
947 }
948 return ret;
949 }
950 mes_clear(&messages);
951 reply_clear(&reply);
952 if ((ret = cl_aarqRequest(&connection->settings, &messages)) != 0 ||
953 (ret = com_readDataBlock(connection, &messages, &reply)) != 0 ||
954 (ret = cl_parseAAREResponse(&connection->settings, &reply.data)) != 0)
955 {
956 bb_clear(&challenge);
957 mes_clear(&messages);
958 reply_clear(&reply);
959 if (ret == DLMS_ERROR_CODE_APPLICATION_CONTEXT_NAME_NOT_SUPPORTED)
960 {
961 if (connection->trace > GX_TRACE_LEVEL_OFF)
962 {
963 printf("Use Logical Name referencing is wrong. Change it!\r\n");
964 }
965 return ret;
966 }
967 if (connection->trace > GX_TRACE_LEVEL_OFF)
968 {
969 printf("AARQRequest failed %s\r\n", hlp_getErrorMessage(ret));
970 }
971 return ret;
972 }
973 mes_clear(&messages);
974 reply_clear(&reply);
975 if (connection->settings.maxPduSize == 0xFFFF)
976 {
977 con_initializeBuffers(connection, connection->settings.maxPduSize);
978 }
979 else
980 {
981 //Allocate 50 bytes more because some meters count this wrong and send few bytes t
982 con_initializeBuffers(connection, 50 + connection->settings.maxPduSize);
983 }
984 gxData d;
985 cosem_init(BASE(d), DLMS_OBJECT_TYPE_DATA, invocationCounter);
986 if ((ret = com_read(connection, BASE(d), 2)) == 0)
987 {
988 connection->settings.cipher.invocationCounter = 1 + var_toInteger(&d.value);
989 if (connection->trace > GX_TRACE_LEVEL_WARNING)
990 {
991 printf("Invocation counter: %lu (0x%lX)\r\n",
992 connection->settings.cipher.invocationCounter,
993 connection->settings.cipher.invocationCounter);
994 }
995 //It's OK if this fails.
996 com_disconnect(connection);
997 connection->settings.clientAddress = add;
998 connection->settings.authentication = auth;
999 connection->settings.cipher.security = security;
1000 bb_clear(&connection->settings.ctoSChallenge);
1001 bb_set(&connection->settings.ctoSChallenge, challenge.data, challenge.size);
1002 bb_clear(&challenge);
1003 }
1004 }
1005 return ret;
1006 }
1007
1008 //Initialize connection to the meter.
1009 int com_initializeConnection(
1010 connection* connection)
1011 {
1012 message messages;
1013 gxReplyData reply;
1014 int ret = 0;
1015 if (connection->trace > GX_TRACE_LEVEL_WARNING)
1016 {
1017 printf("InitializeConnection\r\n");
1018 }
1019
1020 mes_init(&messages);
1021 reply_init(&reply);
1022 //Get meter's send and receive buffers size.
1023 if ((ret = cl_snrmRequest(&connection->settings, &messages)) != 0 ||
1024 (ret = com_readDataBlock(connection, &messages, &reply)) != 0 ||
1025 (ret = cl_parseUAResponse(&connection->settings, &reply.data)) != 0)
1026 {
1027 mes_clear(&messages);
1028 reply_clear(&reply);
1029 if (connection->trace > GX_TRACE_LEVEL_OFF)
1030 {
1031 printf("SNRMRequest failed %s\r\n", hlp_getErrorMessage(ret));
1032 }
1033 return ret;
1034 }
1035 mes_clear(&messages);
1036 reply_clear(&reply);
1037 if ((ret = cl_aarqRequest(&connection->settings, &messages)) != 0 ||
1038 (ret = com_readDataBlock(connection, &messages, &reply)) != 0 ||
1039 (ret = cl_parseAAREResponse(&connection->settings, &reply.data)) != 0)
1040 {
1041 mes_clear(&messages);
1042 reply_clear(&reply);
1043 if (ret == DLMS_ERROR_CODE_APPLICATION_CONTEXT_NAME_NOT_SUPPORTED)
1044 {
1045 if (connection->trace > GX_TRACE_LEVEL_OFF)
1046 {
1047 printf("Use Logical Name referencing is wrong. Change it!\r\n");
1048 }
1049 return ret;
1050 }
1051 if (connection->trace > GX_TRACE_LEVEL_OFF)
1052 {
1053 printf("AARQRequest failed %s\r\n", hlp_getErrorMessage(ret));
1054 }
1055 return ret;
1056 }
1057 mes_clear(&messages);
1058 reply_clear(&reply);
1059 if (connection->settings.maxPduSize == 0xFFFF)
1060 {
1061 con_initializeBuffers(connection, connection->settings.maxPduSize);
1062 }
1063 else
1064 {
1065 //Allocate 50 bytes more because some meters count this wrong and send few bytes too m
1066 con_initializeBuffers(connection, 50 + connection->settings.maxPduSize);
1067 }
1068
1069 // Get challenge Is HLS authentication is used.
1070 if (connection->settings.authentication > DLMS_AUTHENTICATION_LOW)
1071 {
1072 if ((ret = cl_getApplicationAssociationRequest(&connection->settings, &messages)) != 0
1073 (ret = com_readDataBlock(connection, &messages, &reply)) != 0 ||
1074 (ret = cl_parseApplicationAssociationResponse(&connection->settings, &reply.data)
1075 {
1076 mes_clear(&messages);
1077 reply_clear(&reply);
1078 return ret;
1079 }
1080 mes_clear(&messages);
1081 reply_clear(&reply);
1082 }
1083 return DLMS_ERROR_CODE_OK;
1084 }
1085
1086 //Report error on output;
1087 void com_reportError(const char* description,
1088 gxObject* object,
1089 unsigned char attributeOrdinal, int ret)
1090 {
1091 char ln[25];
1092 hlp_getLogicalNameToString(object->logicalName, ln);
1093 printf("%s %s %s:%d %s\r\n", description, obj_typeToString2(object->objectType), ln, attri
1094 }
1095
1096 static unsigned char CURRENT_LN[] = { 0, 0, 40, 0, 0, 0xFF };
1097
1098 //Association view is read from association logical name object.
1099 int com_loadAssociationView(connection* connection, const char* outputFile)
1100 {
1101 int ret = 0;
1102 if (outputFile != NULL)
1103 {
1104 //Load settings.
1105 #if _MSC_VER > 1400
1106 FILE* f = NULL;
1107 fopen_s(&f, outputFile, "rb");
1108 #else
1109 FILE* f = fopen(outputFile, "rb");
1110 #endif
1111 if (f != NULL)
1112 {
1113 gxObject* CURRENT_ASSOCIATION[1] = { NULL };
1114 gxAssociationLogicalName ln;
1115 gxAssociationShortName sn;
1116 gxSerializerSettings serializerSettings;
1117 ser_init(&serializerSettings);
1118 serializerSettings.stream = f;
1119 gxSerializerIgnore NON_SERIALIZED_OBJECTS[] = { IGNORE_ATTRIBUTE_BY_TYPE(DLMS_OBJE
1120 serializerSettings.ignoredAttributes = NON_SERIALIZED_OBJECTS;
1121 serializerSettings.count = sizeof(NON_SERIALIZED_OBJECTS) / sizeof(NON_SERIALIZED_
1122 if (connection->settings.useLogicalNameReferencing)
1123 {
1124 INIT_OBJECT(ln, DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME, CURRENT_LN);
1125 CURRENT_ASSOCIATION[0] = BASE(ln);
1126 }
1127 else
1128 {
1129 NON_SERIALIZED_OBJECTS[0].objectType = DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME
1130 INIT_OBJECT(sn, DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME, CURRENT_LN);
1131 CURRENT_ASSOCIATION[0] = BASE(sn);
1132 }
1133 oa_clear(&connection->settings.objects, 1);
1134 ret = ser_loadObjects(&connection->settings, &serializerSettings, CURRENT_ASSOCIAT
1135 fclose(f);
1136 if (ret == 0)
1137 {
1138 if (connection->settings.useLogicalNameReferencing)
1139 {
1140 oa_move(&connection->settings.objects, &ln.objectList);
1141 obj_clear(BASE(ln));
1142 }
1143 else
1144 {
1145 oa_move(&connection->settings.objects, &sn.objectList);
1146 obj_clear(BASE(sn));
1147 }
1148 }
1149 }
1150 else
1151 {
1152 //Return false, if file doesn't exist.
1153 ret = DLMS_ERROR_CODE_FALSE;
1154 }
1155 }
1156 return ret;
1157 }
1158
1159 //Get Association view.
1160 int com_getAssociationView(connection* connection, const char* outputFile)
1161 {
1162 //If association view is already read.
1163 if (connection->settings.objects.size != 0)
1164 {
1165 return 0;
1166 }
1167 int ret;
1168 message data;
1169 gxReplyData reply;
1170 if (outputFile != NULL)
1171 {
1172 //Load settings.
1173 ret = com_loadAssociationView(connection, outputFile);
1174 if (ret != DLMS_ERROR_CODE_FALSE)
1175 {
1176 return ret;
1177 }
1178 }
1179 printf("GetAssociationView\r\n");
1180 mes_init(&data);
1181 reply_init(&reply);
1182 /*
1183 if ((ret = cl_getObjectsRequest(&connection->settings, &data)) != 0 ||
1184 (ret = com_readDataBlock(connection, &data, &reply)) != 0 ||
1185 (ret = cl_parseObjects(&connection->settings, &reply.data)) != 0)
1186 {
1187 printf("GetObjects failed %s\r\n", hlp_getErrorMessage(ret));
1188 }
1189 */
1190 // Parse object one at the time. This can be used if there is a limited amount of the memo
1191 // Only needed object can be created.
1192 if ((ret = cl_getObjectsRequest(&connection->settings, &data)) != 0 ||
1193 (ret = com_readDataBlock(connection, &data, &reply)) != 0)
1194 {
1195 printf("cl_getObjectsRequest failed %s\r\n", hlp_getErrorMessage(ret));
1196 }
1197 else
1198 {
1199 gxObject* CURRENT_ASSOCIATION[1] = { NULL };
1200 uint16_t pos, count;
1201 if ((ret = cl_parseObjectCount(&reply.data, &count)) != 0)
1202 {
1203 printf("cl_parseObjectCount failed %s\r\n", hlp_getErrorMessage(ret));
1204 }
1205 gxObject obj;
1206 for (pos = 0; pos != count; ++pos)
1207 {
1208 memset(&obj, 0, sizeof(gxObject));
1209 if ((ret = cl_parseNextObject(&connection->settings, &reply.data, &obj)) != 0)
1210 {
1211 break;
1212 }
1213 if (connection->settings.useLogicalNameReferencing && obj.objectType == DLMS_OBJEC
1214 {
1215 gxAssociationLogicalName* ln = (gxAssociationLogicalName*)malloc(sizeof(gxAsso
1216 INIT_OBJECT((*ln), obj.objectType, obj.logicalName);
1217 ln->base.shortName = obj.shortName;
1218 oa_push(&connection->settings.objects, (gxObject*) ln);
1219 CURRENT_ASSOCIATION[0] = (gxObject*)ln;
1220 }
1221 else if (!connection->settings.useLogicalNameReferencing && obj.objectType == DLMS
1222 {
1223 gxAssociationShortName* sn = (gxAssociationShortName*)malloc(sizeof(gxAssociat
1224 INIT_OBJECT((*sn), obj.objectType, obj.logicalName);
1225 sn->base.shortName = obj.shortName;
1226 oa_push(&connection->settings.objects, (gxObject*)sn);
1227 CURRENT_ASSOCIATION[0] = (gxObject*)sn;
1228 }
1229 else if (obj.objectType == DLMS_OBJECT_TYPE_DATA)
1230 {
1231 gxData* data = (gxData*)malloc(sizeof(gxData));
1232 INIT_OBJECT((*data), obj.objectType, obj.logicalName);
1233 data->base.shortName = obj.shortName;
1234 oa_push(&connection->settings.objects, (gxObject*)data);
1235 }
1236 else
1237 {
1238 gxObject* pObj = NULL;
1239 if ((ret = cosem_createObject(obj.objectType, &pObj)) == 0 &&
1240 (ret = cosem_init2(pObj, obj.objectType, obj.logicalName)) == 0)
1241 {
1242 pObj->shortName = obj.shortName;
1243 oa_push(&connection->settings.objects, pObj);
1244 }
1245 }
1246 }
1247 if (outputFile != NULL && CURRENT_ASSOCIATION[0] != NULL)
1248 {
1249 //Write settings.
1250 #if _MSC_VER > 1400
1251 FILE* f = NULL;
1252 fopen_s(&f, outputFile, "wb");
1253 #else
1254 FILE* f = fopen(outputFile, "wb");
1255 #endif
1256 if (f != NULL)
1257 {
1258 gxSerializerSettings serializerSettings;
1259 ser_init(&serializerSettings);
1260 i li S tti t f
1260 serializerSettings.stream = f;
1261 gxSerializerIgnore NON_SERIALIZED_OBJECTS[] = { IGNORE_ATTRIBUTE_BY_TYPE(DLMS_
1262 serializerSettings.ignoredAttributes = NON_SERIALIZED_OBJECTS;
1263 serializerSettings.count = sizeof(NON_SERIALIZED_OBJECTS) / sizeof(NON_SERIALI
1264 if (connection->settings.useLogicalNameReferencing)
1265 {
1266 oa_copy(&((gxAssociationLogicalName*)CURRENT_ASSOCIATION[0])->objectList,
1267 }
1268 else
1269 {
1270 NON_SERIALIZED_OBJECTS[0].objectType = DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_
1271 oa_copy(&((gxAssociationShortName*)CURRENT_ASSOCIATION[0])->objectList, &c
1272 }
1273 ret = ser_saveObjects(&serializerSettings, CURRENT_ASSOCIATION, sizeof(CURRENT
1274 fclose(f);
1275 }
1276 }
1277 }
1278 mes_clear(&data);
1279 reply_clear(&reply);
1280 return ret;
1281 }
1282
1283
1284 //Read object.
1285 int com_read(
1286 connection* connection,
1287 gxObject* object,
1288 unsigned char attributeOrdinal)
1289 {
1290 int ret;
1291 message data;
1292 gxReplyData reply;
1293 mes_init(&data);
1294 reply_init(&reply);
1295 if ((ret = cl_read(&connection->settings, object, attributeOrdinal, &data)) != 0 ||
1296 (ret = com_readDataBlock(connection, &data, &reply)) != 0 ||
1297 (ret = cl_updateValue(&connection->settings, object, attributeOrdinal, &reply.dataValu
1298 {
1299 com_reportError("ReadObject failed", object, attributeOrdinal, ret);
1300 }
1301 mes_clear(&data);
1302 reply_clear(&reply);
1303 return ret;
1304 }
1305
1306 int com_getKeepAlive(
1307 connection* connection)
1308 {
1309 i t t
1309 int ret;
1310 message data;
1311 gxReplyData reply;
1312 mes_init(&data);
1313 reply_init(&reply);
1314 if ((ret = cl_getKeepAlive(&connection->settings, &data)) != 0 ||
1315 (ret = com_readDataBlock(connection, &data, &reply)) != 0)
1316 {
1317 }
1318 mes_clear(&data);
1319 reply_clear(&reply);
1320 return ret;
1321 }
1322
1323 int com_write(
1324 connection* connection,
1325 gxObject* object,
1326 unsigned char attributeOrdinal)
1327 {
1328 int ret;
1329 message data;
1330 gxReplyData reply;
1331 mes_init(&data);
1332 reply_init(&reply);
1333 if ((ret = cl_write(&connection->settings, object, attributeOrdinal, &data)) != 0 ||
1334 (ret = com_readDataBlock(connection, &data, &reply)) != 0)
1335 {
1336 com_reportError("Write failed", object, attributeOrdinal, ret);
1337 }
1338 mes_clear(&data);
1339 reply_clear(&reply);
1340 return ret;
1341 }
1342
1343 int com_method(
1344 connection* connection,
1345 gxObject* object,
1346 unsigned char attributeOrdinal,
1347 dlmsVARIANT* params)
1348 {
1349 int ret;
1350 message messages;
1351 gxReplyData reply;
1352 mes_init(&messages);
1353 reply_init(&reply);
1354 if ((ret = cl_method(&connection->settings, object, attributeOrdinal, params, &messages))
1355 (ret = com_readDataBlock(connection, &messages, &reply)) != 0)
1356 {
1357 printf("Method failed %s\r\n", hlp_getErrorMessage(ret));
1358 }
1358 }
1359 mes_clear(&messages);
1360 reply_clear(&reply);
1361 return ret;
1362 }
1363
1364 int com_updateHighLevelPassword(
1365 connection* connection,
1366 gxAssociationLogicalName* object)
1367 {
1368 int ret;
1369 dlmsVARIANT tmp;
1370 var_init(&tmp);
1371 tmp.byteArr = &object->secret;
1372 if (tmp.byteArr == NULL || tmp.byteArr->size == 0)
1373 {
1374 return DLMS_ERROR_CODE_INVALID_PARAMETER;
1375 }
1376 //Secret is not copied and for that reason position is set to zero.
1377 tmp.byteArr->position = 0;
1378 tmp.vt = DLMS_DATA_TYPE_OCTET_STRING;
1379 ret = com_method(connection, &object->base, 1, &tmp);
1380 tmp.byteArr->position = 0;
1381 var_clear(&tmp);
1382 return ret;
1383 }
1384
1385 //Read objects.
1386 int com_readList(
1387 connection* connection,
1388 gxArray* list)
1389 {
1390 int pos, ret = DLMS_ERROR_CODE_OK;
1391 gxByteBuffer bb, rr;
1392 message messages;
1393 gxReplyData reply;
1394 if (list->size != 0)
1395 {
1396 mes_init(&messages);
1397 if ((ret = cl_readList(&connection->settings, list, &messages)) != 0)
1398 {
1399 printf("ReadList failed %s\r\n", hlp_getErrorMessage(ret));
1400 }
1401 else
1402 {
1403 reply_init(&reply);
1404 //NOTE! Set ignore value to true because list is parsed differently than normal re
1405 reply.ignoreValue = 1;
1406 bb_init(&rr);
1407 bb init(&bb);
1407 bb_init(&bb);
1408 //Send data.
1409 for (pos = 0; pos != messages.size; ++pos)
1410 {
1411 //Send data.
1412 reply_clear(&reply);
1413 if ((ret = readDLMSPacket(connection, messages.data[pos], &reply)) != DLMS_ERR
1414 {
1415 break;
1416 }
1417 //Check is there errors or more data from server
1418 while (reply_isMoreData(&reply))
1419 {
1420 //NOTE! Set ignore value to true because list is parsed differently than n
1421 reply.ignoreValue = 1;
1422 if ((ret = cl_receiverReady(&connection->settings, reply.moreData, &rr))
1423 (ret = readDLMSPacket(connection, &rr, &reply)) != DLMS_ERROR_CODE_OK
1424 {
1425 break;
1426 }
1427 bb_clear(&rr);
1428 }
1429 bb_set2(&bb, &reply.data, reply.data.position, -1);
1430 }
1431 if (ret == 0)
1432 {
1433 ret = cl_updateValues(&connection->settings, list, &bb);
1434 }
1435 bb_clear(&bb);
1436 bb_clear(&rr);
1437 reply_clear(&reply);
1438 }
1439 mes_clear(&messages);
1440 }
1441 return ret;
1442 }
1443
1444 int com_readRowsByEntry(
1445 connection* connection,
1446 gxProfileGeneric* object,
1447 unsigned long index,
1448 unsigned long count)
1449 {
1450 int ret;
1451 message data;
1452 gxReplyData reply;
1453 mes_init(&data);
1454 reply_init(&reply);
1455 if ((ret = cl_readRowsByEntry(&connection->settings, object, index, count, &data)) != 0 |
1456 (ret = com readDataBlock(connection &data &reply)) != 0 ||
1456 (ret = com_readDataBlock(connection, &data, &reply)) != 0 ||
1457 (ret = cl_updateValue(&connection->settings, (gxObject*)object, 2, &reply.dataValue))
1458 {
1459 printf("ReadObject failed %s\r\n", hlp_getErrorMessage(ret));
1460 }
1461 mes_clear(&data);
1462 reply_clear(&reply);
1463 return ret;
1464 }
1465
1466 int com_readRowsByEntry2(
1467 connection* connection,
1468 gxProfileGeneric* object,
1469 unsigned long index,
1470 unsigned long count,
1471 unsigned short colStartIndex,
1472 unsigned short colEndIndex)
1473 {
1474 int ret;
1475 message data;
1476 gxReplyData reply;
1477 gxByteBuffer bb;
1478 char* str;
1479 mes_init(&data);
1480 reply_init(&reply);
1481 if ((ret = cl_readRowsByEntry2(&connection->settings, object, index, count, colStartIndex
1482 (ret = com_readDataBlock(connection, &data, &reply)) != 0)
1483 {
1484 printf("ReadObject failed %s\r\n", hlp_getErrorMessage(ret));
1485 }
1486 else
1487 {
1488 bb_init(&bb);
1489 var_toString(&reply.dataValue, &bb);
1490 str = bb_toString(&bb);
1491 printf(str);
1492 bb_clear(&bb);
1493 }
1494 mes_clear(&data);
1495 reply_clear(&reply);
1496 return ret;
1497 }
1498
1499 ///////////////////////////////////////////////////////////////////////////////////
1500 int com_readRowsByRange(
1501 connection* connection,
1502 gxProfileGeneric* object,
1503 struct tm* start,
1504 struct tm* end)
1505 {
1505 {
1506 int ret;
1507 message data;
1508 gxReplyData reply;
1509 mes_init(&data);
1510 reply_init(&reply);
1511 if ((ret = cl_readRowsByRange(&connection->settings, object, start, end, &data)) != 0 ||
1512 (ret = com_readDataBlock(connection, &data, &reply)) != 0 ||
1513 (ret = cl_updateValue(&connection->settings, (gxObject*)object, 2, &reply.dataValue))
1514 {
1515 printf("ReadObject failed %s\r\n", hlp_getErrorMessage(ret));
1516 }
1517 mes_clear(&data);
1518 reply_clear(&reply);
1519 return ret;
1520 }
1521
1522 ///////////////////////////////////////////////////////////////////////////////////
1523 int com_readRowsByRange2(
1524 connection* connection,
1525 gxProfileGeneric* object,
1526 gxtime* start,
1527 gxtime* end)
1528 {
1529 int ret;
1530 message data;
1531 gxReplyData reply;
1532 mes_init(&data);
1533 reply_init(&reply);
1534 if ((ret = cl_readRowsByRange2(&connection->settings, object, start, end, &data)) != 0 ||
1535 (ret = com_readDataBlock(connection, &data, &reply)) != 0 ||
1536 (ret = cl_updateValue(&connection->settings, (gxObject*)object, 2, &reply.dataValue))
1537 {
1538 printf("ReadObject failed %s\r\n", hlp_getErrorMessage(ret));
1539 }
1540 mes_clear(&data);
1541 reply_clear(&reply);
1542 return ret;
1543 }
1544
1545
1546 ///////////////////////////////////////////////////////////////////////////////////
1547 //Read scalers and units. They are static so they are read only once.
1548 int com_readScalerAndUnits(
1549 connection* connection)
1550 {
1551 gxObject* obj;
1552 int ret, pos;
1553 objectArray objects;
1554 gxArray list;
1554 gxArray list;
1555 gxObject* object;
1556 DLMS_OBJECT_TYPE types[] = { DLMS_OBJECT_TYPE_EXTENDED_REGISTER, DLMS_OBJECT_TYPE_REGISTER
1557 oa_init(&objects);
1558 //Find registers and demand registers and read them.
1559 ret = oa_getObjects2(&connection->settings.objects, types, 3, &objects);
1560 if (ret != DLMS_ERROR_CODE_OK)
1561 {
1562 return ret;
1563 }
1564 if ((connection->settings.negotiatedConformance & DLMS_CONFORMANCE_MULTIPLE_REFERENCES) !=
1565 {
1566 arr_init(&list);
1567 //Try to read with list first. All meters do not support it.
1568 for (pos = 0; pos != connection->settings.objects.size; ++pos)
1569 {
1570 ret = oa_getByIndex(&connection->settings.objects, pos, &obj);
1571 if (ret != DLMS_ERROR_CODE_OK)
1572 {
1573 oa_empty(&objects);
1574 arr_clear(&list);
1575 return ret;
1576 }
1577 if (obj->objectType == DLMS_OBJECT_TYPE_REGISTER ||
1578 obj->objectType == DLMS_OBJECT_TYPE_EXTENDED_REGISTER)
1579 {
1580 arr_push(&list, key_init(obj, (void*)3));
1581 }
1582 else if (obj->objectType == DLMS_OBJECT_TYPE_DEMAND_REGISTER)
1583 {
1584 arr_push(&list, key_init(obj, (void*)4));
1585 }
1586 }
1587 ret = com_readList(connection, &list);
1588 arr_clear(&list);
1589 }
1590 //If read list failed read items one by one.
1591 if (ret != 0)
1592 {
1593 for (pos = 0; pos != objects.size; ++pos)
1594 {
1595 ret = oa_getByIndex(&objects, pos, &object);
1596 if (ret != DLMS_ERROR_CODE_OK)
1597 {
1598 oa_empty(&objects);
1599 return ret;
1600 }
1601 ret = com_read(connection, object, object->objectType == DLMS_OBJECT_TYPE_DEMAND_R
1602 if (ret != DLMS_ERROR_CODE_OK)
1603 {
603 {
1604 oa_empty(&objects);
1605 return ret;
1606 }
1607 }
1608 }
1609 //Do not clear objects list because it will free also objects from association view list.
1610 oa_empty(&objects);
1611 return ret;
1612 }
1613
1614 ///////////////////////////////////////////////////////////////////////////////////
1615 //Read profile generic columns. They are static so they are read only once.
1616 int com_readProfileGenericColumns(
1617 connection* connection)
1618 {
1619 int ret, pos;
1620 objectArray objects;
1621 gxObject* object;
1622 oa_init(&objects);
1623 ret = oa_getObjects(&connection->settings.objects, DLMS_OBJECT_TYPE_PROFILE_GENERIC, &obje
1624 if (ret != DLMS_ERROR_CODE_OK)
1625 {
1626 oa_empty(&objects);
1627 return ret;
1628 }
1629 for (pos = 0; pos != objects.size; ++pos)
1630 {
1631 ret = oa_getByIndex(&objects, pos, &object);
1632 if (ret != DLMS_ERROR_CODE_OK)
1633 {
1634 break;
1635 }
1636 ret = com_read(connection, object, 3);
1637 if (ret != DLMS_ERROR_CODE_OK)
1638 {
1639 break;
1640 }
1641 }
1642 //Do not clear objects list because it will free also objects from association view list.
1643 oa_empty(&objects);
1644 return ret;
1645 }
1646
1647 ///////////////////////////////////////////////////////////////////////////////////
1648 //Read profile generics rows.
1649 int com_readProfileGenerics(
1650 connection* connection)
1651 {
1652 gxtime startTime, endTime;
g , ;
1653 int ret, pos;
1654 char str[50];
1655 char ln[25];
1656 char* data = NULL;
1657 gxByteBuffer ba;
1658 objectArray objects;
1659 gxProfileGeneric* pg;
1660 oa_init(&objects);
1661 ret = oa_getObjects(&connection->settings.objects, DLMS_OBJECT_TYPE_PROFILE_GENERIC, &obje
1662 if (ret != DLMS_ERROR_CODE_OK)
1663 {
1664 //Do not clear objects list because it will free also objects from association view li
1665 oa_empty(&objects);
1666 return ret;
1667 }
1668 bb_init(&ba);
1669 for (pos = 0; pos != objects.size; ++pos)
1670 {
1671 ret = oa_getByIndex(&objects, pos, (gxObject**)&pg);
1672 if (ret != DLMS_ERROR_CODE_OK)
1673 {
1674 //Do not clear objects list because it will free also objects from association vie
1675 oa_empty(&objects);
1676 return ret;
1677 }
1678 //Read entries in use.
1679 ret = com_read(connection, (gxObject*)pg, 7);
1680 if (ret != DLMS_ERROR_CODE_OK)
1681 {
1682 if (connection->trace > GX_TRACE_LEVEL_OFF)
1683 {
1684 printf("Failed to read object %s %s attribute index %d\r\n", str, ln, 7);
1685 }
1686 //Do not clear objects list because it will free also objects from association vie
1687 oa_empty(&objects);
1688 return ret;
1689 }
1690 //Read entries.
1691 ret = com_read(connection, (gxObject*)pg, 8);
1692 if (ret != DLMS_ERROR_CODE_OK)
1693 {
1694 if (connection->trace > GX_TRACE_LEVEL_OFF)
1695 {
1696 printf("Failed to read object %s %s attribute index %d\r\n", str, ln, 8);
1697 }
1698 //Do not clear objects list because it will free also objects from association vie
1699 oa_empty(&objects);
1700 return ret;
1701 }
}
1702 if (connection->trace > GX_TRACE_LEVEL_WARNING)
1703 {
1704 printf("Entries: %ld/%ld\r\n", pg->entriesInUse, pg->profileEntries);
1705 }
1706 //If there are no columns or rows.
1707 if (pg->entriesInUse == 0 || pg->captureObjects.size == 0)
1708 {
1709 continue;
1710 }
1711 //Read capture period.
1712 ret = com_read(connection, (gxObject*)pg, 4);
1713 if (ret != DLMS_ERROR_CODE_OK)
1714 {
1715 if (connection->trace > GX_TRACE_LEVEL_OFF)
1716 {
1717 printf("Failed to read object %s %s attribute index %d\r\n", str, ln, 8);
1718 }
1719 //Do not clear objects list because it will free also objects from association vie
1720 oa_empty(&objects);
1721 return ret;
1722 }
1723 //Read Sort method.
1724 ret = com_read(connection, (gxObject*)pg, 5);
1725 if (ret != DLMS_ERROR_CODE_OK)
1726 {
1727 if (connection->trace > GX_TRACE_LEVEL_OFF)
1728 {
1729 printf("Failed to read object %s %s attribute index %d\r\n", str, ln, 8);
1730 }
1731 //Do not clear objects list because it will free also objects from association vie
1732 oa_empty(&objects);
1733 return ret;
1734 }
1735 //Read first row from Profile Generic.
1736 ret = com_readRowsByEntry(connection, pg, 1, 1);
1737 if (ret != DLMS_ERROR_CODE_OK)
1738 {
1739 if (connection->trace > GX_TRACE_LEVEL_OFF)
1740 {
1741 printf("Failed to read object %s %s rows by entry\r\n", str, ln);
1742 }
1743 }
1744 else
1745 {
1746 if (connection->trace > GX_TRACE_LEVEL_WARNING)
1747 {
1748 bb_init(&ba);
1749 obj_rowsToString(&ba, &pg->buffer);
1750 data = bb_toString(&ba);
1751 bb_clear(&ba);
1752 printf("%s\r\n", data);
1753 free(data);
1754 }
1755 }
1756 //Read last day from Profile Generic.
1757 time_now(&startTime);
1758 endTime = startTime;
1759 time_clearTime(&startTime);
1760 ret = com_readRowsByRange2(connection, pg, &startTime, &endTime);
1761 if (ret != DLMS_ERROR_CODE_OK)
1762 {
1763 if (connection->trace > GX_TRACE_LEVEL_OFF)
1764 {
1765 printf("Failed to read object %s %s rows by entry\r\n", str, ln);
1766 }
1767 }
1768 else
1769 {
1770 if (connection->trace > GX_TRACE_LEVEL_WARNING)
1771 {
1772 bb_init(&ba);
1773 obj_rowsToString(&ba, &pg->buffer);
1774 data = bb_toString(&ba);
1775 bb_clear(&ba);
1776 printf("%s\r\n", data);
1777 free(data);
1778 }
1779 }
1780 }
1781 //Do not clear objects list because it will free also objects from association view list.
1782 oa_empty(&objects);
1783 return ret;
1784 }
1785
1786 int com_readValue(connection* connection, gxObject* object, unsigned char index)
1787 {
1788 int ret;
1789 char* data = NULL;
1790 char ln[25];
1791 ret = hlp_getLogicalNameToString(object->logicalName, ln);
1792 if (ret != DLMS_ERROR_CODE_OK)
1793 {
1794 return ret;
1795 }
1796 if (connection->trace > GX_TRACE_LEVEL_WARNING)
1797 {
1798 printf("-------- Reading Object %s %s\r\n", obj_typeToString2(object->objectType), ln
1799 }
1800 ret = com_read(connection, object, index);
1801 if (ret != DLMS_ERROR_CODE_OK)
1802 {
1803 if (connection->trace > GX_TRACE_LEVEL_OFF)
1804 {
1805 printf("Failed to read object %s %s attribute index %d\r\n", obj_typeToString2(obj
1806 }
1807 //Return error if not DLMS error.
1808 if (ret != DLMS_ERROR_CODE_READ_WRITE_DENIED)
1809 {
1810 return ret;
1811 }
1812 }
1813 if (connection->trace > GX_TRACE_LEVEL_WARNING)
1814 {
1815 ret = obj_toString(object, &data);
1816 if (ret != DLMS_ERROR_CODE_OK)
1817 {
1818 return ret;
1819 }
1820 if (data != NULL)
1821 {
1822 printf("%s", data);
1823 free(data);
1824 data = NULL;
1825 }
1826 }
1827 return 0;
1828 }
1829
1830 // This function reads ALL objects that meter have excluded profile generic objects.
1831 // It will loop all object's attributes.
1832 int com_readValues(connection* connection)
1833 {
1834 gxByteBuffer attributes;
1835 unsigned char ch;
1836 char* data = NULL;
1837 char ln[25];
1838 gxObject* object;
1839 unsigned long index;
1840 int ret = 0, pos;
1841 bb_init(&attributes);
1842 for (pos = 0; pos != connection->settings.objects.size; ++pos)
1843 {
1844 ret = oa_getByIndex(&connection->settings.objects, pos, &object);
1845 if (ret != DLMS_ERROR_CODE_OK)
1846 {
1847 break;
1848 }
1849 ///////////////////////////////////////////////////////////////////////////////////
1850 // Profile generics are read later because they are special cases.
1851 // (There might be so lots of data and we so not want waste time to read all the data
1852 if (object->objectType == DLMS_OBJECT_TYPE_PROFILE_GENERIC)
1853 {
1854 continue;
1855 }
1856 ret = hlp_getLogicalNameToString(object->logicalName, ln);
1857 if (ret != DLMS_ERROR_CODE_OK)
1858 {
1859 break;
1860 }
1861 if (connection->trace > GX_TRACE_LEVEL_WARNING)
1862 {
1863 printf("-------- Reading Object %s %s\n", obj_typeToString2(object->objectType), l
1864 }
1865 ret = obj_getAttributeIndexToRead(object, &attributes);
1866 if (ret != DLMS_ERROR_CODE_OK)
1867 {
1868 break;
1869 }
1870 for (index = 0; index < attributes.size; ++index)
1871 {
1872 ret = bb_getUInt8ByIndex(&attributes, index, &ch);
1873 if (ret != DLMS_ERROR_CODE_OK)
1874 {
1875 break;
1876 }
1877 ret = com_read(connection, object, ch);
1878 if (ret != DLMS_ERROR_CODE_OK)
1879 {
1880 if (connection->trace > GX_TRACE_LEVEL_OFF)
1881 {
1882 printf("Failed to read object %s %s attribute index %d\r\n", obj_typeToStr
1883 }
1884 //Return error if not DLMS error.
1885 if (ret != DLMS_ERROR_CODE_READ_WRITE_DENIED)
1886 {
1887 continue;
1888 }
1889 ret = 0;
1890 }
1891 }
1892 if (ret != 0)
1893 {
1894 break;
1895 }
1896 bb_clear(&attributes);
1897 if (connection->trace > GX_TRACE_LEVEL_WARNING)
1898 {
1899 ret = obj_toString(object, &data);
1900 if (ret != DLMS_ERROR_CODE_OK)
1901 {
1902 break;
1903 }
1904 if (data != NULL)
1905 {
1906 printf("%s", data);
1907 free(data);
1908 data = NULL;
1909 }
1910 }
1911 }
1912 bb_clear(&attributes);
1913 return ret;
1914 }
1915
1916 int com_readAllObjects(connection* connection, const char* outputFile)
1917 {
1918 //Get objects from the meter and read them.
1919 int ret = com_getAssociationView(connection, outputFile);
1920 if (ret != DLMS_ERROR_CODE_OK)
1921 {
1922 return ret;
1923 }
1924 ret = com_readScalerAndUnits(connection);
1925 if (ret != DLMS_ERROR_CODE_OK)
1926 {
1927 return ret;
1928 }
1929 ///////////////////////////////////////////////////////////////////////////////////
1930 //Read Profile Generic columns.
1931 ret = com_readProfileGenericColumns(connection);
1932 if (ret != DLMS_ERROR_CODE_OK)
1933 {
1934 return ret;
1935 }
1936 ret = com_readValues(connection);
1937 if (ret != DLMS_ERROR_CODE_OK)
1938 {
1939 return ret;
1940 }
1941 ret = com_readProfileGenerics(connection);
1942 if (ret != DLMS_ERROR_CODE_OK)
1943 {
1944 return ret;
1945 }
1946 return ret;
1947 }