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 }