1 module puppeteer.puppeteer; 2 3 import test.puppeteer.puppeteer_test : test; 4 mixin test; 5 6 public import puppeteer.serial.iserial_port; 7 public import puppeteer.serial.baud_rate; 8 public import puppeteer.serial.parity; 9 10 import puppeteer.signal_wrapper; 11 import puppeteer.var_monitor_utils; 12 13 import puppeteer.logging.ipuppeteer_logger; 14 15 import puppeteer.value_adapter.invalid_adapter_expression_exception; 16 17 import puppeteer.communication.icommunicator; 18 19 import puppeteer.configuration.iconfiguration; 20 21 import std.stdio; 22 import std.concurrency; 23 import std.conv; 24 import std.typecons; 25 import std.exception; 26 import std.signals; 27 import std.meta; 28 import std.datetime; 29 import std.exception; 30 31 import core.time; 32 33 public alias pinListenerDelegate = void delegate (ubyte, float, float, long) shared; 34 private alias varMonitorDelegateType(VarType) = AliasSeq!(ubyte, VarType, VarType, long); 35 36 // Currently disabled due to not being able to be expanded into a function argument and then used from another type 37 // Manually writing the delegate type as the function argument will have to do by now 38 @disable 39 public alias varMonitorDelegate(VarType) = void delegate (varMonitorDelegateType!VarType) shared; 40 41 shared class Puppeteer(VarMonitorTypes...) 42 if(allSatisfy!(isVarMonitorTypeSupported, VarMonitorTypes)) 43 { 44 alias PinSignalWrapper = SignalWrapper!(ubyte, float, float, long); 45 46 //Manually synchronized between both logical threads 47 protected PinSignalWrapper[ubyte] pinSignalWrappers; 48 protected mixin(unrollVariableSignalWrappers!VarMonitorTypes()); 49 50 protected ICommunicator!VarMonitorTypes communicator; 51 52 protected IConfiguration!VarMonitorTypes config; 53 54 protected IPuppeteerLogger logger; 55 56 @property 57 shared(IConfiguration!VarMonitorTypes) configuration() 58 { 59 return config; 60 } 61 62 enum canMonitor(T) = __traits(compiles, mixin(getVarMonitorSignalWrappersName!T)); 63 64 @property 65 bool isCommunicationEstablished() 66 { 67 return communicator.isCommunicationOngoing; 68 } 69 70 this(shared ICommunicator!VarMonitorTypes communicator, shared IConfiguration!VarMonitorTypes configuration, shared IPuppeteerLogger logger) 71 { 72 this.communicator = communicator; 73 this.config = configuration; 74 this.logger = logger; 75 } 76 77 public void addPinListener(ubyte pin, pinListenerDelegate listener) 78 in 79 { 80 assert(listener !is null); 81 } 82 body 83 { 84 auto wrapper = pin in pinSignalWrappers; 85 if(!wrapper) 86 { 87 shared PinSignalWrapper signalWrapper = new PinSignalWrapper; 88 89 pinSignalWrappers[pin] = signalWrapper; 90 91 //No need to synchronize this call since it is the first listener 92 signalWrapper.addListener(listener); 93 94 communicator.changeAIMonitorStatus(pin, true); 95 } 96 else 97 { 98 shared PinSignalWrapper signalWrapper = *wrapper; 99 100 synchronized(signalWrapper) 101 signalWrapper.addListener(listener); 102 } 103 } 104 105 public void removePinListener(ubyte pin, pinListenerDelegate listener) 106 in 107 { 108 assert(listener !is null); 109 } 110 body 111 { 112 enforce(pin in pinSignalWrappers); 113 114 shared PinSignalWrapper signalWrapper = pinSignalWrappers[pin]; 115 116 synchronized(signalWrapper) 117 signalWrapper.removeListener(listener); 118 119 if(signalWrapper.listenersNumber == 0) 120 { 121 pinSignalWrappers.remove(pin); 122 communicator.changeAIMonitorStatus(pin, false); 123 } 124 } 125 126 public void addVariableListener(VarType)(ubyte varIndex, void delegate(ubyte, VarType, VarType, long) shared listener) 127 if(canMonitor!VarType) 128 in 129 { 130 assert(listener !is null); 131 } 132 body 133 { 134 alias typeSignalWrappers = Alias!(mixin(getVarMonitorSignalWrappersName!VarType)); 135 auto wrapper = varIndex in typeSignalWrappers; 136 137 if(wrapper) 138 { 139 synchronized(*wrapper) 140 wrapper.addListener(listener); 141 } 142 else 143 { 144 auto signalWrapper = new shared SignalWrapper!(varMonitorDelegateType!VarType); 145 signalWrapper.addListener(listener); 146 typeSignalWrappers[varIndex] = signalWrapper; 147 148 communicator.changeVarMonitorStatus!VarType(varIndex, true); 149 } 150 } 151 152 public void removeVariableListener(VarType)(ubyte varIndex, void delegate(ubyte, VarType, VarType, long) shared listener) 153 if(canMonitor!VarType) 154 in 155 { 156 assert(listener !is null); 157 } 158 body 159 { 160 alias typeSignalWrappers = Alias!(mixin(getVarMonitorSignalWrappersName!VarType)); 161 162 enforce(varIndex in typeSignalWrappers); 163 164 auto signalWrapper = typeSignalWrappers[varIndex]; 165 166 synchronized(signalWrapper) 167 signalWrapper.removeListener(listener); 168 169 if(signalWrapper.listenersNumber == 0) 170 { 171 typeSignalWrappers.remove(varIndex); 172 communicator.changeVarMonitorStatus!VarType(varIndex, false); 173 } 174 } 175 176 public void setPWM(ubyte pin, ubyte value) 177 { 178 communicator.setPWMValue(pin, value); 179 } 180 181 public void setPWMAverage(ubyte pin, float averageValue) 182 { 183 enum pwmHigh = 5; 184 float adaptedAvg = configuration.adaptPWMOutAvgValue(pin, averageValue); 185 setPWM(pin, getPWMFromAverage!pwmHigh(adaptedAvg)); 186 } 187 188 private ubyte getPWMFromAverage(float pwmHigh)(float averageValue) 189 { 190 import std.math; 191 return to!ubyte(round(averageValue / pwmHigh * ubyte.max)); 192 } 193 194 public bool startCommunication(string devFilename, BaudRate baudRate, Parity parity, string logFilename) 195 { 196 return communicator.startCommunication(this, devFilename, baudRate, parity, logFilename); 197 } 198 199 public void endCommunication() 200 { 201 communicator.endCommunication(); 202 203 //Remove all listeners 204 foreach(pin; pinSignalWrappers.byKey()) 205 synchronized(pinSignalWrappers[pin]) 206 pinSignalWrappers.remove(pin); 207 208 209 //TODO: remove listeners for variables as well 210 } 211 212 package void emitAIRead(byte pin, float readValue, long readMilliseconds) 213 { 214 auto signalWrapper = pin in pinSignalWrappers; 215 if(signalWrapper) 216 { 217 synchronized(*signalWrapper) 218 signalWrapper.emit(pin, readValue, config.adaptAIValue(pin, readValue), readMilliseconds); 219 } 220 else 221 { 222 debug(2) writeln("No listeners registered for pin ",pin); 223 } 224 225 logger.logSensor(readMilliseconds, config.getAISensorName(pin), to!string(readValue), to!string(config.adaptAIValue(pin, readValue))); 226 } 227 228 package void emitVarRead(VarType)(byte varIndex, VarType readValue, long readMilliseconds) 229 if(canMonitor!VarType) 230 { 231 VarType adaptedValue = config.adaptVarMonitorValue(varIndex, readValue); 232 233 alias varTypeSignalWrappers = Alias!(mixin(getVarMonitorSignalWrappersName!VarType)); 234 235 auto wrapper = varIndex in varTypeSignalWrappers; 236 if(wrapper) 237 { 238 synchronized(*wrapper) 239 wrapper.emit(varIndex, readValue, adaptedValue, readMilliseconds); 240 } 241 else 242 { 243 debug(2) writeln("SignalWrapper for type ", VarType.stringof, "and varIndex ", varIndex, "was no longer in its SignalWrapper assoc array. Skipping signal emission."); 244 } 245 246 logger.logSensor(readMilliseconds, config.getVarMonitorSensorName!VarType(varIndex), to!string(readValue), to!string(adaptedValue)); 247 } 248 } 249 250 private pure string unrollVariableSignalWrappers(VarTypes...)() 251 { 252 string unroll = ""; 253 254 foreach(varType; VarTypes) 255 unroll ~= getVarMonitorSignalWrappersType!varType ~ " " ~ getVarMonitorSignalWrappersName!varType ~ ";\n"; 256 257 return unroll; 258 } 259 260 261 private enum getVarMonitorSignalWrappersType(VarType) = "SignalWrapper!(ubyte, " ~ VarType.stringof ~ ", " ~ VarType.stringof ~ ", long)[ubyte]"; 262 unittest 263 { 264 assert(getVarMonitorSignalWrappersType!int == "SignalWrapper!(ubyte, int, int, long)[ubyte]"); 265 assert(getVarMonitorSignalWrappersType!float == "SignalWrapper!(ubyte, float, float, long)[ubyte]"); 266 assert(getVarMonitorSignalWrappersType!byte == "SignalWrapper!(ubyte, byte, byte, long)[ubyte]"); 267 } 268 269 270 private enum getVarMonitorSignalWrappersName(VarType) = VarType.stringof ~ "SignalWrappers"; 271 unittest 272 { 273 assert(getVarMonitorSignalWrappersName!int == "intSignalWrappers"); 274 }