1 #include "hardwareController.h"
2 #include "serialDriver.h"
3 #include "logging.h"
4 #include "gameGlobalInfo.h"
5 #include "playerInfo.h"
6 #include "spaceObjects/nebula.h"
7 #include "spaceObjects/warpJammer.h"
8
9 #include "devices/dmx512SerialDevice.h"
10 #include "devices/enttecDMXProDevice.h"
11 #include "devices/virtualOutputDevice.h"
12 #include "devices/sACNDMXDevice.h"
13 #include "devices/uDMXDevice.h"
14 #include "devices/philipsHueDevice.h"
15
16 #include "hardwareMappingEffects.h"
17
~HardwareController()18 HardwareController::~HardwareController()
19 {
20 for(HardwareOutputDevice* device : devices)
21 delete device;
22 for(HardwareMappingState& state : states)
23 delete state.effect;
24 for(HardwareMappingEvent& event : events)
25 delete event.effect;
26 }
27
loadConfiguration(string filename)28 void HardwareController::loadConfiguration(string filename)
29 {
30 FILE* f = fopen(filename.c_str(), "r");
31 if (!f)
32 {
33 LOG(INFO) << filename << " not found. Not controlling external hardware.";
34 return;
35 }
36
37 std::unordered_map<string, string> settings;
38 string section = "";
39 char buffer[512];
40 while(fgets(buffer, sizeof(buffer), f))
41 {
42 string line = string(buffer).strip();
43 if(line.find("#") > -1)
44 line = line.substr(0, line.find("#")).strip();
45 if (line.startswith("[") && line.endswith("]"))
46 {
47 if (section != "")
48 {
49 handleConfig(section, settings);
50 settings.clear();
51 }
52 section = line;
53 }else if (line.find("=") > -1)
54 {
55 string key = line.substr(0, line.find("=")).strip();
56 string value = line.substr(line.find("=") + 1).strip();
57 settings[key] = value;
58 }
59 }
60 if (section != "")
61 handleConfig(section, settings);
62
63 fclose(f);
64
65 channels.resize(0);
66 for(HardwareOutputDevice* device : devices)
67 {
68 channels.resize(channels.size() + device->getChannelCount(), 0.0f);
69 }
70 LOG(INFO) << "Hardware subsystem initialized with: " << channels.size() << " channels";
71
72 if (devices.size() < 1)
73 {
74 LOG(INFO) << "List of available serial ports:";
75 for(string port : SerialPort::getAvailablePorts())
76 {
77 LOG(INFO) << port << " - " << SerialPort::getPseudoDriverName(port);
78 }
79 }
80 }
81
handleConfig(string section,std::unordered_map<string,string> & settings)82 void HardwareController::handleConfig(string section, std::unordered_map<string, string>& settings)
83 {
84 if (section == "[hardware]")
85 {
86 HardwareOutputDevice* device = nullptr;
87
88 if (settings["device"] == "")
89 LOG(ERROR) << "No device definition in [hardware] section";
90 else if (settings["device"] == "DMX512SerialDevice")
91 device = new DMX512SerialDevice();
92 else if (settings["device"] == "EnttecDMXProDevice")
93 device = new EnttecDMXProDevice();
94 else if (settings["device"] == "VirtualOutputDevice")
95 device = new VirtualOutputDevice();
96 else if (settings["device"] == "sACNDevice")
97 device = new StreamingAcnDMXDevice();
98 else if (settings["device"] == "uDMXDevice")
99 device = new UDMXDevice();
100 else if (settings["device"] == "PhilipsHueDevice")
101 device = new PhilipsHueDevice();
102 else
103 LOG(ERROR) << "Unknown device definition in [hardware] section: " << settings["device"];
104 if (device)
105 {
106 if (!device->configure(settings))
107 {
108 LOG(ERROR) << "Failed to configure device: " << settings["device"];
109 delete device;
110 }else{
111 LOG(INFO) << "New hardware device: " << settings["device"] << " with: " << device->getChannelCount() << " channels";
112 devices.push_back(device);
113 }
114 }
115 }else if(section == "[channel]")
116 {
117 if (settings["channel"] == "" || settings["name"] == "")
118 {
119 LOG(ERROR) << "Incorrect properties in [channel] section";
120 }
121 else
122 {
123 channel_mapping[settings["name"]].clear();
124 channel_mapping[settings["name"]].push_back((settings["channel"].toInt() - 1));
125 LOG(INFO) << "Channel #" << settings["channel"] << ": " << settings["name"];
126 }
127 }else if(section == "[channels]")
128 {
129 for(std::pair<string, string> item : settings)
130 {
131 channel_mapping[item.first].clear();
132 for(string number : item.second.split(","))
133 {
134 channel_mapping[item.first].push_back((number.strip().toInt() - 1));
135 LOG(INFO) << "Channel #" << item.second << ": " << number;
136 }
137 }
138 }else if(section == "[state]")
139 {
140 if (channel_mapping.find(settings["target"]) == channel_mapping.end())
141 {
142 LOG(ERROR) << "Unknown target channel in hardware.ini: " << settings["target"];
143 }else{
144 std::vector<int> channel_numbers = channel_mapping[settings["target"]];
145 for(unsigned int idx=0; idx<channel_numbers.size(); idx++)
146 {
147 std::unordered_map<string, string> per_channel_settings;
148 for(std::pair<string, string> item : settings)
149 {
150 std::vector<string> values = item.second.split(",");
151 if (values.size() > idx)
152 per_channel_settings[item.first] = values[idx].strip();
153 else
154 per_channel_settings[item.first] = values[values.size() - 1].strip();
155 }
156 createNewHardwareMappingState(channel_numbers[idx], per_channel_settings);
157 }
158 }
159 }else if(section == "[event]")
160 {
161 if (channel_mapping.find(settings["target"]) == channel_mapping.end())
162 {
163 LOG(ERROR) << "Unknown target channel in hardware.ini: " << settings["target"];
164 }else{
165 std::vector<int> channel_numbers = channel_mapping[settings["target"]];
166 for(unsigned int idx=0; idx<channel_numbers.size(); idx++)
167 {
168 std::unordered_map<string, string> per_channel_settings;
169 for(std::pair<string, string> item : settings)
170 {
171 std::vector<string> values = item.second.split(",");
172 if (values.size() > idx)
173 per_channel_settings[item.first] = values[idx];
174 else
175 per_channel_settings[item.first] = values[values.size() - 1];
176 }
177 createNewHardwareMappingEvent(channel_numbers[idx], per_channel_settings);
178 }
179 }
180 }else{
181 LOG(ERROR) << "Unknown section in hardware.ini: " << section;
182 }
183 }
184
update(float delta)185 void HardwareController::update(float delta)
186 {
187 if (channels.size() < 1)
188 return;
189 for(float& value : channels)
190 value = 0.0;
191 for(HardwareMappingState& state : states)
192 {
193 float value;
194 bool active = false;
195 if (getVariableValue(state.variable, value))
196 {
197 switch(state.compare_operator)
198 {
199 case HardwareMappingState::Less: active = value < state.compare_value; break;
200 case HardwareMappingState::Greater: active = value > state.compare_value; break;
201 case HardwareMappingState::Equal: active = value == state.compare_value; break;
202 case HardwareMappingState::NotEqual: active = value != state.compare_value; break;
203 }
204 }
205
206 if (active && state.channel_nr < int(channels.size()))
207 {
208 channels[state.channel_nr] = state.effect->onActive();
209 }else{
210 state.effect->onInactive();
211 }
212 }
213 for(HardwareMappingEvent& event : events)
214 {
215 float value;
216 bool trigger = false;
217 if (getVariableValue(event.trigger_variable, value))
218 {
219 if (event.previous_valid)
220 {
221 switch(event.compare_operator)
222 {
223 case HardwareMappingEvent::Change:
224 if (fabs(event.previous_value - value) > 0.1)
225 trigger = true;
226 break;
227 case HardwareMappingEvent::Increase:
228 if (value > event.previous_value + 0.1)
229 trigger = true;
230 break;
231 case HardwareMappingEvent::Decrease:
232 if (value < event.previous_value - 0.1)
233 trigger = true;
234 break;
235 }
236 }
237 event.previous_value = value;
238 event.previous_valid = true;
239 }else{
240 event.previous_valid = false;
241 }
242 if (trigger)
243 {
244 event.triggered = true;
245 event.start_time.restart();
246 }
247 if (event.triggered && event.channel_nr < int(channels.size()))
248 {
249 channels[event.channel_nr] = event.effect->onActive();
250 if (event.start_time.getElapsedTime().asSeconds() > event.runtime)
251 event.triggered = false;
252 }else{
253 event.effect->onInactive();
254 }
255 }
256
257 int idx = 0;
258 for(HardwareOutputDevice* device : devices)
259 {
260 for(int n=0; n<device->getChannelCount(); n++)
261 device->setChannelData(n, channels[idx++]);
262 }
263 }
264
createNewHardwareMappingState(int channel_number,std::unordered_map<string,string> & settings)265 void HardwareController::createNewHardwareMappingState(int channel_number, std::unordered_map<string, string>& settings)
266 {
267 string condition = settings["condition"];
268
269 HardwareMappingState state;
270 state.variable = condition;
271 state.compare_operator = HardwareMappingState::Greater;
272 state.compare_value = 0.0;
273 state.channel_nr = channel_number;
274
275 for(HardwareMappingState::EOperator compare_operator : {HardwareMappingState::Less, HardwareMappingState::Greater, HardwareMappingState::Equal, HardwareMappingState::NotEqual})
276 {
277 string compare_string = "<";
278 switch(compare_operator)
279 {
280 case HardwareMappingState::Less: compare_string = "<"; break;
281 case HardwareMappingState::Greater: compare_string = ">"; break;
282 case HardwareMappingState::Equal: compare_string = "=="; break;
283 case HardwareMappingState::NotEqual: compare_string = "!="; break;
284 }
285 if (condition.find(compare_string) > -1)
286 {
287 state.variable = condition.substr(0, condition.find(compare_string)).strip();
288 state.compare_operator = compare_operator;
289 state.compare_value = condition.substr(condition.find(compare_string) + 1).strip().toFloat();
290 }
291 }
292
293 state.effect = createEffect(settings);
294
295 if (state.effect)
296 {
297 LOG(DEBUG) << "New hardware state: " << state.channel_nr << ":" << state.variable << " " << state.compare_operator << " " << state.compare_value;
298 states.push_back(state);
299 }
300 }
301
createNewHardwareMappingEvent(int channel_number,std::unordered_map<string,string> & settings)302 void HardwareController::createNewHardwareMappingEvent(int channel_number, std::unordered_map<string, string>& settings)
303 {
304 string trigger = settings["trigger"];
305
306 HardwareMappingEvent event;
307 event.compare_operator = HardwareMappingEvent::Change;
308 if (trigger.startswith("<"))
309 {
310 event.compare_operator = HardwareMappingEvent::Decrease;
311 trigger = trigger.substr(1).strip();
312 }
313 if (trigger.startswith(">"))
314 {
315 event.compare_operator = HardwareMappingEvent::Increase;
316 trigger = trigger.substr(1).strip();
317 }
318 event.trigger_variable = trigger;
319 event.channel_nr = channel_number;
320 event.runtime = settings["runtime"].toFloat();
321 event.triggered = false;
322 event.previous_value = 0.0;
323
324 event.effect = createEffect(settings);
325 if (event.effect)
326 {
327 LOG(DEBUG) << "New hardware event: " << event.channel_nr << ":" << event.trigger_variable << " " << event.compare_operator;
328 events.push_back(event);
329 }
330 }
331
createEffect(std::unordered_map<string,string> & settings)332 HardwareMappingEffect* HardwareController::createEffect(std::unordered_map<string, string>& settings)
333 {
334 HardwareMappingEffect* effect = nullptr;
335 string effect_name = settings["effect"].lower();
336 if (effect_name == "static" || effect_name == "")
337 effect = new HardwareMappingEffectStatic();
338 else if (effect_name == "glow")
339 effect = new HardwareMappingEffectGlow();
340 else if (effect_name == "blink")
341 effect = new HardwareMappingEffectBlink();
342 else if (effect_name == "variable")
343 effect = new HardwareMappingEffectVariable(this);
344 else if (effect_name == "noise")
345 effect = new HardwareMappingEffectNoise();
346
347 if (effect->configure(settings))
348 return effect;
349 delete effect;
350 return nullptr;
351 }
352
353 #define SHIP_VARIABLE(name, formula) if (variable_name == name) { if (ship) { value = (formula); return true; } return false; }
getVariableValue(string variable_name,float & value)354 bool HardwareController::getVariableValue(string variable_name, float& value)
355 {
356 P<PlayerSpaceship> ship = my_spaceship;
357 if (!ship && gameGlobalInfo)
358 ship = gameGlobalInfo->getPlayerShip(0);
359
360 if (variable_name == "Always")
361 {
362 value = 1.0;
363 return true;
364 }
365 if (variable_name == "HasShip")
366 {
367 value = bool(ship) ? 1.0f : 0.0f;
368 return true;
369 }
370 SHIP_VARIABLE("Hull", 100.0f * ship->hull_strength / ship->hull_max);
371 SHIP_VARIABLE("FrontShield", ship->getShieldPercentage(0));
372 SHIP_VARIABLE("RearShield", ship->getShieldPercentage(1));
373 SHIP_VARIABLE("Shield0", ship->getShieldPercentage(0));
374 SHIP_VARIABLE("Shield1", ship->getShieldPercentage(1));
375 SHIP_VARIABLE("Shield2", ship->getShieldPercentage(2));
376 SHIP_VARIABLE("Shield3", ship->getShieldPercentage(3));
377 SHIP_VARIABLE("Shield4", ship->getShieldPercentage(4));
378 SHIP_VARIABLE("Shield5", ship->getShieldPercentage(5));
379 SHIP_VARIABLE("Shield6", ship->getShieldPercentage(6));
380 SHIP_VARIABLE("Shield7", ship->getShieldPercentage(7));
381 SHIP_VARIABLE("Energy", ship->energy_level * 100 / ship->max_energy_level);
382 SHIP_VARIABLE("ShieldsUp", ship->shields_active ? 1.0f : 0.0f);
383 SHIP_VARIABLE("Impulse", ship->current_impulse * ship->getSystemEffectiveness(SYS_Impulse));
384 SHIP_VARIABLE("Warp", ship->current_warp * ship->getSystemEffectiveness(SYS_Warp));
385 SHIP_VARIABLE("Docking", ship->docking_state == DS_Docking ? 1.0f : 0.0f);
386 SHIP_VARIABLE("Docked", ship->docking_state == DS_Docked ? 1.0f : 0.0f);
387 SHIP_VARIABLE("InNebula", Nebula::inNebula(ship->getPosition()) ? 1.0f : 0.0f);
388 SHIP_VARIABLE("IsJammed", WarpJammer::isWarpJammed(ship->getPosition()) ? 1.0f : 0.0f);
389 SHIP_VARIABLE("Jumping", ship->jump_delay > 0.0f ? 1.0f : 0.0f);
390 SHIP_VARIABLE("Jumped", ship->jump_indicator > 0.0f ? 1.0f : 0.0f);
391 SHIP_VARIABLE("Alert", ship->getAlertLevel() != AL_Normal ? 1.0f : 0.0f);
392 SHIP_VARIABLE("YellowAlert", ship->getAlertLevel() == AL_YellowAlert ? 1.0f : 0.0f);
393 SHIP_VARIABLE("RedAlert", ship->getAlertLevel() == AL_RedAlert ? 1.0f : 0.0f);
394 for(int n=0; n<max_weapon_tubes; n++)
395 {
396 SHIP_VARIABLE("TubeLoaded" + string(n), ship->weapon_tube[n].isLoaded() ? 1.0f : 0.0f);
397 SHIP_VARIABLE("TubeLoading" + string(n), ship->weapon_tube[n].isLoading() ? 1.0f : 0.0f);
398 SHIP_VARIABLE("TubeUnloading" + string(n), ship->weapon_tube[n].isUnloading() ? 1.0f : 0.0f);
399 SHIP_VARIABLE("TubeFiring" + string(n), ship->weapon_tube[n].isFiring() ? 1.0f : 0.0f);
400 }
401 for(int n=0; n<SYS_COUNT; n++)
402 {
403 SHIP_VARIABLE(getSystemName(ESystem(n)).replace(" ", "") + "Health", ship->systems[n].health);
404 SHIP_VARIABLE(getSystemName(ESystem(n)).replace(" ", "") + "Power", ship->systems[n].power_level / 3.0);
405 SHIP_VARIABLE(getSystemName(ESystem(n)).replace(" ", "") + "Heat", ship->systems[n].heat_level);
406 SHIP_VARIABLE(getSystemName(ESystem(n)).replace(" ", "") + "Coolant", ship->systems[n].coolant_level);
407 SHIP_VARIABLE(getSystemName(ESystem(n)).replace(" ", "") + "Hacked", ship->systems[n].hacked_level);
408 }
409
410 LOG(WARNING) << "Unknown variable: " << variable_name;
411 value = 0.0;
412 return false;
413 }
414