15#include "../../libMagAOX/libMagAOX.hpp"
16#include "../../magaox_git_version.h"
166 const std::
string &line
171 const std::
string &line
250 const std::string::size_type
first =
token.find_first_not_of(
" \t\r" );
252 if(
first == std::string::npos )
257 const std::string::size_type
last =
token.find_last_not_of(
" \t\r" );
264 return static_cast<double>(
ts.tv_sec ) + 1
e-9 *
static_cast<double>(
ts.tv_nsec );
276 std::vector<std::string>
fields;
277 std::stringstream
ss( line );
280 while( std::getline(
ss,
field,
'|' ) )
297 std::vector<std::string>
lines;
301 while( std::getline(
ss, line ) )
303 if( !line.empty() && line.back() ==
'\r' )
313 lines.push_back( line );
332 config.add(
"input.path",
340 "Path to the file containing the two-line flow RPM record." );
341 config.add(
"input.maxAge",
349 "Maximum source age in seconds before the reading is treated as stale. Default is 60." );
350 config.add(
"input.fanDescriptor",
352 "input.fanDescriptor",
358 "Expected descriptor token in the source file. Default is CHA_FAN1." );
359 config.add(
"input.badValue",
367 "Bad-value sentinel published when the file can not be read or parsed. Default is -999." );
368 config.add(
"input.errorLogInterval",
370 "input.errorLogInterval",
376 "Minimum interval in seconds between repeated logs of the same persistent error. Default is 60." );
405 indi::addNumberElement<double>(
m_indiP_status,
"flow_rate", -1
e9, 1
e9, 0.0,
"%0.3f",
"Flow Rate [LPM]" );
406 indi::addNumberElement<double>(
m_indiP_status,
"age", -1
e9, 1
e9, 0.0,
"%0.3f",
"Age [s]" );
479 std::stringstream
ss( line );
483 if( !(
ss >>
sec >> nsec ) )
500 ts.tv_nsec =
static_cast<long>( nsec );
548 result.m_sourceTs = { 0, 0 };
564 if(
lines.size() < 2 )
570 if(
lines.size() != 2 )
604 result.m_sourceTs = { 0, 0 };
611 std::stringstream buffer;
612 buffer <<
ifs.rdbuf();
624 return "open_failed";
626 return "missing_timestamp";
628 return "timestamp_parse_failed";
630 return "missing_record";
632 return "record_parse_failed";
634 return "wrong_descriptor";
636 return "wrong_units";
752 std::vector<std::string>( {
"flow_rate",
"age" } ),
The base-class for XWCTk applications.
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
stateCodes::stateCodeT state()
Get the current state code.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
The MagAO-X flow-from-RPM monitor.
timespec m_lastErrorLogTs
Time of the most recent error log emission.
parseStatus m_status
Outcome of the parse attempt.
double m_age
Current published age in seconds or the bad-value sentinel.
parseStatus
Status returned by the file parser.
dev::telemeter< flowRPM > telemeterT
The telemeter base type.
bool m_lastTelemValid
Last validity state written to telemetry.
double m_flowRate
Displayed flow rate in LPM or the bad-value sentinel.
virtual int readAndParse(parseResult &result, const timespec &now) const
Read the configured file and parse its current contents.
const std::string & fanDescriptor() const
Get the configured fan descriptor.
int recordTelem(const logger::telem_flowrpm *)
Record telemetry when requested by the telemeter helper.
~flowRPM() noexcept
D'tor, declared and defined for noexcept.
parseResult reconcileResult(const parseResult &result, const timespec &now) const
Reconcile a newly parsed result against the currently displayed state.
double m_flowRate
Current published flow rate in LPM or the bad-value sentinel.
double m_errorLogInterval
Minimum interval between repeated logs for the same persistent error.
virtual void setupConfig()
Set up the application configuration.
double m_age
Displayed age in seconds or the bad-value sentinel.
std::string m_lastErrorKey
Key for the most recently logged error class.
double badValue() const
Get the configured bad-value sentinel.
int parseTimestamp(timespec &ts, const std::string &line) const
Parse a source timestamp line into a timespec.
virtual void loadConfig()
Load the application configuration.
const std::string & inputPath() const
Get the configured input path.
parseStatus parseRecordLine(double &flowRate, const std::string &line) const
Parse the sensor record line into a flow rate in LPM.
friend class flowRPM_test
bool m_haveValidReading
Whether the current displayed value is valid.
static std::string statusKey(parseStatus status)
Get the string key used for a parse status in log rate limiting.
timespec m_sourceTs
Timestamp parsed from the input file for the currently displayed value.
double flowRate() const
Get the currently published flow rate.
std::string m_inputPath
Path to the file written by the systemd producer.
double age() const
Get the currently published age.
virtual int appLogic()
Implementation of the FSM for flowRPM.
pcf::IndiProperty m_indiP_status
Read-only status property exposing flow rate and age.
double m_badValue
Bad-value sentinel published on parse or availability failures.
double m_maxAge
Maximum allowed source age in seconds before the reading is treated as stale.
timespec m_sourceTs
Parsed source timestamp when available.
std::string m_fanDescriptor
Expected fan descriptor in the pipe-delimited record.
virtual int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
bool haveValidReading() const
Get whether the current published value is valid.
virtual int recordFlow(bool force=false)
Record the currently displayed flow state to telemetry.
virtual int publishResult(const parseResult &result)
Publish a parse result to INDI and runtime state.
virtual int appStartup()
Perform application startup.
virtual int appShutdown()
Shut the application down.
double m_lastTelemFlowRate
Last flow value written to telemetry.
bool shouldLogError(const std::string &key, const timespec &now)
Determine whether a repeated error should be logged now.
double maxAge() const
Get the configured maximum reading age in seconds.
int parseFileContents(parseResult &result, const std::string &contents, const timespec &now) const
Parse the two-line file contents using a supplied current time.
int checkRecordTimes()
Check whether telemetry records need to be forced.
double errorLogInterval() const
Get the configured repeated-error log interval.
Parsed result of the current file contents.
std::vector< std::string > splitPipeDelimited(const std::string &line)
Split a pipe-delimited sensor line into trimmed fields.
double elapsedSeconds(const timespec &start, const timespec &end)
Measure elapsed seconds between two timestamps.
std::vector< std::string > splitLogicalLines(const std::string &contents)
Split file contents into non-empty logical lines.
double timespecToDouble(const timespec &ts)
Convert a timespec to fractional seconds.
std::string trimToken(const std::string &token)
Trim leading and trailing ASCII whitespace from a token.
bool isValidStatusField(const std::string &statusField)
Determine whether the IPMI sensor status token represents a valid flow reading.
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
A device base class which saves telemetry.
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
@ READY
The device is ready for operation, but is not operating.
The type of the input message.
Log entry recording the displayed flow value and source age.
#define TELEMETER_APP_LOGIC
Call telemeter::appLogic with error checking.
#define TELEMETER_LOAD_CONFIG(cfig)
Call telemeter::loadConfig with error checking.
#define TELEMETER_APP_STARTUP
Call telemeter::appStartup with error checking.
#define TELEMETER_SETUP_CONFIG(cfig)
Call telemeter::setupConfig with error checking.
#define TELEMETER_APP_SHUTDOWN
Call telemeter::appShutdown with error checking.