forked from goncalossilva/levd
-
Notifications
You must be signed in to change notification settings - Fork 1
/
leviathan_service.cpp
163 lines (142 loc) · 5.8 KB
/
leviathan_service.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include "leviathan_service.hpp"
#include "constants.h" // #defines
#include "kraken_driver.hpp"
#include "leviathan_config.hpp"
#include "cpu_temperature_monitor.hpp"
#include <chrono>
#include <fstream>
#include <glog/logging.h>
#include <limits>
#include <thread>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
using namespace std::chrono_literals;
// Global kill all armed when SIGTERM is detected
volatile sig_atomic_t done = 0;
void term(int signum) { done = 1; }
/** *********** Private Interface ************** */
bool detect_kraken(libusb_device *device) {
struct libusb_device_descriptor desc = {0};
int err = libusb_get_device_descriptor(device, &desc);
if (err != 0) {
LOG(ERROR) << "Failed to get descriptor for generic device: "
<< libusb_error_name(err);
return false;
}
return desc.idVendor == KRAKEN_X61_VENDOR
&& desc.idProduct == KRAKEN_X61_PRODUCT;
}
uint32_t next_speed(const std::map<int32_t, LineFunction> &fan_profile,
const uint32_t current_temp) {
const auto slopeFn = fan_profile.upper_bound(current_temp)->second;
return slopeFn(current_temp);
}
int file_is_modified(const char *path, time_t oldMTime) {
struct stat file_stat;
int err = stat(path, &file_stat);
LOG_IF(FATAL, err != 0) << "Error when attempting to stat " << path;
return file_stat.st_mtime > oldMTime;
}
/** *********** Public Interface ************** */
libusb_device *leviathan_init(libusb_device **devices, ssize_t num_devices) {
libusb_device *kraken_device = NULL;
for (auto i = 0u; i < num_devices; ++i) {
if (detect_kraken(devices[i])) {
kraken_device = devices[i];
break;
}
}
return kraken_device;
}
void leviathan_start(libusb_device *kraken_device) {
// Init Kraken, display diagnostics
auto kd = std::make_unique<KrakenDriver>(kraken_device);
LOG(INFO) << "Kraken Driver Initialized";
LOG(INFO) << "Kraken Serial No: " << kd->getSerialNumber();
// The following two lines throw/crash on config error
CpuTemperatureMonitor cpu_temp_mon;
auto config_opts = parse_config_file(kDefaultConfigFile);
// Local variables for state management
uint32_t cpu_temp = 0;
uint32_t liquid_temp = 0;
uint32_t old_fan_speed = 0; // Take first reported value as
uint32_t old_pump_speed = 0; // .. an update
time_t last_time_modified = std::numeric_limits<time_t>::min();
// Init signal handler
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = term;
sigaction(SIGTERM, &action, NULL);
sigaction(SIGQUIT, &action, NULL);
sigaction(SIGINT, &action, NULL);
// Main program loop
// 1. Update config_options if config file was edited
// 2. Update color according to the given settings
// 3. Read CPU and liquid temperatures
// 4. Set fan/pump speed according to temp and given parameters
// 5. Sleep for the defined interval and repeat
while (!done) {
// Grab latest parameters, if they've been changed
if (file_is_modified(kDefaultConfigFile, last_time_modified)) {
LOG(INFO)
<< "Detected modifications to config file, updating preferences...";
config_opts = parse_config_file(kDefaultConfigFile);
last_time_modified = time(0);
}
// Update color, which also provides the liquid temp in case it's needed.
kd->setColor(config_opts.main_color_);
auto update = kd->sendColorUpdate();
// Grab latest cpu and liquid temperatures
cpu_temp = cpu_temp_mon.getPackageIdTemperature();
liquid_temp =
!update.empty() ? update.find("liquid_temperature")->second : cpu_temp;
// Based on parameters and current temp, set desired fan and pump speeds
uint32_t next_fan, next_pump;
if (config_opts.temp_source_ == TempSource::LIQUID) {
next_fan = next_speed(config_opts.fan_profile_, liquid_temp);
next_pump = next_speed(config_opts.pump_profile_, liquid_temp);
VLOG(2) << "Current liquid temperature: " << liquid_temp << "C";
} else {
next_fan = next_speed(config_opts.fan_profile_, cpu_temp);
next_pump = next_speed(config_opts.pump_profile_, cpu_temp);
VLOG(2) << "Current CPU temperature: " << cpu_temp << "C";
}
// Step down: If we are decreasing fan/pump speed, do it slowly
if (next_fan < old_fan_speed) {
next_fan = old_fan_speed - 5;
} if (next_pump < old_pump_speed) {
next_pump = old_pump_speed - 5;
}
VLOG(2) << "Setting fan speed: " << next_fan;
VLOG(2) << "Setting pump speeds: " << next_pump;
kd->setFanSpeed(next_fan);
kd->setPumpSpeed(next_pump);
update = kd->sendSpeedUpdate();
if (update.empty() == true) {
LOG(WARNING) << "Bad update detected, attempting reconnection...";
// NOTE: Must ensure that destructor of old object pointed to by kd
// is cleaned up before reassignment to a new instance of kraken
// driver. i.e. only one can be alive at any given time.
kd.reset(nullptr);
std::this_thread::sleep_for(5s);
kd.reset(new KrakenDriver(kraken_device));
}
if (next_fan != old_fan_speed || next_pump != old_pump_speed) {
LOG(INFO) << "Changed fan speed to " << update.find("fan_speed")->second
<< "rpm, pump speed to " << update.find("pump_speed")->second
<< "rpm, with fan percentage at " << next_fan
<< ", with pump percentage at " << next_pump
<< ", current CPU temperature at " << cpu_temp << "C"
<< ", and current liquid temperature at " << liquid_temp << "C";
old_fan_speed = next_fan;
old_pump_speed = next_pump;
}
std::this_thread::sleep_for(
std::chrono::milliseconds(config_opts.interval_));
}
kd.reset(nullptr);
}