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             *this = std::move(move);
78 
79             // Mark the moved object, so destructor knows it was moved
80             move.moved = true;
81         }
82         return *this;
83     }
84 
85     ~PidCoreLog()
86     {
87         // Do not close files if ownership was moved to another object
88         if (!moved)
89         {
90             fileContext.close();
91             fileCoeffs.close();
92         }
93     }
94 };
95 
96 // Initializes logging files, call once per PID loop initialization
97 void LogInit(const std::string& name, pid_info_t* pidinfoptr);
98 
99 // Returns PidCoreLog pointer, or nullptr if this PID loop not being logged
100 PidCoreLog* LogPeek(const std::string& name);
101 
102 // Logs a line of logging, if different, or it has been long enough
103 void LogContext(PidCoreLog& pidLog, const std::chrono::milliseconds& msNow,
104                 const PidCoreContext& coreLog);
105 
106 // Takes a timestamp, suitable for column 1 of logging file output
107 std::chrono::milliseconds LogTimestamp(void);
108 
109 } // namespace ec
110 } // namespace pid_control
111