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