1 #include "pid/buildjson.hpp"
2 
3 #include <gmock/gmock.h>
4 #include <gtest/gtest.h>
5 
6 namespace pid_control
7 {
8 namespace
9 {
10 
11 TEST(ZoneFromJson, emptyZone)
12 {
13     // There is a zone key, but it's empty.
14     // This is technically invalid.
15 
16     std::map<int64_t, conf::PIDConf> pidConfig;
17     std::map<int64_t, conf::ZoneConfig> zoneConfig;
18 
19     auto j2 = R"(
20       {
21         "zones": []
22       }
23     )"_json;
24 
25     std::tie(pidConfig, zoneConfig) = buildPIDsFromJson(j2);
26 
27     EXPECT_TRUE(pidConfig.empty());
28     EXPECT_TRUE(zoneConfig.empty());
29 }
30 
31 TEST(ZoneFromJson, oneZoneOnePid)
32 {
33     // Parse a valid configuration with one zone and one PID.
34     // Intentionally omits "derivativeCoeff" to test that it is optional.
35 
36     std::map<int64_t, conf::PIDConf> pidConfig;
37     std::map<int64_t, conf::ZoneConfig> zoneConfig;
38 
39     auto j2 = R"(
40       {
41         "zones" : [{
42           "id": 1,
43           "minThermalOutput": 3000.0,
44           "failsafePercent": 75.0,
45           "pids": [{
46             "name": "fan1-5",
47             "type": "fan",
48             "inputs": ["fan1", "fan5"],
49             "setpoint": 90.0,
50             "pid": {
51               "samplePeriod": 0.1,
52               "proportionalCoeff": 0.0,
53               "integralCoeff": 0.0,
54               "feedFwdOffsetCoeff": 0.0,
55               "feedFwdGainCoeff": 0.010,
56               "integralLimit_min": 0.0,
57               "integralLimit_max": 0.0,
58               "outLim_min": 30.0,
59               "outLim_max": 100.0,
60               "slewNeg": 0.0,
61               "slewPos": 0.0
62             }
63           }]
64         }]
65       }
66     )"_json;
67 
68     std::tie(pidConfig, zoneConfig) = buildPIDsFromJson(j2);
69     EXPECT_EQ(pidConfig.size(), static_cast<u_int64_t>(1));
70     EXPECT_EQ(zoneConfig.size(), static_cast<u_int64_t>(1));
71 
72     EXPECT_EQ(pidConfig[1]["fan1-5"].type, "fan");
73     EXPECT_DOUBLE_EQ(zoneConfig[1].minThermalOutput, 3000.0);
74 }
75 
76 TEST(ZoneFromJson, marginZone)
77 {
78     // Parse a valid configuration with one zone and one PID.
79     // This is a margin zone, and has both kinds of temperature
80     // sensors in it, absolute temperature and margin temperature.
81     // Tests that TempToMargin is parsed correctly.
82 
83     std::map<int64_t, conf::PIDConf> pidConfig;
84     std::map<int64_t, conf::ZoneConfig> zoneConfig;
85 
86     auto j2 = R"(
87       {
88         "zones" : [{
89           "id": 1,
90           "minThermalOutput": 3000.0,
91           "failsafePercent": 75.0,
92           "pids": [{
93             "name": "myPid",
94             "type": "margin",
95             "inputs": ["absolute0", "absolute1", "margin0", "margin1"],
96             "tempToMargin": [
97               85.0,
98               100.0
99             ],
100             "setpoint": 10.0,
101             "pid": {
102               "samplePeriod": 0.1,
103               "proportionalCoeff": 0.0,
104               "integralCoeff": 0.0,
105               "feedFwdOffsetCoeff": 0.0,
106               "feedFwdGainCoeff": 0.010,
107               "integralLimit_min": 0.0,
108               "integralLimit_max": 0.0,
109               "outLim_min": 30.0,
110               "outLim_max": 100.0,
111               "slewNeg": 0.0,
112               "slewPos": 0.0
113             }
114           }]
115         }]
116       }
117     )"_json;
118 
119     std::tie(pidConfig, zoneConfig) = buildPIDsFromJson(j2);
120     EXPECT_EQ(pidConfig.size(), static_cast<u_int64_t>(1));
121     EXPECT_EQ(zoneConfig.size(), static_cast<u_int64_t>(1));
122 
123     EXPECT_EQ(pidConfig[1]["myPid"].type, "margin");
124     EXPECT_DOUBLE_EQ(zoneConfig[1].minThermalOutput, 3000.0);
125 
126     EXPECT_EQ(pidConfig[1]["myPid"].inputs[0].name, "absolute0");
127     EXPECT_DOUBLE_EQ(pidConfig[1]["myPid"].inputs[0].convertMarginZero, 85.0);
128     EXPECT_EQ(pidConfig[1]["myPid"].inputs[0].convertTempToMargin, true);
129 
130     EXPECT_EQ(pidConfig[1]["myPid"].inputs[1].name, "absolute1");
131     EXPECT_DOUBLE_EQ(pidConfig[1]["myPid"].inputs[1].convertMarginZero, 100.0);
132     EXPECT_EQ(pidConfig[1]["myPid"].inputs[1].convertTempToMargin, true);
133 
134     EXPECT_EQ(pidConfig[1]["myPid"].inputs[2].name, "margin0");
135     EXPECT_EQ(pidConfig[1]["myPid"].inputs[2].convertTempToMargin, false);
136 
137     EXPECT_EQ(pidConfig[1]["myPid"].inputs[3].name, "margin1");
138     EXPECT_EQ(pidConfig[1]["myPid"].inputs[3].convertTempToMargin, false);
139 }
140 
141 TEST(ZoneFromJson, oneZoneOnePidWithHysteresis)
142 {
143     // Parse a valid configuration with one zone and one PID and the PID uses
144     // Hysteresis parameters.
145 
146     std::map<int64_t, conf::PIDConf> pidConfig;
147     std::map<int64_t, conf::ZoneConfig> zoneConfig;
148 
149     auto j2 = R"(
150       {
151         "zones" : [{
152           "id": 1,
153           "minThermalOutput": 3000.0,
154           "failsafePercent": 75.0,
155           "pids": [{
156             "name": "fan1-5",
157             "type": "fan",
158             "inputs": ["fan1", "fan5"],
159             "setpoint": 90.0,
160             "pid": {
161               "samplePeriod": 0.1,
162               "proportionalCoeff": 0.0,
163               "integralCoeff": 0.0,
164               "derivativeCoeff": 0.0,
165               "feedFwdOffsetCoeff": 0.0,
166               "feedFwdGainCoeff": 0.010,
167               "integralLimit_min": 0.0,
168               "integralLimit_max": 0.0,
169               "outLim_min": 30.0,
170               "outLim_max": 100.0,
171               "slewNeg": 0.0,
172               "slewPos": 0.0,
173               "positiveHysteresis": 1000.0,
174               "negativeHysteresis": 9000.0
175             }
176           }]
177         }]
178       }
179     )"_json;
180 
181     std::tie(pidConfig, zoneConfig) = buildPIDsFromJson(j2);
182     EXPECT_EQ(pidConfig.size(), static_cast<u_int64_t>(1));
183     EXPECT_EQ(zoneConfig.size(), static_cast<u_int64_t>(1));
184 
185     EXPECT_EQ(pidConfig[1]["fan1-5"].type, "fan");
186     EXPECT_DOUBLE_EQ(pidConfig[1]["fan1-5"].pidInfo.positiveHysteresis, 1000.0);
187 
188     EXPECT_DOUBLE_EQ(zoneConfig[1].minThermalOutput, 3000.0);
189 }
190 
191 TEST(ZoneFromJson, oneZoneOneStepwiseWithHysteresis)
192 {
193     // Parse a valid configuration with one zone and one PID and the PID uses
194     // Hysteresis parameters.
195 
196     std::map<int64_t, conf::PIDConf> pidConfig;
197     std::map<int64_t, conf::ZoneConfig> zoneConfig;
198 
199     auto j2 = R"(
200       {
201         "zones" : [{
202           "id": 1,
203           "minThermalOutput": 3000.0,
204           "failsafePercent": 75.0,
205           "pids": [{
206             "name": "temp1",
207             "type": "stepwise",
208             "inputs": ["temp1"],
209             "setpoint": 30.0,
210             "pid": {
211               "samplePeriod": 0.1,
212               "positiveHysteresis": 1.0,
213               "negativeHysteresis": 1.0,
214               "isCeiling": false,
215               "reading": {
216                 "0": 45,
217                 "1": 46,
218                 "2": 47,
219                 "3": 48,
220                 "4": 49,
221                 "5": 50,
222                 "6": 51,
223                 "7": 52,
224                 "8": 53,
225                 "9": 54,
226                 "10": 55,
227                 "11": 56,
228                 "12": 57,
229                 "13": 58,
230                 "14": 59,
231                 "15": 60,
232                 "16": 61,
233                 "17": 62,
234                 "18": 63,
235                 "19": 64
236               },
237               "output": {
238                 "0": 5000,
239                 "1": 2400,
240                 "2": 2600,
241                 "3": 2800,
242                 "4": 3000,
243                 "5": 3200,
244                 "6": 3400,
245                 "7": 3600,
246                 "8": 3800,
247                 "9": 4000,
248                 "10": 4200,
249                 "11": 4400,
250                 "12": 4600,
251                 "13": 4800,
252                 "14": 5000,
253                 "15": 5200,
254                 "16": 5400,
255                 "17": 5600,
256                 "18": 5800,
257                 "19": 6000
258               }
259             }
260           }]
261         }]
262       }
263     )"_json;
264 
265     std::tie(pidConfig, zoneConfig) = buildPIDsFromJson(j2);
266     EXPECT_EQ(pidConfig.size(), static_cast<u_int64_t>(1));
267     EXPECT_EQ(zoneConfig.size(), static_cast<u_int64_t>(1));
268 
269     EXPECT_EQ(pidConfig[1]["temp1"].type, "stepwise");
270     EXPECT_DOUBLE_EQ(pidConfig[1]["temp1"].stepwiseInfo.positiveHysteresis,
271                      1.0);
272 
273     EXPECT_DOUBLE_EQ(zoneConfig[1].minThermalOutput, 3000.0);
274 }
275 
276 TEST(ZoneFromJson, getCycleInterval)
277 {
278     // Parse a valid configuration with one zone and one PID and the zone have
279     // cycleIntervalTime and updateThermalsTime parameters.
280 
281     std::map<int64_t, conf::PIDConf> pidConfig;
282     std::map<int64_t, conf::ZoneConfig> zoneConfig;
283 
284     auto j2 = R"(
285       {
286         "zones" : [{
287           "id": 1,
288           "minThermalOutput": 3000.0,
289           "failsafePercent": 75.0,
290           "cycleIntervalTimeMS": 1000.0,
291           "updateThermalsTimeMS": 1000.0,
292           "pids": [{
293             "name": "fan1-5",
294             "type": "fan",
295             "inputs": ["fan1", "fan5"],
296             "setpoint": 90.0,
297             "pid": {
298               "samplePeriod": 0.1,
299               "proportionalCoeff": 0.0,
300               "integralCoeff": 0.0,
301               "derivativeCoeff": 0.0,
302               "feedFwdOffsetCoeff": 0.0,
303               "feedFwdGainCoeff": 0.010,
304               "integralLimit_min": 0.0,
305               "integralLimit_max": 0.0,
306               "outLim_min": 30.0,
307               "outLim_max": 100.0,
308               "slewNeg": 0.0,
309               "slewPos": 0.0
310             }
311           }]
312         }]
313       }
314     )"_json;
315 
316     std::tie(pidConfig, zoneConfig) = buildPIDsFromJson(j2);
317     EXPECT_EQ(pidConfig.size(), static_cast<u_int64_t>(1));
318     EXPECT_EQ(zoneConfig.size(), static_cast<u_int64_t>(1));
319 
320     EXPECT_EQ(pidConfig[1]["fan1-5"].type, "fan");
321     EXPECT_EQ(zoneConfig[1].cycleTime.cycleIntervalTimeMS,
322               static_cast<u_int64_t>(1000));
323     EXPECT_EQ(zoneConfig[1].cycleTime.updateThermalsTimeMS,
324               static_cast<u_int64_t>(1000));
325     EXPECT_DOUBLE_EQ(zoneConfig[1].minThermalOutput, 3000.0);
326 }
327 
328 } // namespace
329 } // namespace pid_control
330