1 #pragma once 2 3 #include "pid.hpp" 4 5 #include <chrono> 6 #include <cstring> 7 #include <fstream> 8 #include <iostream> 9 #include <string> 10 11 namespace pid_control 12 { 13 namespace ec 14 { 15 16 // Trivial class for information exported from core PID loop function 17 struct PidCoreContext 18 { 19 double input; 20 double setpoint; 21 double error; 22 23 double proportionalTerm; 24 double integralTerm1; 25 double integralTerm2; 26 27 double derivativeTerm; 28 29 double feedFwdTerm; 30 double output1; 31 double output2; 32 33 double minOut; 34 double maxOut; 35 36 double integralTerm3; 37 double output3; 38 39 double integralTerm; 40 double output; 41 42 bool operator!=(const PidCoreContext& rhs) const = default; 43 bool operator==(const PidCoreContext& rhs) const = default; 44 }; 45 46 // Optional decorator class for each PID loop, to support logging 47 // Although this is a trivial class, it ended up needing the Six Horsemen 48 struct PidCoreLog 49 { 50 std::string nameOriginal; 51 std::string nameClean; 52 std::ofstream fileContext; 53 std::ofstream fileCoeffs; 54 std::chrono::milliseconds lastLog; 55 PidCoreContext lastContext; 56 bool moved; 57 58 PidCoreLog() : 59 nameOriginal(), nameClean(), fileContext(), fileCoeffs(), lastLog(), 60 lastContext(), moved(false) 61 {} 62 63 PidCoreLog(const PidCoreLog& copy) = delete; 64 65 PidCoreLog& operator=(const PidCoreLog& copy) = delete; 66 67 PidCoreLog(PidCoreLog&& move) 68 { 69 // Reuse assignment operator below 70 *this = std::move(move); 71 } 72 73 PidCoreLog& operator=(PidCoreLog&& move) 74 { 75 if (this != &move) 76 { 77 // Move each field individually 78 nameOriginal = std::move(move.nameOriginal); 79 nameClean = std::move(move.nameClean); 80 fileContext = std::move(move.fileContext); 81 fileCoeffs = std::move(move.fileCoeffs); 82 lastLog = std::move(move.lastLog); 83 lastContext = std::move(move.lastContext); 84 85 // Mark the moved object, so destructor knows it was moved 86 move.moved = true; 87 } 88 return *this; 89 } 90 91 ~PidCoreLog() 92 { 93 // Do not close files if ownership was moved to another object 94 if (!moved) 95 { 96 fileContext.close(); 97 fileCoeffs.close(); 98 } 99 } 100 }; 101 102 // Initializes logging files, call once per PID loop initialization 103 void LogInit(const std::string& name, pid_info_t* pidinfoptr); 104 105 // Returns PidCoreLog pointer, or nullptr if this PID loop not being logged 106 PidCoreLog* LogPeek(const std::string& name); 107 108 // Logs a line of logging, if different, or it has been long enough 109 void LogContext(PidCoreLog& pidLog, const std::chrono::milliseconds& msNow, 110 const PidCoreContext& coreLog); 111 112 // Takes a timestamp, suitable for column 1 of logging file output 113 std::chrono::milliseconds LogTimestamp(void); 114 115 } // namespace ec 116 } // namespace pid_control 117