#pragma once #include "pid.hpp" #include #include #include #include #include namespace pid_control { namespace ec { // Trivial class for information exported from core PID loop function struct PidCoreContext { double input; double setpoint; double error; double proportionalTerm; double integralTerm1; double integralTerm2; double derivativeTerm; double feedFwdTerm; double output1; double output2; double minOut; double maxOut; double integralTerm3; double output3; double integralTerm; double output; bool operator!=(const PidCoreContext& rhs) const = default; bool operator==(const PidCoreContext& rhs) const = default; }; // Optional decorator class for each PID loop, to support logging // Although this is a trivial class, it ended up needing the Six Horsemen struct PidCoreLog { std::string nameOriginal; std::string nameClean; std::ofstream fileContext; std::ofstream fileCoeffs; std::chrono::milliseconds lastLog; PidCoreContext lastContext; bool moved; PidCoreLog() : nameOriginal(), nameClean(), fileContext(), fileCoeffs(), lastLog(), lastContext(), moved(false) {} PidCoreLog(const PidCoreLog& copy) = delete; PidCoreLog& operator=(const PidCoreLog& copy) = delete; PidCoreLog(PidCoreLog&& move) { // Reuse assignment operator below *this = std::move(move); } PidCoreLog& operator=(PidCoreLog&& move) { if (this != &move) { // Move each field individually nameOriginal = std::move(move.nameOriginal); nameClean = std::move(move.nameClean); fileContext = std::move(move.fileContext); fileCoeffs = std::move(move.fileCoeffs); lastLog = std::move(move.lastLog); lastContext = std::move(move.lastContext); // Mark the moved object, so destructor knows it was moved move.moved = true; } return *this; } ~PidCoreLog() { // Do not close files if ownership was moved to another object if (!moved) { fileContext.close(); fileCoeffs.close(); } } }; // Initializes logging files, call once per PID loop initialization void LogInit(const std::string& name, pid_info_t* pidinfoptr); // Returns PidCoreLog pointer, or nullptr if this PID loop not being logged PidCoreLog* LogPeek(const std::string& name); // Logs a line of logging, if different, or it has been long enough void LogContext(PidCoreLog& pidLog, const std::chrono::milliseconds& msNow, const PidCoreContext& coreLog); // Takes a timestamp, suitable for column 1 of logging file output std::chrono::milliseconds LogTimestamp(void); } // namespace ec } // namespace pid_control