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.
@ READY
The device is ready for operation, but is not operating.
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.
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.