Line data Source code
1 : /** \file trippLitePDU.hpp
2 : * \brief The MagAO-X Tripp Lite Power Distribution Unit controller.
3 : *
4 : * \author Jared R. Males (jaredmales@gmail.com)
5 : *
6 : * \ingroup trippLitePDU_files
7 : */
8 :
9 : #ifndef trippLitePDU_hpp
10 : #define trippLitePDU_hpp
11 :
12 :
13 : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
14 : #include "../../magaox_git_version.h"
15 :
16 : #ifdef XWC_SIM_MODE
17 :
18 : #include "trippLitePDU_simulator.hpp"
19 :
20 : #endif
21 :
22 : /** \defgroup trippLitePDU Tripp Lite PDU
23 : * \brief Control of MagAO-X Tripp Lite PDUs.
24 : *
25 : * <a href="../handbook/operating/software/apps/trippLitePDU.html">Application Documentation</a>
26 : *
27 : * \ingroup apps
28 : *
29 : */
30 :
31 : /** \defgroup trippLitePDU_files Tripp Lite PDU Files
32 : * \ingroup trippLitePDU
33 : */
34 :
35 : namespace MagAOX
36 : {
37 : namespace app
38 : {
39 :
40 : /// MagAO-X application to control a Tripp Lite power distribution unit (PDU).
41 : /** The device outlets are organized into channels. See \ref dev::outletController for details of configuring the channels.
42 : *
43 : * The line frequency and voltage, and the total load on the PDU, are monitored.
44 : *
45 : * \todo need username and secure password handling
46 : * \todo need to recognize signals in tty polls and not return errors, etc.
47 : * \todo begin logging freq/volt/amps telemetry
48 : * \todo research load warnings
49 : * \todo tests for parser
50 : * \todo test for load warnings
51 : * \todo load warnings/crit values can be logged on parse errors -- make this an issue
52 : * \todo segfaults if device can not be reached on network -- make this an issue
53 : *
54 : * \ingroup trippLitePDU
55 : */
56 : class trippLitePDU : public MagAOXApp<>, public dev::outletController<trippLitePDU>, public dev::ioDevice
57 : {
58 :
59 : protected:
60 :
61 : std::string m_deviceAddr; ///< The device address
62 : std::string m_devicePort; ///< The device port
63 : std::string m_deviceUsername; ///< The login username for this device
64 : std::string m_devicePassFile; ///< The login password for this device
65 : int m_deviceVersion {0}; ///< Version 0 = the old PDUs, version 1 = new PDUMH15NET2LX, which is a new login procedure to get to the CLI.
66 :
67 : float m_freqLowWarn {59}; ///< The low-frequency warning threshold
68 : float m_freqHighWarn {61}; ///< The high-frequency warning threshold
69 :
70 : float m_freqLowAlert {58}; ///< The low-frequency alert threshold
71 : float m_freqHighAlert {62}; ///< The high-frequency alert threshold
72 :
73 : float m_freqLowEmerg {57}; ///< The low-frequency emergency threshold
74 : float m_freqHighEmerg {63}; ///< The high-frequency emergency threshold
75 :
76 : float m_voltLowWarn {105}; ///< The low-voltage warning threshold
77 : float m_voltHighWarn {125}; ///< The high-voltage warning threshold
78 :
79 : float m_voltLowAlert {101}; ///< The low-voltage alert threshold
80 : float m_voltHighAlert {126}; ///< The high-voltage alert threshold
81 :
82 : float m_voltLowEmerg {99}; ///< The low-voltage emergency threshold
83 : float m_voltHighEmerg {128}; ///< The high-voltage emergency threshold
84 :
85 : float m_currWarn {15}; ///< The high-current warning threshold
86 : float m_currAlert {16}; ///< The high-current alert threshold
87 : float m_currEmerg {20}; ///< The high-current emergency threshold
88 :
89 : #ifndef XWC_SIM_MODE
90 : tty::telnetConn m_telnetConn; ///< The telnet connection manager
91 : #endif
92 :
93 : std::string m_status; ///< The device status
94 : float m_frequency {0}; ///< The line frequency reported by the device.
95 : float m_voltage {0}; ///< The line voltage reported by the device.
96 : float m_current {0}; ///< The current being reported by the device.
97 :
98 : public:
99 :
100 : /// Default c'tor.
101 : trippLitePDU();
102 :
103 : /// D'tor, declared and defined for noexcept.
104 1 : ~trippLitePDU() noexcept
105 1 : {}
106 :
107 : /** \name MagAOXApp Interface
108 : *
109 : * @{
110 : */
111 :
112 : /// Setup the configuration system (called by MagAOXApp::setup())
113 : virtual void setupConfig();
114 :
115 : /// load the configuration system results (called by MagAOXApp::setup())
116 : virtual void loadConfig();
117 :
118 : /// Startup functions
119 : /** Setsup the INDI vars.
120 : * Checks if the device was found during loadConfig.
121 : */
122 : virtual int appStartup();
123 :
124 : /// Implementation of the FSM for the tripp lite PDU.
125 : virtual int appLogic();
126 :
127 : /// Do any needed shutdown tasks. Currently nothing in this app.
128 : virtual int appShutdown();
129 :
130 : ///@}
131 :
132 : /** \name outletController Interface
133 : *
134 : * @{
135 : */
136 :
137 : /// Update a single outlet state
138 : /** For the trippLitePDU this isn't possible for a single outlet, so this calls updateOutletStates.
139 : *
140 : * \returns 0 on success
141 : * \returns -1 on error
142 : */
143 : virtual int updateOutletState( int outletNum /**< [in] the outlet number to update */);
144 :
145 : /// Queries the device to find the state of each outlet, as well as other parameters.
146 : /** Sends `devstatus` to the device, and parses the result.
147 : *
148 : * \returns 0 on success
149 : * \returns -1 on error
150 : */
151 : virtual int updateOutletStates();
152 :
153 : /// Turn on an outlet.
154 : /**
155 : * \returns 0 on success
156 : * \returns -1 on error
157 : */
158 : virtual int turnOutletOn( int outletNum /**< [in] the outlet number to turn on */);
159 :
160 : /// Turn off an outlet.
161 : /**
162 : * \returns 0 on success
163 : * \returns -1 on error
164 : */
165 : virtual int turnOutletOff( int outletNum /**< [in] the outlet number to turn off */);
166 :
167 : ///@}
168 :
169 : /** \name Device Interface
170 : *
171 : * These functions invoke the simulator code when enabled.
172 : *
173 : * @{
174 : */
175 : int devConnect();
176 :
177 : int devLogin();
178 :
179 : void devPostLogin();
180 :
181 : int devStatus(std::string & strRead);
182 :
183 : /// Parse the PDU devstatus response.
184 : /**
185 : * \returns 0 on success
186 : * \returns \<0 on error, with value indicating location of error.
187 : */
188 : int parsePDUStatus( std::string & strRead );
189 :
190 : ///@}
191 :
192 : void updateAlarmsAndWarnings();
193 :
194 : protected:
195 :
196 : //declare our properties
197 : pcf::IndiProperty m_indiP_status; ///< The device's status string
198 : pcf::IndiProperty m_indiP_load; ///< The line and load characteristics
199 :
200 :
201 : #ifdef XWC_SIM_MODE
202 :
203 : public:
204 :
205 : trippLitePDU_simulator m_simulator;
206 :
207 : #endif
208 :
209 : };
210 :
211 3 : trippLitePDU::trippLitePDU() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
212 : {
213 1 : m_firstOne = true;
214 1 : m_stateDelay = 5;
215 :
216 1 : setNumberOfOutlets(8);
217 1 : m_loopPause=2000000000;//Default to 2 sec loop pause to lessen the load on the PDUs.
218 :
219 1 : return;
220 0 : }
221 :
222 0 : void trippLitePDU::setupConfig()
223 : {
224 0 : config.add("device.address", "a", "device.address", argType::Required, "device", "address", false, "string", "The device address.");
225 0 : config.add("device.port", "p", "device.port", argType::Required, "device", "port", false, "string", "The device port.");
226 0 : config.add("device.username", "u", "device.username", argType::Required, "device", "username", false, "string", "The device login username.");
227 0 : config.add("device.passfile", "", "device.passfile", argType::Required, "device", "passfile", false, "string", "The device login password file (relative to secrets dir).");
228 0 : config.add("device.powerAlertVersion", "", "device.powerAlertVersion", argType::Required, "device", "powerAlertVersion", false, "int", "The device network interface version. 0 is PDU..., 1 is newer LX platform.");
229 :
230 0 : dev::ioDevice::setupConfig(config);
231 :
232 0 : config.add("limits.freqLowWarn", "", "limits.freqLowWarn", argType::Required, "limits", "freqLowWarn", false, "int", "The low-frequency warning threshold");
233 0 : config.add("limits.freqHighWarn", "", "limits.freqHighWarn", argType::Required, "limits", "freqHighWarn", false, "int", "The high-frequency warning threshold");
234 0 : config.add("limits.freqLowAlert", "", "limits.freqLowAlert", argType::Required, "limits", "freqLowAlert", false, "int", "The low-frequency alert threshold");
235 0 : config.add("limits.freqHighAlert", "", "limits.freqHighAlert", argType::Required, "limits", "freqHighAlert", false, "int", "The high-frequency alert threshold");
236 0 : config.add("limits.freqLowEmerg", "", "limits.freqLowEmerg", argType::Required, "limits", "freqLowEmerg", false, "int", "The low-frequency emergency threshold");
237 0 : config.add("limits.freqHighEmerg", "", "limits.freqHighEmerg", argType::Required, "limits", "freqHighEmerg", false, "int", "The high-frequency emergency threshold");
238 :
239 0 : config.add("limits.voltLowWarn", "", "limits.voltLowWarn", argType::Required, "limits", "voltLowWarn", false, "int", "The low-voltage warning threshold");
240 0 : config.add("limits.voltHighWarn", "", "limits.voltHighWarn", argType::Required, "limits", "voltHighWarn", false, "int", "The high-voltage warning threshold");
241 0 : config.add("limits.voltLowAlert", "", "limits.voltLowAlert", argType::Required, "limits", "voltLowAlert", false, "int", "The low-voltage alert threshold");
242 0 : config.add("limits.voltHighAlert", "", "limits.voltHighAlert", argType::Required, "limits", "voltHighAlert", false, "int", "The high-voltage alert threshold");
243 0 : config.add("limits.voltLowEmerg", "", "limits.voltLowEmerg", argType::Required, "limits", "voltLowEmerg", false, "int", "The low-voltage emergency threshold");
244 0 : config.add("limits.voltHighEmerg", "", "limits.voltHighEmerg", argType::Required, "limits", "voltHighEmerg", false, "int", "The high-voltage emergency threshold");
245 :
246 0 : config.add("limits.currWarn", "", "limits.currWarn", argType::Required, "limits", "currWarn", false, "int", "The high-current warning threshold");
247 0 : config.add("limits.currAlert", "", "limits.currAlert", argType::Required, "limits", "currAlert", false, "int", "The high-current alert threshold");
248 0 : config.add("limits.currEmerg", "", "limits.currEmerg", argType::Required, "limits", "currEmerg", false, "int", "The high-current emergency threshold");
249 :
250 0 : dev::outletController<trippLitePDU>::setupConfig(config);
251 :
252 0 : }
253 :
254 :
255 0 : void trippLitePDU::loadConfig()
256 : {
257 0 : config(m_deviceAddr, "device.address");
258 0 : config(m_devicePort, "device.port");
259 0 : config(m_deviceUsername, "device.username");
260 0 : config(m_devicePassFile, "device.passfile");
261 0 : config(m_deviceVersion, "device.powerAlertVersion");
262 :
263 0 : dev::ioDevice::loadConfig(config);
264 :
265 0 : config(m_freqLowWarn, "limits.freqLowWarn");
266 0 : config(m_freqHighWarn, "limits.freqHighWarn");
267 0 : config(m_freqLowAlert, "limits.freqLowAlert");
268 0 : config(m_freqHighAlert, "limits.freqHighAlert");
269 0 : config(m_freqLowEmerg, "limits.freqLowEmerg");
270 0 : config(m_freqHighEmerg, "limits.freqHighEmerg");
271 :
272 0 : config(m_voltLowWarn, "limits.voltLowWarn");
273 0 : config(m_voltHighWarn, "limits.voltHighWarn");
274 0 : config(m_voltLowAlert, "limits.voltLowAlert");
275 0 : config(m_voltHighAlert, "limits.voltHighAlert");
276 0 : config(m_voltLowEmerg, "limits.voltLowEmerg");
277 0 : config(m_voltHighEmerg, "limits.voltHighEmerg");
278 :
279 0 : config(m_currWarn, "limits.currWarn");
280 0 : config(m_currAlert, "limits.currAlert");
281 0 : config(m_currEmerg, "limits.currEmerg");
282 :
283 0 : dev::outletController<trippLitePDU>::loadConfig(config);
284 :
285 :
286 0 : }
287 :
288 0 : int trippLitePDU::appStartup()
289 : {
290 : #ifdef XWC_SIM_MODE
291 0 : log<text_log>("XWC_SIM_MODE active");
292 : #endif
293 :
294 : // set up the INDI properties
295 0 : REG_INDI_NEWPROP_NOCB(m_indiP_status, "status", pcf::IndiProperty::Text);
296 0 : m_indiP_status.add (pcf::IndiElement("value"));
297 :
298 0 : REG_INDI_NEWPROP_NOCB(m_indiP_load, "load", pcf::IndiProperty::Number);
299 0 : m_indiP_load.add (pcf::IndiElement("frequency"));
300 0 : m_indiP_load.add (pcf::IndiElement("voltage"));
301 0 : m_indiP_load.add (pcf::IndiElement("current"));
302 :
303 0 : if(dev::outletController<trippLitePDU>::appStartup() < 0)
304 : {
305 0 : return log<text_log,-1>("Error setting up INDI for outlet control.", logPrio::LOG_CRITICAL);
306 : }
307 :
308 0 : state(stateCodes::NOTCONNECTED);
309 :
310 0 : return 0;
311 : }
312 :
313 0 : int trippLitePDU::appLogic()
314 : {
315 0 : if( state() == stateCodes::NOTCONNECTED )
316 : {
317 : static int lastrv = 0; //Used to handle a change in error within the same state. Make general?
318 : static int lasterrno = 0;
319 :
320 0 : int rv = devConnect();
321 :
322 0 : if(rv == 0)
323 : {
324 0 : state(stateCodes::CONNECTED);
325 :
326 0 : if(!stateLogged())
327 : {
328 0 : std::string logs = "Connected to " + m_deviceAddr + ":" + m_devicePort;
329 0 : log<text_log>(logs);
330 0 : }
331 0 : lastrv = rv;
332 0 : lasterrno = errno;
333 : }
334 : else
335 : {
336 0 : if(!stateLogged())
337 : {
338 0 : log<text_log>({"Failed to connect to " + m_deviceAddr + ":" + m_devicePort}, logPrio::LOG_ERROR);
339 : }
340 0 : if( rv != lastrv )
341 : {
342 0 : log<software_error>( {__FILE__,__LINE__, 0, rv, tty::ttyErrorString(rv)} );
343 0 : lastrv = rv;
344 : }
345 0 : if( errno != lasterrno )
346 : {
347 0 : log<software_error>( {__FILE__,__LINE__, errno});
348 0 : lasterrno = errno;
349 : }
350 0 : return 0;
351 : }
352 : }
353 :
354 0 : if( state() == stateCodes::CONNECTED )
355 : {
356 0 : int rv = devLogin();
357 :
358 0 : if(rv == 0)
359 : {
360 0 : state(stateCodes::LOGGEDIN);
361 : }
362 : else
363 : {
364 0 : if(rv == TELNET_E_LOGINTIMEOUT)
365 : {
366 0 : state(stateCodes::NOTCONNECTED);
367 0 : log<text_log>("login timedout", logPrio::LOG_ERROR);
368 0 : return 0;
369 : }
370 :
371 0 : state(stateCodes::FAILURE);
372 0 : log<text_log>("login failure", logPrio::LOG_CRITICAL);
373 0 : return -1;
374 : }
375 : }
376 :
377 0 : if(state() == stateCodes::LOGGEDIN)
378 : {
379 0 : devPostLogin();
380 :
381 0 : state(stateCodes::READY);
382 : }
383 :
384 0 : if(state() == stateCodes::READY)
385 : {
386 0 : std::unique_lock<std::mutex> lock(m_indiMutex, std::try_to_lock);
387 :
388 0 : if( !lock.owns_lock())
389 : {
390 0 : return 0;
391 : }
392 :
393 0 : int rv = updateOutletStates();
394 :
395 0 : if(rv < 0) return log<software_error,-1>();
396 :
397 0 : updateAlarmsAndWarnings();
398 :
399 0 : return 0;
400 0 : }
401 :
402 0 : state(stateCodes::FAILURE);
403 0 : log<text_log>("appLogic fell through", logPrio::LOG_CRITICAL);
404 0 : return -1;
405 :
406 : }
407 :
408 0 : int trippLitePDU::appShutdown()
409 : {
410 : //don't bother
411 0 : return 0;
412 : }
413 :
414 0 : int trippLitePDU::updateOutletState( int outletNum )
415 : {
416 : static_cast<void>(outletNum);
417 :
418 0 : return updateOutletStates(); //We can't do just one.
419 : }
420 :
421 :
422 0 : int trippLitePDU::updateOutletStates()
423 : {
424 : int rv;
425 0 : std::string strRead;
426 :
427 0 : rv = devStatus(strRead);
428 :
429 0 : if(rv < 0 )
430 : {
431 0 : log<software_error>("error getting device status");
432 0 : state(stateCodes::NOTCONNECTED);
433 0 : return 0;
434 : }
435 :
436 0 : if(rv > 0)
437 : {
438 0 : return 0; //this means the re-read was successful, but we don't want to parse this time.
439 : }
440 :
441 0 : rv = parsePDUStatus( strRead);
442 :
443 0 : if(rv == 0)
444 : {
445 0 : updateIfChanged(m_indiP_status, "value", m_status);
446 :
447 0 : updateIfChanged(m_indiP_load, "frequency", m_frequency);
448 :
449 0 : updateIfChanged(m_indiP_load, "voltage", m_voltage);
450 :
451 0 : updateIfChanged(m_indiP_load, "current", m_current);
452 :
453 0 : dev::outletController<trippLitePDU>::updateINDI();
454 : }
455 : else
456 : {
457 0 : log<software_error>({0, rv, "parse error"});
458 : }
459 :
460 0 : return 0;
461 0 : }
462 :
463 0 : int trippLitePDU::turnOutletOn( int outletNum )
464 : {
465 0 : std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before doing anything
466 :
467 : #ifndef XWC_SIM_MODE
468 : std::string cmd = std::format("loadctl on -o {} --force\r", (outletNum+1)); //Internally 0 counted, device starts at 1.
469 :
470 : int rv = m_telnetConn.writeRead( cmd, true, m_writeTimeout, m_readTimeout);
471 :
472 : #else
473 :
474 0 : int rv = m_simulator.turnOutletOn(outletNum);
475 :
476 : #endif
477 :
478 0 : if(rv < 0)
479 : {
480 0 : return log<software_error, -1>({0, rv, "telnet error"});
481 : }
482 :
483 0 : return 0;
484 0 : }
485 :
486 0 : int trippLitePDU::turnOutletOff( int outletNum )
487 : {
488 0 : std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before doing anything
489 :
490 : #ifndef XWC_SIM_MODE
491 : std::string cmd = std::format("loadctl off -o {} --force\r", (outletNum+1)); //Internally 0 counted, device starts at 1.
492 :
493 : int rv = m_telnetConn.writeRead( cmd, true, m_writeTimeout, m_readTimeout);
494 :
495 : #else
496 :
497 0 : int rv = m_simulator.turnOutletOff(outletNum);
498 :
499 : #endif
500 :
501 0 : if(rv < 0) return log<software_error, -1>({__FILE__, __LINE__, 0, rv, "telnet error"});
502 :
503 0 : return 0;
504 0 : }
505 :
506 0 : int trippLitePDU::devConnect()
507 : {
508 : #ifndef XWC_SIM_MODE
509 :
510 : return m_telnetConn.connect(m_deviceAddr, m_devicePort);
511 :
512 : #else
513 :
514 0 : return m_simulator.connect(m_deviceAddr, m_devicePort);
515 :
516 : #endif
517 : }
518 :
519 0 : int trippLitePDU::devLogin()
520 : {
521 : #ifndef XWC_SIM_MODE
522 :
523 : //Newer version of power alert changed login (at least the first one)
524 : if(m_deviceVersion > 0)
525 : {
526 : m_telnetConn.m_usernamePrompt = "login:";
527 : m_telnetConn.m_prompt = ">>";
528 : }
529 :
530 : return m_telnetConn.login("localadmin", "localadmin");
531 :
532 : #else
533 :
534 0 : return m_simulator.login("localadmin", "localadmin");
535 :
536 : #endif
537 : }
538 :
539 0 : void trippLitePDU::devPostLogin()
540 : {
541 :
542 : #ifndef XWC_SIM_MODE
543 :
544 : //For newer version of power alert we need to select C.L.I.
545 : if(m_deviceVersion > 0)
546 : {
547 : m_telnetConn.writeRead("E\n", false, m_writeTimeout, m_readTimeout);
548 : m_telnetConn.m_prompt = "$> ";
549 : }
550 :
551 : #else
552 :
553 0 : m_simulator.postLogin();
554 :
555 : #endif
556 0 : }
557 :
558 0 : int trippLitePDU::devStatus(std::string & strRead)
559 : {
560 : #ifndef XWC_SIM_MODE
561 :
562 : int rv = m_telnetConn.writeRead("devstatus\n", true, m_writeTimeout, m_readTimeout);
563 :
564 : strRead = m_telnetConn.m_strRead;
565 :
566 : if(rv == TTY_E_TIMEOUTONREAD || rv == TTY_E_TIMEOUTONREADPOLL)
567 : {
568 : rv = m_telnetConn.read(m_readTimeout, false);
569 :
570 : if( rv < 0 )
571 : {
572 : log<software_error>({__FILE__, __LINE__, 0, rv, "devstatus timeout, timed out on re-read: " + tty::ttyErrorString(rv)});
573 : return -1;
574 : }
575 :
576 : log<text_log>("devstatus timeout, re-read successful");
577 :
578 : return 1;
579 : }
580 : else if(rv < 0 )
581 : {
582 : log<software_error>({__FILE__, __LINE__, 0, rv, tty::ttyErrorString(rv)});
583 : return -1;
584 : }
585 :
586 : return 0;
587 :
588 : #else
589 :
590 0 : return m_simulator.devStatus(strRead);
591 :
592 : #endif
593 : }
594 :
595 0 : int trippLitePDU::parsePDUStatus( std::string & strRead )
596 : {
597 0 : size_t curpos = 0;
598 :
599 0 : curpos = strRead.find_first_of("\r\n", curpos);
600 :
601 0 : std::string sstr;
602 :
603 0 : while(curpos < strRead.size())
604 : {
605 0 : size_t eol = strRead.find_first_of("\r\n", curpos);
606 :
607 0 : if(eol == std::string::npos) eol = strRead.size();
608 :
609 0 : if(eol == curpos)
610 : {
611 0 : curpos = eol + 1;
612 0 : continue;
613 : }
614 :
615 0 : sstr = strRead.substr(curpos, eol-curpos);
616 0 : curpos = eol + 1;
617 :
618 0 : if(sstr[0] == '-' || sstr[0] == '0' || sstr[0] == 'L' || sstr[0] == ' ' || sstr[0] == 'D' || sstr[0] == '$') continue;
619 :
620 0 : if(sstr[0] == 'I')
621 : {
622 0 : if(sstr[6] == 'V')
623 : {
624 0 : size_t begin = sstr.find(' ',6);
625 0 : if(begin == std::string::npos)
626 : {
627 0 : return -1;
628 : }
629 :
630 0 : begin = sstr.find_first_not_of(' ', begin);
631 0 : if(begin == std::string::npos)
632 : {
633 0 : return -2;
634 : }
635 :
636 0 : size_t end = sstr.find('V', begin);
637 0 : if(end == std::string::npos)
638 : {
639 0 : return -3;
640 : }
641 :
642 0 : float V = mx::ioutils::stoT<float>( sstr.substr(begin, end-begin) );
643 :
644 0 : m_voltage = V;
645 : }
646 :
647 0 : else if(sstr[6] == 'F')
648 : {
649 0 : size_t begin = sstr.find(' ',6);
650 0 : if(begin == std::string::npos)
651 : {
652 0 : return -4;
653 : }
654 :
655 0 : begin = sstr.find_first_not_of(' ', begin);
656 0 : if(begin == std::string::npos)
657 : {
658 0 : return -5;
659 : }
660 :
661 0 : size_t end = sstr.find('H', begin);
662 0 : if(end == std::string::npos)
663 : {
664 0 : return -6;
665 : }
666 :
667 0 : float F = mx::ioutils::stoT<float>( sstr.substr(begin, end-begin) );
668 :
669 0 : m_frequency = F;
670 : }
671 0 : else return -1;
672 : }
673 0 : else if(sstr[0] == 'O')
674 : {
675 0 : if(sstr[7] == 'C')
676 : {
677 0 : size_t begin = sstr.find(' ',7);
678 0 : if(begin == std::string::npos)
679 : {
680 0 : return -7;
681 : }
682 :
683 0 : begin = sstr.find_first_not_of(' ', begin);
684 0 : if(begin == std::string::npos)
685 : {
686 0 : return -8;
687 : }
688 :
689 0 : size_t end = sstr.find('A', begin);
690 0 : if(end == std::string::npos)
691 : {
692 0 : return -9;
693 : }
694 :
695 0 : float C = mx::ioutils::stoT<float>( sstr.substr(begin, end-begin) );
696 :
697 0 : m_current = C;
698 : }
699 0 : else if(sstr[8] == 'O')
700 : {
701 0 : std::vector<int> outletStates(m_outletStates.size(),OUTLET_STATE_OFF);
702 :
703 0 : size_t begin = sstr.find(' ',8);
704 0 : if(begin == std::string::npos)
705 : {
706 0 : return -10;
707 : }
708 :
709 0 : begin = sstr.find_first_not_of(' ', begin);
710 :
711 0 : if(begin == std::string::npos)
712 : {
713 0 : return -11;
714 : }
715 :
716 0 : while(begin < sstr.size())
717 : {
718 0 : size_t end = sstr.find(' ', begin);
719 0 : if(end == std::string::npos)
720 : {
721 0 : end = sstr.size();
722 : }
723 :
724 0 : int onum = atoi(sstr.substr(begin, end-begin).c_str());
725 :
726 0 : if(onum > 0 && onum < 9)
727 : {
728 0 : outletStates[onum-1] = OUTLET_STATE_ON; //this outlet is on.
729 : }
730 0 : begin = sstr.find_first_not_of(' ', end+1);
731 : }
732 :
733 0 : for(size_t i=0;i<m_outletStates.size();++i)
734 : {
735 0 : m_outletStates[i]=outletStates[i];
736 : }
737 0 : }
738 0 : else if( sstr[7] == 'V' || sstr[7] == 'F')
739 : {
740 0 : continue;
741 : }
742 : else
743 : {
744 0 : return -12;
745 : }
746 : }
747 : else
748 : {
749 0 : return -13;
750 : }
751 : }
752 :
753 0 : return 0;
754 0 : }
755 :
756 0 : void trippLitePDU::updateAlarmsAndWarnings()
757 : {
758 0 : if (m_frequency <= m_freqLowEmerg)
759 : {
760 0 : log<text_log>("Frequency is " + std::to_string(m_frequency) + " Hz, below " +
761 0 : std::to_string(m_freqLowEmerg) + " Hz.", logPrio::LOG_EMERGENCY);
762 : }
763 0 : else if (m_frequency >= m_freqHighEmerg)
764 : {
765 0 : log<text_log>("Frequency is " + std::to_string(m_frequency) + " Hz, above " +
766 0 : std::to_string(m_freqHighEmerg) + " Hz.", logPrio::LOG_EMERGENCY);
767 : }
768 0 : else if (m_frequency <= m_freqLowAlert)
769 : {
770 0 : log<text_log>("Frequency is " + std::to_string(m_frequency) + " Hz, below " +
771 0 : std::to_string(m_freqLowAlert) + " Hz.", logPrio::LOG_ALERT);
772 : }
773 0 : else if (m_frequency >= m_freqHighAlert)
774 : {
775 0 : log<text_log>("Frequency is " + std::to_string(m_frequency) + " Hz, above " +
776 0 : std::to_string(m_freqHighAlert) + " Hz.", logPrio::LOG_ALERT);
777 : }
778 0 : else if(m_frequency <= m_freqLowWarn)
779 : {
780 0 : log<text_log>("Frequency is " + std::to_string(m_frequency) + " Hz, below " +
781 0 : std::to_string(m_freqLowWarn) + " Hz.", logPrio::LOG_WARNING);
782 : }
783 0 : else if (m_frequency >= m_freqHighWarn)
784 : {
785 0 : log<text_log>("Frequency is " + std::to_string(m_frequency) + " Hz, above " +
786 0 : std::to_string(m_freqHighWarn) + " Hz.", logPrio::LOG_WARNING);
787 : }
788 :
789 0 : if (m_voltage <= m_voltLowEmerg)
790 : {
791 0 : log<text_log>("Voltage is " + std::to_string(m_voltage) + " V, below " +
792 0 : std::to_string(m_voltLowEmerg) + " V.", logPrio::LOG_EMERGENCY);
793 : }
794 0 : else if (m_voltage >= m_voltHighEmerg)
795 : {
796 0 : log<text_log>("Voltage is " + std::to_string(m_voltage) + " V, above " +
797 0 : std::to_string(m_voltHighEmerg) + " V.", logPrio::LOG_EMERGENCY);
798 : }
799 0 : else if (m_voltage <= m_voltLowAlert)
800 : {
801 0 : log<text_log>("Voltage is " + std::to_string(m_voltage) + " V, below " +
802 0 : std::to_string(m_voltLowAlert) + " V.", logPrio::LOG_ALERT);
803 : }
804 0 : else if (m_voltage >= m_voltHighAlert)
805 : {
806 0 : log<text_log>("Voltage is " + std::to_string(m_voltage) + " V, above " +
807 0 : std::to_string(m_voltHighAlert) + " V.", logPrio::LOG_ALERT);
808 : }
809 0 : else if(m_voltage <= m_voltLowWarn)
810 : {
811 0 : log<text_log>("Voltage is " + std::to_string(m_voltage) + " V, below " +
812 0 : std::to_string(m_voltLowWarn) + " V.", logPrio::LOG_WARNING);
813 : }
814 0 : else if (m_voltage >= m_voltHighWarn)
815 : {
816 0 : log<text_log>("Voltage is " + std::to_string(m_voltage) + " V, above " +
817 0 : std::to_string(m_voltHighWarn) + " V.", logPrio::LOG_WARNING);
818 : }
819 :
820 0 : if (m_current >= m_currEmerg)
821 : {
822 0 : log<text_log>("Current is " + std::to_string(m_current) + " A, above " +
823 0 : std::to_string(m_currEmerg) + " A.", logPrio::LOG_EMERGENCY);
824 : }
825 0 : else if (m_current >= m_currAlert)
826 : {
827 0 : log<text_log>("Current is " + std::to_string(m_current) + " A, above " +
828 0 : std::to_string(m_currAlert) + " A.", logPrio::LOG_ALERT);
829 : }
830 0 : else if (m_current >= m_currWarn)
831 : {
832 0 : log<text_log>("Current is " + std::to_string(m_current) + " A, above " +
833 0 : std::to_string(m_currWarn) + " A.", logPrio::LOG_WARNING);
834 : }
835 0 : }
836 :
837 : } //namespace app
838 : } //namespace MagAOX
839 :
840 : #endif //trippLitePDU_hpp
|