1 module puppeteer.communication.communicator; 2 3 import puppeteer.puppeteer; 4 import puppeteer.var_monitor_utils; 5 6 import puppeteer.communication.communicator_messages; 7 import puppeteer.communication.communication_exception; 8 import puppeteer.communication.is_communicator; 9 10 import puppeteer.puppet_link.puppet_link; 11 import puppeteer.puppet_link.is_puppet_link; 12 13 import std.stdio; 14 import std.exception; 15 import std.concurrency; 16 import std.datetime; 17 import std.conv; 18 import std.meta; 19 20 import core.thread; 21 import core.atomic; 22 23 shared class Communicator(PuppetLinkT, IVTypes...) 24 if(isPuppetLink!(PuppetLinkT, IVTypes)) 25 { 26 static assert(isCommunicator!(shared typeof(this), IVTypes)); 27 28 /* Ugly fix for allowing different instances */ 29 30 static int next_id = 0; 31 private int id; 32 33 @property 34 private string workerTidName() 35 { 36 enum nameBase = "valueEmitter.communicator"; 37 38 return nameBase ~ to!string(id); 39 } 40 41 @property 42 private Tid workerTid() 43 { 44 return locate(workerTidName); 45 } 46 47 /* End of ugly fix */ 48 49 private OnAIUpdateCallback onAIUpdateCallback; 50 51 private Tuple!(staticMap!(OnIVUpdateCallback, IVTypes)) onIVUpdateCallbacks; 52 alias onIVUpdateCallback(IVType) = onIVUpdateCallbacks[staticIndexOf!(IVType, IVTypes)]; 53 54 @property 55 public bool isCommunicationOngoing() 56 { 57 return workerTid != Tid.init; 58 } 59 60 private void enforceCommunicationOngoing() 61 { 62 enforce(isCommunicationOngoing); 63 } 64 65 this() 66 { 67 id = next_id++; 68 } 69 70 bool startCommunication(string devFilename, BaudRate baudRate, Parity parity) 71 { 72 enforce!CommunicationException(!isCommunicationOngoing); 73 74 auto workerTid = spawn(&communicationLoop!PuppetLinkT, devFilename, baudRate, parity); 75 register(workerTidName, workerTid); 76 77 auto msg = receiveOnly!CommunicationEstablishedMessage(); 78 79 return msg.success; 80 } 81 82 void endCommunication() 83 { 84 enforceCommunicationOngoing(); 85 86 workerTid.send(EndCommunicationMessage()); 87 receiveOnly!CommunicationEndedMessage(); 88 } 89 90 void setAIMonitor(ubyte pin, bool shouldMonitor) 91 { 92 enforceCommunicationOngoing(); 93 workerTid.send(PinMonitorMessage(shouldMonitor ? 94 PinMonitorMessage.Action.start : 95 PinMonitorMessage.Action.stop, pin)); 96 } 97 98 void setIVMonitor(IVType)(ubyte varIndex, bool shouldMonitor) 99 if(staticIndexOf!(IVType, IVTypes) != -1) 100 { 101 enforceCommunicationOngoing(); 102 workerTid.send(VarMonitorMessage(shouldMonitor ? 103 VarMonitorMessage.Action.start : 104 VarMonitorMessage.Action.stop, 105 varIndex, 106 varMonitorTypeCode!IVType)); 107 } 108 109 void setOnAIUpdateCallback(OnAIUpdateCallback callback) 110 { 111 onAIUpdateCallback = callback; 112 } 113 114 void setOnIVUpdateCallback(IVType)(OnIVUpdateCallback!IVType callback) 115 { 116 onIVUpdateCallback!IVType = callback; 117 } 118 119 void setPWMValue(ubyte pin, ubyte pwmValue) 120 { 121 enforceCommunicationOngoing(); 122 workerTid.send(SetPWMMessage(pin, pwmValue)); 123 } 124 125 private void communicationLoop(string fileName, 126 immutable BaudRate baudRate, 127 immutable Parity parity) 128 { 129 enum receiveTimeoutMs = 10; 130 enum bytesReadAtOnce = 1; 131 132 PuppetLinkT puppetLink = new PuppetLinkT(fileName); 133 puppetLink.AIMonitorListener = this; 134 puppetLink.IVMonitorListener = this; 135 136 if(puppetLink.startCommunication()) 137 ownerTid.send(CommunicationEstablishedMessage(true)); 138 else 139 { 140 ownerTid.send(CommunicationEstablishedMessage(false)); 141 return; 142 } 143 144 StopWatch communicationStopWatch = StopWatch(AutoStart.yes); 145 146 bool shouldContinue = true; 147 148 do 149 { 150 puppetLink.readPuppet(communicationStopWatch.peek().msecs); 151 152 receiveTimeout(msecs(receiveTimeoutMs), 153 (EndCommunicationMessage msg) 154 { 155 shouldContinue = false; 156 }, 157 (PinMonitorMessage msg) 158 { 159 puppetLink.setAIMonitor(msg.pin, msg.action == PinMonitorMessage.Action.start); 160 }, 161 (VarMonitorMessage msg) 162 { 163 puppetLink.setIVMonitor(msg.varTypeCode, msg.varIndex, msg.action == VarMonitorMessage.Action.start); 164 }, 165 (SetPWMMessage msg) 166 { 167 puppetLink.setPWMOut(msg.pin, msg.value); 168 }); 169 170 }while(shouldContinue); 171 172 puppetLink.endCommunication(); 173 communicationStopWatch.stop(); 174 175 ownerTid.send(CommunicationEndedMessage()); 176 } 177 178 void onAIUpdate(ubyte pin, float value, long communicationMillisTime) 179 { 180 enforce(isCommunicationOngoing); 181 182 if(onAIUpdateCallback !is typeof(onAIUpdateCallback).init) 183 onAIUpdateCallback(pin, value, communicationMillisTime); 184 } 185 186 void onIVUpdate(IVType)(ubyte varIndex, IVType value, long communicationMillisTime) 187 if(staticIndexOf!(IVType, IVTypes) !is -1) 188 { 189 enforce(isCommunicationOngoing); 190 191 if(onIVUpdateCallback!IVType !is typeof(onIVUpdateCallback!IVType).init) 192 onIVUpdateCallback!IVType(varIndex, value, communicationMillisTime); 193 } 194 }