1 | /* Copyright (C) 2001 Marc Casaldaliga Albisu <casaldaliga@ifae.es>
|
---|
2 | ================================================================
|
---|
3 |
|
---|
4 | This code is free software; you can redistribute it and/or modify
|
---|
5 | it under the terms of the GNU General Public License as published by
|
---|
6 | the Free Software Foundation; either version 2 of the License, or
|
---|
7 | (at your option) any later version.
|
---|
8 |
|
---|
9 | This code is distributed in the hope that it will be useful,
|
---|
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
12 | GNU General Public License for more details.
|
---|
13 |
|
---|
14 | You should have received a copy of the GNU General Public License
|
---|
15 | along with Emacs (which is required to make this stuff work); if
|
---|
16 | not, write to the Free Software Foundation, Inc., 675 Mass Ave,
|
---|
17 | Cambridge, MA 02139, USA.
|
---|
18 | ==================================================================
|
---|
19 | */
|
---|
20 |
|
---|
21 | //it is a good idea to encapsulate all the configuration file dependence in one a wrapper class SubFormatSubsystem, and derive from it
|
---|
22 | //#include "SubFormatSubsystem.hxx"
|
---|
23 | #include "Subsystem.hxx"
|
---|
24 | #include "PeriodicAction.hxx"
|
---|
25 | #include "TCPListener.hxx"
|
---|
26 | #include "TCPSender.hxx"
|
---|
27 | #include "IONotifier.hxx"
|
---|
28 | #include <string>
|
---|
29 | //for signals/slots (Callbacks in C++, see http://libsigc.sourceforge.net/)
|
---|
30 | #include <sigc++/signal_system.h>
|
---|
31 | //libsigc must be installed
|
---|
32 | #include <stdio.h>
|
---|
33 | using namespace SigC;
|
---|
34 | //there's no agreement in maximum lenght message, by now
|
---|
35 |
|
---|
36 |
|
---|
37 | Subsystem::Subsystem (unsigned short int portCommandListener, unsigned long int reportPeriod, char * ccName, unsigned short int portReportListener , unsigned short int maxTimeoutCount_ )
|
---|
38 | :
|
---|
39 | TCPListener(portCommandListener), reportSender(ccName,portReportListener), tryReportConn(reportPeriod), locked(false), reportingLoop(reportPeriod), reporting(false),reportAckNotifier(0),maxTimeoutCount(maxTimeoutCount_)
|
---|
40 | {
|
---|
41 | //the behaviour is the following: commandListener has been started as a being derived from TCPListener. When connection from cc is opened commands are processed in this->process()
|
---|
42 | //when REPOR arrives reportSender(TCPSender) here tries to connect reportListener in CC. When this happens will start a periodic reporting
|
---|
43 |
|
---|
44 |
|
---|
45 | //tryReportConn is the PeriodicAction which will try periodically reportSender.isTrialToConnectSuccesful ...
|
---|
46 | tryReportConn.DoWhileNot(slot(reportSender, &TCPSender::isTrialToConnectSuccesful) );
|
---|
47 | //... and when it is succesfull will start reportingLoop periodic action
|
---|
48 | tryReportConn.FinallyDo(slot(this, &Subsystem::StartReporting));
|
---|
49 | //tryReportConn is started when REPOR received (See process())
|
---|
50 |
|
---|
51 | //reportingLoop is a PeriodicAction which periodically sends the relevant report
|
---|
52 | reportingLoop.DoWhile(slot(this,&Subsystem::SendReport));
|
---|
53 | //according the line above, it will be started whe reportSender connection is succesfull
|
---|
54 |
|
---|
55 | //by now
|
---|
56 | //demo report:
|
---|
57 | strcpy(reportString,"standby:11:44:20:11:01:6:34:12:6.0:6.1:6.05:11:01:36:34:12:10.6:6.0:6.0:00:00:30.0:00:00:4.6:0.1:0.05:0.004:0.0:0.001:0.003:guiding:0.1deg:on:3.1Hz:0.09deg:J2000:Mkn421:messagefromthedrive:");
|
---|
58 | pthread_mutex_init(&mutex4report,NULL);
|
---|
59 | pthread_mutex_init(&mutex4ack,NULL);
|
---|
60 | }
|
---|
61 |
|
---|
62 | void Subsystem::SetReportString(char * report)
|
---|
63 | {
|
---|
64 | SuspendComm();
|
---|
65 | strcpy(this->reportString,report);
|
---|
66 | ResumeComm();
|
---|
67 |
|
---|
68 | }
|
---|
69 |
|
---|
70 | bool Subsystem::SendSpecialReportContaining(char * specialReport)
|
---|
71 | {
|
---|
72 | pthread_mutex_lock(&mutex4ack);
|
---|
73 | if(!acknowledgeReceivedForLastReport){
|
---|
74 | timeoutCount++;
|
---|
75 | if(timeoutCount==maxTimeoutCount){
|
---|
76 | pthread_mutex_unlock(&mutex4ack);
|
---|
77 | return false; //SendReport is not succesfull so this will stop Periodic action
|
---|
78 | }
|
---|
79 | }
|
---|
80 | //before writing the report or sending it make sure no other thread thouches it
|
---|
81 | pthread_mutex_lock(&mutex4report);
|
---|
82 | // SubFormatSubsystem::ElaborateReport
|
---|
83 | //calls overriden method GenerateReport that will do the
|
---|
84 | //append the tag for specialReport in this protocol
|
---|
85 | sprintf(this->reportString,"SPECIAL:%s",specialReport);
|
---|
86 |
|
---|
87 | //GenerateReport may have filled timestamp. Record it to checkreportacknowledge in future
|
---|
88 |
|
---|
89 |
|
---|
90 | ExtractTimeStampFromLastReportTo(lastTimeStamp);
|
---|
91 | reportSender.Send(this->reportString);
|
---|
92 | cerr<<"Sending "<<this->reportString<<"\n";
|
---|
93 | pthread_mutex_unlock(&mutex4report);
|
---|
94 |
|
---|
95 | acknowledgeReceivedForLastReport=false;
|
---|
96 | pthread_mutex_unlock(&mutex4ack);
|
---|
97 | //????????Order mutexes are lock/unlock????????????
|
---|
98 | return true; //if it goes here reporting was succesfull (Used in PeriodicAction reportingLoop)
|
---|
99 | }
|
---|
100 |
|
---|
101 | inline void Subsystem::StartReporting()
|
---|
102 | {
|
---|
103 | reportingLoop.Start();
|
---|
104 | reportAckNotifier=new IONotifier(reportSender.IODescriptor());
|
---|
105 | //after report is sent via reportSender socket, reportListener in CC is sending an acknowledge. The next signal triggers CheckReportAcknowledge when something is received in reportSender channel (suposedly to be this ack)
|
---|
106 | reportAckNotifier->readable.connect(slot(this,&Subsystem::CheckReportAcknowledge));
|
---|
107 | //the first time
|
---|
108 | timeoutCount=0;
|
---|
109 | acknowledgeReceivedForLastReport=true;
|
---|
110 |
|
---|
111 | }
|
---|
112 | void Subsystem::ResetConnectionToCC () {
|
---|
113 | if(locked){
|
---|
114 | ULock();
|
---|
115 | }
|
---|
116 | reportingLoop.Stop();
|
---|
117 | reporting=false;
|
---|
118 | delete reportAckNotifier;
|
---|
119 | reportSender.CloseConnection();
|
---|
120 | TCPListener::ClosingChannel(); //but still is waiting for new connections
|
---|
121 | }
|
---|
122 | void Subsystem::process(){
|
---|
123 | if (!locked){
|
---|
124 | if(!reporting){
|
---|
125 | //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! pending: compare the whole string, not only the first 5 bytes. strcmp gave problems ???????????????????
|
---|
126 | if ( !strncmp(TCPListener::receivedStream, "REPOR", 5) ) {
|
---|
127 | cerr<<"received REPOR\n";
|
---|
128 | //before startreporting, the connection to cc by reportSender has to be stablished
|
---|
129 | tryReportConn.Start();
|
---|
130 | reporting=true;
|
---|
131 | return;
|
---|
132 | }
|
---|
133 | } else { //already reporting
|
---|
134 | if ( !strncmp(TCPListener::receivedStream,"LOCK!", 5) ) {
|
---|
135 | Lock();
|
---|
136 | return;
|
---|
137 | }
|
---|
138 | }
|
---|
139 |
|
---|
140 | } else { //locked
|
---|
141 | //as preliminary tests instead of commands, the new subsystem state is sent
|
---|
142 | if (! strncmp(TCPListener::receivedStream, "ULOCK", 5) ) {
|
---|
143 | ULock();
|
---|
144 | return;
|
---|
145 | }
|
---|
146 | cerr<<"processing ... seting new state: "<< TCPListener::receivedStream <<"\n";
|
---|
147 | //calls overriden method thet will process the command itself
|
---|
148 | ProcessCmd(TCPListener::receivedStream);
|
---|
149 |
|
---|
150 | //state=receivedStream;
|
---|
151 | return;
|
---|
152 |
|
---|
153 | }
|
---|
154 |
|
---|
155 | //if program reaches here is because it hasn't Received REPOR & LOCK in the proper way. Network partner it's not (a properly working) CC. Closing the opened connection and waiting for new one
|
---|
156 | ResetConnectionToCC();
|
---|
157 | }
|
---|
158 |
|
---|
159 | bool Subsystem::SendReport () {
|
---|
160 |
|
---|
161 | pthread_mutex_lock(&mutex4ack);
|
---|
162 | if(!acknowledgeReceivedForLastReport){
|
---|
163 | timeoutCount++;
|
---|
164 | if(timeoutCount==maxTimeoutCount){
|
---|
165 | pthread_mutex_unlock(&mutex4ack);
|
---|
166 | return false; //SendReport is not succesfull so this will stop Periodic action
|
---|
167 | }
|
---|
168 | }
|
---|
169 | //before writing the report or sending it make sure no other thread thouches it
|
---|
170 | pthread_mutex_lock(&mutex4report);
|
---|
171 | // SubFormatSubsystem::ElaborateReport
|
---|
172 | //calls overriden method GenerateReport that will do the
|
---|
173 | GenerateReport();
|
---|
174 | //GenerateReport may have filled timestamp. Record it to checkreportacknowledge in future
|
---|
175 |
|
---|
176 |
|
---|
177 | ExtractTimeStampFromLastReportTo(lastTimeStamp);
|
---|
178 | reportSender.Send(this->reportString);
|
---|
179 | cerr<<"Sending "<<this->reportString<<"\n";
|
---|
180 | pthread_mutex_unlock(&mutex4report);
|
---|
181 |
|
---|
182 | acknowledgeReceivedForLastReport=false;
|
---|
183 | pthread_mutex_unlock(&mutex4ack);
|
---|
184 | //????????Order mutexes are lock/unlock????????????
|
---|
185 | return true; //if it goes here reporting was succesfull (Used in PeriodicAction reportingLoop)
|
---|
186 | //acknowledge check is done elsewhere, when it arrives (asynchronously)
|
---|
187 | }
|
---|
188 | void Subsystem::CheckReportAcknowledge()
|
---|
189 | {
|
---|
190 | reportSender.Receive();
|
---|
191 |
|
---|
192 | pthread_mutex_lock(&mutex4ack);
|
---|
193 | //incorporate lastTimeStamp in check
|
---|
194 | if (reportSender.ReturnNew() == "RECV@" ) {
|
---|
195 | acknowledgeReceivedForLastReport=true;
|
---|
196 | timeoutCount=0;
|
---|
197 | }else{
|
---|
198 | cerr<<"wrong ack!\n";
|
---|
199 | }
|
---|
200 | pthread_mutex_unlock(&mutex4ack);
|
---|
201 | }
|
---|
202 |
|
---|
203 | void Subsystem::Lock(){
|
---|
204 | cerr<<"Locking to CC mode\n";
|
---|
205 | locked=true;
|
---|
206 | }
|
---|
207 | void Subsystem::ULock(){
|
---|
208 | cerr<<"UNLocking from CC mode\n";
|
---|
209 | locked=false;
|
---|
210 | }
|
---|
211 | void Subsystem::SuspendComm()
|
---|
212 | {
|
---|
213 | pthread_mutex_lock(&mutex4report);
|
---|
214 | };
|
---|
215 | void Subsystem::ResumeComm()
|
---|
216 | {
|
---|
217 | pthread_mutex_unlock(&mutex4report);
|
---|
218 | };
|
---|
219 | void Subsystem::ExtractTimeStampFromLastReportTo(char * lastTimeStamp)
|
---|
220 | {
|
---|
221 | string report(this->reportString);
|
---|
222 | string::iterator it=report.begin();
|
---|
223 | //point it to the first appearence of :
|
---|
224 | it+=report.find(":");
|
---|
225 |
|
---|
226 | //cut the first field (between :) which is the status
|
---|
227 | report.erase(report.begin(),it);
|
---|
228 |
|
---|
229 | //#define TIMESTAMP_LEN 12
|
---|
230 | //%02.2d:%02.2d:%02.2d:%03.3d
|
---|
231 | it=report.begin(); //back to the beginning
|
---|
232 | it+=12;
|
---|
233 | //cut from the timestamp end to the rest
|
---|
234 | report.erase(it,report.end());
|
---|
235 | strcpy(lastTimeStamp,report.c_str());
|
---|
236 |
|
---|
237 | }
|
---|
238 |
|
---|
239 |
|
---|
240 | //TO BE OVERRIDEN:
|
---|
241 |
|
---|
242 | void Subsystem::ProcessCmd(char *)
|
---|
243 | {};
|
---|
244 | void Subsystem::GenerateReport()
|
---|
245 | {};
|
---|
246 | void Subsystem::HandleConnectionTimeoutIsOver()
|
---|
247 | {};
|
---|
248 |
|
---|
249 |
|
---|