0% found this document useful (0 votes)
5 views41 pages

GuruxDLMS.c - GuruxDLMSClientExample - SRC - Communication.c at 2d9b9846 Gurux - GuruxDLMS.c GitHub

The document contains source code for the GuruxDLMS communication module, which includes functions for establishing TCP/IP connections, reading from serial ports, and handling communication errors. It is designed to support both Windows and Linux environments, with specific implementations for socket and serial communication. The code is part of a larger project related to DLMS (Device Language Message Specification) protocol used in smart metering.

Uploaded by

gowthamraj.enthu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views41 pages

GuruxDLMS.c - GuruxDLMSClientExample - SRC - Communication.c at 2d9b9846 Gurux - GuruxDLMS.c GitHub

The document contains source code for the GuruxDLMS communication module, which includes functions for establishing TCP/IP connections, reading from serial ports, and handling communication errors. It is designed to support both Windows and Linux environments, with specific implementations for socket and serial communication. The code is part of a larger project related to DLMS (Device Language Message Specification) protocol used in smart metering.

Uploaded by

gowthamraj.enthu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 41

Gurux / GuruxDLMS.

c Public

Code Pull requests 3 Actions Security Insights

GuruxDLMS.c / GuruxDLMSClientExample / src / communication.c

gurux01 Version 20220712.1 9cde9cc · 3 years ago

1947 lines (1902 loc) · 60.5 KB

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(&notify);
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, &notify, &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(&notify.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(&notify);
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 }

You might also like