forked from hallard/teleinfo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathteleinfo.c
2234 lines (1929 loc) · 60.8 KB
/
teleinfo.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* ======================================================================
Program : teleinfo
Version : 1.0.8
Purpose : send/recevice teleinformation from severals devices then can
- write to MySql
- write to Emoncms
- send UDP frame over network
Author : (c) Charles-Henri Hallard
http://hallard.me
Comments: some code grabbed from picocom and other from teleinfo
: You can use or distribute this code unless you leave this comment
: too see this code correctly indented, please use Tab values of 2
06/09/2013 : Added EMONCMS feature
12/09/2013 : Added Linked List for only post real time changed values
30/03/2014 : Added Emoncms real time post (only changed values)
15/04/2014 : Added configuration parameters with also config file
====================================================================== */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <syslog.h>
#include <termios.h>
#ifdef USE_MYSQL
#include <mysql/mysql.h>
#endif
#ifdef USE_EMONCMS
#include <curl/curl.h>
#endif
#include <netinet/in.h>
#include <getopt.h>
// ----------------
// Constants
// ----------------
#define true 1
#define false 0
// Directory where we are when in daemon mode
#define PRG_DIR "/usr/local/bin"
#define PRG_NAME "teleinfo"
#define PRG_VERSION_NUMBER "1.0.8"
#define PRG_CFG "/etc/teleinfo.conf"
#if (defined USE_EMONCS && defined USE_MYSQL)
#define PRG_VERSION PRG_VERSION_NUMBER " (with mysql and emoncms)"
#elseif defined USE_EMONCS
#define PRG_VERSION PRG_VERSION_NUMBER " (with emoncms)"
#elseif defined USE_MYSQL
#define PRG_VERSION PRG_VERSION_NUMBER " (with mysql)"
#else
#define PRG_VERSION PRG_VERSION_NUMBER
#endif
// Define teleinfo mode, device or network
#define TELEINFO_DEVICE ""
#define TELEINFO_PORT 1200 // Port used to send frame over Network
#define TELEINFO_NETWORK "10.10.0.0" // Broadcast or specific IP where to send frame
#define UUCP_LOCK_DIR "/var/lock"
// Local File for teleinfo
//#define TELEINFO_DATALOG "/tmp/teleinfo.log"
//#define TELEINFO_TRAMELOG "/tmp/teleinfotrame."
#define TELEINFO_BUFSIZE 512
// Teleinfo start and end of frame characters
#define STX 0x02
#define ETX 0x03
#ifdef USE_MYSQL
// Define mysql
#define MYSQL_HOST "localhost"
#define MYSQL_LOGIN ""
#define MYSQL_PASS ""
#define MYSQL_DB ""
#define MYSQL_TABLE "DbiTeleinfo"
#define MYSQL_PORT 3306
#endif
#ifdef USE_EMONCMS
// Define emoncms
#define EMONCMS_URL ""
#define EMONCMS_APIKEY ""
#define EMONCMS_NODE 0
#define HTTP_APIKEY_SIZE 64 // Max size of apikey
#define HTTP_URL_SIZE 128 // Max size of url (url only, not containing posted data)
#define HTTP_BUFFER_SIZE 1024 // Where http returned data will be filled
#endif
// Some enum
enum parity_e { P_NONE, P_EVEN, P_ODD };
enum flowcntrl_e { FC_NONE, FC_RTSCTS, FC_XONXOFF };
enum mode_e { MODE_NONE, MODE_SEND, MODE_RECEIVE, MODE_TEST };
enum value_e { VALUE_NOTHING, VALUE_ADDED, VALUE_EXIST, VALUE_CHANGED};
// Configuration structure
struct
{
char port[128];
int baud;
enum flowcntrl_e flow;
char *flow_str;
enum parity_e parity;
char *parity_str;
int databits;
int nolock;
int mode;
char *mode_str;
int netport;
int verbose;
char value_str[32];
char network[32];
#ifdef USE_MYSQL
int mysql;
char server[32];
char user[32];
char password[32];
char database[32];
char table[32];
unsigned int serverport;
#endif
#ifdef USE_EMONCMS
int emoncms;
int node;
char url[HTTP_URL_SIZE];
char apikey[HTTP_APIKEY_SIZE];
#endif
int daemon;
// Configuration structure defaults values
} opts = {
.port = "",
.baud = 1200,
.flow = FC_NONE,
.flow_str = "none",
.parity = P_EVEN,
.parity_str = "even",
.databits = 7,
.nolock = false,
.mode = MODE_NONE,
.mode_str = "receive",
.netport = TELEINFO_PORT,
.verbose = false,
.value_str = "",
.network = TELEINFO_NETWORK,
#ifdef USE_MYSQL
.mysql = false,
.server = MYSQL_HOST,
.user = MYSQL_LOGIN,
.password = MYSQL_PASS,
.database = MYSQL_DB,
.table = MYSQL_TABLE,
.serverport = MYSQL_PORT,
#endif
#ifdef USE_EMONCMS
.emoncms = false,
.url = EMONCMS_URL,
.apikey = EMONCMS_APIKEY,
.node = EMONCMS_NODE,
#endif
.daemon = false
};
// Statistics and error structure
struct
{
unsigned long framesent;
unsigned long frame;
unsigned long badchecksum;
unsigned long frameformaterror;
unsigned long frameok;
unsigned long framesizeerror;
#ifdef USE_MYSQL
unsigned long mysqlinitok;
unsigned long mysqliniterror;
unsigned long mysqlconnectok;
unsigned long mysqlconnecterror;
unsigned long mysqlqueryerror;
unsigned long mysqlqueryok;
#endif
#ifdef USE_EMONCMS
unsigned long curl_post;
unsigned long curl_postok;
unsigned long curl_posterror;
unsigned long curl_timeout;
#endif
} stats;
// Linked list structure containing all values
// Used to only update changed values to emoncms
typedef struct _ValueList ValueList;
struct _ValueList
{
ValueList *next;
char *name; // LABEL of value name
char *value; // value
};
// ======================================================================
// Global vars
// ======================================================================
int g_fd_teleinfo; // teleinfo serial handle
struct termios g_oldtermios ; // old serial config
int g_tlf_sock; // teleinfo socket
int g_exit_pgm; // indicate en of the program
char g_lockname[256] = ""; // Lock filename
#ifdef USE_EMONCMS
CURL *g_pcurl;
char http_buffer[HTTP_BUFFER_SIZE]; // Where http returned data will be filled
#endif
ValueList g_valueslist; // Linked list of teleinfo values
ValueList *p_valueslist; // pointer on linked list of teleinfo values;
// ======================================================================
// some func declaration
// ======================================================================
void tlf_close_serial(int);
/* ======================================================================
Function: log_syslog
Purpose : write event to syslog
Input : stream to write if needed
string to write in printf format
printf other arguments
Output : -
Comments:
====================================================================== */
void log_syslog( FILE * stream, const char *format, ...)
{
static char tmpbuff[512]="";
va_list args;
int len;
// do a style printf style in ou buffer
va_start (args, format);
len = vsnprintf (tmpbuff, sizeof(tmpbuff), format, args);
tmpbuff[sizeof(tmpbuff) - 1] = '\0';
va_end (args);
// Write to logfile
openlog( PRG_NAME, LOG_PID|LOG_CONS, LOG_USER);
syslog(LOG_INFO, "%s", tmpbuff);
closelog();
// stream passed ? write also to it
if (stream && opts.verbose && !opts.daemon )
{
fprintf(stream, "%s", tmpbuff);
//fprintf(stream, "\n");
fflush(stream);
}
}
/* ======================================================================
Function: show_stats
Purpose : display program statistics
Input : -
Output : -
Comments:
====================================================================== */
void show_stats(void)
{
// Print stats
int old_opt = opts.daemon;
int old_verb = opts.verbose;
// Fake daemon/verbose mode to display info
// We'll restore it after
opts.daemon = false;
opts.verbose = true;
log_syslog(stderr, "\n"PRG_NAME" "PRG_VERSION_NUMBER" Statistics\n");
log_syslog(stderr, "==========================\n");
log_syslog(stderr, "Frames Sent : %ld\n", stats.framesent);
log_syslog(stderr, "Frames checked : %ld\n", stats.frame);
log_syslog(stderr, "Frames OK : %ld\n", stats.frameok);
log_syslog(stderr, "Checksum errors : %ld\n", stats.badchecksum);
log_syslog(stderr, "Frame format Errors : %ld\n", stats.frameformaterror);
log_syslog(stderr, "Frame size Errors : %ld\n", stats.framesizeerror);
#ifdef USE_MYSQL
log_syslog(stderr, "MySQL init OK : %ld\n", stats.mysqlinitok);
log_syslog(stderr, "MySQL init errors : %ld\n", stats.mysqliniterror);
log_syslog(stderr, "MySQL connect OK : %ld\n", stats.mysqlconnectok);
log_syslog(stderr, "MySQL connect errors: %ld\n", stats.mysqlconnecterror);
log_syslog(stderr, "MySQL queries OK : %ld\n", stats.mysqlqueryok);
log_syslog(stderr, "MySQL queries errors: %ld\n", stats.mysqlqueryerror);
#endif
#ifdef USE_EMONCMS
log_syslog(stderr, "EmonCMS total post : %ld\n", stats.curl_post);
log_syslog(stderr, "EmonCMS post OK : %ld\n", stats.curl_postok);
log_syslog(stderr, "EmonCMS post errors : %ld\n", stats.curl_posterror);
log_syslog(stderr, "EmonCMS timeout : %ld\n", stats.curl_timeout);
#endif
log_syslog(stderr, "--------------------------\n");
opts.verbose = old_verb;
opts.daemon = old_opt;
}
/* ======================================================================
Function: clean_exit
Purpose : exit program
Input : exit code
Output : -
Comments:
====================================================================== */
void clean_exit (int exit_code)
{
int r;
// free up linked list
valuelist_delete(p_valueslist);
// close serials
if (g_fd_teleinfo)
{
// Restore Old parameters.
if ( (r = tcsetattr(g_fd_teleinfo, TCSAFLUSH, &g_oldtermios)) < 0 )
log_syslog(stderr, "cannot restore old parameters %s: %s", opts.port, strerror(errno));
// then close
tlf_close_serial(g_fd_teleinfo);
}
// close socket
if (g_tlf_sock)
close(g_tlf_sock);
#ifdef USE_EMONCMS
// cleanup if this part was initialized
if (g_pcurl)
curl_easy_cleanup(g_pcurl);
// always cleanup this part
curl_global_cleanup();
#endif
// display statistics
show_stats();
if ( exit_code == EXIT_SUCCESS)
log_syslog(stdout, "Succeded to do my job\n");
else
log_syslog(stdout, "Closing teleinfo due to error\n");
// wait a bit for output to drain
if (opts.mode == MODE_SEND)
sleep(1);
if (!opts.nolock)
uucp_unlock();
exit(exit_code);
}
/* ======================================================================
Function: fatal
Purpose : exit program due to a fatal error
Input : string to write in printf format
printf other arguments
Output : -
Comments:
====================================================================== */
void fatal (const char *format, ...)
{
char tmpbuff[512] = "";
va_list args;
int len;
va_start(args, format);
len = vsnprintf(tmpbuff, sizeof(tmpbuff), format, args);
tmpbuff[sizeof(tmpbuff) - 1] = '\0';
va_end(args);
// Write to logfile
openlog( PRG_NAME, LOG_PID | LOG_CONS, LOG_USER);
syslog(LOG_INFO, "%s", tmpbuff);
closelog();
fprintf(stderr,"\r\nFATAL: %s \r\n", tmpbuff );
fflush(stderr);
clean_exit(EXIT_FAILURE);
}
/* ======================================================================
Function: daemonize
Purpose : daemonize the pocess
Input : -
Output : -
Comments:
====================================================================== */
static void daemonize(void)
{
pid_t pid, sid;
// already a daemon
if ( getppid() == 1 )
return;
// Fork off the parent process
pid = fork();
if (pid < 0)
fatal( "fork() : %s", strerror(errno));
// If we got a good PID, then we can exit the parent process.
if (pid > 0)
exit(EXIT_SUCCESS);
// At this point we are executing as the child process
// ---------------------------------------------------
// Change the file mode mask
umask(0);
// Create a new SID for the child process
sid = setsid();
if (sid < 0)
fatal( "setsid() : %s", strerror(errno));
// Change the current working directory. This prevents the current
// directory from being locked; hence not being able to remove it.
if ((chdir(PRG_DIR)) < 0)
fatal( "chdir('%s') : %s", PRG_DIR, strerror(errno));
// Close standard files
close(STDIN_FILENO);
// if verbose mode, allow display on stdout
if (!opts.verbose)
close(STDOUT_FILENO);
// Always display errors on stderr
//close(STDERR_FILENO);
}
/* ======================================================================
Function : valuelist_add
Purpose : Add element to the Linked List of values
Input : Pointer to the label name
pointer to the value
size of the label
size of the value
state of the label (filled by function)
Output : pointer to the new node (or founded one)
: state of the label changed by the function
====================================================================== */
ValueList * valuelist_add (ValueList * me, char * name, char * value, int lgname, int lgvalue, enum value_e * valuestate)
{
// Got one
if (me)
{
// Create pointer on the new node
ValueList *newNode = NULL;
ValueList *parNode = NULL ;
// By default we done nothing
*valuestate = VALUE_NOTHING;
// Loop thru the node
while (me->next)
{
// save parent node
parNode = me ;
// go to next node
me = me->next;
// Check if we already have this LABEL
if (strncmp(me->name, name, lgname) == 0)
{
// Already got also this value, return US
if (strncmp(me->value, value, lgvalue) == 0)
{
*valuestate = VALUE_EXIST;
return ( me );
}
else
{
// We changed the value
*valuestate = VALUE_CHANGED;
// Do we have enought space to hold new value ?
if (strlen(me->value) >= lgvalue )
{
// Copy it
strncpy(me->value, value , lgvalue );
// That's all
return (me);
}
else
{
// indicate parent node next following node instead of me
parNode->next = me->next;
// Return to parent (that will now point on next node and not us)
me = parNode;
// free up this node
free (me);
}
}
}
}
// Create new node with size to store strings
if ((newNode = (ValueList *) calloc(1, sizeof(ValueList) + lgname + 1 + lgvalue + 1 ) ) == NULL)
{
return ( (ValueList *) NULL );
}
else
{
// We not changed this node ?
if (*valuestate != VALUE_CHANGED)
{
// so we added this node !
*valuestate = VALUE_ADDED ;
}
}
// Put the new node on the list
me->next = newNode;
// First String located after last struct element
// Second String located after the First + \0
newNode->name = (char *) newNode + sizeof(ValueList);
newNode->value = (char *) newNode->name + lgname + 1;
// Copy the string
memcpy(newNode->name , name , lgname );
memcpy(newNode->value, value , lgvalue );
// return pointer on the new node
return (newNode);
}
// Error or Already Exists
return ( (ValueList *) NULL);
}
/* ======================================================================
Function: values_count
Purpose : Count the number of element in the values list
Input : Pointer to the linked list
Output : element numbers
====================================================================== */
int valuelist_count (ValueList * me)
{
int count = 0;
if (me)
while ((me = me->next))
count++;
return (count);
}
/* ======================================================================
Function: valuelist_delete
Purpose : Delete a Linked List
Input : Pointer to the linked list
Output : True if Ok False Otherwise
====================================================================== */
int valuelist_delete (ValueList * me)
{
// Got a pointer
if (me)
{
ValueList *current;
// For each linked list
while ((current = me->next))
{
// Get the next
me->next = current->next;
// Free the current
free(current);
}
// Free the top element
me->next = NULL ;
// Ok
return (true);
}
return (false);
}
/* ======================================================================
Function: uucp_lockname
Purpose : create the lock filename
Input :
Output : -
Comments: Routine taken from picocom source code
===================================================================== */
int uucp_lockname(const char *dir, const char *file)
{
char *p, *cp;
struct stat sb;
if ( !dir || *dir=='\0' || stat(dir, &sb) != 0 )
return -1;
// cut-off initial "/dev/" from file-name
p = strchr(file + 1, '/');
p = p ? p + 1 : (char *)file;
// replace '/'s with '_'s in what remains (after making a copy)
p = cp = strdup(p);
do
{
if ( *p == '/' )
*p = '_';
}
while(*p++);
// build lockname
snprintf(g_lockname, sizeof(g_lockname), "%s/LCK..%s", dir, cp);
// destroy the copy
free(cp);
return 0;
}
/* ======================================================================
Function: uucp_lock
Purpose : create lock file
Input : -
Output : -
Comments: Routine taken from picocom source code
===================================================================== */
int uucp_lock(void)
{
int r, fd, pid;
char buf[16];
mode_t m;
// alread a lock ?
if ( g_lockname[0] == '\0' )
return 0;
fd = open(g_lockname, O_RDONLY);
if ( fd >= 0 )
{
r = read(fd, buf, sizeof(buf));
close(fd);
// if r == 4, lock file is binary (old-style)
pid = (r == 4) ? *(int *)buf : strtol(buf, NULL, 10);
if ( pid > 0 && kill((pid_t)pid, 0) < 0 && errno == ESRCH )
{
// stale lock file
fprintf(stdout, "Removing stale lock: %s\n", g_lockname);
sleep(1);
unlink(g_lockname);
}
else
{
g_lockname[0] = '\0';
errno = EEXIST;
return -1;
}
}
// lock it
m = umask(022);
fd = open(g_lockname, O_WRONLY|O_CREAT|O_EXCL, 0666);
if ( fd < 0 )
{
g_lockname[0] = '\0';
return -1;
}
umask(m);
snprintf(buf, sizeof(buf), "%04d\n", getpid());
write(fd, buf, strlen(buf));
close(fd);
return 0;
}
/* ======================================================================
Function: uucp_unlock
Purpose : unlock open lock
Input : -
Output : -
Comments: Routine taken from picocom source code
===================================================================== */
int uucp_unlock(void)
{
if ( g_lockname[0] )
unlink(g_lockname);
return 0;
}
#ifdef USE_MYSQL
/* ======================================================================
Function: db_open
Purpose : open database connexion
Input : pointer to mysql structure
Output : EXIT_SUCCESS if ok else EXIT_FAILURE
Comments: -
===================================================================== */
int db_open( MYSQL * pmysql)
{
MYSQL *mysql_connection;
// Open MySQL Database and read timestamp of the last record written
if(!mysql_init(pmysql))
{
log_syslog(stderr, "Cannot initialize MySQL");
stats.mysqliniterror++;
}
else
{
stats.mysqlinitok++;
// connect to database
mysql_connection = mysql_real_connect(pmysql, opts.server, opts.user, opts.password, opts.database, opts.serverport, NULL, 0);
if(mysql_connection == NULL)
{
log_syslog(stderr, "%d: %s \n", mysql_errno(pmysql), mysql_error(pmysql));
stats.mysqlconnecterror++;
return(EXIT_FAILURE);
}
else
{
stats.mysqlconnectok++;
}
}
return (EXIT_SUCCESS);
}
/* ======================================================================
Function: db_close
Purpose : close database connexion
Input : pointer to mysql structure
Output : -
Comments: -
===================================================================== */
void db_close( MYSQL * pmysql)
{
// close MySQL Database
mysql_close(pmysql);
}
#endif
#ifdef USE_EMONCMS
/* ======================================================================
Function: http_write
Purpose : callback function when curl write return data
Input : see curl API
Output : -
Comments: -
===================================================================== */
size_t http_write(char *ptr, size_t size, size_t nmemb, void *userdata)
{
// clean up our own receive buffer
bzero(&http_buffer, HTTP_BUFFER_SIZE);
/* Copy curl's received data into our own buffer */
if (size*nmemb < HTTP_BUFFER_SIZE - 1 )
{
memcpy(&http_buffer, ptr, size*nmemb);
return (size*nmemb);
}
else
{
memcpy(&http_buffer, ptr, HTTP_BUFFER_SIZE - 1);
return (HTTP_BUFFER_SIZE);
}
}
/* ======================================================================
Function: http_post
Purpose : post data to emoncms
Input : full url to post data
Output : true if emoncms returned ok, false otherwise
Comments: we don't exit if not working, neither mind, take it next time
===================================================================== */
int http_post( char * str_url )
{
CURLcode res;
int retcode = false;
// New post
stats.curl_post++;
// Set curl URL
if ( curl_easy_setopt(g_pcurl, CURLOPT_URL, str_url) != CURLE_OK )
log_syslog(stderr, "Error while setting curl url %s : %s", str_url, curl_easy_strerror(res));
else
{
if (curl_easy_setopt(g_pcurl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER) != CURLE_OK )
log_syslog(stderr, "Error while setting curl header : %s", curl_easy_strerror(res));
if ( curl_easy_setopt(g_pcurl, CURLOPT_XOAUTH2_BEARER, opts.apikey) != CURLE_OK )
log_syslog(stderr, "Error while setting curl header : %s", curl_easy_strerror(res));
else
{
// Perform the request, res will get the return code
if( (res = curl_easy_perform(g_pcurl)) != CURLE_OK)
{
log_syslog(stderr, "Error on http request %s : %s", str_url, curl_easy_strerror(res));
stats.curl_posterror++;
if (res == CURLE_OPERATION_TIMEDOUT)
stats.curl_timeout++;
}
else
{
// return data received
if (opts.verbose)
log_syslog(stdout, "http_post %s ==> '%s'\n", str_url, http_buffer);
// emoncms returned string "ok", all went fine
if (strcmp(http_buffer, "ok") == 0 )
{
retcode = true;
stats.curl_postok++;
}
}
}
}
return retcode;
}
#endif
/* ======================================================================
Function: signal_handler
Purpose : Interrupt routine Code for signal
Input : signal received
Output : -
Comments:
====================================================================== */
void signal_handler (int signum)
{
// Does we received CTRL-C ?
if ( signum==SIGINT )
{
// Indicate we want to quit
g_exit_pgm = true;
log_syslog(stdout, "\nReceived SIGINT\n");
}
else if ( signum==SIGTERM )
{
// Indicate we want to quit
g_exit_pgm = true;
log_syslog(stdout, "\nReceived SIGTERM\n");
}
else if (signum == SIGUSR1)
{
log_syslog(stdout, "\nReceived SIGUSR1");
show_stats();
}
else if (signum == SIGHUP)
{
log_syslog(stdout, "\nReceived SIGHUP");
// Reload configuration would be a good job;
// To DO
}
}
/* ======================================================================
Function: tlf_init_serial
Purpose : initialize serial port for receiving teleinfo
Input : -
Output : Serial Port Handle
Comments: -
====================================================================== */
int tlf_init_serial(void)
{
int tty_fd, r ;
struct termios termios ;
// Open serial device
if ( (tty_fd = open(opts.port, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) < 0 )
fatal( "tlf_init_serial %s: %s", opts.port, strerror(errno));
else
log_syslog( stdout, "'%s' opened.\n",opts.port);
// Set descriptor status flags
fcntl (tty_fd, F_SETFL, O_RDWR ) ;
// lock serial ?
if ( !opts.nolock )
{
uucp_lockname(UUCP_LOCK_DIR, opts.port);
if ( uucp_lock() < 0 )
fatal("cannot lock %s: %s", opts.port, strerror(errno));
}
// Get current parameters for saving
if ( (r = tcgetattr(tty_fd, &g_oldtermios)) < 0 )
log_syslog(stderr, "cannot get current parameters %s: %s", opts.port, strerror(errno));
// copy current parameters and change for our own
memcpy( &termios, &g_oldtermios, sizeof(termios));
// raw mode
cfmakeraw(&termios);
// Set serial speed to 1200 bps
if (cfsetospeed(&termios, B1200) < 0 || cfsetispeed(&termios, B1200) < 0 )
log_syslog(stderr, "cannot set serial speed to 1200 bps: %s", strerror(errno));
// Parity Even
termios.c_cflag &= ~PARODD;
termios.c_cflag |= PARENB;
// 7 databits
termios.c_cflag = (termios.c_cflag & ~CSIZE) | CS7;
// No Flow Control
termios.c_cflag &= ~(CRTSCTS);
termios.c_iflag &= ~(IXON | IXOFF | IXANY);
// Local
termios.c_cflag |= CLOCAL;
// No minimal char but 5 sec timeout
termios.c_cc [VMIN] = 0 ;
termios.c_cc [VTIME] = 50 ;
// now setup the whole parameters
if ( tcsetattr (tty_fd, TCSANOW | TCSAFLUSH, &termios) <0)
log_syslog(stderr, "cannot set current parameters %s: %s", opts.port, strerror(errno));
// Sleep 50ms
// trust me don't forget this one, it will remove you some
// headache to find why serial is not working
usleep(50000);
return tty_fd ;
}
/* ======================================================================
Function: tlf_close_serial
Purpose : close serial port for receiving teleinfo
Input : Serial Port Handle
Output : -
Comments:
====================================================================== */
void tlf_close_serial(int device)
{
if (device)
{
// flush and restore old settings
tcsetattr(device, TCSANOW | TCSAFLUSH, &g_oldtermios);
close(device) ;
}
}
/* ======================================================================
Function: tlf_checksum_ok
Purpose : check if teleinfo frame checksum is correct
Input : -
Output : true or false
Comments:
====================================================================== */
int tlf_checksum_ok(char *etiquette, char *valeur, char checksum)
{
int i ;
unsigned char sum = 32 ; // Somme des codes ASCII du message + un espace
for (i=0; i < strlen(etiquette); i++)
sum = sum + etiquette[i] ;
for (i=0; i < strlen(valeur); i++)
sum = sum + valeur[i] ;
sum = (sum & 63) + 32 ;
// Return 1 si checkum ok.
if ( sum == checksum)
return ( true ) ;
return ( false ) ;
}