├── .gitignore
├── 123solar
├── 1.5
│ └── scritps
│ │ └── protocols
│ │ ├── sdm120c.php
│ │ ├── sdm120c_checks.php
│ │ └── sdm120c_startup.php
└── 1.6
│ └── scritps
│ └── protocols
│ ├── is_valid.php
│ ├── sdm120c-pool.php
│ ├── sdm120c-pool_checks.php
│ ├── sdm120c-pool_startup.php
│ ├── sdm120c.php
│ ├── sdm120c_checks.php
│ └── sdm120c_startup.php
├── LICENSE
├── Makefile
├── README.md
├── aurora
├── aurora-1.8.8.diff
└── aurora-1.9.0.diff
├── domoticz
├── meter_domoticz.sh
└── sdm120c_domoticz.sh
├── libmodbus
├── libmodbus-dev_3.0.6-1_armhf.deb
├── libmodbus-dev_3.1.1-1_armhf.deb
├── libmodbus5_3.0.6-1_armhf.deb
└── libmodbus5_3.1.1-1_armhf.deb
├── metern
└── comapps
│ ├── README.md
│ ├── poolen485.php
│ ├── poolen485_2.php
│ ├── pooler485.php
│ ├── pooler485.sh
│ └── poolmeters485.sh
├── sdm120c.c
├── sdm120c.py
├── sdm120c2.py
└── sdm120c3.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Object files
2 | *.o
3 | *.ko
4 | *.obj
5 | *.elf
6 |
7 | # Precompiled Headers
8 | *.gch
9 | *.pch
10 |
11 | # Libraries
12 | *.lib
13 | *.a
14 | *.la
15 | *.lo
16 |
17 | # Shared objects (inc. Windows DLLs)
18 | *.dll
19 | *.so
20 | *.so.*
21 | *.dylib
22 |
23 | # Executables
24 | *.exe
25 | *.out
26 | *.app
27 | *.i*86
28 | *.x86_64
29 | *.hex
30 |
31 | # Debug files
32 | *.dSYM/
33 |
34 | sdm120c
35 |
36 |
--------------------------------------------------------------------------------
/123solar/1.5/scritps/protocols/sdm120c.php:
--------------------------------------------------------------------------------
1 |
70 |
--------------------------------------------------------------------------------
/123solar/1.5/scritps/protocols/sdm120c_checks.php:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/123solar/1.5/scritps/protocols/sdm120c_startup.php:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/123solar/1.6/scritps/protocols/is_valid.php:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/123solar/1.6/scritps/protocols/sdm120c-pool.php:
--------------------------------------------------------------------------------
1 |
58 |
--------------------------------------------------------------------------------
/123solar/1.6/scritps/protocols/sdm120c-pool_checks.php:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/123solar/1.6/scritps/protocols/sdm120c-pool_startup.php:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/123solar/1.6/scritps/protocols/sdm120c.php:
--------------------------------------------------------------------------------
1 |
70 |
--------------------------------------------------------------------------------
/123solar/1.6/scritps/protocols/sdm120c_checks.php:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/123solar/1.6/scritps/protocols/sdm120c_startup.php:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
16 | # SDM120C 17 | SDM120C ModBus RTU client to read EASTRON SDM120C smart mini power meter registers 18 | 19 | It works with SDM120C and SDM220 models 20 | 21 | It depends on libmodbus (http://libmodbus.org) 22 | 23 | To compile and install 24 | make clean && make install 25 | 26 |27 | Usage: sdm120c [-a address] [-d] [-x] [-p] [-v] [-c] [-e] [-i] [-t] [-f] [-g] [-T] [[-m]|[-q]] [-b baud_rate] [-P parity] [-S bit] [-z num_retries] [-j seconds] [-w seconds] [-1 | -2] device 28 | sdm120c [-a address] [-d] [-b baud_rate] [-P parity] [-S bit] [-1 | -2] -s new_address device 29 | sdm120c [-a address] [-d] [-b baud_rate] [-P parity] [-S bit] [-1 | -2] -r baud_rate device 30 | sdm120c [-a address] [-d] [-b baud_rate] [-P parity] [-S bit] [-1 | -2] -R new_time device 31 | 32 | where 33 | -a address Meter number (between 1 and 247). Default: 1 34 | -s new_address Set new meter number (between 1 and 247) 35 | -p Get power (W) 36 | -v Get voltage (V) 37 | -c Get current (A) 38 | -f Get frequency (Hz) 39 | -g Get power factor 40 | -e Get exported energy (Wh) 41 | -i Get imported energy (Wh) 42 | -t Get total energy (Wh) 43 | -T Get Time for rotating display values (0 = no rotation) 44 | -d Debug 45 | -x Trace (libmodbus debug on) 46 | -b baud_rate Use baud_rate serial port speed (1200, 2400, 4800, 9600) 47 | Default: 2400 48 | -P parity Use parity (E, N, O) 49 | -S bit Use stop bits (1, 2). Default: 1 50 | -r baud_rate Set baud_rate meter speed (1200, 2400, 4800, 9600) 51 | -R new_time Change rotation time for displaying values (0 - 30s) (0 = no totation) 52 | -m Output values in IEC 62056 format ID(VALUE*UNIT) 53 | -q Output values in compact mode 54 | -z num_retries Try to read max num_retries times on bus before exiting 55 | with error. Default: 1 (no retry) 56 | -j 1/10 secs Response timeout. Default: 2=0.2s 57 | -D 1/1000 secs Delay before sending commands (wait line set). Default: 30=0.03s 58 | -w seconds Time to wait to lock serial port. (1-30s) Default: 0s 59 | -1 Model: SDM120C (default) 60 | -2 Model: SDM220 61 | device Serial device, i.e. /dev/ttyUSB0 62 | 63 | Serial device is required. When no parameter is passed, retrives all values64 | -------------------------------------------------------------------------------- /aurora/aurora-1.8.8.diff: -------------------------------------------------------------------------------- 1 | --- aurora-1.8.8/comm.c 2015-01-27 02:37:36.000000000 +0100 2 | +++ my-aurora-1.8.8/comm.c 2015-10-09 19:09:15.000000000 +0200 3 | @@ -1783,7 +1783,7 @@ 4 | if (bVerbose) 5 | fprintf(stderr, "Waiting %d milli-seconds before reading inverter response\n",yReadPause); 6 | else 7 | - if (bRptReadPause) fprintf(stderr, "\n%s: %s: Waiting %d milli-seconds before reading inverter response",getCurTime(),ProgramName,yReadPause); 8 | + if (bRptReadPause) fprintf(stderr, "\n%s: %s: Waiting %d milli-seconds before reading inverter response\n",getCurTime(),ProgramName,yReadPause); 9 | usleep(yReadPause*1000); 10 | } 11 | nCnt = ReadToBuffer(fdser, szSerBuffer, aSIZE); 12 | @@ -1802,7 +1802,7 @@ 13 | if ((unsigned char)szSerBuffer[aCRC_L] != LOBYTE(crcValue) || (unsigned char)szSerBuffer[aCRC_H] != HIBYTE(crcValue)) { 14 | if (yMaxAttempts == 1 || attempts == yMaxAttempts) 15 | if (!bCommCheck) { 16 | - if (! bVerbose && bRptReadPause) fprintf(stderr, "\n"); 17 | + if (! bVerbose ) fprintf(stderr, "\n"); 18 | fprintf(stderr, "%s: CRC receive error (%i attempts made) %04x %02x %02x\n",getCurTime(),attempts,crcValue,(unsigned char)szSerBuffer[aCRC_H],(unsigned char)szSerBuffer[aCRC_L]); 19 | } 20 | } else { 21 | @@ -1819,8 +1819,8 @@ 22 | if (bRptReties) { 23 | fprintf(stderr, "\n%s: %s: %i attempts made",getCurTime(),ProgramName,attempts-1); 24 | if (bVerbose) fprintf(stderr, "\n"); 25 | - } else 26 | - if (bRptReadPause) fprintf(stderr, "\n"); 27 | + } //else 28 | + // if (bRptReadPause) fprintf(stderr, "\n"); 29 | return(nCnt); 30 | } 31 | 32 | --- aurora-1.8.8/include/main.h 2015-01-27 02:38:07.000000000 +0100 33 | +++ my-aurora-1.8.8/include/main.h 2015-10-12 12:14:15.000000000 +0200 34 | @@ -85,6 +85,7 @@ 35 | extern FILE *outfp; 36 | 37 | extern char* getCurTime(); 38 | +extern int AddSerLock(long unsigned int PID); 39 | extern int ClrSerLock(long unsigned int PID); 40 | extern int RestorePort(int fdser); 41 | 42 | --- aurora-1.8.8/main.c 2015-01-27 02:37:17.000000000 +0100 43 | +++ my-aurora-1.8.8/main.c 2015-10-19 11:10:11.000000000 +0200 44 | @@ -79,6 +79,7 @@ 45 | #include46 | #include 47 | #include 48 | +#include 49 | #include 50 | #include 51 | #include 52 | @@ -92,10 +93,16 @@ 53 | #include 54 | #include 55 | #include 56 | +#include 57 | + 58 | #include "include/main.h" 59 | #include "include/comm.h" 60 | #include "include/names.h" 61 | 62 | +// Enable checks for inter-lock problems debug 63 | +#define CHECKFORGHOSTAPPEND 0 64 | +#define CHECKFORCLEARLOCKRACE 0 65 | + 66 | BOOL bVerbose = FALSE; 67 | BOOL bColumns = FALSE; /* Output data in columns */ 68 | int yGetDSP = -1; /* Measure request to the DSP */ 69 | @@ -203,7 +210,8 @@ 70 | char EndTime[18] = " "; 71 | long unsigned int rPID; 72 | char sPID[10]; 73 | - int bRead, bWrite, lckCNT; 74 | + struct timeval tLockStart, tLockNow; 75 | + int bRead; 76 | int errno_save = 0; 77 | int fLen = 0; 78 | char *cmdFile = NULL; 79 | @@ -345,27 +353,16 @@ 80 | exit(0); 81 | } 82 | 83 | - if (bVerbose) fprintf(stderr, "\nAttempting to get lock on Serial Port %s...\n",szttyDevice); 84 | - fdserlck = fopen((const char *)devLCKfile, "a"); 85 | - if (fdserlck == NULL) { 86 | - if (bVerbose) fprintf(stderr, "\n"); 87 | - fprintf(stderr, "%s: %s: Problem locking serial device, can't open lock file: %s for write.\n\n",getCurTime(),ProgramName,devLCKfile); 88 | - exit(2); 89 | - } 90 | - bWrite = fprintf(fdserlck, "%lu\n", PID); 91 | - errno_save = errno; 92 | - fclose(fdserlck); 93 | - fdserlck = NULL; 94 | - if (bWrite < 0 || errno_save != 0) { 95 | - if (bVerbose) fprintf(stderr, "\n"); 96 | - fprintf(stderr, "%s: %s: Problem locking serial device, can't write lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror (errno_save)); 97 | - exit(2); 98 | - } 99 | + AddSerLock(PID); 100 | 101 | rPID = 0; 102 | - lckCNT = -1; 103 | - while(rPID != PID && lckCNT++ < yLockWait) { 104 | - if (bVerbose && lckCNT == 0) fprintf(stderr, "Checking for lock\n"); 105 | + 106 | + gettimeofday(&tLockStart, NULL); 107 | + tLockNow=tLockStart; 108 | + 109 | + while(rPID != PID && 110 | + tLockNow.tv_sec*1000000L+tLockNow.tv_usec <= tLockStart.tv_sec*1000000L + tLockStart.tv_usec + yLockWait*1000000L) { 111 | + if (bVerbose) fprintf(stderr, "Checking for lock\n"); 112 | SubStrPos = NULL; 113 | fdserlck = fopen(devLCKfile, "r"); 114 | if (fdserlck == NULL) { 115 | @@ -373,17 +370,26 @@ 116 | fprintf(stderr, "%s: %s: Problem locking serial device, can't open lock file: %s for read.\n\n",getCurTime(),ProgramName,devLCKfile); 117 | exit(2); 118 | } 119 | - bRead = fscanf(fdserlck, "%lu", &rPID); 120 | + errno=0; 121 | + bRead = fscanf(fdserlck, "%lu%*[^\n]\n", &rPID); 122 | errno_save = errno; 123 | fclose(fdserlck); 124 | - if (bVerbose) fprintf(stderr, "\nChecking process %lu for lock\n",rPID); 125 | fdserlck = NULL; 126 | if (bRead == EOF || errno_save != 0) { 127 | if (bVerbose) fprintf(stderr, "\n"); 128 | - fprintf(stderr, "%s: %s: Problem locking serial device, can't read lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror (errno_save)); 129 | + fprintf(stderr, "%s: %s: Problem locking serial device, can't read PID from lock file: %s.\n",getCurTime(),ProgramName,devLCKfile); 130 | + if (errno_save != 0) fprintf(stderr, "%s: %s: (%u) %s\n\n",getCurTime(),ProgramName,errno,strerror(errno_save)); 131 | + else { 132 | + // Self PID missing... 133 | + if (bVerbose) fprintf(stderr, "%s: %s miss process self PID from lock file, amending.",ProgramName,devLCKfile); 134 | + AddSerLock(PID); 135 | + rPID=0; 136 | + continue; 137 | + } 138 | exit(2); 139 | } 140 | 141 | + if (bVerbose) fprintf(stderr, "\nChecking process %lu for lock\n",rPID); 142 | sPID[0] = '\0'; 143 | sprintf(sPID,"%lu",rPID); 144 | cmdFile = getMemPtr(strlen(sPID)+14+1); 145 | @@ -414,19 +420,22 @@ 146 | if (rPID == PID) fprintf (stderr, " = me"); 147 | fprintf (stderr, "\n"); 148 | } 149 | - if (command != NULL) { 150 | - free(command); 151 | - command = NULL; 152 | - } 153 | if (rPID != PID) { 154 | - if (SubStrPos == NULL) { 155 | + if (command == NULL) { // Clear stale only rPID process is dead 156 | if (bVerbose) fprintf (stderr, "\n"); 157 | fprintf(stderr, "%s: %s: Clearing stale serial port lock. (%lu)\n",getCurTime(),ProgramName,rPID); 158 | ClrSerLock(rPID); 159 | - } else if (yLockWait > 0) 160 | - sleep(1); 161 | + } else if (yLockWait > 0) { 162 | + if (bVerbose) fprintf(stderr, "Sleeping 25ms"); 163 | + usleep(25000); 164 | } 165 | } 166 | + if (command != NULL) { 167 | + free(command); 168 | + command = NULL; 169 | + } 170 | + gettimeofday(&tLockNow, NULL); 171 | + } 172 | if (bVerbose && rPID == PID) fprintf(stderr, "Appears we got the lock.\n"); 173 | if (rPID != PID) { 174 | ClrSerLock(PID); 175 | @@ -681,31 +690,31 @@ 176 | ----------------------------------------------------------------------------*/ 177 | int RestorePort(int fdser) { 178 | 179 | - if (bVerbose) fprintf(stderr, "\nRestoring Serial Port settings %s...", szttyDevice); 180 | - if (tcsetattr(fdser, TCSANOW, &oldtio)) { /* restore previous port settings */ 181 | + if (bVerbose) { fprintf(stderr, "\nFlushing serial device buffer..."); } 182 | + errno = 0; 183 | + if (tcflush(fdser, TCIOFLUSH)) { 184 | if (bVerbose) fprintf(stderr, "\n"); 185 | - fprintf(stderr, "%s: %s: Problem restoring serial device settings.\n",getCurTime(),ProgramName); 186 | + fprintf(stderr, "\n%s: %s: Problem flushing serial device: (%i) %s\n\n",getCurTime(),ProgramName,errno,strerror(errno)); 187 | if (bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice); 188 | if (close(fdser)) { 189 | if (bVerbose) fprintf(stderr, "\n"); 190 | - fprintf(stderr, "%s: %s: Problem closing serial device, check device name.\n",getCurTime(),ProgramName); 191 | + fprintf(stderr, "%s: %s: Problem closing serial device.\n",getCurTime(),ProgramName); 192 | } 193 | - if (bVerbose) fprintf(stderr, " Success!\n"); 194 | + if (bVerbose) { fprintf(stderr, " Success!\n"); } 195 | return 2; 196 | } 197 | 198 | - if (bVerbose) { fprintf(stderr, " Success!\nFlushing serial device buffer..."); } 199 | + if (bVerbose) fprintf(stderr, " Success!\nRestoring Serial Port settings %s...", szttyDevice); 200 | 201 | - errno = 0; 202 | - if (tcflush(fdser, TCIOFLUSH)) { 203 | + if (tcsetattr(fdser, TCSADRAIN, &oldtio)) { /* restore previous port settings */ 204 | if (bVerbose) fprintf(stderr, "\n"); 205 | - fprintf(stderr, "\n%s: %s: Problem flushing serial device: (%i) %s\n\n",getCurTime(),ProgramName,errno,strerror(errno)); 206 | + fprintf(stderr, "%s: %s: Problem restoring serial device settings.\n",getCurTime(),ProgramName); 207 | if (bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice); 208 | if (close(fdser)) { 209 | if (bVerbose) fprintf(stderr, "\n"); 210 | - fprintf(stderr, "%s: %s: Problem closing serial device.\n",getCurTime(),ProgramName); 211 | + fprintf(stderr, "%s: %s: Problem closing serial device, check device name.\n",getCurTime(),ProgramName); 212 | } 213 | - if (bVerbose) { fprintf(stderr, " Success!\n"); } 214 | + if (bVerbose) fprintf(stderr, " Success!\n"); 215 | return 2; 216 | } 217 | 218 | @@ -720,6 +729,52 @@ 219 | return 0; 220 | } 221 | 222 | +/*-------------------------------------------------------------------------- 223 | + AddSerLock 224 | + Queue Serial Port lock intent. 225 | +----------------------------------------------------------------------------*/ 226 | +int AddSerLock(const long unsigned int PID) { 227 | + FILE *fdserlck; 228 | + int bWrite; 229 | + int errno_save = 0; 230 | + 231 | + if (bVerbose) fprintf(stderr, "\nAttempting to get lock on Serial Port %s...\n",szttyDevice); 232 | + do { 233 | + fdserlck = fopen((const char *)devLCKfile, "a"); 234 | + if (fdserlck == NULL) { 235 | + if (bVerbose) fprintf(stderr, "\n"); 236 | + fprintf(stderr, "%s: %s: Problem locking serial device, can't open lock file: %s for write.\n\n",getCurTime(),ProgramName,devLCKfile); 237 | + exit(2); 238 | + } 239 | + if (bVerbose) fprintf(stderr, "Acquiring shared lock on %s...\n",devLCKfile); 240 | + errno = 0; 241 | + 242 | + if (flock(fileno(fdserlck), LOCK_SH | LOCK_NB) == 0 ) break; // Lock Acquired 243 | + errno_save=errno; 244 | + 245 | + if (errno_save == EWOULDBLOCK) { 246 | + if (bVerbose) fprintf(stderr, "Whould block %s, retry (%d)...\n",devLCKfile, errno_save); 247 | + usleep(25000); 248 | + fclose(fdserlck); 249 | + } else { 250 | + fprintf(stderr, "%s: Problem locking serial device, can't open lock file: %s for write. (%d)\n",ProgramName,devLCKfile, errno_save); 251 | + exit(2); 252 | + } 253 | + } while (errno_save == EWOULDBLOCK); 254 | + if (bVerbose) fprintf(stderr, "Shared lock on %s acquired...\n",devLCKfile); 255 | + 256 | + errno=0; 257 | + bWrite = fprintf(fdserlck, "%lu\n", PID); 258 | + errno_save = errno; 259 | + fclose(fdserlck); // Will release lock 260 | + fdserlck = NULL; 261 | + if (bWrite < 0 || errno_save != 0) { 262 | + if (bVerbose) fprintf(stderr, "\n"); 263 | + fprintf(stderr, "%s: %s: Problem locking serial device, can't write lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror(errno_save)); 264 | + exit(2); 265 | + } 266 | + return -1; 267 | + } 268 | 269 | /*-------------------------------------------------------------------------- 270 | ClrSerLock 271 | @@ -732,39 +787,106 @@ 272 | int errno_save = 0; 273 | 274 | errno = 0; 275 | - if (bVerbose) fprintf(stderr, "\ndevLCKfile: <%s>\ndevLCKfileNew: <%s>\nClearing Serial Port Lock (%lu)...", devLCKfile, devLCKfileNew, PID); 276 | + if (bVerbose) fprintf(stderr, "\ndevLCKfile: <%s>\ndevLCKfileNew: <%s>\nClearing Serial Port Lock (%lu)...\n", devLCKfile, devLCKfileNew, PID); 277 | fdserlck = fopen(devLCKfile, "r"); 278 | if (fdserlck == NULL) { 279 | if (bVerbose) fprintf(stderr, "\n"); 280 | fprintf(stderr, "\n%s: %s: Problem opening serial device lock file to clear PID %lu: %s for read.\n\n",getCurTime(),ProgramName,PID,devLCKfile); 281 | return(0); 282 | } 283 | - fdserlcknew = fopen(devLCKfileNew, "w"); 284 | + if (bVerbose) fprintf(stderr, "Acquiring exclusive lock on %s...\n",devLCKfile); 285 | + flock(fileno(fdserlck), LOCK_EX); // Will wait to acquire lock then continue 286 | + if (bVerbose) fprintf(stderr, "Exclusive lock on %s acquired (%d)...\n",devLCKfile, errno); 287 | + 288 | +#if CHECKFORCLEARLOCKRACE 289 | + 290 | + // Check for potential conflicts 291 | + glob_t globbuf; 292 | + int iGlob, fGlob = TRUE; 293 | + 294 | + if (bVerbose) fprintf(stderr, "GlobCheck - Check to avoid simultaneous PID clearing\n"); 295 | + for (iGlob=5; iGlob>0 && fGlob; iGlob-- ) { 296 | + fGlob = FALSE; 297 | + if (glob("/var/lock/LCK..ttyUSB0.*", GLOB_NOSORT, NULL, &globbuf) != GLOB_NOMATCH) { 298 | + if (bVerbose) fprintf(stderr, "%s: GlobCheck (%u), some other process is clearing lock too!!! (%s)\n",ProgramName, iGlob, globbuf.gl_pathv[0]); 299 | + fGlob=TRUE; 300 | + if (bVerbose) fprintf(stderr, "Sleeping 500ms\n"); 301 | + usleep(500000); 302 | + } 303 | + globfree(&globbuf); 304 | + } 305 | + 306 | +#endif 307 | + 308 | + fdserlcknew = fopen(devLCKfileNew, "a"); 309 | if (fdserlcknew == NULL) { 310 | if (bVerbose) fprintf(stderr, "\n"); 311 | fprintf(stderr, "\n%s: %s: Problem opening new serial device lock file to clear PID %lu: %s for write.\n\n",getCurTime(),ProgramName,PID,devLCKfileNew); 312 | fclose(fdserlck); 313 | return(0); 314 | } 315 | - bRead = fscanf(fdserlck, "%lu", &rPID); 316 | - while (bRead != EOF) { 317 | + errno = 0; 318 | + bRead = fscanf(fdserlck, "%lu%*[^\n]\n", &rPID); 319 | + errno_save = errno; 320 | + if (bVerbose) fprintf(stderr, "%s: errno=%i, bRead=%i PID=%lu rPID=%lu\n", ProgramName, errno_save, bRead, PID, rPID); 321 | + 322 | + while (bRead != EOF && bRead > 0) { 323 | if (rPID != PID) { 324 | errno = 0; 325 | bWrite = fprintf(fdserlcknew, "%lu\n", rPID); 326 | errno_save = errno; 327 | + if (bVerbose) fprintf(stderr, "%s: errno=%i, bWrite=%i rPID=%lu\n", ProgramName, errno, bWrite, rPID); 328 | + if (bWrite < 0 || errno_save != 0) { 329 | + fprintf(stderr, "\n%s: %s: Problem clearing serial device lock, can't write lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror(errno_save)); 330 | + fclose(fdserlcknew); 331 | + return(0); 332 | + } 333 | + } 334 | + errno=0; rPID=0; 335 | + bRead = fscanf(fdserlck, "%lu%*[^\n]\n", &rPID); 336 | + errno_save = errno; 337 | + if (bVerbose) fprintf(stderr, "%s: errno=%i, bRead=%i PID=%lu rPID=%lu\n", ProgramName, errno_save, bRead, PID, rPID); 338 | + } 339 | + 340 | + fflush(fdserlcknew); 341 | + 342 | + errno = 0; 343 | + if (rename(devLCKfileNew,devLCKfile)) fprintf(stderr, "\n%s: %s: Problem clearing serial device lock, can't update lock file: %s.\n(%d) %s\n\n",getCurTime(),ProgramName,devLCKfile,errno,strerror(errno)); 344 | + 345 | +#if CHECKFORGHOSTAPPEND 346 | + 347 | + if (bVerbose) fprintf(stderr, " Clearing Serial Port Lock amost done...\n"); 348 | + 349 | + fprintf(stderr, " Sleeping 10ms\n"); 350 | + usleep(10000); 351 | + 352 | + // Check for latest appends (ghost appends) 353 | + int iGhost=10; 354 | + bRead = fscanf(fdserlck, "%lu%*[^\n]\n", &rPID); 355 | + while (iGhost > 0) { 356 | + if (bRead > 0 ) { 357 | + if (bVerbose) fprintf(stderr, "%s: Found ghost append: %s. %lu\n",ProgramName,devLCKfile,rPID); 358 | + errno = 0; 359 | + bWrite = fprintf(fdserlcknew, "%lu\n", rPID); 360 | + errno_save = errno; 361 | if (bWrite < 0 || errno_save != 0) { 362 | fprintf(stderr, "\n%s: %s: Problem clearing serial device lock, can't write lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror (errno_save)); 363 | fclose(fdserlcknew); 364 | return(0); 365 | } 366 | } 367 | - bRead = fscanf(fdserlck, "%lu", &rPID); 368 | + fprintf(stderr, " Sleeping 10ms\n"); 369 | + usleep(10000); 370 | + iGhost--; rPID=0; 371 | + bRead = fscanf(fdserlck, "%lu%*[^\n]\n", &rPID); 372 | } 373 | + 374 | +#endif 375 | + 376 | fclose(fdserlck); 377 | fclose(fdserlcknew); 378 | - errno = 0; 379 | - if (rename(devLCKfileNew,devLCKfile)) fprintf(stderr, "\n%s: %s: Problem clearing serial device lock, can't update lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror (errno)); 380 | - if (bVerbose) fprintf(stderr, " done.\n"); 381 | + 382 | + if (bVerbose) fprintf(stderr, " Clearing Serial Port Lock done.\n"); 383 | 384 | return -1; 385 | } 386 | @@ -1138,7 +1260,7 @@ 387 | 388 | ptr = malloc(mSize); 389 | if (!ptr) { 390 | - fprintf(stderr, "\nvproweather: malloc failed\n"); 391 | + fprintf(stderr, "\n%s: malloc failed\n", ProgramName); 392 | exit(2); 393 | } 394 | cptr = (char *)ptr; 395 | -------------------------------------------------------------------------------- /aurora/aurora-1.9.0.diff: -------------------------------------------------------------------------------- 1 | --- aurora-1.9.0/comm.c 2015-09-26 20:56:09.001758260 +0200 2 | +++ my-aurora-1.9.0/comm.c 2015-10-15 16:29:42.000000000 +0200 3 | @@ -1816,7 +1816,7 @@ 4 | attempts++; 5 | } 6 | if (CRCrc < 0) return(-1); 7 | - if (bRptReties) { 8 | + if (bRptRetries) { 9 | fprintf(stderr, "\n%s: %s: %i attempts made",getCurTime(),ProgramName,attempts-1); 10 | if (bVerbose) fprintf(stderr, "\n"); 11 | } else 12 | --- aurora-1.9.0/include/main.h 2015-01-27 02:38:07.596037510 +0100 13 | +++ my-aurora-1.9.0/include/main.h 2015-10-15 16:33:16.000000000 +0200 14 | @@ -62,7 +62,7 @@ 15 | extern BOOL bCommCheck; 16 | extern BOOL bColOutput; 17 | extern BOOL bCalcGridPwr; 18 | -extern BOOL bRptReties; 19 | +extern BOOL bRptRetries; 20 | extern BOOL bRptReadPause; 21 | extern BOOL bSwapEndian; 22 | extern BOOL bUseMultiplierQ; 23 | --- aurora-1.9.0/main.c 2015-09-26 20:55:46.215899208 +0200 24 | +++ my-aurora-1.9.0/main.c 2015-11-03 19:40:30.000000000 +0100 25 | @@ -75,15 +75,21 @@ 26 | static char VersionM[] = "1.9.0"; 27 | char VersionC[7]; 28 | 29 | +// Enable checks for inter-lock problems debug 30 | +#define CHECKFORGHOSTAPPEND 0 31 | +#define CHECKFORCLEARLOCKRACE 0 32 | + 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | +#include 39 | #include 40 | #include 41 | #include 42 | #include 43 | +#include 44 | #include 45 | #include 46 | #include 47 | @@ -97,7 +103,16 @@ 48 | #include "include/comm.h" 49 | #include "include/names.h" 50 | 51 | -BOOL bVerbose = FALSE; 52 | +#if CHECKFORCLEARLOCKRACE 53 | +#include 54 | +#endif 55 | + 56 | +#define DEBUG_STDERR 1 57 | +#define DEBUG_SYSLOG 2 58 | + 59 | +BOOL bVerbose = 0; // FALSE; 60 | +//int debug_mask = 0; /* Default, no debug log */ 61 | +int debug_mask = DEBUG_SYSLOG; /* Let only Syslog Pass */ 62 | BOOL bColumns = FALSE; /* Output data in columns */ 63 | int yGetDSP = -1; /* Measure request to the DSP */ 64 | BOOL bGetDSPExtended = FALSE; /* Measure request to the DSP more parameters */ 65 | @@ -112,12 +127,16 @@ 66 | BOOL bCalcGridPwr = FALSE; 67 | BOOL bXonXoff = FALSE; 68 | BOOL bRTSCTS = FALSE; 69 | -BOOL bRptReties = FALSE; 70 | +BOOL bRptRetries = FALSE; 71 | BOOL bRptReadPause = FALSE; 72 | BOOL bSwapEndian = FALSE; 73 | BOOL bUseMultiplierQ = FALSE; 74 | float yMultiplierK = 1.0; 75 | int yTimeout = 0; /* read timeout value in us */ 76 | + 77 | +#define CMDLINESIZE 128 /* should be enough for debug */ 78 | +char cmdline[CMDLINESIZE]=""; 79 | + 80 | long unsigned int PID; 81 | int yMaxAttempts = 1; 82 | int yReadPause = 0; 83 | @@ -170,8 +189,39 @@ 84 | static int GetParms(int argc, char *argv[]); 85 | static void *getMemPtr(size_t mSize); 86 | static void Version(); 87 | -static int getPIDcmdLen(long unsigned int PID); 88 | -static void getPIDcmd(long unsigned int PID, char* COMMAND); 89 | +static void *getPIDcmd(long unsigned int PID); 90 | +static void AddSerLock(long unsigned int PID, char *COMMAND); 91 | + 92 | +/*-------------------------------------------------------------------------- 93 | + tv_diff 94 | +----------------------------------------------------------------------------*/ 95 | +long inline tv_diff(struct timeval const * const t1, struct timeval const * const t2) 96 | +{ 97 | + struct timeval res; 98 | + timersub(t1, t2, &res); 99 | + return res.tv_sec*1000000 + res.tv_usec; 100 | +} 101 | + 102 | +/*-------------------------------------------------------------------------- 103 | + rnd_usleep 104 | +----------------------------------------------------------------------------*/ 105 | +long rnd_usleep(const useconds_t usecs) 106 | +{ 107 | + long unsigned rnd10 = 10.0*rand()/(RAND_MAX+1.0) + 1; 108 | + if (usleep(usecs*rnd10) == 0) 109 | + return usecs*rnd10; 110 | + else 111 | + return -1; 112 | +} 113 | + 114 | +/*-------------------------------------------------------------------------- 115 | + getIntLen 116 | +----------------------------------------------------------------------------*/ 117 | +int getIntLen(long value){ 118 | + long l=!value; 119 | + while(value) { l++; value/=10; } 120 | + return l; 121 | +} 122 | 123 | /*-------------------------------------------------------------------------- 124 | getCurTime 125 | @@ -191,66 +241,87 @@ 126 | } 127 | 128 | /*-------------------------------------------------------------------------- 129 | - getPIDcmdLen 130 | + getPIDcmd 131 | ----------------------------------------------------------------------------*/ 132 | -int getPIDcmdLen(long unsigned int PID) 133 | +void *getPIDcmd(long unsigned int PID) 134 | { 135 | - FILE *fdserlck = NULL; 136 | - int fLen = 0; 137 | - char sPID[10]; 138 | - char *cmdFile = NULL; 139 | - 140 | - sPID[0] = '\0'; 141 | - sprintf(sPID,"%lu",PID); 142 | - cmdFile = getMemPtr(strlen(sPID)+14+1); 143 | - cmdFile[0] = '\0'; 144 | - sprintf(cmdFile,"/proc/%lu/cmdline",PID); 145 | - cmdFile[strlen(cmdFile)] = '\0'; 146 | - fdserlck = fopen(cmdFile, "r"); 147 | - if (fdserlck != NULL) { 148 | - while (fgetc(fdserlck) != EOF) fLen++; 149 | - fclose(fdserlck); 150 | - fdserlck = NULL; 151 | - } 152 | - if (cmdFile != NULL) { 153 | - free(cmdFile); 154 | - cmdFile = NULL; 155 | - } 156 | - return fLen; 157 | + int fdcmd; 158 | + char *COMMAND = NULL; 159 | + size_t cmdLen = 0; 160 | + size_t length; 161 | + char buffer[1024]; 162 | + char cmdFilename[getIntLen(PID)+14+1]; 163 | + 164 | + // Generate the name of the cmdline file for the process 165 | + *cmdFilename = '\0'; 166 | + snprintf(cmdFilename,sizeof(cmdFilename),"/proc/%lu/cmdline",PID); 167 | + 168 | + // Read the contents of the file 169 | + if ((fdcmd = open(cmdFilename, O_RDONLY)) < 0) return NULL; 170 | + if ((length = read(fdcmd, buffer, sizeof(buffer))) <= 0) { 171 | + close(fdcmd); return NULL; 172 | + } 173 | + close(fdcmd); 174 | + 175 | + // read does not NUL-terminate the buffer, so do it here 176 | + buffer[length] = '\0'; 177 | + // Get 1st string (command) 178 | + cmdLen=strlen(buffer)+1; 179 | + if((COMMAND = getMemPtr(cmdLen)) != NULL ) { 180 | + strncpy(COMMAND, buffer, cmdLen); 181 | + COMMAND[cmdLen-1] = '\0'; 182 | } 183 | 184 | + return COMMAND; 185 | +} 186 | 187 | /*-------------------------------------------------------------------------- 188 | - getPIDcmd 189 | + getCmdLine 190 | ----------------------------------------------------------------------------*/ 191 | -void getPIDcmd(long unsigned int PID, char* COMMAND) 192 | +void getCmdLine() 193 | { 194 | - FILE *fdserlck = NULL; 195 | - int bRead = 0; 196 | - int fLen = 0; 197 | - char sPID[10]; 198 | - char *cmdFile = NULL; 199 | + int fd = open("/proc/self/cmdline", O_RDONLY); 200 | + int nbytesread = read(fd, cmdline, CMDLINESIZE); 201 | + char *p; 202 | + if (nbytesread>0) { 203 | + for (p=cmdline; p < cmdline+nbytesread; p++) if (*p=='\0') *p=' '; 204 | + cmdline[nbytesread-1]='\0'; 205 | + } else 206 | + cmdline[0]='\0'; 207 | + close(fd); 208 | +} 209 | 210 | - sPID[0] = '\0'; 211 | - sprintf(sPID,"%lu",PID); 212 | - cmdFile = getMemPtr(strlen(sPID)+14+1); 213 | - cmdFile[0] = '\0'; 214 | - sprintf(cmdFile,"/proc/%lu/cmdline",PID); 215 | - cmdFile[strlen(cmdFile)] = '\0'; 216 | - fLen = getPIDcmdLen(PID); 217 | - if (fLen > 0) { 218 | - fdserlck = fopen(cmdFile, "r"); 219 | - if (fdserlck != NULL) { 220 | - COMMAND[0] = '\0'; 221 | - bRead = fscanf(fdserlck, "%s", COMMAND); 222 | - if (bRead) COMMAND[strlen(COMMAND)] = '\0'; 223 | - fclose(fdserlck); 224 | - fdserlck = NULL; 225 | +/*-------------------------------------------------------------------------- 226 | + log_message 227 | +----------------------------------------------------------------------------*/ 228 | +void log_message(const int log, const char* format, ...) { 229 | + va_list args; 230 | + char buffer[1024]; 231 | + static int bCmdlineSyslogged = 0; 232 | + 233 | + if (log) { 234 | + va_start(args, format); 235 | + vsnprintf(buffer, 1024, format, args); 236 | + va_end(args); 237 | } 238 | + 239 | + if (log & debug_mask & DEBUG_STDERR) { 240 | + fprintf(stderr, "%s: %s(%lu) ", getCurTime(), ProgramName, PID); 241 | + fprintf(stderr, buffer); 242 | + fprintf(stderr, "\n"); 243 | + } 244 | + 245 | + if (log & debug_mask & DEBUG_SYSLOG) { 246 | + openlog("aurora", LOG_PID|LOG_CONS, LOG_USER); 247 | + if (!bCmdlineSyslogged) { 248 | + char versionbuffer[strlen(ProgramName)+strlen(VersionM)+3]; 249 | + snprintf(versionbuffer, strlen(ProgramName)+strlen(VersionM)+3, "%s v%s", ProgramName, VersionM); 250 | + syslog(LOG_INFO, versionbuffer); 251 | + syslog(LOG_INFO, cmdline); 252 | + bCmdlineSyslogged++; 253 | } 254 | - if (cmdFile != NULL) { 255 | - free(cmdFile); 256 | - cmdFile = NULL; 257 | + syslog(LOG_INFO, buffer); 258 | + closelog(); 259 | } 260 | } 261 | 262 | @@ -269,11 +340,11 @@ 263 | char RunTime[18] = " "; 264 | char EndTime[18] = " "; 265 | long unsigned int LckPID; 266 | - int bRead, bWrite, lckCNT; 267 | + struct timeval tLockStart, tLockNow; 268 | + int bRead; 269 | int errno_save = 0; 270 | int fLen = 0; 271 | int curChar = 0; 272 | - BOOL lckCNTbeg = TRUE; 273 | char *COMMAND = NULL; 274 | char *LckCOMMAND = NULL; 275 | char *LckPIDcommand = NULL; 276 | @@ -289,9 +360,11 @@ 277 | strftime(RunTime,sizeof(RunTime),"%Y%m%d-%H:%M:%S",&timStruct); 278 | RunTime[sizeof(RunTime)-1] = '\0'; 279 | 280 | + srand(getpid()^time(NULL)); // Init random numbers 281 | + 282 | errno = 0; 283 | PID = getpid(); 284 | - 285 | + getCmdLine(); 286 | 287 | /* Get command line parms */ 288 | if ((!GetParms(argc, argv) && !bVersion) || bHelp) { 289 | @@ -379,7 +452,7 @@ 290 | printf(" -X, --rts-cts Enable RTS/CTS on the serial port.\n"); 291 | printf(" -x, --xon-xoff Enable XON/XOFF on the serial port.\n"); 292 | printf(" -Y , --retries= Retry failed communications with inverter up to times (1-100)\n"); 293 | - printf(" -y, --rpt-retries Report the number of retires done\n\n"); 294 | + printf(" -y, --rpt-retries Report the number of retries done\n\n"); 295 | printf(" *** Required Parameters ***\n"); 296 | printf(" -a , --address= Inverter address. 1-31 on older inverters, 1-63 on newer inverters.\n"); 297 | printf(" Device Serial Device.\n"); 298 | @@ -413,84 +486,135 @@ 299 | exit(0); 300 | } 301 | 302 | - fLen = getPIDcmdLen(PID); 303 | - COMMAND = getMemPtr(fLen+1); 304 | - getPIDcmd(PID,COMMAND); 305 | + COMMAND = getPIDcmd(PID); 306 | + AddSerLock(PID, COMMAND); 307 | 308 | - if (bVerbose) fprintf(stderr, "\nAttempting to get lock on Serial Port %s...\n",szttyDevice); 309 | - fdserlck = fopen((const char *)devLCKfile, "a"); 310 | + LckPID = 0; 311 | + long unsigned int oldLckPID = 0; 312 | + int staleLockRetries = 0; 313 | + int const staleLockRetriesMax = 2; 314 | + long unsigned int clrStaleTargetPID = 0; 315 | + int missingPidRetries = 0; 316 | + int const missingPidRetriesMax = 2; 317 | + 318 | + gettimeofday(&tLockStart, NULL); 319 | + tLockNow=tLockStart; 320 | + 321 | + if (bVerbose) fprintf(stderr, "Checking for lock\n"); 322 | + while(LckPID != PID && tv_diff(&tLockNow, &tLockStart) <= yLockWait*1000000L) { 323 | + 324 | + do { 325 | + fdserlck = fopen(devLCKfile, "r"); 326 | if (fdserlck == NULL) { 327 | - if (bVerbose) fprintf(stderr, "\n"); 328 | - fprintf(stderr, "%s: %s: Problem locking serial device, can't open lock file: %s for write.\n\n",getCurTime(),ProgramName,devLCKfile); 329 | + log_message(bVerbose | DEBUG_SYSLOG, "Problem locking serial device, can't open lock file: %s for read.",devLCKfile); 330 | exit(2); 331 | } 332 | - bWrite = fprintf(fdserlck, "%lu %s\n", PID, COMMAND); 333 | + //log_message(bVerbose, "Acquiring shared lock on %s...",devLCKfile); 334 | + errno = 0; 335 | + if (flock(fileno(fdserlck), LOCK_SH | LOCK_NB) == 0) break; // Lock Acquired 336 | errno_save = errno; 337 | + 338 | + if (errno_save == EWOULDBLOCK) { 339 | + log_message(bVerbose, "Would block %s, retry (%d) %s...", devLCKfile, errno_save, strerror(errno_save)); 340 | + rnd_usleep(25000); 341 | fclose(fdserlck); 342 | - fdserlck = NULL; 343 | - if (bWrite < 0 || errno_save != 0) { 344 | - if (bVerbose) fprintf(stderr, "\n"); 345 | - fprintf(stderr, "%s: %s: Problem locking serial device, can't write lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror (errno_save)); 346 | + } else { 347 | + log_message(DEBUG_STDERR | DEBUG_SYSLOG, "Problem locking serial device, can't open lock file: %s for read. (%d) %s", devLCKfile, errno_save, strerror(errno_save)); 348 | exit(2); 349 | } 350 | + } while (errno_save == EWOULDBLOCK); 351 | + //log_message(bVerbose, "Shared lock on %s acquired...",devLCKfile); 352 | 353 | - LckPID = 0; 354 | - lckCNT = -1; 355 | - lckCNTbeg = TRUE; 356 | - while(LckPID != PID && lckCNT++ < yLockWait) { 357 | - if (bVerbose && lckCNT == 0 && lckCNTbeg) { 358 | - fprintf(stderr, "Checking for lock\n"); 359 | - lckCNTbeg = FALSE; 360 | - } 361 | - fdserlck = fopen(devLCKfile, "r"); 362 | - if (fdserlck == NULL) { 363 | - if (bVerbose) fprintf(stderr, "\n"); 364 | - fprintf(stderr, "%s: %s: Problem locking serial device, can't open lock file: %s for read.\n\n",getCurTime(),ProgramName,devLCKfile); 365 | - exit(2); 366 | - } 367 | 368 | fLen = 0; 369 | while ((curChar = fgetc(fdserlck)) != EOF && curChar != '\n' && curChar != ' ') fLen++; 370 | fLen = 0; 371 | - while ((curChar = fgetc(fdserlck)) != EOF && curChar != '\n') fLen++; 372 | + if (curChar == ' ') while ((curChar = fgetc(fdserlck)) != EOF && curChar != '\n') fLen++; 373 | + 374 | rewind(fdserlck); 375 | + 376 | + //if (LckPID != oldLckPID) log_message(bVerbose, "fLen=%i", fLen); 377 | LckCOMMAND = getMemPtr(fLen+1); 378 | + //if (LckPID != oldLckPID) log_message(bVerbose, "fLen=%i LckCOMMAND %s", fLen, (LckCOMMAND==NULL ? "is null" : "is not null")); 379 | LckCOMMAND[0] = '\0'; 380 | + LckPID=0; 381 | 382 | - bRead = fscanf(fdserlck, "%lu %s", &LckPID, LckCOMMAND); 383 | + errno = 0; 384 | + bRead = fscanf(fdserlck, "%lu%*[ ]%[^\n]\n", &LckPID, LckCOMMAND); 385 | errno_save = errno; 386 | fclose(fdserlck); 387 | - if (bVerbose) fprintf(stderr, "\nChecking process %lu (%s) for lock\n", LckPID, LckCOMMAND); 388 | - fdserlck = NULL; 389 | - if (bRead == EOF || errno_save != 0) { 390 | - if (bVerbose) fprintf(stderr, "\n"); 391 | - fprintf(stderr, "%s: %s: Problem locking serial device, can't read lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror (errno_save)); 392 | + if (LckPID != oldLckPID) { 393 | + log_message(bVerbose | (bRead==EOF || errno_save != 0 ? DEBUG_SYSLOG : 0), "errno=%i, bRead=%i PID=%lu LckPID=%lu", errno_save, bRead, PID, LckPID); 394 | + log_message(bVerbose, "Checking process %lu (%s) for lock", LckPID, LckCOMMAND); 395 | + //oldLckPID = LckPID; 396 | + } 397 | + if (bRead == EOF || LckPID == 0 || errno_save != 0) { 398 | + log_message(bVerbose | DEBUG_SYSLOG, "Problem locking serial device, can't read PID from lock file: %s.",devLCKfile); 399 | + if (errno_save != 0) { 400 | + // Real error 401 | + log_message(bVerbose | DEBUG_SYSLOG, "(%u) %s", errno_save, strerror(errno_save)); 402 | + free(LckCOMMAND); free(LckPIDcommand); free(COMMAND); 403 | exit(2); 404 | - } 405 | + } else { 406 | + if (missingPidRetries < missingPidRetriesMax) { 407 | + missingPidRetries++; 408 | + log_message(bVerbose, "%s miss process self PID from lock file?",devLCKfile); 409 | + } else if (missingPidRetries >= missingPidRetriesMax) { 410 | + // Self PID missing... (Should never happen) 411 | + log_message(bVerbose | DEBUG_SYSLOG, "%s miss process self PID from lock file, amending.",devLCKfile); 412 | + AddSerLock(PID, COMMAND); 413 | + //LckPID=0; 414 | + missingPidRetries = 0; 415 | + } 416 | + } 417 | + oldLckPID = LckPID; 418 | + } else { //fread OK 419 | + 420 | + // We got a pid from lockfile, let's clear missing pid status 421 | + missingPidRetries = 0; 422 | 423 | - fLen = getPIDcmdLen(LckPID); 424 | - LckPIDcommand = getMemPtr(fLen+1); 425 | - getPIDcmd(LckPID,LckPIDcommand); 426 | + LckPIDcommand = getPIDcmd(LckPID); 427 | 428 | + if (LckPID != oldLckPID) { 429 | if (bVerbose) { 430 | fprintf (stderr, "PID: %lu COMMAND: \"%s\" LckPID: %lu LckCOMMAND: \"%s\" LckPIDcommand \"%s\"", PID, COMMAND, LckPID, LckCOMMAND, LckPIDcommand); 431 | if (LckPID == PID) fprintf (stderr, " = me"); 432 | fprintf (stderr, "\n"); 433 | } 434 | + oldLckPID = LckPID; 435 | + } 436 | 437 | // PID - this process 438 | // LckPID - PID from lock file 439 | // COMMAND - this process 440 | // LckCOMMAND - process command from lock file 441 | // LckPIDcommand - process command of process using PID from lock file 442 | - if ((PID != LckPID && LckPIDcommand == NULL) || strcmp(LckCOMMAND,LckPIDcommand) != 0 || strcmp(LckPIDcommand,"") == 0) { 443 | + if ((PID != LckPID && LckPIDcommand == NULL) || (LckCOMMAND[0]!='\0' && strcmp(LckPIDcommand,LckCOMMAND) != 0) || strcmp(LckPIDcommand,"") == 0) { 444 | if (bVerbose) fprintf (stderr, "\n"); 445 | - fprintf(stderr, "%s: %s: Clearing stale serial port lock. (%lu)\n",getCurTime(),ProgramName,LckPID); 446 | + // Is it a stale lock pid? 447 | + if (staleLockRetries < staleLockRetriesMax) { 448 | + staleLockRetries++; 449 | + clrStaleTargetPID = LckPID; 450 | + log_message(bVerbose | (staleLockRetries > 1 ? DEBUG_SYSLOG : 0), "Stale pid lock? PID=%lu, LckPID=%lu, LckCOMMAND='%s', LckPIDCommand='%s'", PID, LckPID, LckCOMMAND, LckPIDcommand); 451 | + } else if (LckPID == clrStaleTargetPID && staleLockRetries >= staleLockRetriesMax) { 452 | + log_message(bVerbose | DEBUG_SYSLOG, "Clearing stale serial port lock. (%lu)", LckPID); 453 | ClrSerLock(LckPID); 454 | - lckCNT = -1; 455 | - } else if (yLockWait > 0) 456 | - sleep(1); 457 | + staleLockRetries = 0; 458 | + clrStaleTargetPID = 0; 459 | + } 460 | + } else { 461 | + // Pid lock have a process running, let's reset stale pid retries 462 | + staleLockRetries = 0; 463 | + clrStaleTargetPID = 0; 464 | + } 465 | + } 466 | + 467 | + if (yLockWait > 0 && LckPID != PID) { 468 | + rnd_usleep(25000); 469 | + //log_message(bVerbose, "Sleeping %luus", rnd_usleep(25000)); 470 | + } 471 | 472 | + // Cleanup and loop 473 | if (LckCOMMAND != NULL) { 474 | free(LckCOMMAND); 475 | LckCOMMAND = NULL; 476 | @@ -499,12 +623,15 @@ 477 | free(LckPIDcommand); 478 | LckPIDcommand = NULL; 479 | } 480 | + gettimeofday(&tLockNow, NULL); 481 | } 482 | free(COMMAND); 483 | if (bVerbose && LckPID == PID) fprintf(stderr, "Appears we got the lock.\n"); 484 | if (LckPID != PID) { 485 | if (bVerbose) fprintf (stderr, "\n"); 486 | fprintf(stderr, "%s: %s: Problem locking serial device %s, couldn't get the lock for %lu, locked by %lu.\n",getCurTime(),ProgramName,szttyDevice,PID,LckPID); 487 | + log_message(bVerbose | DEBUG_SYSLOG, "Unable to get lock on serial %s for %lu in %ds: still locked by %lu.",szttyDevice,PID,(yLockWait)%30,LckPID); 488 | + log_message(bVerbose | DEBUG_SYSLOG, "Getting lock timed out after %ldus", tv_diff(&tLockNow, &tLockStart)); 489 | ClrSerLock(PID); 490 | exit(2); 491 | } 492 | @@ -728,9 +855,12 @@ 493 | 494 | RestorePort(fdser); 495 | ClrSerLock(PID); 496 | + free(szttyDevice); 497 | + free(devLCKfile); 498 | + free(devLCKfileNew); 499 | 500 | if (rc < 0) { 501 | - if (bRptReties) fprintf(stderr, "\n"); 502 | + if (bRptRetries) fprintf(stderr, "\n"); 503 | if (bVerbose) fprintf(stderr, "\n"); 504 | fprintf(stderr, "%s: %s: ERROR: Received bad return code (%i %i",getCurTime(),ProgramName,rc,invOp); 505 | if (invFunc > 0) 506 | @@ -755,31 +885,31 @@ 507 | ----------------------------------------------------------------------------*/ 508 | int RestorePort(int fdser) { 509 | 510 | - if (bVerbose) fprintf(stderr, "\nRestoring Serial Port settings %s...", szttyDevice); 511 | - if (tcsetattr(fdser, TCSANOW, &oldtio)) { /* restore previous port settings */ 512 | + if (bVerbose) { fprintf(stderr, "\nFlushing serial device buffer..."); } 513 | + errno = 0; 514 | + if (tcflush(fdser, TCIOFLUSH)) { 515 | if (bVerbose) fprintf(stderr, "\n"); 516 | - fprintf(stderr, "%s: %s: Problem restoring serial device settings.\n",getCurTime(),ProgramName); 517 | + fprintf(stderr, "\n%s: %s: Problem flushing serial device: (%i) %s\n\n",getCurTime(),ProgramName,errno,strerror(errno)); 518 | if (bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice); 519 | if (close(fdser)) { 520 | if (bVerbose) fprintf(stderr, "\n"); 521 | - fprintf(stderr, "%s: %s: Problem closing serial device, check device name.\n",getCurTime(),ProgramName); 522 | + fprintf(stderr, "%s: %s: Problem closing serial device.\n",getCurTime(),ProgramName); 523 | } 524 | - if (bVerbose) fprintf(stderr, " Success!\n"); 525 | + if (bVerbose) { fprintf(stderr, " Success!\n"); } 526 | return 2; 527 | } 528 | 529 | - if (bVerbose) { fprintf(stderr, " Success!\nFlushing serial device buffer..."); } 530 | + if (bVerbose) fprintf(stderr, " Success!\nRestoring Serial Port settings %s...", szttyDevice); 531 | 532 | - errno = 0; 533 | - if (tcflush(fdser, TCIOFLUSH)) { 534 | + if (tcsetattr(fdser, TCSADRAIN, &oldtio)) { /* restore previous port settings */ 535 | if (bVerbose) fprintf(stderr, "\n"); 536 | - fprintf(stderr, "\n%s: %s: Problem flushing serial device: (%i) %s\n\n",getCurTime(),ProgramName,errno,strerror(errno)); 537 | + fprintf(stderr, "%s: %s: Problem restoring serial device settings.\n",getCurTime(),ProgramName); 538 | if (bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice); 539 | if (close(fdser)) { 540 | if (bVerbose) fprintf(stderr, "\n"); 541 | - fprintf(stderr, "%s: %s: Problem closing serial device.\n",getCurTime(),ProgramName); 542 | + fprintf(stderr, "%s: %s: Problem closing serial device, check device name.\n",getCurTime(),ProgramName); 543 | } 544 | - if (bVerbose) { fprintf(stderr, " Success!\n"); } 545 | + if (bVerbose) fprintf(stderr, " Success!\n"); 546 | return 2; 547 | } 548 | 549 | @@ -794,6 +924,52 @@ 550 | return 0; 551 | } 552 | 553 | +/*-------------------------------------------------------------------------- 554 | + AddSerLock 555 | + Queue Serial Port lock intent. 556 | +----------------------------------------------------------------------------*/ 557 | +void AddSerLock(long unsigned int PID, char *COMMAND) { 558 | + FILE *fdserlck; 559 | + int bWrite; 560 | + int errno_save = 0; 561 | + 562 | + if (bVerbose) fprintf(stderr, "\nAttempting to get lock on Serial Port %s...\n",szttyDevice); 563 | + do { 564 | + fdserlck = fopen((const char *)devLCKfile, "a"); 565 | + if (fdserlck == NULL) { 566 | + if (bVerbose) fprintf(stderr, "\n"); 567 | + fprintf(stderr, "%s: %s: Problem locking serial device, can't open lock file: %s for write.\n\n",getCurTime(),ProgramName,devLCKfile); 568 | + exit(2); 569 | + } 570 | + if (bVerbose) fprintf(stderr, "Acquiring shared lock on %s...\n",devLCKfile); 571 | + errno = 0; 572 | + if (flock(fileno(fdserlck), LOCK_SH | LOCK_NB) == 0 ) break; // Lock Acquired 573 | + errno_save=errno; 574 | + 575 | + if (errno_save == EWOULDBLOCK) { 576 | + if (bVerbose) fprintf(stderr, "Whould block %s, retry (%d)...\n",devLCKfile, errno_save); 577 | + rnd_usleep(25000); 578 | + fclose(fdserlck); 579 | + } else { 580 | + fprintf(stderr, "%s: Problem locking serial device, can't open lock file: %s for write. (%d)\n",ProgramName,devLCKfile, errno_save); 581 | + exit(2); 582 | + } 583 | + } while (errno_save == EWOULDBLOCK); 584 | + if (bVerbose) fprintf(stderr, "Shared lock on %s acquired...\n",devLCKfile); 585 | + 586 | + errno=0; 587 | + bWrite = fprintf(fdserlck, "%lu %s\n", PID, COMMAND); 588 | + errno_save = errno; 589 | + fclose(fdserlck); // Will release lock 590 | + //fdserlck = NULL; 591 | + if (bWrite < 0 || errno_save != 0) { 592 | + if (bVerbose) fprintf(stderr, "\n"); 593 | + fprintf(stderr, "%s: %s: Problem locking serial device, can't write lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror(errno_save)); 594 | + exit(2); 595 | + } 596 | + } 597 | + 598 | + 599 | 600 | /*-------------------------------------------------------------------------- 601 | ClrSerLock 602 | @@ -818,7 +994,31 @@ 603 | fprintf(stderr, "\n%s: %s: Problem opening serial device lock file to clear LckPID %lu: %s for read.\n\n",getCurTime(),ProgramName,LckPID,devLCKfile); 604 | return(0); 605 | } 606 | - fdserlcknew = fopen(devLCKfileNew, "w"); 607 | + if (bVerbose) fprintf(stderr, "Acquiring exclusive lock on %s...\n",devLCKfile); 608 | + flock(fileno(fdserlck), LOCK_EX); // Will wait to acquire lock then continue 609 | + if (bVerbose) fprintf(stderr, "Exclusive lock on %s acquired (%d)...\n",devLCKfile, errno); 610 | + 611 | +#if CHECKFORCLEARLOCKRACE 612 | + 613 | + // Check for potential conflicts 614 | + glob_t globbuf; 615 | + int iGlob, fGlob = TRUE; 616 | + 617 | + if (bVerbose) fprintf(stderr, "GlobCheck - Check to avoid simultaneous PID clearing\n"); 618 | + for (iGlob=5; iGlob>0 && fGlob; iGlob--) { 619 | + fGlob = FALSE; 620 | + if (glob("/var/lock/LCK..ttyUSB0.*", GLOB_NOSORT, NULL, &globbuf) != GLOB_NOMATCH) { 621 | + if (bVerbose) fprintf(stderr, "GlobCheck (%u), some other process is clearing lock too!!! (%s)",iGlob, globbuf.gl_pathv[0]); 622 | + fGlob=TRUE; 623 | + if (bVerbose) fprintf(stderr, "Sleeping 500ms"); 624 | + usleep(500000); 625 | + } 626 | + globfree(&globbuf); 627 | + } 628 | + 629 | +#endif 630 | + 631 | + fdserlcknew = fopen(devLCKfileNew, "a"); 632 | if (fdserlcknew == NULL) { 633 | if (bVerbose) fprintf(stderr, "\n"); 634 | fprintf(stderr, "\n%s: %s: Problem opening new serial device lock file to clear LckPID %lu: %s for write.\n\n",getCurTime(),ProgramName,LckPID,devLCKfileNew); 635 | @@ -826,38 +1026,86 @@ 636 | return(0); 637 | } 638 | 639 | + // Find cmdLen max len in file 640 | curChar = 0; 641 | while (curChar != EOF) { 642 | fLen = 0; 643 | while ((curChar = fgetc(fdserlck)) != EOF && curChar != '\n' && curChar != ' ') fLen++; 644 | + if (curChar == ' ') { 645 | fLen = 0; 646 | while ((curChar = fgetc(fdserlck)) != EOF && curChar != '\n') fLen++; 647 | if (fLen > cmdLen) cmdLen = fLen; 648 | } 649 | + } 650 | rewind(fdserlck); 651 | 652 | COMMAND = getMemPtr(cmdLen+1); 653 | - COMMAND[0] = '\0'; 654 | - bRead = fscanf(fdserlck, "%lu %s", &PID, COMMAND); 655 | + COMMAND[0] = '\0'; PID = 0; 656 | + errno = 0; 657 | + bRead = fscanf(fdserlck, "%lu%*[ ]%[^\n]\n", &PID, COMMAND); 658 | + errno_save = errno; 659 | + if (bVerbose) fprintf(stderr, "errno=%i, bRead=%i LckPID=%lu PID=%lu COMMAND='%s'\n", errno_save, bRead, LckPID, PID, COMMAND); 660 | 661 | - while (bRead != EOF) { 662 | + while (bRead != EOF && bRead>0) { 663 | if (PID != LckPID) { 664 | errno = 0; 665 | + if (COMMAND[0] != '\0') { 666 | bWrite = fprintf(fdserlcknew, "%lu %s\n", PID, COMMAND); 667 | errno_save = errno; 668 | + } else { 669 | + bWrite = fprintf(fdserlcknew, "%lu\n", PID); 670 | + errno_save = errno; 671 | + } 672 | + if (bVerbose) fprintf(stderr, "%s: errno=%i, bWrite=%i PID=%lu\n", ProgramName, errno, bWrite, PID); 673 | if (bWrite < 0 || errno_save != 0) { 674 | fprintf(stderr, "\n%s: %s: Problem clearing serial device lock, can't write lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror (errno_save)); 675 | fclose(fdserlcknew); 676 | return(0); 677 | } 678 | } 679 | - bRead = fscanf(fdserlck, "%lu %s", &PID, COMMAND); 680 | + errno=0; PID=0; COMMAND[0] = '\0'; 681 | + bRead = fscanf(fdserlck, "%lu%*[ ]%[^\n]\n", &PID, COMMAND); 682 | + if (bVerbose) fprintf(stderr, "errno=%i, bRead=%i LckPID=%lu PID=%lu COMMAND='%s'\n", errno_save, bRead, LckPID, PID, COMMAND); 683 | } 684 | + 685 | + fflush(fdserlcknew); 686 | + 687 | + errno = 0; 688 | + if (rename(devLCKfileNew,devLCKfile)) fprintf(stderr, "\n%s: %s: Problem clearing serial device lock, can't update lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror (errno)); 689 | + 690 | +#if CHECKFORGHOSTAPPEND 691 | + 692 | + log_message(bVerbose, "Clearing Serial Port Lock amost done..."); 693 | + log_message(bVerbose, "Sleeping %luus", rnd_usleep(10000)); 694 | + 695 | + // Check for latest appends (ghost appends) 696 | + int iGhost=10; 697 | + bRead = fscanf(fdserlck, "%lu%*[ ]%*[^\n]\n", &PID); 698 | + while (iGhost > 0) { 699 | + if (bRead > 0) { 700 | + log_message(bVerbose | DEBUG_SYSLOG, "Found ghost append (%d): %s. %lu",iGhost,devLCKfile,PID); 701 | + errno = 0; 702 | + bWrite = fprintf(fdserlcknew, "%lu\n", PID); 703 | + errno_save = errno; 704 | + if (bWrite < 0 || errno_save != 0) { 705 | + log_message(bVerbose | DEBUG_SYSLOG, "Problem clearing serial device lock, can't write lock file: %s. %s",devLCKfile,strerror (errno_save)); 706 | + log_message(bVerbose | DEBUG_SYSLOG, "(%u) %s", errno_save, strerror(errno_save)); 707 | + fclose(fdserlcknew); 708 | + return(0); 709 | + } 710 | + } 711 | + fflush(fdserlcknew); 712 | + log_message(bVerbose, "Sleeping %ldus", rnd_usleep(10000)); 713 | + iGhost--; PID=0; 714 | + bRead = fscanf(fdserlck, "%lu%*[ ]%*[^\n]\n", &PID); 715 | + } 716 | + 717 | +#endif 718 | + 719 | fclose(fdserlck); 720 | fclose(fdserlcknew); 721 | free(COMMAND); 722 | - errno = 0; 723 | - if (rename(devLCKfileNew,devLCKfile)) fprintf(stderr, "\n%s: %s: Problem clearing serial device lock, can't update lock file: %s.\n%s\n\n",getCurTime(),ProgramName,devLCKfile,strerror (errno)); 724 | + 725 | if (bVerbose) fprintf(stderr, " done.\n"); 726 | 727 | return -1; 728 | @@ -889,7 +1137,6 @@ 729 | BOOL b = FALSE; 730 | char *pos; 731 | char *SubStrPos = NULL; 732 | - char sPID[10]; 733 | static char *Cost = NULL; 734 | 735 | if (strpbrk(VersionM,"abcdefghijklmnopqurtsuvwxyz") != NULL) fprintf(stderr, "\n**** THIS IS EXPERIMENTAL CODE %-6s ****\n",VersionM); 736 | @@ -973,7 +1220,10 @@ 737 | yAddress = atoi(optarg); 738 | break; 739 | case 'A': bGetLastAlarms = TRUE; break; 740 | - case 'b': bVerbose = TRUE; break; 741 | + case 'b': 742 | + bVerbose = 1; 743 | + debug_mask = debug_mask | DEBUG_STDERR; 744 | + break; 745 | case 'C': 746 | SubStrPos = strstr(optarg, ":"); 747 | if (SubStrPos == NULL) 748 | @@ -1166,7 +1416,7 @@ 749 | return 0; 750 | } 751 | break; 752 | - case 'y': bRptReties = TRUE; break; 753 | + case 'y': bRptRetries = TRUE; break; 754 | case 'V': bVersion = TRUE; break; 755 | case 'v': bGetVer = TRUE; break; 756 | 757 | @@ -1194,8 +1444,7 @@ 758 | strcpy(devLCKfile,ttyLCKloc); 759 | strcat(devLCKfile, pos); 760 | devLCKfile[strlen(devLCKfile)] = '\0'; 761 | - sprintf(sPID,"%lu",PID); 762 | - devLCKfileNew = getMemPtr(strlen(devLCKfile)+strlen(sPID)+2); /* dot & terminator */ 763 | + devLCKfileNew = getMemPtr(strlen(devLCKfile)+getIntLen(PID)+2); /* dot & terminator */ 764 | devLCKfileNew[0] = '\0'; 765 | strcpy(devLCKfileNew,devLCKfile); 766 | sprintf(devLCKfileNew,"%s.%lu",devLCKfile,PID); 767 | @@ -1227,20 +1476,15 @@ 768 | void *getMemPtr(size_t mSize) 769 | { 770 | void *ptr; 771 | - char *cptr; 772 | - int i; 773 | 774 | - ptr = malloc(mSize); 775 | + ptr = calloc(sizeof(char), mSize); 776 | if (!ptr) { 777 | - fprintf(stderr, "\nvproweather: malloc failed\n"); 778 | + fprintf(stderr, "\n%s: malloc failed\n", ProgramName); 779 | exit(2); 780 | } 781 | - cptr = (char *)ptr; 782 | - for (i = 0; i < mSize; i++) cptr[i] = '\0'; 783 | return ptr; 784 | } 785 | 786 | - 787 | /*-------------------------------------------------------------------------- 788 | Version 789 | Display program component versions 790 | -------------------------------------------------------------------------------- /domoticz/meter_domoticz.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ID=1 4 | DOMOTICZ="http://192.168.2.31:8080/json.htm?type=command" 5 | 6 | W=`cat /run/shm/metern${ID}.txt | egrep "^1\(" | grep "*W)" | egrep -o '[0-9]*(\.)?[0-9]*\*' | egrep -o '[0-9]*(\.)?[0-9]*'` 7 | WH=`cat /run/shm/metern${ID}.txt | egrep "^1\(" | grep "*Wh)" | egrep -o '[0-9]*\*' | egrep -o '[0-9]*'` 8 | V=`cat /run/shm/metern${ID}.txt | egrep "^1_1\(" | grep "*V)" | egrep -o '[0-9]*(\.)?[0-9]*\*' | egrep -o '[0-9]*(\.)?[0-9]*'` 9 | A=`cat /run/shm/metern${ID}.txt | egrep "^1_2\(" | grep "*A)" | egrep -o '[0-9]*(\.)?[0-9]*\*' | egrep -o '[0-9]*(\.)?[0-9]*'` 10 | 11 | curl -s "${DOMOTICZ}¶m=udevice&idx=3&nvalue=0&svalue=$W" 12 | curl -s "${DOMOTICZ}¶m=udevice&idx=4&nvalue=0&svalue=$V" 13 | curl -s "${DOMOTICZ}¶m=udevice&idx=5&nvalue=0&svalue=$A" 14 | curl -s "${DOMOTICZ}¶m=udevice&idx=2&nvalue=0&svalue=$W;$WH" 15 | curl -s "${DOMOTICZ}¶m=addlogmessage&message=SDM120C:$W;$WH" 16 | -------------------------------------------------------------------------------- /domoticz/sdm120c_domoticz.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DOMOTICZ="http://192.168.2.31:8080/json.htm?type=command" 4 | 5 | values=`sudo sdm120c -a 1 -b 9600 -z 10 -w 5 -j 1 -i -p -v -c -P N -q /dev/ttyUSB0` 6 | 7 | echo ${values} 8 | 9 | V=`echo ${values} | awk '{print $1}'` 10 | A=`echo ${values} | awk '{print $2}'` 11 | W=`echo ${values} | awk '{print $3}'` 12 | WH=`echo ${values} | awk '{print $4}'` 13 | 14 | curl -s "${DOMOTICZ}¶m=udevice&idx=3&nvalue=0&svalue=$W" 15 | curl -s "${DOMOTICZ}¶m=udevice&idx=4&nvalue=0&svalue=$V" 16 | curl -s "${DOMOTICZ}¶m=udevice&idx=5&nvalue=0&svalue=$A" 17 | curl -s "${DOMOTICZ}¶m=udevice&idx=2&nvalue=0&svalue=$W;$WH" 18 | curl -s "${DOMOTICZ}¶m=addlogmessage&message=SDM120C" 19 | -------------------------------------------------------------------------------- /libmodbus/libmodbus-dev_3.0.6-1_armhf.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianfrdp/SDM120C/f36fb38c2f8985e2330ed2492178211d63542361/libmodbus/libmodbus-dev_3.0.6-1_armhf.deb -------------------------------------------------------------------------------- /libmodbus/libmodbus-dev_3.1.1-1_armhf.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianfrdp/SDM120C/f36fb38c2f8985e2330ed2492178211d63542361/libmodbus/libmodbus-dev_3.1.1-1_armhf.deb -------------------------------------------------------------------------------- /libmodbus/libmodbus5_3.0.6-1_armhf.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianfrdp/SDM120C/f36fb38c2f8985e2330ed2492178211d63542361/libmodbus/libmodbus5_3.0.6-1_armhf.deb -------------------------------------------------------------------------------- /libmodbus/libmodbus5_3.1.1-1_armhf.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianfrdp/SDM120C/f36fb38c2f8985e2330ed2492178211d63542361/libmodbus/libmodbus5_3.1.1-1_armhf.deb -------------------------------------------------------------------------------- /metern/comapps/README.md: -------------------------------------------------------------------------------- 1 | These are poolers for meterN (http://metern.org/) to collect and render data. 2 | 3 | There are 2 blocks of files: 4 | 1. pooler485.php and poolmeters485.sh 5 | 2. pooler485.sh 6 | 7 | First ones are the first version, the latter is the last version. 8 | 9 | To use pooler485.sh, create a symbolic link 10 | ln -s /var/www/metern/comapps/pooler485.sh /usr/local/bin/pooler485 11 |12 | then add in /etc/rc.local 13 |14 | touch /run/shm/metern1.txt 15 | chown www-data:www-data /run/shm/metern1.txt 16 | pooler485 1 9600 /dev/ttyUSB0& 17 |18 | where 1 is RS485 address (and meterN ID), 9600 port speed, /dev/ttyUSB0 is USB-RS485 device 19 | 20 | In meter configuration use 21 |cat /run/shm/metern1.txt | egrep "^1\(" | grep "*Wh)"22 | for energy and 23 |cat /run/shm/metern1.txt | egrep "^1\(" | grep "*W)"24 | for live power 25 | 26 | You can use more meters on the same bus, setting meters with different addresses. 27 | MeterN configuration is the same, dimply change 1 with 2, 3, etc. 28 | 29 | NOTE: meterN ID must be equal to RS485 address 30 | 31 | poolen485.php is a new pooler only for total consumation that prevent passover when, for some reason, meter returns a value lesser than previous one. 32 | Use 33 |poolen485 134 | -------------------------------------------------------------------------------- /metern/comapps/poolen485.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php -f 2 | 1000) { 54 | $val_tot = $last_val; 55 | } else { 56 | $val_tot = $meter_val; 57 | } 58 | 59 | echo "$metnum($val_tot*Wh)\n"; 60 | ?> -------------------------------------------------------------------------------- /metern/comapps/poolen485_2.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | 1) { 57 | $prevarray = preg_split("/,/", $file[$contalines - 1]); 58 | 59 | } elseif ($contalines == 1 && file_exists($output[$cnt - 2])) { // yesterday, only header 60 | $file = file($output[$cnt - 2]); 61 | $contalines = count($file); 62 | $prevarray = preg_split("/,/", $file[$contalines - 1]); 63 | } 64 | $cons_val_first = trim($prevarray[$metnum]); 65 | } else { 66 | $cons_val_first = null; 67 | } 68 | 69 | sleep(1); // oh why ? 70 | // Now retrieve the current value 71 | $datareturn = shell_exec($cmd); 72 | $datareturn = trim($datareturn); 73 | $datareturn = preg_replace("/^${'ID'.$id}\(/i", '', $datareturn); // VALUE*UNIT) 74 | $lastval = preg_replace("/\*[a-z0-9]+\)$/i", '', $datareturn); // VALUE 75 | //echo "last_val = $lastval\n"; 76 | 77 | settype($lastval, 'float'); 78 | settype($prevcount, 'float'); 79 | settype($cons_val_first, 'float'); 80 | 81 | $prevcount = file_get_contents("$pathtomn/data/correcons$id.txt"); // file correttore del totale 82 | $lastval += $prevcount; // aggiunge il correttore del totale 83 | //echo "prevcount = $prevcount\n"; 84 | 85 | if ($lastval < $cons_val_first) { // controlla se il contatore segna meno del valore precedente 86 | $blackout = abs ($lastval - $cons_val_first); 87 | $prevcount += $blackout; 88 | file_put_contents("$pathtomn/data/correcons$id.txt", $prevcount); 89 | $lastval = $cons_val_first; 90 | } 91 | 92 | $lastval = round($lastval, ${'PRECI' . $metnum}); 93 | $str = utf8_decode("${'ID'.$metnum}($lastval*${'UNIT'.$metnum})\n"); 94 | file_put_contents("$tdir/consumi$id.txt", $str); 95 | echo "$str"; 96 | 97 | ?> 98 | -------------------------------------------------------------------------------- /metern/comapps/pooler485.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | /dev/null 2>&1 &'); // Kill temporary the "live values fetching" 11 | 12 | if (isset($argv[1])) { 13 | 14 | $metnum = $argv[2]; 15 | $address = $argv[3]; 16 | $baud = $argv[4]; 17 | $device = $argv[5]; 18 | 19 | if ($argv[1] == 'elect') { 20 | $cmd = "$pathtomn/comapps/poolmeters485.sh relect $address $baud $device"; // Request counters values during a 5 min period 21 | #echo "$cmd\n"; 22 | } else if ($argv[1] == 'live') { 23 | $cmd = "$pathtomn/comapps/poolmeters485.sh live $address $baud $device > /dev/null 2>&1 &"; // Restart live fetching at the last counter request 24 | echo "$cmd\n"; 25 | $output = shell_exec($cmd); 26 | exit(0); 27 | } else { 28 | die('Aborting: no valid argument given\n'); 29 | } 30 | } else { 31 | die("Usage: pooler485 {elect|live} metnum address baud device\n"); 32 | } 33 | // End of setup 34 | 35 | define('checkaccess', TRUE); 36 | include("$pathtomn/config/config_main.php"); 37 | include("$pathtomn/config/config_met$metnum.php"); 38 | 39 | // Retrieve previous value in the last daily csv 40 | $dir = "$pathtomn/data/csv"; 41 | $output = glob($dir . '/*.csv'); 42 | sort($output); 43 | $csvcnt = count($output); 44 | $file = file($output[$csvcnt - 1]); 45 | $cnt = count($file); 46 | 47 | if ($cnt==1 && $csvcnt>1) { // Midnight takes yesterday file 48 | $file = file($output[$csvcnt - 2]); 49 | } 50 | $cnt = count($file); 51 | 52 | $i = 0; 53 | while (!isset($prevval)) { 54 | $i++; 55 | $array = preg_split('/,/', $file[$cnt - $i]); 56 | if (!empty($array[$metnum])) { 57 | $prevval = $array[$metnum]; 58 | } 59 | if ($i == $cnt) { 60 | $prevval = 0; // didn't find any prev. value 61 | } 62 | } 63 | sleep(1); // oh why ? 64 | // Now retrieve the current value 65 | $datareturn = shell_exec($cmd); 66 | #echo "$datareturn\n"; 67 | $datareturn = trim($datareturn); 68 | #echo "$datareturn\n"; 69 | $datareturn = preg_replace("/^${'ID'.$metnum}\(/i", '', $datareturn); // VALUE*UNIT) 70 | #echo "$datareturn\n"; 71 | $lastval = preg_replace("/\*[a-z0-9]+\)$/i", '', $datareturn); // VALUE 72 | #echo "$lastval\n"; 73 | 74 | settype($lastval, 'float'); 75 | settype($prevval, 'float'); 76 | if ($lastval == 0) 77 | $lastval = $prevval; 78 | 79 | if (${'PASSO' . $metnum} > 0 && $lastval > ${'PASSO' . $metnum}) { // counter pass over 80 | $lastval -= ${'PASSO' . $metnum}; 81 | } 82 | $lastval = round($lastval, ${'PRECI' . $metnum}); 83 | $str = utf8_decode("${'ID'.$metnum}($lastval*${'UNIT'.$metnum})\n"); 84 | echo "$str"; 85 | 86 | ?> 87 | -------------------------------------------------------------------------------- /metern/comapps/pooler485.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ADDRESSES="$1" 4 | BAUD_RATE="$2" 5 | DEVICE="$3" 6 | 7 | ADDR_ARR=$(echo $ADDRESSES | tr "," "\n") 8 | 9 | while [ true ]; do 10 | 11 | ID=0 12 | POWER="" 13 | ENERGY="" 14 | 15 | for ADDRESS in $ADDR_ARR 16 | do 17 | #((ID++)) 18 | ID=$ADDRESS 19 | CMD="sdm120c -a ${ADDRESS} -b ${BAUD_RATE} -z 10 -i -p -v -c -f -g -P E -q ${DEVICE}" 20 | 21 | #echo $CMD 22 | 23 | VALUE=`$CMD` 24 | 25 | VOLTAGE=$(echo ${VALUE} | awk '{print $1}') 26 | CURRENT=$(echo ${VALUE} | awk '{print $2}') 27 | POWER=$(echo ${VALUE} | awk '{print $3}') 28 | FACTOR=$(echo ${VALUE} | awk '{print $4}') 29 | FREQUENCY=$(echo ${VALUE} | awk '{print $5}') 30 | ENERGY=$(echo ${VALUE} | awk '{print $6}') 31 | 32 | if [ "$ENERGY" != "0" -a x"$ENERGY" != x -a "$POWER" != "0" -a x"$POWER" != x ]; then 33 | echo -e "$ID($POWER*W)\n$ID($ENERGY*Wh)\n${ID}_1($VOLTAGE*V)\n${ID}_2($CURRENT*A)\n${ID}_3($FREQUENCY*Hz)\n${ID}_4($FACTOR*F)" > /run/shm/metern${ADDRESS}.txt 34 | #echo -e "$VALUE" > /run/shm/metern${ADDRESS}.txt 35 | fi 36 | sleep 5s 37 | 38 | done 39 | 40 | done 41 | -------------------------------------------------------------------------------- /metern/comapps/poolmeters485.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ADDRESS="$2" 4 | BAUD_RATE="$3" 5 | DEVICE="$4" 6 | 7 | TYPE="$1" 8 | 9 | if [ "$TYPE" == 'relect' ]; then 10 | 11 | VALUE_ENERGY=`sdm120c -a ${ADDRESS} -b ${BAUD_RATE} -i -m ${DEVICE}` 12 | echo ${VALUE_ENERGY} 13 | 14 | elif [ "$TYPE" == 'live' ]; then 15 | 16 | while [ true ]; do 17 | VALUE_LIVE=`sdm120c -a ${ADDRESS} -b ${BAUD_RATE} -p -m ${DEVICE}` 18 | #echo ${VALUE_LIVE} 19 | echo ${VALUE_LIVE} > /run/shm/metern${ADDRESS}.txt 20 | sleep 2s 21 | done 22 | 23 | fi 24 | -------------------------------------------------------------------------------- /sdm120c.c: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | /* 6 | * sdm120c: ModBus RTU client to read EASTRON SDM120C smart mini power meter registers 7 | * 8 | * Copyright (C) 2015 Gianfranco Di Prinzio9 | * 10 | * Locking code partially from aurora by Curtronis. 11 | * Some code by TheDrake too. :) 12 | * 13 | * This program is free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * You should have received a copy of the GNU General Public License 23 | * along with this program; if not, write to the Free Software 24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25 | */ 26 | 27 | // Enable checks for inter-lock problems debug 28 | #define CHECKFORGHOSTAPPEND 0 29 | #define CHECKFORCLEARLOCKRACE 0 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #if CHECKFORCLEARLOCKRACE 48 | #include 49 | #endif 50 | 51 | #include 52 | #include 53 | 54 | #define DEFAULT_RATE 2400 55 | 56 | #define MODEL_120 1 57 | #define MODEL_220 2 58 | 59 | // Read 60 | #define VOLTAGE 0x0000 61 | #define CURRENT 0x0006 62 | #define POWER 0x000C 63 | #define APOWER 0x0012 64 | #define RAPOWER 0x0018 65 | #define PFACTOR 0x001E 66 | #define PANGLE 0x0024 67 | #define FREQUENCY 0x0046 68 | #define IAENERGY 0x0048 69 | #define EAENERGY 0x004A 70 | #define IRAENERGY 0x004C 71 | #define ERAENERGY 0x004E 72 | #define TAENERGY 0x0156 73 | #define TRENERGY 0x0158 74 | 75 | // Write 76 | #define NPARSTOP 0x0012 77 | #define DEVICE_ID 0x0014 78 | #define BAUD_RATE 0x001C 79 | #define TIME_DISP_220 0xF500 80 | #define TIME_DISP 0xF900 81 | #define PULSE_OUT 0xF910 82 | #define TOT_MODE 0xF920 83 | 84 | #define BR1200 5 85 | #define BR2400 0 86 | #define BR4800 1 87 | #define BR9600 2 88 | 89 | #define MAX_RETRIES 100 90 | 91 | #define E_PARITY 'E' 92 | #define O_PARITY 'O' 93 | #define N_PARITY 'N' 94 | 95 | #define RESTART_TRUE 1 96 | #define RESTART_FALSE 0 97 | 98 | #define DEBUG_STDERR 1 99 | #define DEBUG_SYSLOG 2 100 | 101 | int debug_mask = 0; //DEBUG_STDERR | DEBUG_SYSLOG; // Default, let pass all 102 | int debug_flag = 0; 103 | int trace_flag = 0; 104 | 105 | int metern_flag = 0; 106 | 107 | const char *version = "1.3.5.6"; 108 | char *programName; 109 | const char *ttyLCKloc = "/var/lock/LCK.."; /* location and prefix of serial port lock file */ 110 | 111 | #define CMDLINESIZE 128 /* should be enough for debug */ 112 | char cmdline[CMDLINESIZE]=""; 113 | long unsigned int PID; 114 | 115 | long unsigned int PPID; 116 | char *PARENTCOMMAND = NULL; 117 | 118 | static int yLockWait = 0; /* Seconds to wait to lock serial port */ 119 | static time_t command_delay = -1; // = 30; /* MilliSeconds to wait before sending a command */ 120 | static time_t settle_time = -1; // us to wait line to settle before starting chat 121 | 122 | char *devLCKfile = NULL; 123 | char *devLCKfileNew = NULL; 124 | 125 | void usage(char* program) { 126 | printf("sdm120c %s: ModBus RTU client to read EASTRON SDM120C smart mini power meter registers\n",version); 127 | printf("Copyright (C) 2015 Gianfranco Di Prinzio \n"); 128 | printf("Complied with libmodbus %s\n\n", LIBMODBUS_VERSION_STRING); 129 | printf("Usage: %s [-a address] [-d n] [-x] [-p] [-v] [-c] [-e] [-i] [-t] [-f] [-g] [-T] [[-m]|[-q]] [-b baud_rate] [-P parity] [-S bit] [-z num_retries] [-j seconds] [-w seconds] [-1 | -2] device\n", program); 130 | printf(" %s [-a address] [-d n] [-x] [-b baud_rate] [-P parity] [-S bit] [-1 | -2] [-z num_retries] [-j seconds] [-w seconds] -s new_address device\n", program); 131 | printf(" %s [-a address] [-d n] [-x] [-b baud_rate] [-P parity] [-S bit] [-1 | -2] [-z num_retries] [-j seconds] [-w seconds] -r baud_rate device \n", program); 132 | printf(" %s [-a address] [-d n] [-x] [-b baud_rate] [-P parity] [-S bit] [-1 | -2] [-z num_retries] [-j seconds] [-w seconds] -N parity device \n", program); 133 | printf(" %s [-a address] [-d n] [-x] [-b baud_rate] [-P parity] [-S bit] [-1 | -2] [-z num_retries] [-j seconds] [-w seconds] -R new_time device \n", program); 134 | printf(" %s [-a address] [-d n] [-x] [-b baud_rate] [-P parity] [-S bit] [-1 | -2] [-z num_retries] [-j seconds] [-w seconds] -M new_mmode device \n", program); 135 | printf(" %s [-a address] [-d n] [-x] [-b baud_rate] [-P parity] [-S bit] [-1 | -2] [-z num_retries] [-j seconds] [-w seconds] -O new_pulse device\n\n", program); 136 | printf("Required:\n"); 137 | printf("\tdevice\t\tSerial device (i.e. /dev/ttyUSB0)\n"); 138 | printf("Connection parameters:\n"); 139 | printf("\t-a address \tMeter number (1-247). Default: 1\n"); 140 | printf("\t-b baud_rate \tUse baud_rate serial port speed (1200, 2400, 4800, 9600)\n"); 141 | printf("\t\t\tDefault: 2400\n"); 142 | printf("\t-P parity \tUse parity (E, N, O)\n"); 143 | printf("\t-S bit \t\tUse stop bits (1, 2). Default: 1\n"); 144 | printf("\t-1 \t\tModel: SDM120C (default)\n"); 145 | printf("\t-2 \t\tModel: SDM220\n"); 146 | printf("Reading parameters (no parameter = retrieves all values):\n"); 147 | printf("\t-p \t\tGet power (W)\n"); 148 | printf("\t-v \t\tGet voltage (V)\n"); 149 | printf("\t-c \t\tGet current (A)\n"); 150 | printf("\t-l \t\tGet apparent power (VA)\n"); 151 | printf("\t-n \t\tGet reactive power (VAR)\n"); 152 | printf("\t-f \t\tGet frequency (Hz)\n"); 153 | printf("\t-o \t\tGet phase angle (Degree)\n"); 154 | printf("\t-g \t\tGet power factor\n"); 155 | printf("\t-i \t\tGet imported energy (Wh)\n"); 156 | printf("\t-e \t\tGet exported energy (Wh)\n"); 157 | printf("\t-t \t\tGet total energy (Wh)\n"); 158 | printf("\t-A \t\tGet imported reactive energy (VARh)\n"); 159 | printf("\t-B \t\tGet exported reactive energy (VARh)\n"); 160 | printf("\t-C \t\tGet total reactive energy (VARh)\n"); 161 | printf("\t-T \t\tGet Time for rotating display values (0=no rotation)\n"); 162 | printf("\t-m \t\tOutput values in IEC 62056 format ID(VALUE*UNIT)\n"); 163 | printf("\t-q \t\tOutput values in compact mode\n"); 164 | printf("Writing new settings parameters:\n"); 165 | printf("\t-s new_address \tSet new meter number (1-247)\n"); 166 | printf("\t-r baud_rate \tSet baud_rate meter speed (1200, 2400, 4800, 9600)\n"); 167 | printf("\t-N parity \tSet parity and stop bits (0-3)\n"); 168 | printf("\t\t\t0: N1, 1: E1, 2: O1, 3: N2\n"); 169 | printf("\t-R new_time \tSet rotation time for displaying values (0=no rotation)\n"); 170 | printf("\t\t\tSDM120: (0-30s)\n"); 171 | printf("\t\t\tSDM220: (m-m-s-m) Demand interval, Slide time, Scroll time, Backlight time\n"); 172 | printf("\t-M new_mmode \tSet total energy measurement mode (1-3)\n"); 173 | printf("\t\t\t1: Total=Import, 2: Total=Import+Export, 3: Total=Import-Export\n"); 174 | printf("\t-O new_pulse \tSet Pulse 1 output (0-3)\n"); 175 | printf("\t\t\t0: 0.001kWh/imp(default), 1: 0.01kWh/imp, 2: 0.1kWh/imp, 3: 1kWh/imp\n"); 176 | printf("Fine tuning & debug parameters:\n"); 177 | printf("\t-z num_retries\tTry to read max num_retries times on bus before exiting\n"); 178 | printf("\t\t\twith error. Default: 1 (no retry)\n"); 179 | printf("\t-j 1/10 secs\tResponse timeout. Default: 2=0.2s\n"); 180 | printf("\t-D 1/1000 secs\tDelay before sending commands. Default: 0ms\n"); 181 | printf("\t-w seconds\tTime to wait to lock serial port (1-30s). Default: 0s\n"); 182 | printf("\t-W 1/1000 secs\tTime to wait for 485 line to settle. Default: 0ms\n"); 183 | printf("\t-y 1/1000 secs\tSet timeout between every bytes (1-500). Default: disabled\n"); 184 | printf("\t-d debug_level\tDebug (0=disable, 1=debug, 2=errors to syslog, 3=both)\n"); 185 | printf("\t\t\tDefault: 0\n"); 186 | printf("\t-x \t\tTrace (libmodbus debug on)\n"); 187 | } 188 | 189 | /*-------------------------------------------------------------------------- 190 | tv_diff 191 | ----------------------------------------------------------------------------*/ 192 | static long inline tv_diff(struct timeval const * const t1, struct timeval const * const t2) 193 | { 194 | struct timeval res; 195 | timersub(t1, t2, &res); 196 | return res.tv_sec*1000000 + res.tv_usec; 197 | } 198 | 199 | /*-------------------------------------------------------------------------- 200 | rnd_usleep 201 | ----------------------------------------------------------------------------*/ 202 | static long inline rnd_usleep(const useconds_t usecs) 203 | { 204 | long unsigned rnd10 = 10.0*rand()/(RAND_MAX+1.0) + 1; 205 | if (usleep(usecs*rnd10) == 0) 206 | return usecs*rnd10; 207 | else 208 | return -1; 209 | } 210 | 211 | /*-------------------------------------------------------------------------- 212 | getCurTime 213 | ----------------------------------------------------------------------------*/ 214 | char* getCurTime() 215 | { 216 | time_t curTimeValue; 217 | struct tm *ltime; 218 | static struct timeval _t; 219 | static struct timezone tz; 220 | static char CurTime[100]; 221 | 222 | time(&curTimeValue); 223 | ltime = (struct tm *) localtime(&curTimeValue); 224 | gettimeofday(&_t, &tz); 225 | 226 | sprintf(CurTime, "%04d%02d%02d-%02d:%02d:%02d.%06d", ltime->tm_year + 1900, ltime->tm_mon + 1, ltime->tm_mday, ltime->tm_hour, ltime->tm_min, ltime->tm_sec, (int)_t.tv_usec); 227 | 228 | return CurTime; 229 | } 230 | 231 | /*-------------------------------------------------------------------------- 232 | getCmdLine 233 | ----------------------------------------------------------------------------*/ 234 | void getCmdLine() 235 | { 236 | int fd = open("/proc/self/cmdline", O_RDONLY); 237 | int nbytesread = read(fd, cmdline, CMDLINESIZE); 238 | char *p; 239 | if (nbytesread>0) { 240 | for (p=cmdline; p < cmdline+nbytesread; p++) if (*p=='\0') *p=' '; 241 | cmdline[nbytesread-1]='\0'; 242 | } else 243 | cmdline[0]='\0'; 244 | close(fd); 245 | } 246 | 247 | /*-------------------------------------------------------------------------- 248 | log_message 249 | ----------------------------------------------------------------------------*/ 250 | void log_message(const int log, const char* format, ...) { 251 | va_list args; 252 | char buffer[1024]; 253 | static int bCmdlineSyslogged = 0; 254 | 255 | if (log) { 256 | va_start(args, format); 257 | vsnprintf(buffer, 1024, format, args); 258 | va_end(args); 259 | } 260 | 261 | if (log & debug_mask & DEBUG_STDERR) { 262 | fprintf(stderr, "%s: %s(%lu) ", getCurTime(), programName, PID); 263 | fprintf(stderr, "%s", buffer); 264 | fprintf(stderr, "\n"); 265 | } 266 | 267 | if (log & debug_mask & DEBUG_SYSLOG) { 268 | openlog("sdm120c", LOG_PID|LOG_CONS, LOG_USER); 269 | if (!bCmdlineSyslogged) { 270 | char versionbuffer[strlen(programName)+strlen(version)+3]; 271 | snprintf(versionbuffer, strlen(programName)+strlen(version)+3, "%s v%s", programName, version); 272 | syslog(LOG_INFO, "%s", versionbuffer); 273 | char parent[80]; 274 | snprintf(parent, sizeof(parent), "parent: %s(%lu)", PARENTCOMMAND, PPID); 275 | syslog(LOG_INFO, "%s", parent); 276 | syslog(LOG_INFO, "%s", cmdline); 277 | bCmdlineSyslogged++; 278 | } 279 | syslog(LOG_INFO, buffer); 280 | closelog(); 281 | } 282 | } 283 | 284 | /*-------------------------------------------------------------------------- 285 | getMemPtr 286 | ----------------------------------------------------------------------------*/ 287 | void *getMemPtr(size_t mSize) 288 | { 289 | void *ptr; 290 | 291 | ptr = calloc(sizeof(char),mSize); 292 | if (!ptr) { 293 | log_message(debug_flag | LOG_SYSLOG, "malloc failed"); 294 | exit(2); 295 | } 296 | //cptr = (char *)ptr; 297 | //for (i = 0; i < mSize; i++) cptr[i] = '\0'; 298 | return ptr; 299 | } 300 | 301 | /*-------------------------------------------------------------------------- 302 | ClrSerLock 303 | Clear Serial Port lock. 304 | ----------------------------------------------------------------------------*/ 305 | int ClrSerLock(long unsigned int LckPID) { 306 | FILE *fdserlck, *fdserlcknew; 307 | long unsigned int PID; 308 | int bWrite, bRead; 309 | int errno_save = 0; 310 | int fLen = 0; 311 | int cmdLen = 0; 312 | int curChar = 0; 313 | char *COMMAND = NULL; 314 | 315 | errno = 0; 316 | log_message(debug_flag, "devLCKfile: <%s>", devLCKfile); 317 | log_message(debug_flag, "devLCKfileNew: <%s> ", devLCKfileNew); 318 | log_message(debug_flag, "Clearing Serial Port Lock (%lu)...", LckPID); 319 | 320 | fdserlck = fopen(devLCKfile, "r"); 321 | if (fdserlck == NULL) { 322 | log_message(debug_flag | DEBUG_SYSLOG, "Problem opening serial device lock file to clear PID %lu: %s for read.",LckPID,devLCKfile); 323 | return(0); 324 | } 325 | log_message(debug_flag, "Acquiring exclusive lock on %s...",devLCKfile); 326 | flock(fileno(fdserlck), LOCK_EX); // Will wait to acquire lock then continue 327 | log_message(debug_flag, "Exclusive lock on %s acquired (%d) %s...",devLCKfile, errno, strerror(errno)); 328 | 329 | #if CHECKFORCLEARLOCKRACE 330 | 331 | // Check for potential conflicts 332 | glob_t globbuf; 333 | int iGlob, fGlob = TRUE; 334 | 335 | log_message(debug_flag, "GlobCheck - Check to avoid simultaneous PID clearing"); 336 | for (iGlob=5; iGlob>0 && fGlob; iGlob--) { 337 | fGlob = FALSE; 338 | if (glob("/var/lock/LCK..ttyUSB0.*", GLOB_NOSORT, NULL, &globbuf) != GLOB_NOMATCH) { 339 | log_message(debug_flag | DEBUG_SYSLOG, "GlobCheck (%u), some other process is clearing lock too!!! (%s)",iGlob, globbuf.gl_pathv[0]); 340 | fGlob=TRUE; 341 | log_message(debug_flag, "Sleeping %ldus", rnd_usleep(500000)); 342 | } 343 | globfree(&globbuf); 344 | } 345 | 346 | #endif 347 | 348 | fdserlcknew = fopen(devLCKfileNew, "a"); 349 | if (fdserlcknew == NULL) { 350 | log_message(debug_flag | DEBUG_SYSLOG, "Problem opening new serial device lock file to clear PID %lu: %s for write.",LckPID,devLCKfileNew); 351 | fclose(fdserlck); 352 | return(0); 353 | } 354 | 355 | // Find cmdLen max len in file 356 | curChar = 0; 357 | while (curChar != EOF) { 358 | fLen = 0; 359 | while ((curChar = fgetc(fdserlck)) != EOF && curChar != '\n' && curChar != ' ') fLen++; 360 | if (curChar == ' ') { 361 | fLen = 0; 362 | while ((curChar = fgetc(fdserlck)) != EOF && curChar != '\n') fLen++; 363 | if (fLen > cmdLen) cmdLen = fLen; 364 | } 365 | } 366 | rewind(fdserlck); 367 | 368 | log_message(debug_flag, "cmdLen=%i", cmdLen); 369 | COMMAND = getMemPtr(cmdLen+1); 370 | log_message(debug_flag, "cmdLen=%i COMMAND %s", cmdLen, (COMMAND==NULL ? "is null" : "is not null")); 371 | COMMAND[0] = '\0'; PID = 0; 372 | errno = 0; 373 | bRead = fscanf(fdserlck, "%lu%*[ ]%[^\n]\n", &PID, COMMAND); 374 | errno_save = errno; 375 | log_message(debug_flag, "errno=%i, bRead=%i LckPID=%lu PID=%lu COMMAND='%s'", errno_save, bRead, LckPID, PID, COMMAND); 376 | 377 | while (bRead != EOF && bRead > 0) { 378 | if (PID != LckPID) { 379 | errno = 0; 380 | if (COMMAND[0] != '\0') { 381 | bWrite = fprintf(fdserlcknew, "%lu %s\n", PID, COMMAND); 382 | errno_save = errno; 383 | } else { 384 | bWrite = fprintf(fdserlcknew, "%lu\n", PID); 385 | errno_save = errno; 386 | } 387 | log_message(debug_flag, "errno=%i, bWrite=%i PID=%lu", errno, bWrite, PID); 388 | if (bWrite < 0 || errno_save != 0) { 389 | log_message(debug_flag | DEBUG_SYSLOG, "Problem clearing serial device lock, can't write lock file: %s. %s",devLCKfile,strerror(errno_save)); 390 | log_message(debug_flag | DEBUG_SYSLOG, "(%u) %s",errno_save,strerror(errno_save)); 391 | fclose(fdserlcknew); 392 | return(0); 393 | } 394 | } 395 | errno=0; PID=0; COMMAND[0] = '\0'; 396 | bRead = fscanf(fdserlck, "%lu%*[ ]%[^\n]\n", &PID, COMMAND); 397 | errno_save = errno; 398 | log_message(debug_flag, "errno=%i, bRead=%i LckPID=%lu PID=%lu COMMAND='%s'", errno_save, bRead, LckPID, PID, COMMAND); 399 | } 400 | 401 | fflush(fdserlcknew); 402 | 403 | errno = 0; 404 | if (rename(devLCKfileNew,devLCKfile)) { 405 | log_message(debug_flag | DEBUG_SYSLOG, "Problem clearing serial device lock, can't update lock file: %s.",devLCKfile); 406 | log_message(debug_flag | DEBUG_SYSLOG, "(%d) %s", errno, strerror(errno)); 407 | } 408 | 409 | #if CHECKFORGHOSTAPPEND 410 | 411 | log_message(debug_flag, "Clearing Serial Port Lock almost done..."); 412 | log_message(debug_flag, "Sleeping %luus", rnd_usleep(10000)); 413 | 414 | // Check for latest appends (ghost appends) 415 | int iGhost=10; 416 | bRead = fscanf(fdserlck, "%lu%*[ ]%*[^\n]\n", &PID); 417 | while (iGhost > 0) { 418 | if (bRead > 0) { 419 | log_message(debug_flag | DEBUG_SYSLOG, "Found ghost append (%d): %s. %lu",iGhost,devLCKfile,PID); 420 | errno = 0; 421 | bWrite = fprintf(fdserlcknew, "%lu\n", PID); 422 | errno_save = errno; 423 | if (bWrite < 0 || errno_save != 0) { 424 | log_message(debug_flag | DEBUG_SYSLOG, "Problem clearing serial device lock, can't write lock file: %s. %s",devLCKfile,strerror (errno_save)); 425 | log_message(debug_flag | DEBUG_SYSLOG, "(%u) %s", errno_save, strerror(errno_save)); 426 | fclose(fdserlcknew); 427 | return(0); 428 | } 429 | } 430 | fflush(fdserlcknew); 431 | log_message(debug_flag, "Sleeping %ldus", rnd_usleep(10000)); 432 | iGhost--; PID=0; 433 | bRead = fscanf(fdserlck, "%lu%*[ ]%*[^\n]\n", &PID); 434 | } 435 | 436 | #endif 437 | 438 | fclose(fdserlck); 439 | fclose(fdserlcknew); 440 | free(COMMAND); 441 | 442 | log_message(debug_flag, "Clearing Serial Port Lock done"); 443 | 444 | return -1; 445 | } 446 | 447 | /*-------------------------------------------------------------------------- 448 | AddSerLock 449 | Queue Serial Port lock intent. 450 | ----------------------------------------------------------------------------*/ 451 | void AddSerLock(const char *szttyDevice, const char *devLCKfile, const long unsigned int PID, char *COMMAND, const int debug_flag) { 452 | FILE *fdserlck; 453 | int bWrite; 454 | int errno_save = 0; 455 | 456 | log_message(debug_flag, "Attempting to get lock on Serial Port %s...",szttyDevice); 457 | do { 458 | fdserlck = fopen((const char *)devLCKfile, "a"); 459 | if (fdserlck == NULL) { 460 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "Problem locking serial device, can't open lock file: %s for write.",devLCKfile); 461 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "Check owner and execution permission for '%s', they shoud be root '-rws--x--x'.",programName); 462 | exit(2); 463 | } 464 | log_message(debug_flag, "Acquiring shared lock on %s...",devLCKfile); 465 | errno = 0; 466 | if (flock(fileno(fdserlck), LOCK_SH | LOCK_NB) == 0) break; // Lock Acquired 467 | errno_save=errno; 468 | 469 | if (errno_save == EWOULDBLOCK) { 470 | log_message(debug_flag, "Would block %s, retry (%d) %s...", devLCKfile, errno_save, strerror(errno_save)); 471 | rnd_usleep(25000); 472 | fclose(fdserlck); 473 | } else { 474 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "Problem locking serial device, can't open lock file: %s for write. (%d) %s", devLCKfile, errno_save, strerror(errno_save)); 475 | exit(2); 476 | } 477 | } while (errno_save == EWOULDBLOCK); 478 | log_message(debug_flag, "Shared lock on %s acquired...",devLCKfile); 479 | 480 | errno=0; 481 | bWrite = fprintf(fdserlck, "%lu %s\n", PID, COMMAND); 482 | errno_save = errno; 483 | fflush(fdserlck); 484 | fclose(fdserlck); // Will release lock 485 | //fdserlck = NULL; 486 | if (bWrite < 0 || errno_save != 0) { 487 | log_message(debug_flag | DEBUG_SYSLOG, "Problem locking serial device, can't write lock file: %s.", devLCKfile); 488 | log_message(debug_flag | DEBUG_SYSLOG, "(%u) %s", devLCKfile, errno_save, strerror(errno_save)); 489 | exit(2); 490 | } 491 | } 492 | 493 | void exit_error(modbus_t *ctx) 494 | { 495 | /* 496 | // Wait for line settle 497 | log_message(debug_flag, "Sleeping %dms for line settle...", settle_time); 498 | usleep(1000 * settle_time); 499 | log_message(debug_flag, "Flushed %d bytes", modbus_flush(ctx)); 500 | */ 501 | modbus_close(ctx); 502 | modbus_free(ctx); 503 | ClrSerLock(PID); 504 | free(devLCKfile); 505 | free(devLCKfileNew); 506 | if (!metern_flag) { 507 | printf("NOK\n"); 508 | log_message(debug_flag | DEBUG_SYSLOG, "NOK"); 509 | } 510 | free(PARENTCOMMAND); 511 | exit(EXIT_FAILURE); 512 | } 513 | 514 | inline int bcd2int(int val) 515 | { 516 | return((((val & 0xf0) >> 4) * 10) + (val & 0xf)); 517 | } 518 | 519 | int int2bcd(int val) 520 | { 521 | return(((val / 10) << 4) + (val % 10)); 522 | } 523 | 524 | int bcd2num(const uint16_t *src, int len) 525 | { 526 | int n = 0; 527 | int m = 1; 528 | int i = 0; 529 | int shift = 0; 530 | int digit = 0; 531 | int j = 0; 532 | for (i = 0; i < len; i++) { 533 | for (j = 0; j < 4; j++) { 534 | digit = ((src[len-1-i]>>shift) & 0x0F) * m; 535 | n += digit; 536 | m *= 10; 537 | shift += 4; 538 | } 539 | } 540 | return n; 541 | } 542 | 543 | #if 0 544 | 545 | // unused 546 | 547 | int getMeasureBCD(modbus_t *ctx, int address, int retries, int nb) { 548 | 549 | uint16_t tab_reg[nb * sizeof(uint16_t)]; 550 | int rc; 551 | int i; 552 | int j = 0; 553 | int exit_loop = 0; 554 | 555 | while (j < retries && exit_loop == 0) { 556 | j++; 557 | 558 | if (command_delay) { 559 | log_message(debug_flag, "Sleeping command delay: %ldus", command_delay); 560 | usleep(command_delay); 561 | } 562 | 563 | log_message(debug_flag, "%d/%d. Register Address %d [%04X]", j, retries, 30000+address+1, address); 564 | rc = modbus_read_input_registers(ctx, address, nb, tab_reg); // will wait response_timeout for a reply 565 | 566 | if (rc == -1) { 567 | log_message(debug_flag | ( j==retries ? DEBUG_SYSLOG : 0), "%s: ERROR (%d) %s, %d/%d, Address %d [%04X]", errno, modbus_strerror(errno), j, retries, 30000+address+1, address); 568 | /* libmodbus already flushes 569 | log_message(debug_flag, "Flushed %d bytes", modbus_flush(ctx)); 570 | */ 571 | if (command_delay) { 572 | log_message(debug_flag, "Sleeping command delay: %ldus", command_delay); 573 | usleep(command_delay); 574 | } 575 | } else { 576 | exit_loop = 1; 577 | } 578 | } 579 | 580 | if (rc == -1) { 581 | exit_error(ctx); 582 | } 583 | 584 | if (debug_flag) { 585 | for (i=0; i < rc; i++) { 586 | log_message(debug_flag, "reg[%d/%d]=%d (0x%X)", i, (rc-1), tab_reg[i], tab_reg[i]); 587 | } 588 | } 589 | 590 | int value = bcd2num(&tab_reg[0], rc); 591 | 592 | return value; 593 | } 594 | 595 | #endif 596 | 597 | float getMeasureFloat(modbus_t *ctx, int address, int retries, int nb) { 598 | 599 | uint16_t tab_reg[nb * sizeof(uint16_t)]; 600 | int rc = -1; 601 | int i; 602 | int j = 0; 603 | int exit_loop = 0; 604 | int errno_save=0; 605 | struct timeval tvStart, tvStop; 606 | 607 | while (j < retries && exit_loop == 0) { 608 | j++; 609 | 610 | if (command_delay) { 611 | log_message(debug_flag, "Sleeping command delay: %ldus", command_delay); 612 | usleep(command_delay); 613 | } 614 | 615 | log_message(debug_flag, "%d/%d. Register Address %d [%04X]", j, retries, 30000+address+1, address); 616 | gettimeofday(&tvStart, NULL); 617 | rc = modbus_read_input_registers(ctx, address, nb, tab_reg); 618 | errno_save = errno; 619 | gettimeofday(&tvStop, NULL); 620 | 621 | if (rc == -1) { 622 | if (trace_flag) fprintf(stderr, "%s: ERROR (%d) %s, %d/%d\n", programName, errno_save, modbus_strerror(errno_save), j, retries); 623 | log_message(debug_flag | ( j==retries ? DEBUG_SYSLOG : 0), "ERROR (%d) %s, %d/%d, Address %d [%04X]", errno_save, modbus_strerror(errno_save), j, retries, 30000+address+1, address); 624 | log_message(debug_flag | ( j==retries ? DEBUG_SYSLOG : 0), "Response timeout gave up after %ldus", tv_diff(&tvStop, &tvStart)); 625 | /* libmodbus already flushes 626 | log_message(debug_flag, "Flushing modbus buffer"); 627 | log_message(debug_flag, "Flushed %d bytes", modbus_flush(ctx)); 628 | */ 629 | if (command_delay) { 630 | log_message(debug_flag, "Sleeping command delay: %ldus", command_delay); 631 | usleep(command_delay); 632 | } 633 | } else { 634 | log_message(debug_flag, "Read time: %ldus", tv_diff(&tvStop, &tvStart)); 635 | exit_loop = 1; 636 | } 637 | 638 | } 639 | 640 | if (rc == -1) { 641 | exit_error(ctx); 642 | } 643 | 644 | if (debug_flag) { 645 | for (i=0; i < rc; i++) { 646 | log_message(debug_flag, "reg[%d/%d]=%d (0x%X)", i, (rc-1), tab_reg[i], tab_reg[i]); 647 | } 648 | } 649 | 650 | // swap LSB and MSB 651 | uint16_t tmp1 = tab_reg[0]; 652 | uint16_t tmp2 = tab_reg[1]; 653 | tab_reg[0] = tmp2; 654 | tab_reg[1] = tmp1; 655 | 656 | float value = modbus_get_float(&tab_reg[0]); 657 | 658 | return value; 659 | 660 | } 661 | 662 | int getConfigBCD(modbus_t *ctx, int address, int retries, int nb) { 663 | 664 | uint16_t tab_reg[nb * sizeof(uint16_t)]; 665 | int rc = -1; 666 | int i; 667 | int j = 0; 668 | int exit_loop = 0; 669 | 670 | while (j < retries && exit_loop == 0) { 671 | j++; 672 | 673 | if (command_delay) { 674 | log_message(debug_flag, "Sleeping command delay: %ldus", command_delay); 675 | usleep(command_delay); 676 | } 677 | 678 | log_message(debug_flag, "%d/%d. Register Address %d [%04X]", j, retries, 400000+address+1, address); 679 | rc = modbus_read_registers(ctx, address, nb, tab_reg); 680 | 681 | if (rc == -1) { 682 | log_message(debug_flag | ( j==retries ? DEBUG_SYSLOG : 0), "ERROR (%d) %s, %d/%d, Address %d [%04X]", errno, modbus_strerror(errno), j, retries, 30000+address+1, address); 683 | /* libmodbus already flushes 684 | log_message(debug_flag, "Flushing modbus buffer"); 685 | log_message(debug_flag, "Flushed %d bytes", modbus_flush(ctx)); 686 | */ 687 | if (command_delay) { 688 | log_message(debug_flag, "Sleeping command delay: %ldus", command_delay); 689 | usleep(command_delay); 690 | } 691 | } else { 692 | exit_loop = 1; 693 | } 694 | } 695 | 696 | if (rc == -1) { 697 | exit_error(ctx); 698 | } 699 | 700 | if (debug_flag) { 701 | for (i=0; i < rc; i++) { 702 | log_message(debug_flag, "reg[%d/%d]=%d (0x%X)", i, (rc-1), tab_reg[i], tab_reg[i]); 703 | } 704 | } 705 | 706 | int value = bcd2num(&tab_reg[0], rc); 707 | 708 | return value; 709 | 710 | } 711 | 712 | void changeConfigHex(modbus_t *ctx, int address, int new_value, int restart) 713 | { 714 | uint16_t tab_reg[1]; 715 | tab_reg[0] = new_value; 716 | 717 | if (command_delay) { 718 | log_message(debug_flag, "Sleeping command delay: %ldus", command_delay); 719 | usleep(command_delay); 720 | } 721 | 722 | int n = modbus_write_registers(ctx, address, 1, tab_reg); 723 | if (n != -1) { 724 | printf("New value %d for address 0x%X\n", new_value, address); 725 | if (restart == RESTART_TRUE) printf("You have to restart the meter for apply changes\n"); 726 | } else { 727 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "error 1: (%d) %s, %d, %d", errno, modbus_strerror(errno), n); 728 | if (errno == EMBXILFUN) // Illegal function 729 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "Tip: is the meter in set mode?"); 730 | exit_error(ctx); 731 | } 732 | } 733 | 734 | void changeConfigFloat(modbus_t *ctx, int address, int new_value, int restart, int nb) 735 | { 736 | uint16_t tab_reg[nb * sizeof(uint16_t)]; 737 | 738 | modbus_set_float((float) new_value, &tab_reg[0]); 739 | // swap LSB and MSB 740 | uint16_t tmp1 = tab_reg[0]; 741 | uint16_t tmp2 = tab_reg[1]; 742 | tab_reg[0] = tmp2; 743 | tab_reg[1] = tmp1; 744 | 745 | if (command_delay) { 746 | log_message(debug_flag, "Sleeping command delay: %ldus", command_delay); 747 | usleep(command_delay); 748 | } 749 | 750 | int n = modbus_write_registers(ctx, address, nb, tab_reg); 751 | if (n != -1) { 752 | printf("New value %d for address 0x%X\n", new_value, address); 753 | if (restart == RESTART_TRUE) printf("You have to restart the meter for apply changes\n"); 754 | } else { 755 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "error 2: (%d) %s, %d, %d", errno, modbus_strerror(errno), n); 756 | if (errno == EMBXILFUN) // Illegal function 757 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "Tip: is the meter in set mode?"); 758 | exit_error(ctx); 759 | } 760 | } 761 | 762 | void changeConfigBCD(modbus_t *ctx, int address, int new_value, int restart, int nb) 763 | { 764 | uint16_t tab_reg[nb * sizeof(uint16_t)]; 765 | uint16_t u_new_value = int2bcd(new_value); 766 | tab_reg[0] = u_new_value; 767 | 768 | if (command_delay) { 769 | log_message(debug_flag, "Sleeping command delay: %ldus", command_delay); 770 | usleep(command_delay); 771 | } 772 | 773 | int n = modbus_write_registers(ctx, address, nb, tab_reg); 774 | if (n != -1) { 775 | printf("New value %d for address 0x%X\n", u_new_value, address); 776 | if (restart == RESTART_TRUE) printf("You have to restart the meter for apply changes\n"); 777 | } else { 778 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "error 3: (%d) %s, %d, %d", errno, modbus_strerror(errno), n); 779 | if (errno == EMBXILFUN) // Illegal function 780 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "Tip: is the meter in set mode?"); 781 | exit_error(ctx); 782 | } 783 | } 784 | 785 | /*-------------------------------------------------------------------------- 786 | getIntLen 787 | ----------------------------------------------------------------------------*/ 788 | int getIntLen(long value){ 789 | long l=!value; 790 | while(value) { l++; value/=10; } 791 | return l; 792 | } 793 | 794 | /*-------------------------------------------------------------------------- 795 | getPIDcmd 796 | ----------------------------------------------------------------------------*/ 797 | void *getPIDcmd(long unsigned int PID) 798 | { 799 | int fdcmd; 800 | char *COMMAND = NULL; 801 | size_t cmdLen = 0; 802 | size_t length; 803 | char buffer[1024]; 804 | char cmdFilename[getIntLen(PID)+14+1]; 805 | 806 | // Generate the name of the cmdline file for the process 807 | *cmdFilename = '\0'; 808 | snprintf(cmdFilename,sizeof(cmdFilename),"/proc/%lu/cmdline",PID); 809 | 810 | // Read the contents of the file 811 | if ((fdcmd = open(cmdFilename, O_RDONLY)) < 0) return NULL; 812 | if ((length = read(fdcmd, buffer, sizeof(buffer))) <= 0) { 813 | close(fdcmd); return NULL; 814 | } 815 | close(fdcmd); 816 | 817 | // read does not NUL-terminate the buffer, so do it here 818 | buffer[length] = '\0'; 819 | // Get 1st string (command) 820 | cmdLen=strlen(buffer)+1; 821 | if((COMMAND = getMemPtr(cmdLen)) != NULL ) { 822 | strncpy(COMMAND, buffer, cmdLen); 823 | COMMAND[cmdLen-1] = '\0'; 824 | } 825 | 826 | return COMMAND; 827 | } 828 | 829 | /*-------------------------------------------------------------------------- 830 | lockSer 831 | ----------------------------------------------------------------------------*/ 832 | void lockSer(const char *szttyDevice, const long unsigned int PID, int debug_flag) 833 | { 834 | char *pos; 835 | FILE *fdserlck = NULL; 836 | char *COMMAND = NULL; 837 | long unsigned int LckPID; 838 | struct timeval tLockStart, tLockNow; 839 | int bRead; 840 | int errno_save = 0; 841 | int fLen = 0; 842 | int curChar = 0; 843 | char *LckCOMMAND = NULL; 844 | char *LckPIDcommand = NULL; 845 | 846 | pos = strrchr(szttyDevice, '/'); 847 | if (pos > 0) { 848 | pos++; 849 | devLCKfile = getMemPtr(strlen(ttyLCKloc)+(strlen(szttyDevice)-(pos-szttyDevice))+1); 850 | devLCKfile[0] = '\0'; 851 | strcpy(devLCKfile,ttyLCKloc); 852 | strcat(devLCKfile, pos); 853 | devLCKfile[strlen(devLCKfile)] = '\0'; 854 | devLCKfileNew = getMemPtr(strlen(devLCKfile)+getIntLen(PID)+2); /* dot & terminator */ 855 | devLCKfileNew[0] = '\0'; 856 | strcpy(devLCKfileNew,devLCKfile); 857 | sprintf(devLCKfileNew,"%s.%lu",devLCKfile,PID); 858 | devLCKfileNew[strlen(devLCKfileNew)] = '\0'; 859 | } else { 860 | devLCKfile = NULL; 861 | } 862 | 863 | log_message(debug_flag, "szttyDevice: %s",szttyDevice); 864 | log_message(debug_flag, "devLCKfile: <%s>",devLCKfile); 865 | log_message(debug_flag, "devLCKfileNew: <%s>",devLCKfileNew); 866 | log_message(debug_flag, "PID: %lu", PID); 867 | 868 | COMMAND = getPIDcmd(PID); 869 | AddSerLock(szttyDevice, devLCKfile, PID, COMMAND, debug_flag); 870 | 871 | LckPID = 0; 872 | long unsigned int oldLckPID = 0; 873 | int staleLockRetries = 0; 874 | int const staleLockRetriesMax = 2; 875 | long unsigned int clrStaleTargetPID = 0; 876 | int missingPidRetries = 0; 877 | int const missingPidRetriesMax = 2; 878 | 879 | gettimeofday(&tLockStart, NULL); 880 | tLockNow=tLockStart; 881 | 882 | if (debug_flag) log_message(debug_flag, "Checking for lock"); 883 | while(LckPID != PID && tv_diff(&tLockNow, &tLockStart) <= yLockWait*1000000L) { 884 | 885 | do { 886 | fdserlck = fopen(devLCKfile, "r"); 887 | if (fdserlck == NULL) { 888 | log_message(debug_flag | DEBUG_SYSLOG, "Problem locking serial device, can't open lock file: %s for read.",devLCKfile); 889 | exit(2); 890 | } 891 | //log_message(debug_flag, "Acquiring shared lock on %s...",devLCKfile); 892 | errno = 0; 893 | if (flock(fileno(fdserlck), LOCK_SH | LOCK_NB) == 0) break; // Lock Acquired 894 | errno_save=errno; 895 | 896 | if (errno_save == EWOULDBLOCK) { 897 | log_message(debug_flag, "Would block %s, retry (%d) %s...", devLCKfile, errno_save, strerror(errno_save)); 898 | rnd_usleep(25000); 899 | fclose(fdserlck); 900 | } else { 901 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "Problem locking serial device, can't open lock file: %s for read. (%d) %s", devLCKfile, errno_save, strerror(errno_save)); 902 | exit(2); 903 | } 904 | } while (errno_save == EWOULDBLOCK); 905 | //log_message(debug_flag, "Shared lock on %s acquired...",devLCKfile); 906 | 907 | fLen = 0; 908 | while ((curChar = fgetc(fdserlck)) != EOF && curChar != '\n' && curChar != ' ') fLen++; 909 | fLen = 0; 910 | if (curChar == ' ') while ((curChar = fgetc(fdserlck)) != EOF && curChar != '\n') fLen++; 911 | 912 | rewind(fdserlck); 913 | 914 | //if (LckPID != oldLckPID) log_message(debug_flag, "fLen=%i", fLen); 915 | LckCOMMAND = getMemPtr(fLen+1); 916 | //if (LckPID != oldLckPID) log_message(debug_flag, "fLen=%i LckCOMMAND %s", fLen, (LckCOMMAND==NULL ? "is null" : "is not null")); 917 | LckCOMMAND[0] = '\0'; 918 | LckPID=0; 919 | 920 | errno = 0; 921 | bRead = fscanf(fdserlck, "%lu%*[ ]%[^\n]\n", &LckPID, LckCOMMAND); 922 | errno_save = errno; 923 | fclose(fdserlck); 924 | if (LckPID != oldLckPID) { 925 | log_message(debug_flag | (bRead==EOF || errno_save != 0 ? DEBUG_SYSLOG : 0), "errno=%i, bRead=%i PID=%lu LckPID=%lu", errno_save, bRead, PID, LckPID); 926 | log_message(debug_flag, "Checking process %lu (%s) for lock", LckPID, LckCOMMAND); 927 | //oldLckPID = LckPID; 928 | } 929 | if (bRead == EOF || LckPID == 0 || errno_save != 0) { 930 | log_message(debug_flag | DEBUG_SYSLOG, "Problem locking serial device, can't read PID from lock file: %s.",devLCKfile); 931 | log_message(debug_flag | DEBUG_SYSLOG, "errno=%i, bRead=%i PID=%lu LckPID=%lu", errno_save, bRead, PID, LckPID); 932 | if (errno_save != 0) { 933 | // Real error 934 | log_message(debug_flag | DEBUG_SYSLOG, "(%u) %s", errno_save, strerror(errno_save)); 935 | free(LckCOMMAND); free(LckPIDcommand); free(COMMAND); 936 | exit(2); 937 | } else { 938 | if (missingPidRetries < missingPidRetriesMax) { 939 | missingPidRetries++; 940 | log_message(debug_flag, "%s miss process self PID from lock file?",devLCKfile); 941 | } else if (missingPidRetries >= missingPidRetriesMax) { 942 | // Self PID missing... (Should never happen) 943 | log_message(debug_flag | DEBUG_SYSLOG, "%s miss process self PID from lock file, amending.",devLCKfile); 944 | AddSerLock(szttyDevice, devLCKfile, PID, COMMAND, debug_flag); 945 | //LckPID=0; 946 | missingPidRetries = 0; 947 | } 948 | } 949 | oldLckPID = LckPID; 950 | } else { //fread OK 951 | 952 | // We got a pid from lockfile, let's clear missing pid status 953 | missingPidRetries = 0; 954 | 955 | LckPIDcommand = getPIDcmd(LckPID); 956 | 957 | if (LckPID != oldLckPID) { 958 | log_message(debug_flag, "PID: %lu COMMAND: \"%s\" LckPID: %lu LckCOMMAND: \"%s\" LckPIDcommand \"%s\"%s", PID, COMMAND 959 | , LckPID, LckCOMMAND, LckPIDcommand 960 | , LckPID == PID ? " = me" : ""); 961 | oldLckPID = LckPID; 962 | } 963 | 964 | // PID - this process 965 | // LckPID - PID from lock file 966 | // COMMAND - this process 967 | // LckCOMMAND - process command from lock file 968 | // LckPIDcommand - process command of process using PID from lock file 969 | if ((PID != LckPID && LckPIDcommand == NULL) || (LckCOMMAND[0]!='\0' && strcmp(LckPIDcommand,LckCOMMAND) != 0) || strcmp(LckPIDcommand,"") == 0) { 970 | // Is it a stale lock pid? 971 | if (staleLockRetries < staleLockRetriesMax) { 972 | staleLockRetries++; 973 | clrStaleTargetPID = LckPID; 974 | log_message(debug_flag | (staleLockRetries > 1 ? DEBUG_SYSLOG : 0), "Stale pid lock(%d)? PID=%lu, LckPID=%lu, LckCOMMAND='%s', LckPIDCommand='%s'", staleLockRetries, PID, LckPID, LckCOMMAND, LckPIDcommand); 975 | } else if (LckPID == clrStaleTargetPID && staleLockRetries >= staleLockRetriesMax) { 976 | log_message(debug_flag | DEBUG_SYSLOG, "Clearing stale serial port lock. (%lu)", LckPID); 977 | ClrSerLock(LckPID); 978 | staleLockRetries = 0; 979 | clrStaleTargetPID = 0; 980 | } 981 | } else { 982 | // Pid lock have a process running, let's reset stale pid retries 983 | staleLockRetries = 0; 984 | clrStaleTargetPID = 0; 985 | } 986 | } 987 | 988 | if (yLockWait > 0 && LckPID != PID) { 989 | rnd_usleep(25000); 990 | //log_message(debug_flag, "Sleeping %luus", rnd_usleep(25000)); 991 | } 992 | 993 | // Cleanup and loop 994 | if (LckCOMMAND != NULL) { 995 | free(LckCOMMAND); 996 | LckCOMMAND = NULL; 997 | } 998 | if (LckPIDcommand != NULL) { 999 | free(LckPIDcommand); 1000 | LckPIDcommand = NULL; 1001 | } 1002 | gettimeofday(&tLockNow, NULL); 1003 | } // while 1004 | free(COMMAND); 1005 | if (LckPID == PID) log_message(debug_flag, "Appears we got the lock."); 1006 | if (LckPID != PID) { 1007 | ClrSerLock(PID); 1008 | log_message(DEBUG_STDERR, "Problem locking serial device %s.",szttyDevice); 1009 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "Unable to get lock on serial %s for %lu in %ds: still locked by %lu.",szttyDevice,PID,(yLockWait)%30,LckPID); 1010 | log_message(DEBUG_STDERR, "Try a greater -w value (eg -w%u).", (yLockWait+2)%30); 1011 | free(devLCKfile); free(devLCKfileNew); free(PARENTCOMMAND); 1012 | exit(2); 1013 | } 1014 | } 1015 | 1016 | int main(int argc, char* argv[]) 1017 | { 1018 | int device_address = 1; 1019 | int model = MODEL_120; 1020 | int new_address = 0; 1021 | int power_flag = 0; 1022 | int volt_flag = 0; 1023 | int current_flag = 0; 1024 | int pangle_flag = 0; 1025 | int freq_flag = 0; 1026 | int pf_flag = 0; 1027 | int apower_flag = 0; 1028 | int rapower_flag = 0; 1029 | int export_flag = 0; 1030 | int import_flag = 0; 1031 | int total_flag = 0; 1032 | int rexport_flag = 0; 1033 | int rimport_flag = 0; 1034 | int rtotal_flag = 0; 1035 | int new_baud_rate = -1; 1036 | int new_parity_stop= -1; 1037 | int compact_flag = 0; 1038 | int time_disp_flag = 0; 1039 | int rotation_time_flag = 0; 1040 | int rotation_time = 0; 1041 | int measurement_mode_flag = 0; 1042 | int measurement_mode = 0; 1043 | int pulse_flag = 0; 1044 | int pulse_mode = 0; 1045 | int count_param = 0; 1046 | int num_retries = 1; 1047 | #if LIBMODBUS_VERSION_MAJOR >= 3 && LIBMODBUS_VERSION_MINOR >= 1 && LIBMODBUS_VERSION_MICRO >= 2 1048 | uint32_t resp_timeout = 2; 1049 | uint32_t byte_timeout = -1; 1050 | #else 1051 | time_t resp_timeout = 2; 1052 | time_t byte_timeout = -1; 1053 | #endif 1054 | char *szttyDevice = NULL; 1055 | 1056 | int c; 1057 | int speed = 0; 1058 | int bits = 0; 1059 | int read_count = 0; 1060 | 1061 | const char *EVEN_parity = "E"; 1062 | const char *NONE_parity = "N"; 1063 | const char *ODD_parity = "O"; 1064 | char *c_parity = NULL; 1065 | 1066 | int baud_rate = 0; 1067 | int stop_bits = 0; 1068 | char parity = E_PARITY; 1069 | 1070 | programName = argv[0]; 1071 | 1072 | if (argc == 1) { 1073 | usage(programName); 1074 | exit(EXIT_FAILURE); 1075 | } 1076 | 1077 | srand(getpid()^time(NULL)); // Init random numbers 1078 | 1079 | PID = getpid(); 1080 | getCmdLine(); 1081 | 1082 | PPID = getppid(); 1083 | PARENTCOMMAND = getPIDcmd(PPID); 1084 | 1085 | opterr = 0; 1086 | 1087 | while ((c = getopt (argc, argv, "a:Ab:BcCd:D:efgij:lmM:nN:oOpP:qr:R:s:S:tTvw:W:xy:z:12")) != -1) { 1088 | log_message(debug_flag | DEBUG_SYSLOG, "optind = %d, argc = %d, c = %c, optarg = %s", optind, argc, c, optarg); 1089 | 1090 | switch (c) 1091 | { 1092 | case 'a': 1093 | device_address = atoi(optarg); 1094 | 1095 | if (!(0 < device_address && device_address <= 247)) { 1096 | fprintf (stderr, "%s: Address must be between 1 and 247.\n", programName); 1097 | exit(EXIT_FAILURE); 1098 | } 1099 | log_message(debug_flag | DEBUG_SYSLOG, "device_address = %d", device_address); 1100 | break; 1101 | case 'v': 1102 | volt_flag = 1; 1103 | count_param++; 1104 | log_message(debug_flag | DEBUG_SYSLOG, "volt_flag = %d, count_param = %d", volt_flag, count_param); 1105 | break; 1106 | case 'p': 1107 | power_flag = 1; 1108 | count_param++; 1109 | log_message(debug_flag | DEBUG_SYSLOG, "power_flag = %d, count_param = %d", power_flag, count_param); 1110 | break; 1111 | case 'c': 1112 | current_flag = 1; 1113 | count_param++; 1114 | log_message(debug_flag | DEBUG_SYSLOG, "current_flag = %d, count_param = %d", current_flag, count_param); 1115 | break; 1116 | case 'e': 1117 | export_flag = 1; 1118 | count_param++; 1119 | log_message(debug_flag | DEBUG_SYSLOG, "export_flag = %d, count_param = %d", export_flag, count_param); 1120 | break; 1121 | case 'i': 1122 | import_flag = 1; 1123 | log_message(debug_flag | DEBUG_SYSLOG, "import_flag = %d, count_param = %d", import_flag, count_param); 1124 | count_param++; 1125 | break; 1126 | case 't': 1127 | total_flag = 1; 1128 | count_param++; 1129 | log_message(debug_flag | DEBUG_SYSLOG, "total_flag = %d, count_param = %d", total_flag, count_param); 1130 | break; 1131 | case 'A': 1132 | rimport_flag = 1; 1133 | count_param++; 1134 | log_message(debug_flag | DEBUG_SYSLOG, "rimport_flag = %d, count_param = %d", rimport_flag, count_param); 1135 | break; 1136 | case 'B': 1137 | rexport_flag = 1; 1138 | count_param++; 1139 | log_message(debug_flag | DEBUG_SYSLOG, "rexport_flag = %d, count_param = %d", rexport_flag, count_param); 1140 | break; 1141 | case 'C': 1142 | rtotal_flag = 1; 1143 | count_param++; 1144 | log_message(debug_flag | DEBUG_SYSLOG, "rtotal_flag = %d, count_param = %d", rtotal_flag, count_param); 1145 | break; 1146 | case 'f': 1147 | freq_flag = 1; 1148 | count_param++; 1149 | log_message(debug_flag | DEBUG_SYSLOG, "freq_flag = %d, count_param = %d", freq_flag, count_param); 1150 | break; 1151 | case 'g': 1152 | pf_flag = 1; 1153 | count_param++; 1154 | log_message(debug_flag | DEBUG_SYSLOG, "pf_flag = %d, count_param = %d", pf_flag, count_param); 1155 | break; 1156 | case 'l': 1157 | apower_flag = 1; 1158 | count_param++; 1159 | log_message(debug_flag | DEBUG_SYSLOG, "apower_flag = %d, count_param = %d", apower_flag, count_param); 1160 | break; 1161 | case 'n': 1162 | rapower_flag = 1; 1163 | log_message(debug_flag | DEBUG_SYSLOG, "rapower_flag = %d, count_param = %d", rapower_flag, count_param); 1164 | count_param++; 1165 | break; 1166 | case 'o': 1167 | pangle_flag = 1; 1168 | log_message(debug_flag | DEBUG_SYSLOG, "pangle_flag = %d, count_param = %d", pangle_flag, count_param); 1169 | count_param++; 1170 | break; 1171 | case 'd': 1172 | switch (*optarg) { 1173 | case '0': 1174 | case '1': 1175 | case '2': 1176 | case '3': 1177 | debug_flag = atoi(optarg) & DEBUG_STDERR; 1178 | debug_mask = atoi(optarg); 1179 | break; 1180 | default: 1181 | fprintf (stderr, "%s: Debug value must be one of 0,1,2,3.\n", programName); 1182 | exit(EXIT_FAILURE); 1183 | } 1184 | log_message(debug_flag | DEBUG_SYSLOG, "debug_flag = %d", debug_flag); 1185 | break; 1186 | case 'x': 1187 | trace_flag = 1; 1188 | log_message(debug_flag | DEBUG_SYSLOG, "trace_flag = %d, count_param = %d", trace_flag, count_param); 1189 | break; 1190 | case 'b': 1191 | speed = atoi(optarg); 1192 | if (speed == 1200 || speed == 2400 || speed == 4800 || speed == 9600) { 1193 | baud_rate = speed; 1194 | } else { 1195 | fprintf (stderr, "%s: Baud Rate must be one of 1200, 2400, 4800, 9600\n", programName); 1196 | exit(EXIT_FAILURE); 1197 | } 1198 | log_message(debug_flag | DEBUG_SYSLOG, "speed = %d, count_param = %d", speed, count_param); 1199 | break; 1200 | case 'P': 1201 | c_parity = strdup(optarg); 1202 | if (strcmp(c_parity,EVEN_parity) == 0) { 1203 | parity = E_PARITY; 1204 | } else if (strcmp(c_parity,NONE_parity) == 0) { 1205 | parity = N_PARITY; 1206 | } else if (strcmp(c_parity,ODD_parity) == 0) { 1207 | parity = O_PARITY; 1208 | } else { 1209 | fprintf (stderr, "%s: Parity must be one of E, N, O\n", programName); 1210 | exit(EXIT_FAILURE); 1211 | } 1212 | log_message(debug_flag | DEBUG_SYSLOG, "c_parity = %s, count_param = %d", c_parity, count_param); 1213 | free(c_parity); 1214 | break; 1215 | case 'S': 1216 | bits = atoi(optarg); 1217 | if (bits == 1 || bits == 2) { 1218 | stop_bits = bits; 1219 | } else { 1220 | fprintf (stderr, "%s: Stop bits can be one of 1, 2\n", programName); 1221 | exit(EXIT_FAILURE); 1222 | } 1223 | log_message(debug_flag | DEBUG_SYSLOG, "bits = %d, count_param = %d", bits, count_param); 1224 | break; 1225 | case 'r': 1226 | speed = atoi(optarg); 1227 | switch (speed) { 1228 | case 1200: 1229 | new_baud_rate = BR1200; 1230 | break; 1231 | case 2400: 1232 | new_baud_rate = BR2400; 1233 | break; 1234 | case 4800: 1235 | new_baud_rate = BR4800; 1236 | break; 1237 | case 9600: 1238 | new_baud_rate = BR9600; 1239 | break; 1240 | default: 1241 | fprintf (stderr, "%s: Baud Rate must be one of 1200, 2400, 4800, 9600\n", programName); 1242 | exit(EXIT_FAILURE); 1243 | } 1244 | log_message(debug_flag | DEBUG_SYSLOG, "new_baud_rate = %d, count_param = %d", new_baud_rate, count_param); 1245 | break; 1246 | case 'N': 1247 | new_parity_stop = atoi(optarg); 1248 | if (!(0 <= new_parity_stop && new_parity_stop <= 3)) { 1249 | fprintf (stderr, "%s: New parity/stop (%d) out of range, 0-3.\n", programName, new_parity_stop); 1250 | exit(EXIT_FAILURE); 1251 | } 1252 | log_message(debug_flag | DEBUG_SYSLOG, "new_parity_stop = %s, count_param = %d", new_parity_stop, count_param); 1253 | break; 1254 | case 's': 1255 | new_address = atoi(optarg); 1256 | if (!(0 < new_address && new_address <= 247)) { 1257 | fprintf (stderr, "%s: New address (%d) out of range, 1-247.\n", programName, new_address); 1258 | exit(EXIT_FAILURE); 1259 | } 1260 | log_message(debug_flag | DEBUG_SYSLOG, "new_address = %d, count_param = %d", new_address, count_param); 1261 | break; 1262 | case 'R': 1263 | rotation_time_flag = 1; 1264 | rotation_time = atoi(optarg); 1265 | 1266 | if (model == MODEL_120 && !(0 <= rotation_time && rotation_time <= 30)) { 1267 | fprintf (stderr, "%s: New rotation time (%d) out of range, 0-30.\n", programName, rotation_time); 1268 | exit(EXIT_FAILURE); 1269 | } else if (model == MODEL_220 && !(0 <= rotation_time && rotation_time <= 9999)) { 1270 | fprintf (stderr, "%s: SDM220 display time composite parameter (%d) out of range, 0-9999.\n", programName, rotation_time); 1271 | exit(EXIT_FAILURE); 1272 | } 1273 | log_message(debug_flag | DEBUG_SYSLOG, "rotation_time_flag = %d, rotation_time = %d, count_param = %d", rotation_time_flag, rotation_time, count_param); 1274 | break; 1275 | case 'M': 1276 | measurement_mode_flag = 1; 1277 | measurement_mode = atoi(optarg); 1278 | if (!(1 <= measurement_mode && measurement_mode <= 3)) { 1279 | fprintf (stderr, "%s: New measurement mode (%d) out of range, 1-3.\n", programName, rotation_time); 1280 | exit(EXIT_FAILURE); 1281 | } 1282 | log_message(debug_flag | DEBUG_SYSLOG, "measurement_mode_flag = %d, measurement_mode = %d, count_param = %d", measurement_mode_flag, measurement_mode, count_param); 1283 | break; 1284 | case 'O': 1285 | pulse_flag = 1; 1286 | pulse_mode = atoi(optarg); 1287 | if (!(0 <= pulse_mode && pulse_mode <= 3)) { 1288 | fprintf (stderr, "%s: New pulse mode (%d) out of range, 0-3.\n", programName, pulse_mode); 1289 | exit(EXIT_FAILURE); 1290 | } 1291 | log_message(debug_flag | DEBUG_SYSLOG, "pulse_flag = %d, rotation_mode = %d, count_param = %d", pulse_flag, pulse_mode, count_param); 1292 | break; 1293 | case '1': 1294 | model = MODEL_120; 1295 | log_message(debug_flag | DEBUG_SYSLOG, "model = %d, count_param = %d", model, count_param); 1296 | break; 1297 | case '2': 1298 | model = MODEL_220; 1299 | log_message(debug_flag | DEBUG_SYSLOG, "model = %d, count_param = %d", model, count_param); 1300 | break; 1301 | case 'm': 1302 | metern_flag = 1; 1303 | log_message(debug_flag | DEBUG_SYSLOG, "metern_flag = %d, count_param = %d", metern_flag, count_param); 1304 | break; 1305 | case 'q': 1306 | compact_flag = 1; 1307 | log_message(debug_flag | DEBUG_SYSLOG, "compact_flag = %d, count_param = %d", compact_flag, count_param); 1308 | break; 1309 | case 'z': 1310 | num_retries = atoi(optarg); 1311 | if (!(0 < num_retries && num_retries <= MAX_RETRIES)) { 1312 | fprintf (stderr, "%s: num_retries (%d) out of range, 1-%d.\n", programName, num_retries, MAX_RETRIES); 1313 | exit(EXIT_FAILURE); 1314 | } 1315 | log_message(debug_flag | DEBUG_SYSLOG, "num_retries = %d, count_param = %d", num_retries, count_param); 1316 | break; 1317 | case 'j': 1318 | resp_timeout = atoi(optarg); 1319 | if (resp_timeout < 1 || resp_timeout > 500) { 1320 | fprintf(stderr, "%s: -j Response timeout (%lu) out of range, 0-500.\n",programName,(long unsigned)resp_timeout); 1321 | exit(EXIT_FAILURE); 1322 | } 1323 | log_message(debug_flag | DEBUG_SYSLOG, "resp_timeout = %d, count_param = %d", resp_timeout, count_param); 1324 | break; 1325 | case 'y': 1326 | byte_timeout = atoi(optarg); 1327 | if (byte_timeout < 1 || byte_timeout > 500) { 1328 | fprintf(stderr, "%s: -y Byte timeout (%lu) out of range, 1-500.\n",programName,(long unsigned)byte_timeout); 1329 | exit(EXIT_FAILURE); 1330 | } 1331 | log_message(debug_flag | DEBUG_SYSLOG, "byte_timeout = %d, count_param = %d", byte_timeout, count_param); 1332 | break; 1333 | case 'w': 1334 | yLockWait = atoi(optarg); 1335 | if (yLockWait < 1 || yLockWait > 30) { 1336 | fprintf(stderr, "%s: -w Lock Wait seconds (%d) out of range, 1-30.\n",programName,yLockWait); 1337 | exit(EXIT_FAILURE); 1338 | } 1339 | log_message(debug_flag | DEBUG_SYSLOG, "yLockWait = %d, count_param = %d", yLockWait, count_param); 1340 | break; 1341 | case 'W': 1342 | settle_time = atoi(optarg); 1343 | log_message(debug_flag | DEBUG_SYSLOG, "settle_time = %d, count_param = %d", settle_time, count_param); 1344 | break; 1345 | case 'D': 1346 | command_delay = atoi(optarg); 1347 | log_message(debug_flag | DEBUG_SYSLOG, "command_delay = %d, count_param = %d", command_delay, count_param); 1348 | break; 1349 | case 'T': 1350 | time_disp_flag = 1; 1351 | count_param++; 1352 | log_message(debug_flag | DEBUG_SYSLOG, "time_disp_flag = %d, count_param = %d", time_disp_flag, count_param); 1353 | break; 1354 | case '?': 1355 | if (isprint (optopt)) { 1356 | fprintf (stderr, "%s: Unknown option `-%c'.\n", programName, optopt); 1357 | usage(programName); 1358 | exit(EXIT_FAILURE); 1359 | } else { 1360 | fprintf (stderr,"%s: Unknown option character `\\x%x'.\n",programName, optopt); 1361 | usage(programName); 1362 | exit(EXIT_FAILURE); 1363 | } 1364 | default: 1365 | fprintf (stderr, "%s: Unknown option `-%c'.\n", programName, optopt); 1366 | usage(programName); 1367 | exit(EXIT_FAILURE); 1368 | } 1369 | } 1370 | 1371 | log_message(debug_flag, "cmdline=\"%s\"", cmdline); 1372 | 1373 | if (optind < argc) { /* get serial device name */ 1374 | szttyDevice = argv[optind]; 1375 | } else { 1376 | log_message(debug_flag, "optind = %d, argc = %d", optind, argc); 1377 | usage(programName); 1378 | fprintf(stderr, "%s: No serial device specified\n", programName); 1379 | exit(EXIT_FAILURE); 1380 | } 1381 | 1382 | if (compact_flag == 1 && metern_flag == 1) { 1383 | fprintf(stderr, "%s: Parameter -m and -q are mutually exclusive\n", programName); 1384 | usage(programName); 1385 | exit(EXIT_FAILURE); 1386 | } 1387 | 1388 | lockSer(szttyDevice, PID, debug_flag); 1389 | 1390 | modbus_t *ctx; 1391 | 1392 | // Baud rate 1393 | if (baud_rate == 0) baud_rate = DEFAULT_RATE; 1394 | 1395 | // Response timeout 1396 | resp_timeout *= 100000; 1397 | log_message(debug_flag, "resp_timeout=%ldus", resp_timeout); 1398 | 1399 | // Byte timeout 1400 | if (byte_timeout != -1) { 1401 | byte_timeout *= 1000; 1402 | log_message(debug_flag, "byte_timeout=%ldus", byte_timeout); 1403 | } 1404 | 1405 | // Command delay 1406 | if (command_delay == -1) { 1407 | command_delay = 0; // default = no command delay 1408 | } else { 1409 | command_delay *= 1000; 1410 | log_message(debug_flag, "command_delay=%ldus", command_delay); 1411 | } 1412 | 1413 | // Settle time delay 1414 | if (settle_time == -1) 1415 | settle_time = 0; // default = no settle time 1416 | else { 1417 | settle_time *= 1000; 1418 | log_message(debug_flag, "settle_time=%ldus", settle_time); 1419 | } 1420 | 1421 | if (stop_bits == 0) { 1422 | if (parity != N_PARITY) 1423 | stop_bits=1; // Default if parity != N 1424 | else 1425 | stop_bits=2; // Default if parity == N 1426 | } 1427 | 1428 | //--- Modbus Setup start --- 1429 | 1430 | ctx = modbus_new_rtu(szttyDevice, baud_rate, parity, 8, stop_bits); 1431 | if (ctx == NULL) { 1432 | log_message(debug_flag | DEBUG_SYSLOG, "Unable to create the libmodbus context\n"); 1433 | ClrSerLock(PID); 1434 | exit(EXIT_FAILURE); 1435 | } else { 1436 | log_message(debug_flag, "Libmodbus context open (%d%s%d)", 1437 | baud_rate, 1438 | (parity == E_PARITY) ? EVEN_parity : 1439 | (parity == N_PARITY) ? NONE_parity : 1440 | ODD_parity, 1441 | stop_bits); 1442 | } 1443 | 1444 | #if LIBMODBUS_VERSION_MAJOR >= 3 && LIBMODBUS_VERSION_MINOR >= 1 && LIBMODBUS_VERSION_MICRO >= 2 1445 | 1446 | // Considering to get those values from command line 1447 | if (byte_timeout == -1) { 1448 | modbus_set_byte_timeout(ctx, -1, 0); 1449 | log_message(debug_flag, "Byte timeout disabled."); 1450 | } else { 1451 | modbus_set_byte_timeout(ctx, 0, byte_timeout); 1452 | log_message(debug_flag, "New byte timeout: %ds, %dus", 0, byte_timeout); 1453 | } 1454 | modbus_set_response_timeout(ctx, 0, resp_timeout); 1455 | log_message(debug_flag, "New response timeout: %ds, %dus", 0, resp_timeout); 1456 | 1457 | #else 1458 | 1459 | struct timeval timeout; 1460 | 1461 | if (byte_timeout == -1) { 1462 | timeout.tv_sec = -1; 1463 | timeout.tv_usec = 0; 1464 | modbus_set_byte_timeout(ctx, &timeout); 1465 | log_message(debug_flag, "Byte timeout disabled."); 1466 | } else { 1467 | timeout.tv_sec = 0; 1468 | timeout.tv_usec = byte_timeout; 1469 | modbus_set_byte_timeout(ctx, &timeout); 1470 | log_message(debug_flag, "New byte timeout: %ds, %dus", timeout.tv_sec, timeout.tv_usec); 1471 | } 1472 | 1473 | timeout.tv_sec = 0; 1474 | timeout.tv_usec = resp_timeout; 1475 | modbus_set_response_timeout(ctx, &timeout); 1476 | log_message(debug_flag, "New response timeout: %ds, %dus", timeout.tv_sec, timeout.tv_usec); 1477 | 1478 | #endif 1479 | 1480 | //modbus_set_error_recovery(ctx, MODBUS_ERROR_RECOVERY_LINK | MODBUS_ERROR_RECOVERY_PROTOCOL); 1481 | //modbus_set_error_recovery(ctx, MODBUS_ERROR_RECOVERY_PROTOCOL); 1482 | modbus_set_error_recovery(ctx, MODBUS_ERROR_RECOVERY_NONE); 1483 | 1484 | if (settle_time) { 1485 | // Wait for line settle 1486 | log_message(debug_flag, "Sleeping %ldus for line settle...", settle_time); 1487 | usleep(settle_time); 1488 | } 1489 | 1490 | if (trace_flag == 1) { 1491 | modbus_set_debug(ctx, 1); 1492 | } 1493 | 1494 | modbus_set_slave(ctx, device_address); 1495 | 1496 | if (modbus_connect(ctx) == -1) { 1497 | log_message(DEBUG_STDERR | DEBUG_SYSLOG, "Connection failed: (%d) %s\n", errno, modbus_strerror(errno)); 1498 | modbus_free(ctx); 1499 | ClrSerLock(PID); 1500 | exit(EXIT_FAILURE); 1501 | } 1502 | 1503 | //log_message(debug_flag, "Flushed %d bytes", modbus_flush(ctx)); // Already flushed by connect 1504 | 1505 | float voltage = 0; 1506 | float current = 0; 1507 | float power = 0; 1508 | float apower = 0; 1509 | float rapower = 0; 1510 | float pf = 0; 1511 | float pangle = 0; 1512 | float freq = 0; 1513 | float imp_energy = 0; 1514 | float exp_energy = 0; 1515 | float tot_energy = 0; 1516 | float impr_energy = 0; 1517 | float expr_energy = 0; 1518 | float totr_energy = 0; 1519 | int time_disp = 0; 1520 | 1521 | if (new_address > 0 && new_baud_rate > 0) { 1522 | log_message(DEBUG_STDERR, "Parameter -s and -r are mutually exclusive\n\n"); 1523 | usage(programName); 1524 | exit_error(ctx); 1525 | } else if ((new_address > 0 || new_baud_rate > 0) && new_parity_stop >= 0) { 1526 | log_message(DEBUG_STDERR, "Parameter -s, -r and -N are mutually exclusive\n\n"); 1527 | usage(programName); 1528 | exit_error(ctx); 1529 | } else if (new_address > 0) { 1530 | 1531 | log_message(DEBUG_STDERR, "new_address = %d > 0, count_param = %d", new_address, count_param); 1532 | 1533 | if (count_param > 0) { 1534 | usage(programName); 1535 | modbus_close(ctx); 1536 | modbus_free(ctx); 1537 | ClrSerLock(PID); 1538 | exit(EXIT_FAILURE); 1539 | } else { 1540 | // change Address 1541 | log_message(debug_flag, "Before change Address\n"); 1542 | changeConfigFloat(ctx, DEVICE_ID, new_address, RESTART_FALSE, 2); 1543 | modbus_close(ctx); 1544 | modbus_free(ctx); 1545 | ClrSerLock(PID); 1546 | return 0; 1547 | } 1548 | 1549 | } else if (new_baud_rate >= 0) { 1550 | 1551 | log_message(DEBUG_STDERR, "new_address = %d > 0, count_param = %d", new_address, count_param); 1552 | 1553 | if (count_param > 0) { 1554 | usage(programName); 1555 | modbus_close(ctx); 1556 | modbus_free(ctx); 1557 | ClrSerLock(PID); 1558 | exit(EXIT_FAILURE); 1559 | } else { 1560 | // change Baud Rate 1561 | log_message(debug_flag, "Before change Baud\n"); 1562 | changeConfigFloat(ctx, BAUD_RATE, new_baud_rate, RESTART_FALSE, 2); 1563 | modbus_close(ctx); 1564 | modbus_free(ctx); 1565 | ClrSerLock(PID); 1566 | return 0; 1567 | } 1568 | 1569 | } else if (new_parity_stop >= 0) { 1570 | 1571 | if (count_param > 0) { 1572 | usage(programName); 1573 | modbus_close(ctx); 1574 | modbus_free(ctx); 1575 | ClrSerLock(PID); 1576 | exit(EXIT_FAILURE); 1577 | } else { 1578 | // change Parity/Stop 1579 | log_message(debug_flag, "Before change Parity\n"); 1580 | changeConfigFloat(ctx, NPARSTOP, new_parity_stop, RESTART_TRUE, 2); 1581 | modbus_close(ctx); 1582 | modbus_free(ctx); 1583 | ClrSerLock(PID); 1584 | return 0; 1585 | } 1586 | 1587 | } else if (rotation_time_flag > 0) { 1588 | 1589 | if (count_param > 0) { 1590 | usage(programName); 1591 | modbus_close(ctx); 1592 | modbus_free(ctx); 1593 | ClrSerLock(PID); 1594 | exit(EXIT_FAILURE); 1595 | } else { 1596 | // change Time Rotation 1597 | changeConfigBCD(ctx, 1598 | model == MODEL_120 ? TIME_DISP : TIME_DISP_220, 1599 | rotation_time, RESTART_FALSE, 1); 1600 | modbus_close(ctx); 1601 | modbus_free(ctx); 1602 | ClrSerLock(PID); 1603 | return 0; 1604 | } 1605 | 1606 | } else if (measurement_mode_flag > 0) { 1607 | 1608 | if (count_param > 0) { 1609 | usage(programName); 1610 | modbus_close(ctx); 1611 | modbus_free(ctx); 1612 | ClrSerLock(PID); 1613 | exit(EXIT_FAILURE); 1614 | } else { 1615 | // change Measurement Mode 1616 | changeConfigHex(ctx, TOT_MODE, measurement_mode, RESTART_FALSE); 1617 | modbus_close(ctx); 1618 | modbus_free(ctx); 1619 | ClrSerLock(PID); 1620 | return 0; 1621 | } 1622 | 1623 | } else if (pulse_flag > 0) { 1624 | 1625 | if (count_param > 0) { 1626 | usage(programName); 1627 | modbus_close(ctx); 1628 | modbus_free(ctx); 1629 | ClrSerLock(PID); 1630 | exit(EXIT_FAILURE); 1631 | } else { 1632 | // change Measurement Mode 1633 | changeConfigHex(ctx, PULSE_OUT, pulse_mode, RESTART_FALSE); 1634 | modbus_close(ctx); 1635 | modbus_free(ctx); 1636 | ClrSerLock(PID); 1637 | return 0; 1638 | } 1639 | 1640 | } else if (power_flag == 0 && 1641 | apower_flag == 0 && 1642 | rapower_flag == 0 && 1643 | volt_flag == 0 && 1644 | current_flag == 0 && 1645 | pf_flag == 0 && 1646 | pangle_flag == 0 && 1647 | freq_flag == 0 && 1648 | export_flag == 0 && 1649 | import_flag == 0 && 1650 | total_flag == 0 && 1651 | rexport_flag == 0 && 1652 | rimport_flag == 0 && 1653 | rtotal_flag == 0 && 1654 | time_disp_flag == 0 1655 | ) { 1656 | // if no parameter, retrieve all values 1657 | power_flag = 1; 1658 | apower_flag = 1; 1659 | rapower_flag = 1; 1660 | volt_flag = 1; 1661 | current_flag = 1; 1662 | pangle_flag = 1; 1663 | freq_flag = 1; 1664 | pf_flag = 1; 1665 | export_flag = 1; 1666 | import_flag = 1; 1667 | total_flag = 1; 1668 | rexport_flag = 1; 1669 | rimport_flag = 1; 1670 | rtotal_flag = 1; 1671 | count_param = power_flag + apower_flag + rapower_flag + volt_flag + 1672 | current_flag + pangle_flag + freq_flag + pf_flag + 1673 | export_flag + import_flag + total_flag + 1674 | rexport_flag + rimport_flag + rtotal_flag; 1675 | } 1676 | 1677 | if (volt_flag == 1) { 1678 | voltage = getMeasureFloat(ctx, VOLTAGE, num_retries, 2); 1679 | read_count++; 1680 | if (metern_flag == 1) { 1681 | printf("%d_V(%3.2f*V)\n", device_address, voltage); 1682 | } else if (compact_flag == 1) { 1683 | printf("%3.2f ", voltage); 1684 | } else { 1685 | printf("Voltage: %3.2f V \n",voltage); 1686 | } 1687 | } 1688 | 1689 | if (current_flag == 1) { 1690 | current = getMeasureFloat(ctx, CURRENT, num_retries, 2); 1691 | read_count++; 1692 | if (metern_flag == 1) { 1693 | printf("%d_C(%3.2f*A)\n", device_address, current); 1694 | } else if (compact_flag == 1) { 1695 | printf("%3.2f ", current); 1696 | } else { 1697 | printf("Current: %3.2f A \n",current); 1698 | } 1699 | } 1700 | 1701 | if (power_flag == 1) { 1702 | power = getMeasureFloat(ctx, POWER, num_retries, 2); 1703 | read_count++; 1704 | if (metern_flag == 1) { 1705 | printf("%d_P(%3.2f*W)\n", device_address, power); 1706 | } else if (compact_flag == 1) { 1707 | printf("%3.2f ", power); 1708 | } else { 1709 | printf("Power: %3.2f W \n", power); 1710 | } 1711 | } 1712 | 1713 | if (apower_flag == 1) { 1714 | apower = getMeasureFloat(ctx, APOWER, num_retries, 2); 1715 | read_count++; 1716 | if (metern_flag == 1) { 1717 | printf("%d_VA(%3.2f*VA)\n", device_address, apower); 1718 | } else if (compact_flag == 1) { 1719 | printf("%3.2f ", apower); 1720 | } else { 1721 | printf("Active Apparent Power: %3.2f VA \n", apower); 1722 | } 1723 | } 1724 | 1725 | if (rapower_flag == 1) { 1726 | rapower = getMeasureFloat(ctx, RAPOWER, num_retries, 2); 1727 | read_count++; 1728 | if (metern_flag == 1) { 1729 | printf("%d_VAR(%3.2f*VAR)\n", device_address, rapower); 1730 | } else if (compact_flag == 1) { 1731 | printf("%3.2f ", rapower); 1732 | } else { 1733 | printf("Reactive Apparent Power: %3.2f VAR \n", rapower); 1734 | } 1735 | } 1736 | 1737 | if (pf_flag == 1) { 1738 | pf = getMeasureFloat(ctx, PFACTOR, num_retries, 2); 1739 | read_count++; 1740 | if (metern_flag == 1) { 1741 | printf("%d_PF(%3.2f*F)\n", device_address, pf); 1742 | } else if (compact_flag == 1) { 1743 | printf("%3.2f ", pf); 1744 | } else { 1745 | printf("Power Factor: %3.2f \n", pf); 1746 | } 1747 | } 1748 | 1749 | if (pangle_flag == 1) { 1750 | pangle = getMeasureFloat(ctx, PANGLE, num_retries, 2); 1751 | read_count++; 1752 | if (metern_flag == 1) { 1753 | printf("%d_PA(%3.2f*Dg)\n", device_address, pangle); 1754 | } else if (compact_flag == 1) { 1755 | printf("%3.2f ", pangle); 1756 | } else { 1757 | printf("Phase Angle: %3.2f Degree \n", pangle); 1758 | } 1759 | } 1760 | 1761 | if (freq_flag == 1) { 1762 | freq = getMeasureFloat(ctx, FREQUENCY, num_retries, 2); 1763 | read_count++; 1764 | if (metern_flag == 1) { 1765 | printf("%d_F(%3.2f*Hz)\n", device_address, freq); 1766 | } else if (compact_flag == 1) { 1767 | printf("%3.2f ", freq); 1768 | } else { 1769 | printf("Frequency: %3.2f Hz \n", freq); 1770 | } 1771 | } 1772 | 1773 | if (import_flag == 1) { 1774 | imp_energy = getMeasureFloat(ctx, IAENERGY, num_retries, 2) * 1000; 1775 | read_count++; 1776 | if (metern_flag == 1) { 1777 | printf("%d_IE(%d*Wh)\n", device_address, (int)imp_energy); 1778 | } else if (compact_flag == 1) { 1779 | printf("%d ", (int)imp_energy); 1780 | } else { 1781 | printf("Import Active Energy: %d Wh \n", (int)imp_energy); 1782 | } 1783 | } 1784 | 1785 | if (export_flag == 1) { 1786 | exp_energy = getMeasureFloat(ctx, EAENERGY, num_retries, 2) * 1000; 1787 | read_count++; 1788 | if (metern_flag == 1) { 1789 | printf("%d_EE(%d*Wh)\n", device_address, (int)exp_energy); 1790 | } else if (compact_flag == 1) { 1791 | printf("%d ", (int)exp_energy); 1792 | } else { 1793 | printf("Export Active Energy: %d Wh \n", (int)exp_energy); 1794 | } 1795 | } 1796 | 1797 | if (total_flag == 1) { 1798 | tot_energy = getMeasureFloat(ctx, TAENERGY, num_retries, 2) * 1000; 1799 | read_count++; 1800 | if (metern_flag == 1) { 1801 | printf("%d_TE(%d*Wh)\n", device_address, (int)tot_energy); 1802 | } else if (compact_flag == 1) { 1803 | printf("%d ", (int)tot_energy); 1804 | } else { 1805 | printf("Total Active Energy: %d Wh \n", (int)tot_energy); 1806 | } 1807 | } 1808 | 1809 | if (rimport_flag == 1) { 1810 | impr_energy = getMeasureFloat(ctx, IRAENERGY, num_retries, 2) * 1000; 1811 | read_count++; 1812 | if (metern_flag == 1) { 1813 | printf("%d_IRE(%d*VARh)\n", device_address, (int)impr_energy); 1814 | } else if (compact_flag == 1) { 1815 | printf("%d ", (int)impr_energy); 1816 | } else { 1817 | printf("Import Reactive Energy: %d VARh \n", (int)impr_energy); 1818 | } 1819 | } 1820 | 1821 | if (rexport_flag == 1) { 1822 | expr_energy = getMeasureFloat(ctx, ERAENERGY, num_retries, 2) * 1000; 1823 | read_count++; 1824 | if (metern_flag == 1) { 1825 | printf("%d_ERE(%d*VARh)\n", device_address, (int)expr_energy); 1826 | } else if (compact_flag == 1) { 1827 | printf("%d ", (int)expr_energy); 1828 | } else { 1829 | printf("Export Reactive Energy: %d VARh \n", (int)expr_energy); 1830 | } 1831 | } 1832 | 1833 | if (rtotal_flag == 1) { 1834 | totr_energy = getMeasureFloat(ctx, TRENERGY, num_retries, 2) * 1000; 1835 | read_count++; 1836 | if (metern_flag == 1) { 1837 | printf("%d_TRE(%d*VARh)\n", device_address, (int)totr_energy); 1838 | } else if (compact_flag == 1) { 1839 | printf("%d ", (int)totr_energy); 1840 | } else { 1841 | printf("Total Reactive Energy: %d VARh \n", (int)totr_energy); 1842 | } 1843 | } 1844 | 1845 | if (time_disp_flag == 1) { 1846 | time_disp = getConfigBCD(ctx, 1847 | model == MODEL_120 ? TIME_DISP : TIME_DISP_220, 1848 | num_retries, 1); 1849 | read_count++; 1850 | if (compact_flag == 1) { 1851 | printf("%d ", (int) time_disp); 1852 | } else { 1853 | printf("Display rotation time: %d\n", (int) time_disp); 1854 | } 1855 | } 1856 | 1857 | if (read_count == count_param) { 1858 | // log_message(debug_flag, "Flushed %d bytes", modbus_flush(ctx)); 1859 | modbus_close(ctx); 1860 | modbus_free(ctx); 1861 | ClrSerLock(PID); 1862 | free(devLCKfile); 1863 | free(devLCKfileNew); 1864 | free(PARENTCOMMAND); 1865 | if (!metern_flag) printf("OK\n"); 1866 | } else { 1867 | exit_error(ctx); 1868 | } 1869 | 1870 | return 0; 1871 | } 1872 | 1873 | #ifdef __cplusplus 1874 | } 1875 | #endif 1876 | -------------------------------------------------------------------------------- /sdm120c.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import minimalmodbus 4 | 5 | rs485 = minimalmodbus.Instrument('/dev/ttyUSB0', 1) 6 | rs485.serial.baudrate = 9600 7 | rs485.serial.bytesize = 8 8 | rs485.serial.parity = minimalmodbus.serial.PARITY_NONE 9 | rs485.serial.stopbits = 1 10 | rs485.serial.timeout = 1 11 | rs485.debug = False 12 | rs485.mode = minimalmodbus.MODE_RTU 13 | print rs485 14 | 15 | Volts = rs485.read_float(0, functioncode=4, numberOfRegisters=2) 16 | Current = rs485.read_float(6, functioncode=4, numberOfRegisters=2) 17 | Active_Power = rs485.read_float(12, functioncode=4, numberOfRegisters=2) 18 | Apparent_Power = rs485.read_float(18, functioncode=4, numberOfRegisters=2) 19 | Reactive_Power = rs485.read_float(24, functioncode=4, numberOfRegisters=2) 20 | Power_Factor = rs485.read_float(30, functioncode=4, numberOfRegisters=2) 21 | Phase_Angle = rs485.read_float(36, functioncode=4, numberOfRegisters=2) 22 | Frequency = rs485.read_float(70, functioncode=4, numberOfRegisters=2) 23 | Import_Active_Energy = rs485.read_float(72, functioncode=4, numberOfRegisters=2) 24 | Export_Active_Energy = rs485.read_float(74, functioncode=4, numberOfRegisters=2) 25 | Import_Reactive_Energy = rs485.read_float(76, functioncode=4, numberOfRegisters=2) 26 | Export_Reactive_Energy = rs485.read_float(78, functioncode=4, numberOfRegisters=2) 27 | Total_Active_Energy = rs485.read_float(342, functioncode=4, numberOfRegisters=2) 28 | Total_Reactive_Energy = rs485.read_float(344, functioncode=4, numberOfRegisters=2) 29 | 30 | print 'Voltage: {0:.1f} Volts'.format(Volts) 31 | print 'Current: {0:.1f} Amps'.format(Current) 32 | print 'Active power: {0:.1f} Watts'.format(Active_Power) 33 | print 'Apparent power: {0:.1f} VoltAmps'.format(Apparent_Power) 34 | print 'Reactive power: {0:.1f} VAr'.format(Reactive_Power) 35 | print 'Power factor: {0:.1f}'.format(Power_Factor) 36 | print 'Phase angle: {0:.1f} Degree'.format(Phase_Angle) 37 | print 'Frequency: {0:.1f} Hz'.format(Frequency) 38 | print 'Import active energy: {0:.3f} Kwh'.format(Import_Active_Energy) 39 | print 'Export active energy: {0:.3f} kwh'.format(Export_Active_Energy) 40 | print 'Import reactive energy: {0:.3f} kvarh'.format(Import_Reactive_Energy) 41 | print 'Export reactive energy: {0:.3f} kvarh'.format(Export_Reactive_Energy) 42 | print 'Total active energy: {0:.3f} kwh'.format(Total_Active_Energy) 43 | print 'Total reactive energy: {0:.3f} kvarh'.format(Total_Reactive_Energy) 44 | print 'Current Yield (V*A): {0:.1f} Watt'.format(Volts * Current) 45 | -------------------------------------------------------------------------------- /sdm120c2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf_8 -*- 2 | """ 3 | Modbus TestKit: Implementation of Modbus protocol in python 4 | (C)2009 - Luc Jean - luc.jean@gmail.com 5 | (C)2009 - Apidev - http://www.apidev.fr 6 | This is distributed under GNU LGPL license, see license.txt 7 | """ 8 | 9 | from struct import * 10 | import serial 11 | import modbus_tk 12 | import modbus_tk.defines as cst 13 | from modbus_tk import modbus_rtu 14 | 15 | def read_float(arrRegs): 16 | reg1 = arrRegs[1] 17 | reg2 = arrRegs[0] 18 | f = unpack('f', pack(' > 4, char & 0xF): 31 | if val == 0xF: 32 | return 33 | yield val 34 | 35 | PORT = '/dev/ttyUSB0' 36 | SLAVE = 1 37 | 38 | def main(): 39 | """main""" 40 | logger = modbus_tk.utils.create_logger("console") 41 | 42 | try: 43 | #Connect to the slave 44 | master = modbus_rtu.RtuMaster( 45 | serial.Serial(port=PORT, baudrate=9600, bytesize=8, parity='N', stopbits=1, xonxoff=0) 46 | ) 47 | master.set_timeout(5.0) 48 | master.set_verbose(True) 49 | logger.info("connected") 50 | 51 | Volts = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0000, quantity_of_x=2, data_format='>f')[0] 52 | Current = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0006, quantity_of_x=2, data_format='>f')[0] 53 | Active_Power = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x000C, quantity_of_x=2, data_format='>f')[0] 54 | Apparent_Power = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0012, quantity_of_x=2, data_format='>f')[0] 55 | Reactive_Power = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0018, quantity_of_x=2, data_format='>f')[0] 56 | Power_Factor = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x001E, quantity_of_x=2, data_format='>f')[0] 57 | #Phase_Angle = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0006, quantity_of_x=2, data_format='>f')[0] 58 | Frequency = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0046, quantity_of_x=2, data_format='>f')[0] 59 | Import_Active_Energy = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0048, quantity_of_x=2, data_format='>f')[0] 60 | Export_Active_Energy = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x004A, quantity_of_x=2, data_format='>f')[0] 61 | Import_Reactive_Energy = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x004C, quantity_of_x=2, data_format='>f')[0] 62 | Export_Reactive_Energy = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x004E, quantity_of_x=2, data_format='>f')[0] 63 | Total_Active_Energy = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0156, quantity_of_x=2, data_format='>f')[0] 64 | Total_Reactive_Energy = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0158, quantity_of_x=2, data_format='>f')[0] 65 | #Time_Interval = rs485.read_string(0xF900, functioncode=3, numberOfRegisters=2)[0] 66 | Time_Interval = master.execute(slave=SLAVE, function_code=cst.READ_HOLDING_REGISTERS, starting_address=0xF900, quantity_of_x=1, data_format='>H')[0] 67 | 68 | #send some queries 69 | #logger.info('Voltage: %.1f Volts' % read_float(master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0000, quantity_of_x=2))) 70 | print('Voltage: %.1f Volts' % (Volts)) 71 | print('Current: %.1f Amps' % (Current)) 72 | print('Active power: %.1f Watts' % (Active_Power)) 73 | print('Apparent power: %.1f VoltAmps' % (Apparent_Power)) 74 | print('Reactive power: %.1f VAr' % (Reactive_Power)) 75 | print('Power factor: %.1f' % (Power_Factor)) 76 | #print('Phase angle: %.1f Degree' % (Phase_Angle)) 77 | print('Frequency: %.1f Hz' % (Frequency)) 78 | print('Import active energy: %.3f Kwh' % (Import_Active_Energy)) 79 | print('Export active energy: %.3f kwh' % (Export_Active_Energy)) 80 | print('Import reactive energy: %.3f kvarh' % (Import_Reactive_Energy)) 81 | print('Export reactive energy: %.3f kvarh' % (Export_Reactive_Energy)) 82 | print('Total active energy: %.3f kwh' % (Total_Active_Energy)) 83 | print('Total reactive energy: %.3f kvarh' % (Total_Reactive_Energy)) 84 | #print('Current Yield (V*A): %.1f Watt' % (Volts * Current)) 85 | print('Current Yield (V*A): %.1f Watt' % (Volts*Current)) 86 | print('Time interval: %d' % (Time_Interval)) 87 | #print(Time_Interval) 88 | arrRegs = master.execute(slave=SLAVE, function_code=cst.READ_HOLDING_REGISTERS, starting_address=0xF900, quantity_of_x=1, data_format='>BB') 89 | print(arrRegs) 90 | print(list(bcdDigits(arrRegs))) 91 | 92 | except modbus_tk.modbus.ModbusError as exc: 93 | logger.error("%s- Code=%d", exc, exc.get_exception_code()) 94 | 95 | if __name__ == "__main__": 96 | main() 97 | 98 | -------------------------------------------------------------------------------- /sdm120c3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Pymodbus Synchronous Client Examples 4 | -------------------------------------------------------------------------- 5 | 6 | The following is an example of how to use the synchronous modbus client 7 | implementation from pymodbus. 8 | 9 | It should be noted that the client can also be used with 10 | the guard construct that is available in python 2.5 and up:: 11 | 12 | with ModbusClient('127.0.0.1') as client: 13 | result = client.read_coils(1,10) 14 | print result 15 | ''' 16 | #---------------------------------------------------------------------------# 17 | # import the various server implementations 18 | #---------------------------------------------------------------------------# 19 | #from pymodbus.client.sync import ModbusTcpClient as ModbusClient 20 | #from pymodbus.client.sync import ModbusUdpClient as ModbusClient 21 | from pymodbus.client.sync import ModbusSerialClient as ModbusClient 22 | 23 | #---------------------------------------------------------------------------# 24 | # configure the client logging 25 | #---------------------------------------------------------------------------# 26 | import logging 27 | log = logging.getLogger() 28 | log.setLevel(logging.DEBUG) 29 | logpy = logging.getLogger("pymodbus") 30 | logpy.setLevel(logging.INFO) 31 | logging.basicConfig() 32 | 33 | 34 | 35 | from struct import pack, unpack 36 | from pymodbus.constants import Endian 37 | from pymodbus.interfaces import IPayloadBuilder 38 | from pymodbus.utilities import pack_bitstring 39 | from pymodbus.utilities import unpack_bitstring 40 | from pymodbus.exceptions import ParameterException 41 | from pymodbus.payload import BinaryPayloadDecoder 42 | from pymodbus.payload import BcdPayloadDecoder 43 | 44 | 45 | #---------------------------------------------------------------------------# 46 | # choose the client you want 47 | #---------------------------------------------------------------------------# 48 | # make sure to start an implementation to hit against. For this 49 | # you can use an existing device, the reference implementation in the tools 50 | # directory, or start a pymodbus server. 51 | # 52 | # If you use the UDP or TCP clients, you can override the framer being used 53 | # to use a custom implementation (say RTU over TCP). By default they use the 54 | # socket framer:: 55 | # 56 | # client = ModbusClient('localhost', port=5020, framer=ModbusRtuFramer) 57 | # 58 | # It should be noted that you can supply an ipv4 or an ipv6 host address for 59 | # both the UDP and TCP clients. 60 | # 61 | # There are also other options that can be set on the client that controls 62 | # how transactions are performed. The current ones are: 63 | # 64 | # * retries - Specify how many retries to allow per transaction (default = 3) 65 | # * retry_on_empty - Is an empty response a retry (default = False) 66 | # * source_address - Specifies the TCP source address to bind to 67 | # 68 | # Here is an example of using these options:: 69 | # 70 | # client = ModbusClient('localhost', retries=3, retry_on_empty=True) 71 | #---------------------------------------------------------------------------# 72 | SLAVE = 1 73 | #client = ModbusClient('localhost', port=502) 74 | #client = ModbusClient(method='ascii', port='/dev/pts/2', timeout=1) 75 | client = ModbusClient(method='rtu', port='/dev/ttyUSB0', stopbits=1, bytesize = 8, parity = 'N', baudrate = 9600, timeout=0.2, unit=SLAVE) 76 | 77 | client.connect() 78 | 79 | #---------------------------------------------------------------------------# 80 | # specify slave to query 81 | #---------------------------------------------------------------------------# 82 | # The slave to query is specified in an optional parameter for each 83 | # individual request. This can be done by specifying the `unit` parameter 84 | # which defaults to `0x00` 85 | #---------------------------------------------------------------------------# 86 | #rr = client.read_coils(1, 1, unit=0x02) 87 | 88 | #---------------------------------------------------------------------------# 89 | # example requests 90 | #---------------------------------------------------------------------------# 91 | # simply call the methods that you would like to use. An example session 92 | # is displayed below along with some assert checks. Note that some modbus 93 | # implementations differentiate holding/input discrete/coils and as such 94 | # you will not be able to write to these, therefore the starting values 95 | # are not known to these tests. Furthermore, some use the same memory 96 | # blocks for the two sets, so a change to one is a change to the other. 97 | # Keep both of these cases in mind when testing as the following will 98 | # _only_ pass with the supplied async modbus server (script supplied). 99 | #---------------------------------------------------------------------------# 100 | #rr = client.read_holding_registers(1,1) 101 | # 102 | #rr = client.read_holding_registers(address=0xF900, count=1) 103 | #decoder = BcdPayloadDecoder.fromRegisters(rr.registers) 104 | #Time_Interval = decoder.decode_int(2) 105 | #Time_Interval = rs485.read_string(0xF900, functioncode=3, numberOfRegisters=2)[0] 106 | # 107 | rr = client.read_input_registers(address=0x0000, count=2) 108 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 109 | Volts = decoder.decode_32bit_float() 110 | # 111 | rr = client.read_input_registers(address=0x0006, count=2) 112 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 113 | Current = decoder.decode_32bit_float() 114 | # 115 | rr = client.read_input_registers(address=0x000C, count=2) 116 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 117 | Active_Power = decoder.decode_32bit_float() 118 | # 119 | rr = client.read_input_registers(address=0x0012, count=2) 120 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 121 | Apparent_Power = decoder.decode_32bit_float() 122 | # 123 | rr = client.read_input_registers(address=0x0018, count=2) 124 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 125 | Reactive_Power = decoder.decode_32bit_float() 126 | # 127 | rr = client.read_input_registers(address=0x001E, count=2) 128 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 129 | Power_Factor = decoder.decode_32bit_float() 130 | # 131 | #Phase_Angle = master.execute(slave=SLAVE, function_code=cst.READ_INPUT_REGISTERS, starting_address=0x0006, quantity_of_x=2, data_format='>f')[0] 132 | rr = client.read_input_registers(address=0x0046, count=2) 133 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 134 | Frequency = decoder.decode_32bit_float() 135 | # 136 | rr = client.read_input_registers(address=0x0048, count=2) 137 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 138 | Import_Active_Energy = decoder.decode_32bit_float() 139 | # 140 | rr = client.read_input_registers(address=0x004A, count=2) 141 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 142 | Export_Active_Energy = decoder.decode_32bit_float() 143 | # 144 | rr = client.read_input_registers(address=0x004C, count=2) 145 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 146 | Import_Reactive_Energy = decoder.decode_32bit_float() 147 | # 148 | rr = client.read_input_registers(address=0x004E, count=2) 149 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 150 | Export_Reactive_Energy = decoder.decode_32bit_float() 151 | # 152 | rr = client.read_input_registers(address=0x0156, count=2) 153 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 154 | Total_Active_Energy = decoder.decode_32bit_float() 155 | # 156 | rr = client.read_input_registers(address=0x0158, count=2) 157 | decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) 158 | Total_Reactive_Energy = decoder.decode_32bit_float() 159 | 160 | print('Voltage: %.1f Volts' % (Volts)) 161 | print('Current: %.1f Amps' % (Current)) 162 | print('Active power: %.1f Watts' % (Active_Power)) 163 | print('Apparent power: %.1f VoltAmps' % (Apparent_Power)) 164 | print('Reactive power: %.1f VAr' % (Reactive_Power)) 165 | print('Power factor: %.1f' % (Power_Factor)) 166 | #print('Phase angle: %.1f Degree' % (Phase_Angle)) 167 | print('Frequency: %.1f Hz' % (Frequency)) 168 | print('Import active energy: %.3f Kwh' % (Import_Active_Energy)) 169 | print('Export active energy: %.3f kwh' % (Export_Active_Energy)) 170 | print('Import reactive energy: %.3f kvarh' % (Import_Reactive_Energy)) 171 | print('Export reactive energy: %.3f kvarh' % (Export_Reactive_Energy)) 172 | print('Total active energy: %.3f kwh' % (Total_Active_Energy)) 173 | print('Total reactive energy: %.3f kvarh' % (Total_Reactive_Energy)) 174 | #print('Current Yield (V*A): %.1f Watt' % (Volts * Current)) 175 | print('Current Yield (V*A): %.1f Watt' % (Volts*Current)) 176 | #print('Time interval: %s' % str(Time_Interval)) 177 | 178 | #---------------------------------------------------------------------------# 179 | # close the client 180 | #---------------------------------------------------------------------------# 181 | client.close() 182 | --------------------------------------------------------------------------------