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