1 /*
2 * brasildriver.c
3 *
4 * Interface for apcctrl to the brazil driver.
5 */
6
7 /*
8 * Copyright (C) 2015-20xx Wagner Popov dos Santos
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU General
12 * Public License as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with this program; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1335, USA.
23 */
24
25 #include "apc.h"
26 #include "brazildriver.h"
27 #include "brazilmodel.h"
28 #include <termios.h>
29 #include <dirent.h>
30 //#include <unistd.h>
31
32
33 #ifndef O_BINARY
34 #define O_BINARY 0
35 #endif
36
BrazilUpsDriver(UPSINFO * ups)37 BrazilUpsDriver::BrazilUpsDriver(UPSINFO *ups) : UpsDriver(ups)
38 {
39 this->model = 0;
40 this->_received = time(NULL);
41 this->_autosetup = true;
42 this->_buffst = 0;
43 this->_bufnxt = 0;
44 }
45 /*
46 * Read UPS events. I.e. state changes.
47 */
check_state()48 bool BrazilUpsDriver::check_state()
49 {
50 Dmsg(50, "Check_state.\n");
51 return ReadData(false) == SUCCESS ? true : false;
52 }
refresh()53 bool BrazilUpsDriver::refresh()
54 {
55 Dmsg(50, "Refresh.\n");
56 tcflush(_ups->fd, TCIFLUSH);
57 sleep(1);
58 return ReadData(false) == SUCCESS ? true : false;
59 }
60
61 /*
62 * Open port
63 */
Open()64 bool BrazilUpsDriver::Open()
65 {
66 int cmd;
67 char *opendev = _ups->device;
68
69 sleep(1);
70
71 #ifdef HAVE_MINGW
72 // On Win32 add \\.\ UNC prefix to COMx in order to correctly address
73 // ports >= COM10.
74 char device[MAXSTRING];
75 if (!strnicmp(_ups->device, "COM", 3)) {
76 snprintf(device, sizeof(device), "\\\\.\\%s", _ups->device);
77 opendev = device;
78 }
79 #endif
80 #ifdef HAVE_DARWIN_OS
81
82 Dmsg(50, "Buscando os devices com nome base configurado.\n");
83
84 char path[MAXSTRING];
85 char filebase[MAXSTRING];
86 char *pch;
87 pch=strrchr(opendev,'/');
88 strncpy ( path, opendev, pch - opendev);
89 strcpy ( path+(pch - opendev), "\0");
90 strcpy ( filebase, pch+1);
91
92 Dmsg(50, "Nome configurado quebrado em path e filename! %s = path(%s) e filebase(%s)\n",opendev,path,filebase);
93
94 if(strncmp(filebase,"cu.",3) != 0){
95 log_event(this->_ups, LOG_ERR, "Apcctrl error! Device name is incorrect! it MUST init with cu.");
96 return false;
97 }
98
99 DIR *dir;
100 struct dirent *entry;
101
102 if (!(dir = opendir(path)))
103 return false;
104
105 bool found = false;
106 while ((entry = readdir(dir)) != NULL){
107 if (strncmp(entry->d_name, filebase, strlen(filebase)) == 0){
108 Dmsg(50, "Device localizado!!! %s/%s\n",path,entry->d_name);
109 if(found){
110 log_event(this->_ups, LOG_WARNING, "WARNING!!! Found more than 1 device with namebase %s!",filebase);
111 }
112 sprintf(opendev, "%s/%s",path,entry->d_name);
113 found = true;
114 }
115 }
116 closedir(dir);
117
118 #endif
119
120 Dmsg(50, "Opening port %s\n", opendev);
121 if ((_ups->fd = open(opendev, O_RDWR | O_NOCTTY | O_NDELAY | O_BINARY | O_CLOEXEC)) < 0)
122 {
123 Dmsg(50, "Cannot open UPS port %s: %s\n", opendev, strerror(errno));
124 return false;
125 }
126
127 Dmsg(50, "Chamando a função fcntl\n");
128 /* Cancel the no delay we just set */
129 cmd = fcntl(_ups->fd, F_GETFL, 0);
130 fcntl(_ups->fd, F_SETFL, cmd & ~O_NDELAY);
131
132 Dmsg(50, "Carregando o oldtio\n");
133 /* Save old settings */
134 tcgetattr(_ups->fd, &_oldtio);
135
136 Dmsg(50, "Definindo a newtio\n");
137 _newtio.c_cflag = B9600 | CS8 | CSTOPB | CLOCAL | CREAD;
138 _newtio.c_iflag = IGNPAR | INPCK; /* Ignore errors, raw input */
139 _newtio.c_oflag = 0; /* Raw output */
140 _newtio.c_lflag = 0; /* No local echo */
141
142 #if defined(HAVE_OPENBSD_OS) || \
143 defined(HAVE_FREEBSD_OS) || \
144 defined(HAVE_NETBSD_OS) || \
145 defined(HAVE_QNX_OS)
146 _newtio.c_ispeed = B9600; /* Set input speed */
147 _newtio.c_ospeed = B9600; /* Set output speed */
148 #endif /* __openbsd__ || __freebsd__ || __netbsd__ */
149
150 /* This makes a non.blocking read() with TIMER_READ (10) sec. timeout */
151 _newtio.c_cc[VMIN] = 0;
152 _newtio.c_cc[VTIME] = TIMER_READ * 10;
153
154 #if defined(HAVE_OSF1_OS) || \
155 defined(HAVE_LINUX_OS) || defined(HAVE_DARWIN_OS)
156 (void)cfsetospeed(&_newtio, B9600);
157 (void)cfsetispeed(&_newtio, B9600);
158 #endif /* do it the POSIX way */
159
160 Dmsg(50, "Primeiro tcflush\n");
161 tcflush(_ups->fd, TCIFLUSH);
162
163 Dmsg(50, "Setando a newtio na porta\n");
164 tcsetattr(_ups->fd, TCSANOW, &_newtio);
165
166 Dmsg(50, "Segundo tcflush\n");
167 sleep(2); // without this usleep we can't flush the port buffer (usb-serial converter)
168 tcflush(_ups->fd, TCIFLUSH);
169
170 this->setIOCtlRecv();
171
172 this->model = 0; // force to instantiate a model again if there is a model already instantiated
173 return true;
174 }
setIOCtlRecv()175 void BrazilUpsDriver::setIOCtlRecv(){
176 int ioctl_status;
177
178 int DTR_flag = TIOCM_DTR;
179 int RTS_flag = TIOCM_RTS;
180
181 Dmsg(199, "Controle de fluxo para recebimento! Setando DTR = true e RTS = false\n");
182
183 ioctl(_ups->fd, TIOCMGET, &ioctl_status);
184 Dmsg(199, "Controle de fluxo old (TIOCMGET): %s\n",BrazilUpsDriver::printBits(sizeof(ioctl_status), &ioctl_status));
185
186 ioctl(_ups->fd, TIOCMBIS, &DTR_flag);
187 ioctl(_ups->fd, TIOCMBIC, &RTS_flag);
188
189 ioctl(_ups->fd, TIOCMGET, &ioctl_status);
190 Dmsg(199, "Controle de fluxo new (TIOCMGET): %s\n",BrazilUpsDriver::printBits(sizeof(ioctl_status), &ioctl_status));
191 }
setIOCtlSend()192 void BrazilUpsDriver::setIOCtlSend(){
193 int ioctl_status;
194
195 int DTR_flag = TIOCM_DTR;
196 int RTS_flag = TIOCM_RTS;
197
198 Dmsg(199, "Controle de fluxo para envio! Setando DTR = false e RTS = true\n");
199
200 ioctl(_ups->fd, TIOCMGET, &ioctl_status);
201 Dmsg(199, "Controle de fluxo old (TIOCMGET): %s\n",BrazilUpsDriver::printBits(sizeof(ioctl_status), &ioctl_status));
202
203 ioctl(_ups->fd, TIOCMBIC, &DTR_flag);
204 ioctl(_ups->fd, TIOCMBIS, &RTS_flag);
205
206 ioctl(_ups->fd, TIOCMGET, &ioctl_status);
207 Dmsg(199, "Controle de fluxo new (TIOCMGET): %s\n",BrazilUpsDriver::printBits(sizeof(ioctl_status), &ioctl_status));
208 }
209
setup()210 bool BrazilUpsDriver::setup()
211 {
212 if(this->model != 0){
213 return true;
214 }
215 Dmsg(50, "Setup UPS %s to load specific model.\n",_ups->upsname);
216 int whait = 0;
217 do{
218 sleep(1);
219 whait++;
220 }while(this->ReadData(false) == FAILURE && whait<60);
221
222 if(this->model == 0){
223 log_event(_ups, LOG_ERR, "Setup fail! There is no model instantiated.");
224 return false;
225 }else{
226 if(!(hibernate_ups || shutdown_ups)){
227 if(this->_autosetup){
228 this->_autosetup = false;
229 if(! this->model->isScheduleSet()){
230 this->programmation(false,0,false,0);
231 }
232 if(! this->model->isLineMode()){
233 this->turnLineOn(true);
234 }
235 if(! this->model->isOutputOn()){
236 this->turnOutputOn(true);
237 }
238 this->turnContinueMode();
239 }
240 }
241 return true;
242 }
243 }
shutdown()244 bool BrazilUpsDriver::shutdown()
245 {
246 Dmsg(50, "Setting default shutdown!!!\n");
247 if(! this->setup()){
248 log_event(_ups, LOG_ERR, "Shutdown fail! There is no model instantiated.");
249 return false;
250 }
251 Dmsg(50, "Setting shutdown with timeout in 1 or 2 minutes!!!\n");
252 this->programmation(true, 1, false, 0);
253 return true;
254 }
kill_power()255 bool BrazilUpsDriver::kill_power()
256 {
257 Dmsg(50, "Kill Power!!!\n");
258 if(! this->setup()){
259 log_event(_ups, LOG_ERR, "Kill power fail! There is no model instantiated.");
260 return false;
261 }
262 if(this->model->isLineMode()){
263 printf("\n\nLine is present! Turn off in 1 or 2 minutes and turn on in 2 or 3 minutes!!!\n\n");
264 Dmsg(50, "Setting kill_power with timeout!!! The UPS will turn off in 1 minute and turn on in 2 minutes!\n");
265 this->programmation(true, 1, true, 2);
266 }else{
267 printf("\n\nSetting auto shutdown and auto turn on!!! The UPS only do this if there is NO load!!\n\n");
268 Dmsg(50, "Setting kill_power with shutdownAuto!!!\n");
269 this->shutdownAuto();
270 }
271 return true;
272 }
273
send(unsigned char * cmd,int size)274 void BrazilUpsDriver::send(unsigned char *cmd, int size){
275 this->setIOCtlSend();
276
277 Dmsg(50, "Buffer to send! len: %02d; data:%s.\n",size,this->strBuffer(cmd,size));
278 tcflush(_ups->fd, TCIFLUSH);
279 char byte;
280 for(int i=0 ; i<size ; i++){
281 byte = cmd[i];
282 write(_ups->fd, &byte, 1);
283 }
284
285 usleep(10000);
286 this->setIOCtlRecv();
287 }
288
programmation(bool turnoff,unsigned int turnoff_minutes,bool turnon,unsigned int turnon_minutes)289 bool BrazilUpsDriver::programmation(bool turnoff, unsigned int turnoff_minutes, bool turnon, unsigned int turnon_minutes){
290 Dmsg(50, "programmation(%s, %2d, %s, %2d)\n",(turnoff?"true":"false"),turnoff_minutes,(turnon?"true":"false"),turnon_minutes);
291 if(! this->setup()){
292 log_event(_ups, LOG_ERR, "Programmation fail! There is no model instantiated.");
293 return false;
294 }
295 unsigned char *cmd = 0;
296 int size = this->model->generateCmdScheduleSet(&cmd, turnoff, turnoff_minutes, turnon, turnon_minutes);
297
298 if(size > 0){
299 int whait = 1;
300 while(whait > 0 && whait <= 10){
301 this->send(cmd,size);
302 this->ReadData(false);
303 if(((turnoff || turnon) && this->model->isScheduleSet()) || (!(turnoff || turnon) && !this->model->isScheduleSet())){
304 whait = 0;
305 }else{
306 whait++;
307 sleep(1);
308 }
309 }
310 if(whait<10){
311 if(turnoff || turnon){
312 log_event(_ups, LOG_NOTICE, "BrazilDriver: programmation set! turnoff: %s, turnoff_min: %02d, turnon: %s, turnon_min: %02d!",(turnoff?"true":"false"),turnoff_minutes,(turnon?"true":"false"),turnon_minutes);
313 }else{
314 log_event(_ups, LOG_NOTICE, "BrazilDriver: programmation canceled! Not turnoff or turnon!");
315 }
316 return true;
317 }else{
318 log_event(_ups, LOG_ERR, "programmation fail!!!");
319 }
320 }
321 return false;
322 }
323
turnLineOn(bool turnon)324 bool BrazilUpsDriver::turnLineOn(bool turnon)
325 {
326 Dmsg(50, "turnLineOn(%s).\n",(turnon?"true":"false"));
327 if(! this->setup()){
328 log_event(_ups, LOG_ERR, "TurnLineOn fail! There is no model instantiated.");
329 return false;
330 }
331 unsigned char *cmd = 0;
332 int size = this->model->generateCmdTurnLineOn(&cmd, turnon);
333 if(size > 0){
334 int whait = 1;
335 while(whait > 0 && whait <= 10){
336 this->send(cmd,size);
337 this->refresh();
338 if((turnon && this->model->isLineMode()) || (!turnon && !this->model->isLineMode())){
339 whait = 0;
340 }else{
341 Dmsg(50, "LineOn programmation! set: %s; read_LineOn: %s.\n",(turnon?"true":"false"),(this->model->isLineMode()?"true":"false"));
342 whait++;
343 }
344 }
345 if(whait<10){
346 if(turnon){
347 log_event(_ups, LOG_NOTICE, "turn Line On!");
348 }else{
349 log_event(_ups, LOG_NOTICE, "turn Line Off!");
350 }
351 return true;
352 }else{
353 log_event(_ups, LOG_ERR, "set Line %s FAIL!!!",(turnon?"On":"Off"));
354 }
355 }
356 return false;
357 }
turnOutputOn(bool turnon)358 bool BrazilUpsDriver::turnOutputOn(bool turnon)
359 {
360 Dmsg(50, "turnOutputOn(%s).\n",(turnon?"true":"false"));
361 if(! this->setup()){
362 log_event(_ups, LOG_ERR, "TurnOutputOn fail! There is no model instantiated.");
363 return false;
364 }
365 unsigned char *cmd = 0;
366 int size = this->model->generateCmdTurnOutputOn(&cmd, turnon);
367 if(size > 0){
368 int whait = 1;
369 while(whait > 0 && whait <= 10){
370 this->send(cmd,size);
371 this->refresh();
372 if((turnon && this->model->isOutputOn()) || (!turnon && !this->model->isOutputOn())){
373 whait = 0;
374 }else{
375 Dmsg(50, "LineOutput programmation! set: %s; read_OutputOn: %s.\n",(turnon?"true":"false"),(this->model->isOutputOn()?"true":"false"));
376 whait++;
377 }
378 }
379 if(whait<10){
380 if(turnon){
381 log_event(_ups, LOG_NOTICE, "turn output On!");
382 }else{
383 log_event(_ups, LOG_NOTICE, "turn output Off!");
384 }
385 return true;
386 }else{
387 log_event(_ups, LOG_ERR, "set Output %s FAIL!!!",(turnon?"On":"Off"));
388 }
389 }
390 return false;
391 }
392
shutdownAuto()393 bool BrazilUpsDriver::shutdownAuto()
394 {
395 Dmsg(50, "shutdownAuto start.\n");
396 if(! this->setup()){
397 log_event(_ups, LOG_ERR, "auto-start! There is no model instanciated.");
398 return false;
399 }
400 unsigned char *cmd = 0;
401 int size = 0;
402 size = this->model->generateCmdShutdownAuto(&cmd);
403 if(size > 0){
404 this->send(cmd,size);
405 log_event(_ups, LOG_NOTICE, "shutdown auto sent.\n");
406 }
407 sleep(1);
408 size = this->model->generateCmdScheduleSet(&cmd, false, 0 , false, 0 );
409 if(size > 0){
410 this->send(cmd,size);
411 log_event(_ups, LOG_NOTICE, "schedule sent.\n");
412 }
413 return true;
414 }
turnContinueMode()415 bool BrazilUpsDriver::turnContinueMode()
416 {
417 Dmsg(50, "continueMode start.\n");
418 if(! this->setup()){
419 log_event(_ups, LOG_ERR, "continue mode! There is no model instanciated.");
420 return false;
421 }
422 unsigned char *cmd = 0;
423 int size = this->model->generateCmdContinueMode(&cmd);
424 if(size > 0){
425 this->send(cmd,size);
426 log_event(_ups, LOG_NOTICE, "continue mode sent.\n");
427 return true;
428 }
429 return false;
430 }
431
Close()432 bool BrazilUpsDriver::Close()
433 {
434 /* Reset serial line to old values */
435 if (_ups->fd >= 0) {
436 Dmsg(50, "Closing port\n");
437 tcflush(_ups->fd, TCIFLUSH);
438 tcsetattr(_ups->fd, TCSANOW, &_oldtio);
439 tcflush(_ups->fd, TCIFLUSH);
440 close(_ups->fd);
441 }
442 _ups->fd = -1;
443 this->model = 0;
444 return true;
445 }
446
447
448 /*
449 * Ever read a complete status MSG.
450 */
ReadData(bool getevents)451 int BrazilUpsDriver::ReadData(bool getevents)
452 {
453 if(getevents){
454 Dmsg(99, "Lendo a porta USB/Serial. GetEvents = true\n");
455 }else{
456 Dmsg(99, "Lendo a porta USB/Serial. GetEvents = false\n");
457 }
458
459 /*
460 * Message structure
461 * msg[0] model number
462 * msg[1-22] paramters
463 * msg[23] checksum
464 * msg[24] end of msg. byte = 254
465 */
466
467 bool bufok = false;
468
469 bool ending = false;
470 unsigned char c;
471 int retval;
472 time_t now = time(NULL);
473 int wait;
474
475 if (getevents) {
476 wait = TIMER_FAST; /* 1 sec, expect fast response */
477 }else{
478 wait = _ups->wait_time;
479 }
480
481
482 #ifdef HAVE_MINGW
483 /* Set read() timeout since we have no select() support. */
484 {
485 COMMTIMEOUTS ct;
486 HANDLE h = (HANDLE)_get_osfhandle(_ups->fd);
487 ct.ReadIntervalTimeout = MAXDWORD;
488 ct.ReadTotalTimeoutMultiplier = MAXDWORD;
489 ct.ReadTotalTimeoutConstant = wait * 1000;
490 ct.WriteTotalTimeoutMultiplier = 0;
491 ct.WriteTotalTimeoutConstant = 0;
492 SetCommTimeouts(h, &ct);
493 }
494 #endif
495
496 while (!ending && this->bufferLen() < BrazilModelAbstract::BUFFERLEN) {
497
498 #if !defined(HAVE_MINGW)
499 fd_set rfds;
500 struct timeval tv;
501
502 FD_ZERO(&rfds);
503 FD_SET(_ups->fd, &rfds);
504 tv.tv_sec = wait;
505 tv.tv_usec = 0;
506
507 errno = 0;
508 retval = select((_ups->fd) + 1, &rfds, NULL, NULL, &tv);
509
510 switch (retval) {
511 case 0: /* No chars available in TIMER seconds. */
512 Dmsg(199, "No chars available in 10 milliseconds! Continue...\n",wait);
513 usleep(10000);
514 continue;
515 case -1:
516 if (errno == EINTR || errno == EAGAIN) { /* assume SIGCHLD */
517 continue;
518 } else if (errno == EBADF) {
519 return FAILURE; /* We're probably shutting down */
520 }
521 Error_abort("Select error on UPS FD. %s\n", strerror(errno));
522 break;
523 default:
524 break;
525 }
526 #endif
527
528 Dmsg(199, "ReadData: going to read _ups->fd\n");
529
530 // do {
531 // retval = read(_ups->fd, &c, 1);
532 // } while (retval <= 0 && (errno == EAGAIN || errno == EINTR));
533 do {
534 retval = read(_ups->fd, &c, 1);
535 } while (retval == -1 && (errno == EAGAIN || errno == EINTR));
536 if (retval <= 0) {
537
538 Dmsg(199, "ReadData: reatval <= 0 == %d\n",retval);
539
540 Dmsg(199, "ReadData: usleep 10 ms\n",retval);
541 usleep(10000);
542
543 /*
544 * Test if connection was losted.
545 */
546 if(now - this->_received > 5){
547 Dmsg(50, "Connection lost.\n");
548 if(! _ups->is_commlost()){
549 _ups->set_commlost();
550 generate_event(_ups, CMDCOMMFAILURE);
551 }
552 if(access(_ups->device, F_OK | W_OK) != -1 ) {
553 if (_ups->fd >= 0){
554 this->Close();
555 }
556 if(this->Open()){
557 // if(this->setup()){
558 // this->read_static_data();
559 // commok = true;
560 // }
561 }
562 }
563 }
564 return FAILURE;
565 }
566
567 this->bufferAdd(c);
568 Dmsg(199, "Buffer! len: %03d; fst: %03d: nxt: %03d; data:%s.\n",this->bufferLen(),this->_buffst, this->_bufnxt,this->strBuffer(this->bufferGet(),this->bufferLen()));
569
570 if(!getevents){
571 int ret = BrazilModelAbstract::testBuffer(this->bufferGet(), this->bufferLen());
572 // ret == -1: error! rotate buffer
573 // ret == 0: get next byte
574 // ret == 1: test ok!
575 if(ret == 1){
576 Dmsg(99, "Buffer ok! len: %03d; data:%s.\n",this->bufferLen(),this->strBuffer(this->bufferGet(),this->bufferLen()));
577
578 bufok = true;
579 if(this->model == 0){
580 this->model = BrazilModelAbstract::newInstance(this->bufferGet()[0],_ups->expander_ampere);
581 if(this->model == 0){
582 log_event(_ups, LOG_ERR, "APC Brazil. Model %u not recognized.",this->bufferGet()[0]);
583 this->bufferDel(1);
584 }
585 }
586 if(this->model != 0){
587 this->model->setBuffer(this->bufferGet(), this->bufferLen());
588 this->bufferDel(this->bufferLen());
589 this->model->refreshVariables();
590 time(&this->_received);
591 ending = true;
592 }
593
594 }
595 if(ret == -1){
596 this->bufferDel(1);
597 }
598 }else{
599 int ret = this->model->testEvents(this->bufferGet(), this->bufferLen());
600 // ret == -1: error! rotate buffer
601 // ret == 0: get next byte
602 // ret == 1: test ok!
603 if(ret == 1){
604 Dmsg(99, "Buffer ok! len: %03d; data:%s.\n",this->bufferLen(),this->strBuffer(this->bufferGet(),this->bufferLen()));
605
606 bufok = true;
607 if(this->model->setEvents(this->bufferGet(), this->bufferLen())){
608 this->bufferDel(this->bufferLen());
609 ending = true;
610 }
611 }
612 if(ret == -1){
613 this->bufferDel(1);
614 }
615 }
616 }
617
618 if(_ups->is_commlost() && ending){
619 _ups->clear_commlost();
620 generate_event(_ups, CMDCOMMOK);
621 }
622 if(ending){
623 tcflush(_ups->fd, TCIFLUSH);
624 }
625
626 if(this->model == 0){
627 return FAILURE;
628 }else{
629 if(bufok){
630 return SUCCESS;
631 }else{
632 return FAILURE;
633 }
634 }
635 }
636
637 /*
638 * Setup capabilities structure for UPS
639 */
get_capabilities()640 bool BrazilUpsDriver::get_capabilities()
641 {
642 Dmsg(50, "get_capabilities()\n");
643 write_lock(_ups);
644
645 _ups->UPS_Cap[CI_BATTLEV] = TRUE;
646 _ups->UPS_Cap[CI_RUNTIM] = TRUE;
647 _ups->UPS_Cap[CI_VBATT] = TRUE;
648 _ups->UPS_Cap[CI_VLINE] = TRUE;
649 _ups->UPS_Cap[CI_VMAX] = TRUE;
650 _ups->UPS_Cap[CI_VMIN] = TRUE;
651 _ups->UPS_Cap[CI_LOAD] = TRUE;
652 _ups->UPS_Cap[CI_VOUT] = TRUE;
653 _ups->UPS_Cap[CI_ITEMP] = TRUE;
654 _ups->UPS_Cap[CI_FREQ] = TRUE;
655 _ups->UPS_Cap[CI_UPSMODEL] = TRUE;
656 _ups->UPS_Cap[CI_NOMINV] = TRUE;
657 _ups->UPS_Cap[CI_NOMOUTV] = TRUE;
658 _ups->UPS_Cap[CI_NOMPOWER] = TRUE;
659 _ups->UPS_Cap[CI_NOMBATTV] = TRUE;
660 _ups->UPS_Cap[CI_Overload] = TRUE;
661 _ups->UPS_Cap[CI_LowBattery] = TRUE;
662
663 /*
664 * Sugestão de Levi Pereira (https://sourceforge.net/u/leviweb/profile/)
665 */
666 _ups->UPS_Cap[CI_LoadApparent] = TRUE;
667 _ups->UPS_Cap[CI_OutputCurrent] = TRUE;
668 _ups->UPS_Cap[CI_NomApparent] = TRUE;
669
670 _ups->UPS_Cap[CI_LoadValue] = TRUE;
671 _ups->UPS_Cap[CI_LoadApparentValue] = TRUE;
672
673
674 write_unlock(_ups);
675 return true;
676 }
677
678 /*
679 * Read UPS info that remains unchanged -- e.g. transfer
680 * voltages, shutdown delay, ...
681 *
682 * This routine is called once when apcctrl is starting
683 */
read_static_data()684 bool BrazilUpsDriver::read_static_data()
685 {
686 Dmsg(50, "read_static_data()\n");
687 if(this->model == 0){
688 return false;
689 }
690
691 this->model->setBufferLock();
692 write_lock(_ups);
693
694 /* Clear Status */
695 _ups->Status = 0;
696
697 /* model, firmware */
698 strlcpy(_ups->upsmodel, this->model->getModelName(), sizeof(_ups->upsmodel));
699
700 /* Nominal battery voltage */
701 _ups->nombattv = this->model->getBatteryVoltageNom();
702
703 /* Nominal out voltage */
704 _ups->NomOutputVoltage = this->model->getOutputVoltageNom();
705
706 _ups->NomPower = this->model->getOutputActivePowerNom();
707 _ups->NomApparentPower = this->model->getOutputPowerNom();
708
709 /* there is no information about this. Battery need to be always present, I think. */
710 _ups->set_battpresent();
711
712 _ups->extbatts = 0;
713
714 _ups->lastxfer = XFER_NONE;
715
716 write_unlock(_ups);
717 this->model->setBufferUnlock();
718
719 return true;
720 }
721
722 /*
723 * Read UPS info that changes -- e.g. Voltage, temperature, ...
724 *
725 * This routine is called once every 5 seconds to get
726 * a current idea of what the UPS is doing.
727 */
read_volatile_data()728 bool BrazilUpsDriver::read_volatile_data()
729 {
730 Dmsg(50, "read_volatile_data()\n");
731 if(this->model == 0)
732 return false;
733 this->model->setBufferLock();
734 write_lock(_ups);
735
736 /* save time stamp */
737 time(&_ups->poll_time);
738
739 _ups->set_online(this->model->isLineMode());
740
741 /* Nominal voltage can change because the UPSs are bivolt */
742 _ups->NomInputVoltage = this->model->getLineVoltageNom();
743 _ups->LineMin = this->model->getLineVoltageMin();
744 _ups->LineMax = this->model->getLineVoltageMax();
745
746 /* LINE_FREQ */
747 _ups->LineFreq = this->model->getLineFrequency();
748
749 if(this->model->isLineMode()){
750 _ups->OutputFreq = this->model->getLineFrequency();
751 }else{
752 _ups->OutputFreq = 60;
753 }
754
755 _ups->set_battlow(this->model->isBatteryCritical());
756
757 _ups->TimeLeft = this->model->getBatteryTimeLeft();
758
759 /* BATT_VOLTAGE */
760 _ups->BattVoltage = this->model->getBatteryVoltage();
761
762 /* BATT_FULL Battery level percentage */
763 _ups->BattChg = this->model->getBatteryLevel();;
764
765 /* LINE_VOLTAGE */
766 _ups->LineVoltage = this->model->getLineVoltage();
767
768 /* OUTPUT_VOLTAGE */
769 _ups->OutputVoltage = this->model->getOutputVoltage();
770
771 /* OUTPUT_CURRENT */
772 _ups->OutputCurrent = this->model->getOutputCurrent();
773
774 /* OUTPUT_LOAD */
775 _ups->LoadApparent = this->model->getLoadPowerPercent();
776 _ups->LoadApparentValue = this->model->getOutputPower();
777
778 /* UPS_LOAD */
779 _ups->UPSLoad = this->model->getLoadActivePowerPercent();
780 _ups->UPSLoadValue = this->model->getOutputActivePower();
781
782 /* UPS_TEMP */
783 _ups->UPSTemp = this->model->getTemperature();
784
785 _ups->set_overload(this->model->isOverload());
786
787 Dmsg(99, "Extra status! LineOn: %s; "
788 "OutputOn: %s; "
789 "220V: %s; "
790 "Charging: %s; "
791 "Overload: %s; "
792 "Critical battery: %s; "
793 "Date (yyyy-mm-dd HH:MM:SS WeekDay): %04d-%02d-%02d %02d:%02d:%02d %1d; "
794 "Days of week programmed (Sunday-Saturday): %1d%1d%1d%1d%1d%1d%1d.\n",
795 (this->model->isLineMode()?"true":"false"),
796 (this->model->isOutputOn()?"true":"false"),
797 (this->model->isLine220V()?"true":"false"),
798 (this->model->isCharging()?"true":"false"),
799 (this->model->isOverload()?"true":"false"),
800 (this->model->isBatteryCritical()?"true":"false"),
801 this->model->getYear(),
802 this->model->getMonth(),
803 this->model->getDayOfMonth(),
804 this->model->getHour(),
805 this->model->getMinute(),
806 this->model->getSecond(),
807 this->model->getDayOfWeek(),
808 this->model->isScheduleSetDayOfWeek(Sunday),
809 this->model->isScheduleSetDayOfWeek(Monday),
810 this->model->isScheduleSetDayOfWeek(Tuesday),
811 this->model->isScheduleSetDayOfWeek(Wednesday),
812 this->model->isScheduleSetDayOfWeek(Thursday),
813 this->model->isScheduleSetDayOfWeek(Friday),
814 this->model->isScheduleSetDayOfWeek(Saturday)
815 );
816
817 write_unlock(_ups);
818 this->model->setBufferUnlock();
819
820 return true;
821 }
822
strBuffer(unsigned char * buffer,int len)823 char *BrazilUpsDriver::strBuffer(unsigned char* buffer, int len){
824 static char out[900];
825 for(int i=0 ; i<len && i<99 ; i++){
826 sprintf (out+9*i," %02d(%03u);",i,*(buffer+i));
827 }
828 return out;
829 }
830
getEventsStr(char ** events)831 int BrazilUpsDriver::getEventsStr(char **events){
832 Dmsg(50, "shutdownAuto start.\n");
833 if(! this->setup()){
834 log_event(_ups, LOG_ERR, "auto-start! There is no model instantiated.");
835 return false;
836 }
837 unsigned int sizeout = 0;
838 unsigned char *cmd = 0;
839 int sizecmd = this->model->generateCmdGetEvents(&cmd);
840 if(sizecmd > 0){
841 tcflush(_ups->fd, TCIFLUSH);
842 sleep(1);
843 this->send(cmd,sizecmd);
844 this->ReadData(true);
845 sizeout = this->model->getEventsStr(events);
846 }
847 return sizeout;
848 }
849
bufferAdd(unsigned char c)850 void BrazilUpsDriver::bufferAdd(unsigned char c){
851 this->_buffer[this->_bufnxt] = c;
852 this->_bufnxt++;
853 if(this->_bufnxt == BrazilModelAbstract::BUFFERLEN){
854 this->_bufnxt = 0;
855 }
856
857 if(this->bufferLen() > BrazilModelAbstract::BUFFERLEN){
858 this->bufferDel(1);
859 }
860 Dmsg(199, "bufferAdd(%u) >> buflen = %u, buffst = %u, bufnxt = %u\n",c,this->bufferLen(), this->_buffst,this->_bufnxt);
861 }
bufferDel(unsigned int len)862 void BrazilUpsDriver::bufferDel(unsigned int len){
863 if(len >= BrazilModelAbstract::BUFFERLEN){
864 log_event(_ups, LOG_ERR, "buffferDel! Len parameter greatest than BrazilModelAbstract::BUFFERLEN.");
865 return;
866 }
867 if(len > this->bufferLen()){
868 log_event(_ups, LOG_ERR, "buffferDel! Len parameter greatest than bufferLen function return.");
869 return;
870 }
871 this->_buffst += len;
872 this->_buffst = this->_buffst % BrazilModelAbstract::BUFFERLEN;
873 Dmsg(199, "bufferDel(%u) >> buflen = %u, buffst = %u, bufnxt = %u\n",len,this->bufferLen(), this->_buffst,this->_bufnxt);
874 }
bufferLen()875 unsigned int BrazilUpsDriver::bufferLen(){
876 unsigned int ret = 0;
877 if(this->_buffst > this->_bufnxt){
878 ret = BrazilModelAbstract::BUFFERLEN - this->_buffst;
879 ret += this->_bufnxt;
880 }else{
881 ret = this->_bufnxt - this->_buffst;
882 }
883 return ret;
884 }
bufferGet()885 unsigned char *BrazilUpsDriver::bufferGet(){
886 static unsigned char out[BrazilModelAbstract::BUFFERLEN];
887 for(unsigned int i=0 ; i<this->bufferLen() ; i++){
888 out[i] = this->_buffer[(this->_buffst + i) % BrazilModelAbstract::BUFFERLEN];
889 }
890 return out;
891 }
892
printBits(size_t const size,void const * const ptr)893 const char *BrazilUpsDriver::printBits(size_t const size, void const * const ptr)
894 {
895 unsigned char *in = (unsigned char*) ptr;
896 char *out = (char *)malloc((size * 8) + 1);
897
898 int pos = 0;
899 for (int i = size-1 ; i>=0 ; i--)
900 {
901 for (int j=7 ; j>=0 ; j--)
902 {
903 if(((in[i] >> j) & 1) == 1 ){
904 out[pos] = '1';
905 }else{
906 out[pos] = '0';
907 }
908 pos++;
909 }
910 }
911 out[pos] = '\0';
912 return out;
913 }
914