/[PAMELA software]/yoda/event/PamelaRun.cpp
ViewVC logotype

Contents of /yoda/event/PamelaRun.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3.0 - (show annotations) (download)
Fri Mar 4 15:54:11 2005 UTC (19 years, 11 months ago) by kusanagi
Branch: MAIN
Changes since 2.7: +2 -2 lines
Error proof version.
Implemented all detectors packets plus all the main telemetries packets.
Missing all the Init and Alarm packets.
Disabled CRC control on VarDump, ArrDump, TabDump for CPU debugging needs
(the data formats seems correct even if CRC get wrong)

1 /** @file
2 * $Author: kusanagi $
3 * $Date: 2005/03/03 12:57:48 $
4 * $Revision: 2.7 $
5 *
6 * Implementation of the PamelaRun class.
7 */
8 #include <iostream>
9 #include <iomanip>
10 #include <list>
11 #include <exception>
12 //#include <string>
13 #include <log4cxx/logger.h>
14
15 #include <TFile.h> //Substituted by Maurizio 05 Feb 2004
16 #include <TTree.h> //Substituted by Maurizio 05 Feb 2004
17 #include <TChain.h> //Substituted by Maurizio 05 Feb 2004
18 #include <TKey.h> //Substituted by Maurizio 05 Feb 2004
19 #include <TList.h> //Substituted by Maurizio 05 Feb 2004
20
21
22 #include "PamelaRun.h"
23 #include "EventHeader.h"
24 #include "Algorithm.h"
25 #include "AlgorithmInfo.h"
26 #include "Exception.h"
27 #include <sys/stat.h>
28
29 extern "C" {
30 #include <dirent.h>
31 #include "DirectoryStructure.h"
32 }
33
34 using namespace pamela;
35
36 static log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger(_T("pamela.PamelaRun"));
37
38 /**
39 * Get the run name according to a certain run number.
40 * @param run Run number.
41 * @return a string with the run name.
42 */
43 std::string PamelaRun::GetRunName(int run) {
44 std::stringstream temp;
45 temp.str("");
46 temp << std::setw( 4 ) << std::setfill( '0' ) << run;
47 return "Run" + temp.str();
48 }
49
50 /**
51 * Check if the run number is already assigned
52 * @param int run - Number of the run to check
53 * @return int
54 */
55 /*int PamelaRun::RunExists(int run) throw (std::exception){
56 int res;
57 DIR *dirp;
58 std::string pathDir((char*)getenv("YODA_DATA"));
59 pathDir = pathDir + "/";
60 pathDir = pathDir + GetRunName(run);
61 // Open the directory
62 if ((dirp = opendir(pathDir.c_str())) == NULL) {
63 res = 0;
64 } else {
65 closedir(dirp);
66 res = 1;
67 }
68 return res;
69 }*/
70
71
72 /**
73 * Check if the run number is already assigned
74 * @param int run - Number of the run to check
75 * @return int
76 */
77 void PamelaRun::RunExists(std::string input) throw (std::exception){
78 int res;
79 DIR *dirp;
80 std::string fileName;
81 std::string pathDir((char*)getenv("YODA_DATA"));
82 std::string tempName;
83 std::string::size_type pos;
84
85 pos = input.find_last_of("/");
86 //if ((pos == std::string::npos) && !input.empty()) ;//throw Exception("Directories not allowed....");
87 fileName = input.substr(pos+1);
88 pos = fileName.find_last_of(".");
89 fileName = fileName.substr(0,pos);
90 pathDir = pathDir + "/";
91 tempName = pathDir + fileName + "00";
92 //oss << pathDir << fileName << std::setw( 2 ) << std::setfill( '0' );
93 oss.str(tempName.c_str());
94 int i = 0;
95 while (res){
96 //if ((dirp = opendir(tempName.c_str())) == NULL) {
97 if ((dirp = opendir(oss.str().c_str())) == NULL) {
98 res = 0;
99 } else {
100 closedir(dirp);
101 oss.str("");
102 oss << pathDir << fileName << std::setw( 2 ) << std::setfill( '0' ) << ++i;
103 res = 1;
104 }
105 }
106 oss.str("");
107 oss << Path << "/" << fileName << std::setw( 2 ) << std::setfill( '0' ) << i;
108 Path = oss.str();
109 oss.str("");
110 oss << fileName << std::setw( 2 ) << std::setfill( '0' ) << i;
111 Run = oss.str();
112 //return (oss.str());
113 }
114
115 /**
116 * Create a new Pamela run.
117 * @param run Run number
118 * @param path Base path name to the data
119 */
120 PamelaRun::PamelaRun(std::string fileName, std::string path):
121 Path(path),
122 Run(fileName),
123 RunNumber(1){ //veirificare se รจ possibilie eliminare questa variabile
124 logger->debug(_T("Constructor"));
125 //Run = RunExists(fileName);
126 RunExists(fileName);
127 info = RunInfo(this);
128 }
129
130 /**
131 * Get the directory name that contains the files of a certain event
132 * type for this run.
133 * @param type the packet type.
134 * @return the complete path for this event type.
135 */
136 std::string PamelaRun::GetDirName(PacketType const * type) const {
137 //std::stringstream oss;
138 //std::string name = type->GetName();
139 //oss.str("");
140 //oss << Path << "/" << Run << "/" << "pippo";
141 //return oss.str();
142 //return "pippo";
143 //std::string EventType = type->GetName();
144 return Path + "/" + Run + "/" + type->GetName();
145 }
146
147 /**
148 * Get the file name for a certain event type.
149 * @param type subpacket type.
150 * @param name subpacket name.
151 * @return the complete path and file name.
152 */
153 std::string PamelaRun::GetFileName(const SubPacket* type, std::string name) {
154 if (type->IsDetectorSpecific()) {
155 return Path + "/" + type->GetPacketType()->GetName() + "/"
156 + Run + "." + type->GetPacketType()->GetName() + "."
157 + type->GetSubDetector()->GetName() + "."
158 + name + ".root";
159 } else {
160 return Path + "/" + type->GetPacketType()->GetName() + "/"
161 + Run + "." + type->GetPacketType()->GetName() + "."
162 + name + ".root";
163 /*
164 return Path + "/" + type->GetPacketType()->GetName() + "/"
165 + Run + "." + type->GetPacketType()->GetName() + "."
166 + name + ".root"; */
167 }
168 }
169
170 /**
171 * Get the file name for a certain event type.
172 * @param type subpacket type.
173 * @return the complete path and file name.
174 */
175 std::string PamelaRun::GetFileName(const SubPacket* type) {
176 //return GetFileName(type, type->GetSubPacketName());
177 return GetFileName(type, "pippo");
178 }
179
180 /**
181 * Get the branch name that corresponds to a certain
182 * subpacket. Usually, this is the name of the subpacket.
183 * @param type subpacket type.
184 * @return the corresponding branch name.
185 */
186 static std::string GetDefaultBranchName(const SubPacket* type) {
187 return type->GetSubPacketName();
188 }
189
190 /**
191 * Get the tree name for a certain subpacket. This is the name of the
192 * subdetector for detector specific subpackets, and the packet name
193 * ("Physics" etc.) for common packets.
194 * @param type the packet type.
195 * @return the corresponding tree name.
196 */
197 static std::string GetTreeName(const SubPacket* type) {
198 if (type->IsDetectorSpecific()) {
199 return type->GetSubDetector()->GetName();
200 } else {
201 return type->GetPacketType()->GetName();
202 }
203 }
204
205 /**
206 * Get a list of all root files from a certain path.
207 * @param path Path name to start the search.
208 * @return A list of all file names.
209 */
210 static std::list<std::string> GetRootFiles(std::string path) throw (std::exception) {
211 std::list<std::string> files;
212 DIR *dir = opendir(path.c_str());
213 if (dir == 0) {
214 logger->debug("Could not open " + path);
215 //throw Exception("Could not open " + path);
216 }
217
218 for (struct dirent *d = readdir(dir); d != NULL; d = readdir(dir)) {
219 if ((strcmp(".",d->d_name) == 0) || (strcmp("..",d->d_name) == 0))
220 continue;
221 std::string filename = path + "/" + d->d_name;
222 struct stat buf;
223 stat(filename.c_str(), &buf);
224 if S_ISDIR(buf.st_mode) {
225 std::list<std::string>toAdd = GetRootFiles(filename);
226 files.insert(files.end(), toAdd.begin(), toAdd.end());
227 } else if ((filename.substr(filename.size()-5, filename.size())
228 == ".root") // correct suffix
229 && (!isdigit(filename[filename.size()-6]))) { // base file
230 std::string nextfilename = filename;
231 nextfilename.insert(filename.size()-5, "_1");
232 if (stat(nextfilename.c_str(), &buf) == 0) {
233 filename.insert(filename.size()-5, "*");
234 }
235 logger->debug("Using " + filename);
236 files.push_back(filename);
237 }
238 }
239 return files;
240 }
241
242 /**
243 * Helper function to open the ROOT TTree of the header within the run
244 * framework.
245 * @param type The packet type.
246 * @return the ROOT TTree.
247 */
248 TChain* PamelaRun::ReadHeaderTree(const PacketType *type)
249 throw (std::exception) {
250 EventHeader header(type);
251 std::string FileName = GetFileName(&header);
252 std::string TreeName = GetTreeName(&header);
253 TChain *tree = new TChain(TreeName.c_str());
254 tree->Add(FileName.c_str());
255 return tree;
256 }
257
258 /**
259 * All add trees of a file as friend to a given tree. The tree name of
260 * each tree found in the file is used as the name of the friend alias.
261 * @param tree The base tree to add all other as friends.
262 * @param FileName The name of the file with other trees.
263 */
264 static void AddAllAsFriend(TChain* tree, std::string FileName) {
265 std::string BaseFileName = FileName;
266 bool HaveChain = false;
267 if (BaseFileName[BaseFileName.size()-6] == '*') {
268 BaseFileName.erase(BaseFileName.size()-6, 1);
269 HaveChain = true;
270 }
271 TFile* File = new TFile(BaseFileName.c_str(), "read");
272 if (!File->IsOpen()) {
273 logger->error("Could not open file " + BaseFileName + " for reading.");
274 return;
275 }
276 TList* list = File->GetListOfKeys();
277 if (HaveChain) {
278 for (TIter i(list); TKey* k = (TKey*)i();) {
279 if (std::string(TTree::Class()->GetName()) == k->GetClassName()) {
280 std::string TreeName = k->GetName();
281 TChain* FriendChain = new TChain(TreeName.c_str());
282 FriendChain->Add(FileName.c_str());
283 tree->AddFriend(FriendChain, TreeName.c_str());
284 //logger->warn("Adding chain " + TreeName + " with " + FriendChain->GetEntries() + " entries as Friend");
285 }
286 }
287 File->Close();
288 } else {
289 for (TIter i(list); TKey* k = (TKey*)i();) {
290 if (std::string(TTree::Class()->GetName()) == k->GetClassName()) {
291 std::string TreeName = k->GetName();
292 TTree* FriendTree = (TTree *)File->Get(TreeName.c_str());
293 tree->AddFriend(FriendTree, TreeName.c_str());
294 //logger->debug("Adding tree " + TreeName + " with " + FriendTree->GetEntries() + " entries as Friend");
295 }
296 }
297 }
298 }
299
300 /**
301 * Read all Root TTrees which belong to a certain event type and mount them
302 * together as "friends".
303 * @param type The packet type.
304 * @return The root trees with the friends.
305 */
306 TTree* PamelaRun::ReadTTree(const PacketType* type) {
307 oss.str("");
308 oss << "Getting root files of " << type->GetName();
309 logger->debug(oss.str().c_str());
310 RootTreeMap::iterator t = TTreeMap.find(type);
311 if (t != TTreeMap.end()) {
312 return t->second;
313 } else {
314 oss.str("");
315 oss << "Reading root files of " << type->GetName();
316 logger->debug(oss.str().c_str());
317 EventHeader header(type);
318 std::string HeaderFileName = GetFileName(&header);
319 TChain* HeaderTree = ReadHeaderTree(type);
320 std::list<std::string> rootfiles = GetRootFiles(GetDirName(type));
321 for (std::list<std::string>::iterator i = rootfiles.begin();
322 i != rootfiles.end(); i++){
323 if (*i == HeaderFileName)
324 continue; // dont add the header tree itself.
325 AddAllAsFriend(HeaderTree, *i);
326 }
327 TTreeMap.insert(RootTreeMap::value_type(type, HeaderTree));
328 return HeaderTree;
329 }
330 }
331
332 /**
333 * Register a certain SubPacket, identified by its name, to be read
334 * from the repository. This function is made for interactive work.
335 * @param subpacket A pointer to the pointer of the packet.
336 * @param name The name of the subpacket
337 */
338 void PamelaRun::ReadSubPacket(void* subpacket, std::string name) {
339 SubPacket *packet = *(SubPacket**)subpacket;
340
341 // look into the map of subpackets if we already read it.
342 std::string FullName = packet->GetPacketType()->GetName() + "." + name;
343 SubPacketMap::iterator i = SubPacketAddresses.find(FullName);
344 if (i != SubPacketAddresses.end()) { // it is in the map
345 *(SubPacket**)subpacket = i->second;
346 return;
347 } else { // not found in the map of used subpackets
348 TTree* tree = ReadTTree(packet->GetPacketType());
349 TBranch* branch = tree->GetBranch(name.c_str());
350 if (branch != 0) {
351 branch->SetAddress(subpacket);
352 SubPacketAddresses.insert(SubPacketMap::value_type(FullName, packet));
353 } else {
354 oss.str("");
355 oss << "Could not find data for " << packet->GetPacketType()->GetName() << "/" << name ;
356 logger->error(oss.str().c_str());
357 }
358 }
359 }
360
361 /**
362 * Register a certain SubPacket, identified by its default name, to be
363 * read from the repository. This function is made for
364 * interactive work.
365 * @param subpacket A pointer to the pointer of the packet.
366 */
367 void PamelaRun::ReadSubPacket(void* subpacket) {
368 SubPacket *packet = *(SubPacket**)subpacket;
369 ReadSubPacket(subpacket, GetDefaultBranchName(packet));
370 }
371
372 /**
373 * Register a certain SubPacket with its default name, to be read from
374 * the repository. This functions is for use from the algorithm.
375 * @param algo Algorithm that needs this SubPacket.
376 * @param subpacket A pointer to the pointer of the packet.
377 */
378 void PamelaRun::ReadSubPacket(const Algorithm* algo, void* subpacket) {
379 //:TODO: store the request of the algorithm here.
380 ReadSubPacket(subpacket);
381 }
382
383 /**
384 * Register a certain SubPacket, with a custom name, to
385 * be read from the repository. This functions is for use from
386 * the algorithm.
387 * @param algo Algorithm that needs this SubPacket.
388 * @param subpacket A pointer to the pointer of the packet.
389 * @param name The name of the subpacket
390 */
391 void PamelaRun::ReadSubPacket(const Algorithm* algo, void* subpacket,
392 std::string name) {
393 //:TODO: store the request of the algorithm here.
394 ReadSubPacket(subpacket, name);
395 }
396
397 /**
398 * Helper function to create a ROOT TTree within the run framework.
399 * @param algo Algorithm that creates this SubPacket.
400 * @param packet subpacket type
401 * @param name the name of the subpacket
402 * @return the ROOT TTree.
403 */
404 TTree* PamelaRun::CreateTTree(Algorithm* algo, const SubPacket* packet,
405 std::string name)
406 throw (std::exception) {
407 std::string FileName = GetFileName(packet, name);
408 std::string EventType = packet->GetPacketType()->GetName();
409 CreateDirectoryStructure(FileName.c_str());
410 TFile* File = new TFile(FileName.c_str(), "create");
411 File->SetCompressionLevel(5);
412 if (!File->IsOpen()) {
413 logger->error("Could not open file " + FileName);
414 //throw Exception("Could not open file " + FileName);
415 }
416 std::string TreeName = GetTreeName(packet);
417 TTree *tree = new TTree(TreeName.c_str(), algo->GetAlgorithmName().c_str());
418 WritingRootTrees[packet->GetPacketType()].push_back(tree);
419 tree->GetCurrentFile()->cd();
420 AlgorithmInfo ai(algo);
421 ai.Write();
422 info.Write();
423 logger->debug("Creating file " + FileName + " with Tree " + TreeName);
424 return tree;
425 }
426
427
428 /**
429 * Register a certain SubPacket to be written to the repository. A
430 * usual call sequence for this function ist
431 * MyEvent *event = new MyEvent();
432 * run->WriteSubPacket(this, &event, event->Class()
433 * @param algo Algorithm that produces this SubPacket.
434 * @param subpacket A pointer to the pointer of the subpacket.
435 * @param c The class the subpacket belongs to.
436 * @param name The name of the packet.
437 */
438 void PamelaRun::WriteSubPacket(Algorithm *algo, void* subpacket,
439 const TClass *c, std::string name) {
440 SubPacket *packet = *(SubPacket **)subpacket;
441 oss.str("");
442 oss << "Register: " << name << " for " << algo->GetAlgorithmName() << " (writing)";
443 logger->debug(oss.str().c_str());
444 TTree* HeaderTree = ReadTTree(packet->GetPacketType());
445 TTree* tree = CreateTTree(algo, packet, name);
446 oss.str("");
447 oss << "Branch: " << name << " Class: " << c->GetName();
448 logger->debug(oss.str().c_str());
449 tree->Branch(name.c_str(), c->GetName(), subpacket);
450 HeaderTree->AddFriend(tree, tree->GetName());
451
452 std::string FullName = packet->GetPacketType()->GetName() + "." + name;
453 SubPacketAddresses.insert(SubPacketMap::value_type(FullName, packet));
454 }
455
456 /**
457 * Register a certain SubPacket with its default name to be written to
458 * the repository. A usual call sequence for this function ist
459 * MyEvent *event = new MyEvent();
460 * run->WriteSubPacket(this, &event, event->Class()
461 * @param algo Algorithm that produces this SubPacket.
462 * @param subpacket A pointer to the pointer of the subpacket.
463 * @param c The class the subpacket belongs to.
464 */
465 void PamelaRun::WriteSubPacket(Algorithm *algo, void* subpacket,
466 const TClass *c) {
467 SubPacket *packet = *(SubPacket **)subpacket;
468 WriteSubPacket(algo, subpacket, c, GetDefaultBranchName(packet));
469 }
470
471 /**
472 * Write the header packet of a specified packet type. This is mainly used
473 * for the raw reader to create the base for the event structure.
474 * @param algo Algorithm that produces this SubPacket.
475 * @param subpacket A pointer to the pointer of the packet.
476 * @param type The packet type.
477 */
478 void PamelaRun::WriteHeader(Algorithm* algo, EventHeader** subpacket,
479 const PacketType* type) {
480 EventHeader header(type);
481 std::string FileName = GetFileName(&header, GetDefaultBranchName(&header));
482 std::string EventType = (&header)->GetPacketType()->GetName();
483 CreateDirectoryStructure(FileName.c_str());
484 TFile* File = new TFile(FileName.c_str(), "create");
485 if (!File->IsOpen()) {
486 logger->error("Could not open file " + FileName);
487 //throw Exception("Could not open file " + FileName);
488 }
489 //std::string TreeName = GetTreeName(packet);
490 TTree *tree = new TTree("Pscu", algo->GetAlgorithmName().c_str());
491 WritingRootTrees[(&header)->GetPacketType()].push_back(tree);
492 tree->GetCurrentFile()->cd();
493 AlgorithmInfo ai(algo);
494 ai.Write();
495 info.Write();
496 std::stringstream temp;
497 //temp.str("");
498 //temp << "Created file" << FileName.c_str() << " with Tree Pscu" ;
499 //logger->debug(temp.str());
500 tree->Branch(GetDefaultBranchName(&header).c_str(),
501 (*subpacket)->Class()->GetName(), subpacket);
502 TTreeMap.insert(RootTreeMap::value_type(type, tree));
503 }
504
505
506 /**
507 * Write the header packet to all ROOT files in the tree. Intended to
508 * be used for raw data readers that create the initial event structure.
509 * @param algo Algorithm that produces this SubPacket.
510 * @param subpacket A pointer to the pointer of the header packet.
511 */
512 void PamelaRun::WriteHeaders(Algorithm* algo, EventHeader** subpacket) {
513
514 WriteHeader(algo, subpacket, PacketType::PhysEndRun);
515 WriteHeader(algo, subpacket, PacketType::CalibCalPulse1);
516 WriteHeader(algo, subpacket, PacketType::CalibCalPulse2);
517 WriteHeader(algo, subpacket, PacketType::Physics);
518 WriteHeader(algo, subpacket, PacketType::CalibTrkBoth);
519 WriteHeader(algo, subpacket, PacketType::CalibTrk1);
520 WriteHeader(algo, subpacket, PacketType::CalibTrk2);
521 WriteHeader(algo, subpacket, PacketType::CalibTrd);
522 WriteHeader(algo, subpacket, PacketType::CalibTof);
523 WriteHeader(algo, subpacket, PacketType::CalibS4);
524 WriteHeader(algo, subpacket, PacketType::CalibCalPed);
525 WriteHeader(algo, subpacket, PacketType::Calib1_Ac1);
526 WriteHeader(algo, subpacket, PacketType::Calib1_Ac2);
527 WriteHeader(algo, subpacket, PacketType::Calib2_Ac1);
528 WriteHeader(algo, subpacket, PacketType::Calib2_Ac2);
529 WriteHeader(algo, subpacket, PacketType::RunHeader);
530 WriteHeader(algo, subpacket, PacketType::RunTrailer);
531 WriteHeader(algo, subpacket, PacketType::CalibHeader);
532 WriteHeader(algo, subpacket, PacketType::CalibTrailer);
533 WriteHeader(algo, subpacket, PacketType::InitHeader);
534 WriteHeader(algo, subpacket, PacketType::InitTrailer);
535 WriteHeader(algo, subpacket, PacketType::EventTrk);
536 WriteHeader(algo, subpacket, PacketType::TestTrk);
537 WriteHeader(algo, subpacket, PacketType::TestTof);
538 WriteHeader(algo, subpacket, PacketType::Log);
539 WriteHeader(algo, subpacket, PacketType::VarDump);
540 WriteHeader(algo, subpacket, PacketType::ArrDump);
541 WriteHeader(algo, subpacket, PacketType::TabDump);
542 WriteHeader(algo, subpacket, PacketType::Tmtc);
543 WriteHeader(algo, subpacket, PacketType::Mcmd);
544 WriteHeader(algo, subpacket, PacketType::ForcedFECmd);
545 WriteHeader(algo, subpacket, PacketType::Ac1Init);
546 WriteHeader(algo, subpacket, PacketType::CalInit);
547 WriteHeader(algo, subpacket, PacketType::TrkInit);
548 WriteHeader(algo, subpacket, PacketType::TofInit);
549 WriteHeader(algo, subpacket, PacketType::TrgInit);
550 WriteHeader(algo, subpacket, PacketType::NdInit);
551 WriteHeader(algo, subpacket, PacketType::S4Init);
552 WriteHeader(algo, subpacket, PacketType::Ac2Init);
553 WriteHeader(algo, subpacket, PacketType::CalAlarm);
554 WriteHeader(algo, subpacket, PacketType::AcAlarm);
555 WriteHeader(algo, subpacket, PacketType::TrkAlarm);
556 WriteHeader(algo, subpacket, PacketType::TrgAlarm);
557 WriteHeader(algo, subpacket, PacketType::TofAlarm);
558 WriteHeader(algo, subpacket, PacketType::S4Alarm);
559 WriteHeader(algo, subpacket, PacketType::TsbT);
560 WriteHeader(algo, subpacket, PacketType::TsbB);
561 }
562
563 /**
564 * Write the ROOT files to disk.
565 */
566 void PamelaRun::WriteFiles(void) {
567 // Workaround: unlink all friend trees first top avoid to store
568 // the links in the header tree file.
569 for (RootTreeMap::iterator i = TTreeMap.begin(); i != TTreeMap.end(); i++) {
570 if (i->second->GetListOfFriends() != 0) {
571 i->second->GetListOfFriends()->Delete();
572 }
573 }
574
575 for (TTreeListMap::iterator i = WritingRootTrees.begin();
576 i != WritingRootTrees.end(); i++) {
577 for (TTreeList::iterator j = i->second.begin();
578 j != i->second.end(); j++) {
579 (*j)->GetCurrentFile()->Write();
580 }
581 }
582 }
583
584 /**
585 * Fill all ROOT trees of a certain type that were opened for writing.
586 * @param type the package type of the trees to fill.
587 */
588 void PamelaRun::FillTrees(const PacketType* type) {
589 for (TTreeList::iterator j = WritingRootTrees[type].begin();
590 j != WritingRootTrees[type].end(); j++) {
591 (*j)->Fill();
592 ;
593 }
594 }
595
596
597

  ViewVC Help
Powered by ViewVC 1.1.23