1 module puppeteer.configuration.configuration;
2 
3 import test.puppeteer.configuration.configuration_test : test;
4 mixin test;
5 
6 import puppeteer.var_monitor_utils;
7 import puppeteer.value_adapter.value_adapter;
8 
9 import puppeteer.configuration.i_configuration;
10 
11 import puppeteer.configuration.invalid_configuration_exception;
12 
13 import std.meta;
14 import std.conv;
15 import std.stdio;
16 import std.json;
17 
18 private enum configAIAdaptersKey = "AI_ADAPTERS";
19 private enum configPWMOutAvgAdaptersKey = "PWM_OUT_AVG_ADAPTERS";
20 private enum configVarAdaptersKey = "VAR_ADAPTERS";
21 
22 private enum configAISensorNamesKey = "AI_SENSOR_NAMES";
23 private enum configVarSensorNamesKey = "VAR_SENSOR_NAMES";
24 
25 shared class Configuration(VarMonitorTypes...) : IConfiguration!VarMonitorTypes
26 if(allSatisfy!(isVarMonitorTypeSupported, VarMonitorTypes))
27 {
28     protected ValueAdapter!float[ubyte] AIValueAdapters;
29     protected ValueAdapter!float[ubyte] PWMOutAvgValueAdapters;
30     protected mixin(unrollVariableValueAdapters!VarMonitorTypes());
31 
32     protected string[ubyte] AISensorNames;
33     protected mixin(unrollVarMonitorSensorNames!VarMonitorTypes());
34 
35     mixin(unrollHelperMethods!VarMonitorTypes());
36 
37     public void setAIValueAdapterExpression(ubyte pin, string adapterExpression)
38     {
39         setAdapterExpression(AIValueAdapters, pin, adapterExpression);
40     }
41 
42     public string getAIValueAdapterExpression(ubyte pin) const
43     {
44         auto adapter = pin in AIValueAdapters;
45 
46         return adapter ? adapter.expression : "x";
47     }
48 
49     public float adaptAIValue(ubyte pin, float value) const
50     {
51         auto adapter = pin in AIValueAdapters;
52 
53         return adapter ? adapter.opCall(value) : value;
54     }
55 
56     public void setPWMOutAvgAdapterExpression(ubyte pin, string adapterExpression)
57     {
58         setAdapterExpression(PWMOutAvgValueAdapters, pin, adapterExpression);
59     }
60 
61     public string getPWMOutAvgAdapterExpression(ubyte pin) const
62     {
63         auto adapter = pin in PWMOutAvgValueAdapters;
64 
65         return adapter ? adapter.expression : "x";
66     }
67 
68     public float adaptPWMOutAvgValue(ubyte pin, float value) const
69     {
70         auto adapter = pin in PWMOutAvgValueAdapters;
71 
72         return adapter ? adapter.opCall(value) : value;
73     }
74 
75     public void setVarMonitorValueAdapterExpression(VarType)(ubyte varIndex, string adapterExpression)
76     {
77         alias adapterDict = Alias!(mixin(getVarMonitorValueAdaptersName!VarType));
78         setAdapterExpression(adapterDict, varIndex, adapterExpression);
79     }
80 
81     public string getVarMonitorValueAdapterExpression(VarType)(ubyte varIndex) const
82     {
83         alias adapterDict = Alias!(mixin(getVarMonitorValueAdaptersName!VarType));
84 
85         auto adapter = varIndex in adapterDict;
86 
87         return adapter ? adapter.expression : "x";
88     }
89 
90     public VarType adaptVarMonitorValue(VarType)(ubyte varIndex, VarType value) const
91     {
92         alias adapterDict = Alias!(mixin(getVarMonitorValueAdaptersName!VarType));
93 
94         auto adapter = varIndex in adapterDict;
95 
96         return adapter ? adapter.opCall(value) : value;
97     }
98 
99     private void setAdapterExpression(T)(ref shared ValueAdapter!T[ubyte] adapterDict, ubyte position, string adapterExpression)
100     {
101         if(adapterExpression)
102             adapterDict[position] = shared ValueAdapter!T(adapterExpression);
103         else
104             adapterDict.remove(position);
105     }
106 
107     public void setAISensorName(ubyte pin, string name)
108     {
109         setSensorName(AISensorNames, pin, name);
110     }
111 
112     public string getAISensorName(ubyte pin) const
113     {
114         return getSensorName(AISensorNames, pin, "AI_" ~ to!string(pin));
115     }
116 
117     public void setVarMonitorSensorName(MonitorType)(ubyte position, string name)
118     {
119         setSensorName(varMonitorSensorNames!MonitorType, position, name);
120     }
121 
122     public string getVarMonitorSensorName(MonitorType)(ubyte position) const
123     {
124         return getSensorName(varMonitorSensorNames!MonitorType, position, varMonitorDefaultSensorName!MonitorType ~ "_" ~ to!string(position));
125     }
126 
127     private void setSensorName(ref shared string[ubyte] namesDict, ubyte position, string name)
128     {
129         if(name)
130             namesDict[position] = name;
131         else
132             namesDict.remove(position);
133     }
134 
135     private string getSensorName(ref in shared string[ubyte] namesDict, ubyte position, string defaultName) const
136     {
137         if(position in namesDict)
138             return namesDict[position];
139         else
140             return defaultName;
141     }
142 
143     public void load(File configFile)
144     {
145         debug writeln("Trying to load configuration from file ", configFile.name != "" ? configFile.name : "with no name");
146 
147         string content;
148         configFile.readf("%s", &content);
149 
150         load(content);
151         debug writeln("Success!");
152     }
153 
154     public void load(string configStr)
155     {
156         string generateSwitch(string method, string arguments)
157         {
158             string str = "switch(typeName) {";
159 
160             foreach(t; VarMonitorTypes)
161             {
162                 str ~= "case \"" ~ t.stringof ~ "\":";
163                 str ~=  method ~ "!" ~ t.stringof ~ arguments ~ ";";
164                 str ~= "break;";
165             }
166 
167             str ~= "default: throw new InvalidConfigurationException(\"Type \" ~ typeName ~ \" is not supported by this Puppeteer\");";
168             str ~= "}";
169 
170             return str;
171         }
172 
173         void setVarMonitorAdapterDynamic(string typeName, ubyte varIndex, string expr)
174         {
175             mixin(generateSwitch("setVarMonitorValueAdapterExpression", "(varIndex, expr)"));
176         }
177 
178         void setVarMonitorSensorNameDynamic(string typeName, ubyte varIndex, string name)
179         {
180             mixin(generateSwitch("setVarMonitorSensorName", "(varIndex, name)"));
181         }
182 
183         try
184         {
185             foreach(string key, inner; parseJSON(configStr).object)
186             {
187                 switch(key)
188                 {
189                     case configAIAdaptersKey:
190                         foreach(string pin, expr; inner.object)
191                             setAIValueAdapterExpression(to!ubyte(pin), expr.str);
192                         break;
193 
194                     case configPWMOutAvgAdaptersKey:
195                         foreach(string pin, expr; inner.object)
196                             setPWMOutAvgAdapterExpression(to!ubyte(pin), expr.str);
197                         break;
198 
199                     case configVarAdaptersKey:
200                         foreach(string typeName, innerJson; inner.object)
201                             foreach(string varIndex, expr; innerJson)
202                                 setVarMonitorAdapterDynamic(typeName, to!ubyte(varIndex), expr.str);
203                         break;
204 
205                     case configAISensorNamesKey:
206                         foreach(string pin, name; inner.object)
207                             setAISensorName(to!ubyte(pin), name.str);
208                         break;
209 
210                     case configVarSensorNamesKey:
211                         foreach(string typeName, innerJson; inner.object)
212                             foreach(string varIndex, name; innerJson)
213                                 setVarMonitorSensorNameDynamic(typeName, to!ubyte(varIndex), name.str);
214                         break;
215 
216                     default:
217                         debug writefln("Unexpected entry with key '%s' found
218                         in configuration JSON. Ignoring entry.", key);
219                 }
220             }
221         }
222         catch(JSONException e)
223         {
224             debug writeln(e);
225             throw new InvalidConfigurationException("Invalid configuration string: " ~ configStr);
226         }
227     }
228 
229     public void save(File sinkFile, bool flush = true) const
230     {
231         debug writeln("Trying to save configuration in file ", sinkFile.name != "" ? sinkFile.name : "with no name");
232 
233         sinkFile.write(save());
234         if(flush)
235             sinkFile.flush();
236 
237         debug writeln("Sucess!");
238     }
239 
240     public string save() const
241     {
242         import std.json;
243 
244         enum emptyJson = string[string].init;
245 
246         JSONValue config = JSONValue(emptyJson);
247 
248         // AI value adapters
249         if(AIValueAdapters.length > 0)
250         {
251             JSONValue json = JSONValue(emptyJson);
252 
253             foreach(pin, adapter; AIValueAdapters)
254                 json.object[to!string(pin)] = JSONValue(adapter.expression);
255 
256             config.object[configAIAdaptersKey] = json;
257         }
258 
259         // PWM Out Average value adapters
260         if(PWMOutAvgValueAdapters.length > 0)
261         {
262             JSONValue json = JSONValue(emptyJson);
263 
264             foreach(pin, adapter; PWMOutAvgValueAdapters)
265                 json.object[to!string(pin)] = JSONValue(adapter.expression);
266 
267             config.object[configPWMOutAvgAdaptersKey] = json;
268         }
269 
270         // VarMonitor value adapters
271         {
272             JSONValue varAdapters = JSONValue(emptyJson);
273             bool anyWritten = false;
274 
275             foreach(T; VarMonitorTypes)
276             {
277                 alias varMonitorAdapters = varMonitorValueAdapters!T;
278 
279                 if(varMonitorAdapters.length > 0)
280                 {
281                     anyWritten = true;
282                     JSONValue json = JSONValue(emptyJson);
283 
284                     foreach(varIndex, adapter; varMonitorAdapters)
285                         json.object[to!string(varIndex)] = JSONValue(adapter.expression);
286 
287                         varAdapters.object[T.stringof] = json;
288                 }
289             }
290 
291             if(anyWritten)
292                 config.object[configVarAdaptersKey] = varAdapters;
293         }
294 
295         // AI sensor names
296         if(AISensorNames.length > 0)
297         {
298             JSONValue json = JSONValue(emptyJson);
299 
300             foreach(pin, name; AISensorNames)
301                 json.object[to!string(pin)] = JSONValue(name);
302 
303             config.object[configAISensorNamesKey] = json;
304         }
305 
306         // VarMonitor sensor names
307         {
308             JSONValue varNames = JSONValue(emptyJson);
309             bool anyWritten = false;
310 
311             foreach(T; VarMonitorTypes)
312             {
313                 alias varMonitorNames = varMonitorSensorNames!T;
314 
315                 if(varMonitorNames.length > 0)
316                 {
317                     anyWritten = true;
318                     JSONValue typeMonitorNamesJSON = JSONValue(emptyJson);
319 
320                     foreach(varIndex, name; varMonitorNames)
321                         typeMonitorNamesJSON.object[to!string(varIndex)] = JSONValue(name);
322 
323                     varNames.object[T.stringof] = typeMonitorNamesJSON;
324                 }
325             }
326 
327             if(anyWritten)
328                 config.object[configVarSensorNamesKey] = varNames;
329         }
330 
331         return config.toPrettyString();
332     }
333 
334     private alias varMonitorValueAdapters(T) = Alias!(mixin(getVarMonitorValueAdaptersName!T));
335     private alias varMonitorSensorNames(T) =  Alias!(mixin(getVarMonitorSensorNamesName!T));
336 }
337 
338 //Clean this
339 private pure string unrollVariableValueAdapters(VarTypes...)()
340 {
341     string unroll = "";
342 
343     foreach(T; VarTypes)
344         unroll ~= getVarMonitorValueAdaptersType!T ~ " " ~ getVarMonitorValueAdaptersName!T ~ ";\n";
345 
346     return unroll;
347 }
348 
349 private enum getVarMonitorValueAdaptersType(VarType) = "ValueAdapter!(" ~ VarType.stringof ~ ")[ubyte]";
350 private enum getVarMonitorValueAdaptersName(VarType) = VarType.stringof ~ "ValueAdapters";
351 
352 private enum getVarMonitorSensorNamesName(VarType) = VarType.stringof ~ "SensorNames";
353 
354 private pure string unrollVarMonitorSensorNames(VarTypes...)()
355 {
356     string unroll = "";
357 
358     foreach(T; VarTypes)
359         unroll ~= "string[ubyte] " ~ getVarMonitorSensorNamesName!T ~ ";\n";
360 
361     return unroll;
362 }