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