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