OrocosComponentLibrary  2.6.0
ReportingComponent.cpp
00001 /***************************************************************************
00002   tag: Peter Soetens  Mon May 10 19:10:38 CEST 2004  ReportingComponent.cxx
00003 
00004                         ReportingComponent.cxx -  description
00005                            -------------------
00006     begin                : Mon May 10 2004
00007     copyright            : (C) 2004 Peter Soetens
00008     email                : peter.soetens@mech.kuleuven.ac.be
00009 
00010  ***************************************************************************
00011  *   This library is free software; you can redistribute it and/or         *
00012  *   modify it under the terms of the GNU Lesser General Public            *
00013  *   License as published by the Free Software Foundation; either          *
00014  *   version 2.1 of the License, or (at your option) any later version.    *
00015  *                                                                         *
00016  *   This library is distributed in the hope that it will be useful,       *
00017  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00018  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00019  *   Lesser General Public License for more details.                       *
00020  *                                                                         *
00021  *   You should have received a copy of the GNU Lesser General Public      *
00022  *   License along with this library; if not, write to the Free Software   *
00023  *   Foundation, Inc., 59 Temple Place,                                    *
00024  *   Suite 330, Boston, MA  02111-1307  USA                                *
00025  *                                                                         *
00026  ***************************************************************************/
00027 
00028 #include "ReportingComponent.hpp"
00029 #include <rtt/Logger.hpp>
00030 
00031 // Impl.
00032 #include "EmptyMarshaller.hpp"
00033 #include <rtt/marsh/PropertyDemarshaller.hpp>
00034 #include <rtt/marsh/PropertyMarshaller.hpp>
00035 #include <iostream>
00036 #include <fstream>
00037 #include <exception>
00038 #include <boost/algorithm/string.hpp>
00039 
00040 #include "ocl/Component.hpp"
00041 #include <rtt/types/PropertyDecomposition.hpp>
00042 #include <boost/lexical_cast.hpp>
00043 
00044 ORO_CREATE_COMPONENT_TYPE()
00045 
00046 
00047 namespace OCL
00048 {
00049     using namespace std;
00050     using namespace RTT;
00051     using namespace RTT::detail;
00052 
00055     class CheckSizeDataSource : public ValueDataSource<bool>
00056     {
00057         mutable int msize;
00058         DataSource<int>::shared_ptr mds;
00059         DataSource<bool>::shared_ptr mupstream;
00060     public:
00061         CheckSizeDataSource(int size, DataSource<int>::shared_ptr ds, DataSource<bool>::shared_ptr upstream)
00062             : msize(size), mds(ds), mupstream(upstream)
00063         {}
00067         bool get() const{
00068             // it's very important to first check upstream, because
00069             // if upstream changed size, downstream might already be corrupt !
00070             // (downstream will be corrupt upon capacity changes upstream)
00071             bool result = true;
00072             if (mupstream)
00073                 result = (mupstream->get() && msize == mds->get());
00074             else
00075                 result = (msize == mds->get());
00076             msize = mds->get();
00077             return result;
00078         }
00079     };
00080 
00087     bool memberDecomposition( base::DataSourceBase::shared_ptr dsb, PropertyBag& targetbag, DataSource<bool>::shared_ptr& resized)
00088     {
00089         assert(dsb);
00090 
00091         vector<string> parts = dsb->getMemberNames();
00092         if ( parts.empty() ) {
00093             return false;
00094         }
00095 
00096         targetbag.setType( dsb->getTypeName() );
00097 
00098         // needed for recursion.
00099         auto_ptr< Property<PropertyBag> > recurse_bag( new Property<PropertyBag>("recurse_bag","") );
00100         // First at the explicitly listed parts:
00101         for(vector<string>::iterator it = parts.begin(); it != parts.end(); ++it ) {
00102             // we first force getMember to get to the type, then we do it again but with a reference set.
00103             DataSourceBase::shared_ptr part = dsb->getMember( *it );
00104             if (!part) {
00105                 log(Error) <<"memberDecomposition: Inconsistent type info for "<< dsb->getTypeName() << ": reported to have part '"<<*it<<"' but failed to return it."<<endlog();
00106                 continue;
00107             }
00108             if ( !part->isAssignable() ) {
00109                 // For example: the case for size() and capacity() in SequenceTypeInfo
00110                 log(Debug)<<"memberDecomposition: Part "<< *it << ":"<< part->getTypeName() << " is not changeable."<<endlog();
00111                 continue;
00112             }
00113             // now the reference magic:
00114             DataSourceBase::shared_ptr ref = part->getTypeInfo()->buildReference( 0 );
00115             dsb->getTypeInfo()->getMember( dynamic_cast<Reference*>(ref.get() ), dsb, *it); // fills in ref
00116             // newpb will contain a reference to the port's datasource data !
00117             PropertyBase* newpb = part->getTypeInfo()->buildProperty(*it,"Part", ref);
00118             if ( !newpb ) {
00119                 log(Error)<< "Decomposition failed because Part '"<<*it<<"' is not known to type system."<<endlog();
00120                 continue;
00121             }
00122             // finally recurse or add it to the target bag:
00123             if ( !memberDecomposition( ref, recurse_bag->value(), resized) ) {
00124                 assert( recurse_bag->value().empty() );
00125                 // finally: check for conversions (enums use this):
00126                 base::DataSourceBase::shared_ptr converted = newpb->getTypeInfo()->convertType( dsb );
00127                 if ( converted && converted != dsb ) {
00128                     // converted contains another type.
00129                     targetbag.add( converted->getTypeInfo()->buildProperty(*it, "", converted) );
00130                     delete newpb;
00131                 } else
00132                     targetbag.ownProperty( newpb ); // leaf
00133             } else {
00134                 recurse_bag->setName(*it);
00135                 // setType() is done by recursive of self.
00136                 targetbag.ownProperty( recurse_bag.release() ); //recursed.
00137                 recurse_bag.reset( new Property<PropertyBag>("recurse_bag","") );
00138                 delete newpb; // since we recursed, the recurse_bag now 'embodies' newpb.
00139             }
00140         }
00141 
00142         // Next get the numbered parts. This is much more involved since sequences may be resizable.
00143         // We keep track of the size, and if that changes, we will have to force a re-decomposition
00144         // of the sequence's internals.
00145         DataSource<int>::shared_ptr size = DataSource<int>::narrow( dsb->getMember("size").get() );
00146         if (size) {
00147             int msize = size->get();
00148             for (int i=0; i < msize; ++i) {
00149                 string indx = boost::lexical_cast<string>( i );
00150                 DataSourceBase::shared_ptr item = dsb->getMember(indx);
00151                 resized = new CheckSizeDataSource( msize, size, resized );
00152                 if (item) {
00153                     if ( !item->isAssignable() ) {
00154                         // For example: the case for size() and capacity() in SequenceTypeInfo
00155                         log(Warning)<<"memberDecomposition: Item '"<< indx << "' of type "<< dsb->getTypeName() << " is not changeable."<<endlog();
00156                         continue;
00157                     }
00158                     // finally recurse or add it to the target bag:
00159                     PropertyBase* newpb = item->getTypeInfo()->buildProperty( indx,"",item);
00160                     if ( !memberDecomposition( item, recurse_bag->value(), resized) ) {
00161                         targetbag.ownProperty( newpb ); // leaf
00162                     } else {
00163                         delete newpb;
00164                         recurse_bag->setName( indx );
00165                         // setType() is done by recursive of self.
00166                         targetbag.ownProperty( recurse_bag.release() ); //recursed.
00167                         recurse_bag.reset( new Property<PropertyBag>("recurse_bag","") );
00168                     }
00169                 }
00170             }
00171         }
00172         if (targetbag.empty() )
00173             log(Debug) << "memberDecomposition: "<<  dsb->getTypeName() << " returns an empty property bag." << endlog();
00174         return true;
00175     }
00176 
00177   ReportingComponent::ReportingComponent( std::string name /*= "Reporting" */ )
00178         : TaskContext( name ),
00179           report("Report"), snapshotted(false),
00180           writeHeader("WriteHeader","Set to true to start each report with a header.", true),
00181           decompose("Decompose","Set to false in order to not decompose the port data. The marshaller must be able to handle this itself for this to work.", true),
00182           insnapshot("Snapshot","Set to true to enable snapshot mode. This will cause a non-periodic reporter to only report data upon the snapshot() operation.",false),
00183           synchronize_with_logging("Synchronize","Set to true if the timestamp should be synchronized with the logging",false),
00184           report_data("ReportData","A PropertyBag which defines which ports or components to report."),
00185           report_policy( ConnPolicy::data(ConnPolicy::LOCK_FREE,true,false) ),
00186           onlyNewData(false),
00187           starttime(0),
00188           timestamp("TimeStamp","The time at which the data was read.",0.0)
00189     {
00190         this->provides()->doc("Captures data on data ports. A periodic reporter will sample each added port according to its period, a non-periodic reporter will write out data as it comes in, or only during a snapshot() if the Snapshot property is true.");
00191 
00192         this->properties()->addProperty( writeHeader );
00193         this->properties()->addProperty( decompose );
00194         this->properties()->addProperty( insnapshot );
00195         this->properties()->addProperty( synchronize_with_logging);
00196         this->properties()->addProperty( report_data);
00197         this->properties()->addProperty( "ReportPolicy", report_policy).doc("The ConnPolicy for the reporter's port connections.");
00198         this->properties()->addProperty( "ReportOnlyNewData", onlyNewData).doc("Turn on in order to only write out NewData on ports and omit unchanged ports. Turn off in order to sample and write out all ports (even old data).");
00199         // Add the methods, methods make sure that they are
00200         // executed in the context of the (non realtime) caller.
00201 
00202         this->addOperation("snapshot", &ReportingComponent::snapshot , this, RTT::OwnThread).doc("Take a new shapshot of all data and cause them to be written out.");
00203         this->addOperation("screenComponent", &ReportingComponent::screenComponent , this, RTT::ClientThread).doc("Display the variables and ports of a Component.").arg("Component", "Name of the Component");
00204         this->addOperation("reportComponent", &ReportingComponent::reportComponent , this, RTT::ClientThread).doc("Add a peer Component and report all its data ports").arg("Component", "Name of the Component");
00205         this->addOperation("unreportComponent", &ReportingComponent::unreportComponent , this, RTT::ClientThread).doc("Remove all Component's data ports from reporting.").arg("Component", "Name of the Component");
00206         this->addOperation("reportData", &ReportingComponent::reportData , this, RTT::ClientThread).doc("Add a Component's Property or attribute for reporting.").arg("Component", "Name of the Component").arg("Data", "Name of the Data to report. A property's or attribute's name.");
00207         this->addOperation("unreportData", &ReportingComponent::unreportData , this, RTT::ClientThread).doc("Remove a Data object from reporting.").arg("Component", "Name of the Component").arg("Data", "Name of the property or attribute.");
00208         this->addOperation("reportPort", &ReportingComponent::reportPort , this, RTT::ClientThread).doc("Add a Component's OutputPort for reporting.").arg("Component", "Name of the Component").arg("Port", "Name of the Port.");
00209         this->addOperation("unreportPort", &ReportingComponent::unreportPort , this, RTT::ClientThread).doc("Remove a Port from reporting.").arg("Component", "Name of the Component").arg("Port", "Name of the Port.");
00210 
00211     }
00212 
00213     ReportingComponent::~ReportingComponent() {}
00214 
00215 
00216     bool ReportingComponent::addMarshaller( marsh::MarshallInterface* headerM, marsh::MarshallInterface* bodyM)
00217     {
00218         boost::shared_ptr<marsh::MarshallInterface> header(headerM);
00219         boost::shared_ptr<marsh::MarshallInterface> body(bodyM);
00220         if ( !header && !body)
00221             return false;
00222         if ( !header )
00223             header.reset( new EmptyMarshaller() );
00224         if ( !body)
00225             body.reset( new EmptyMarshaller());
00226 
00227         marshallers.push_back( std::make_pair( header, body ) );
00228         return true;
00229     }
00230 
00231     bool ReportingComponent::removeMarshallers()
00232     {
00233         marshallers.clear();
00234         return true;
00235     }
00236 
00237     void ReportingComponent::cleanupHook()
00238     {
00239         root.clear(); // uses shared_ptr.
00240         deletePropertyBag( report );
00241     }
00242 
00243     bool ReportingComponent::configureHook()
00244     {
00245         Logger::In in("ReportingComponent");
00246 
00247         // we make a copy to be allowed to iterate over and exted report_data:
00248         PropertyBag bag = report_data.value();
00249 
00250         if ( bag.empty() ) {
00251             log(Error) <<"No port or component configuration loaded."<<endlog();
00252             log(Error) <<"Please use marshalling.loadProperties(), reportComponent() (scripting) or LoadProperties (XML) in order to fill in ReportData." <<endlog();
00253             return false;
00254         }
00255 
00256         bool ok = true;
00257         PropertyBag::const_iterator it = bag.getProperties().begin();
00258         while ( it != bag.getProperties().end() )
00259             {
00260                 Property<std::string>* compName = dynamic_cast<Property<std::string>* >( *it );
00261                 if ( !compName )
00262                     log(Error) << "Expected Property \""
00263                                   << (*it)->getName() <<"\" to be of type string."<< endlog();
00264                 else if ( compName->getName() == "Component" ) {
00265                     std::string name = compName->value(); // we will delete this property !
00266                     this->unreportComponent( name );
00267                     ok &= this->reportComponent( name );
00268                 }
00269                 else if ( compName->getName() == "Port" ) {
00270                     string cname = compName->value().substr(0, compName->value().find("."));
00271                     string pname = compName->value().substr( compName->value().find(".")+1, string::npos);
00272                     if (cname.empty() || pname.empty() ) {
00273                         log(Error) << "The Port value '"<<compName->getName()<< "' must at least consist of a component name followed by a dot and the port name." <<endlog();
00274                         ok = false;
00275                         continue;
00276                     }
00277                     this->unreportPort(cname,pname);
00278                     ok &= this->reportPort(cname, pname);
00279                 }
00280                 else if ( compName->getName() == "Data" ) {
00281                     string cname = compName->value().substr(0, compName->value().find("."));
00282                     string pname = compName->value().substr( compName->value().find(".")+1, string::npos);
00283                     if (cname.empty() || pname.empty() ) {
00284                         log(Error) << "The Data value '"<<compName->getName()<< "' must at least consist of a component name followed by a dot and the property/attribute name." <<endlog();
00285                         ok = false;
00286                         continue;
00287                     }
00288                     this->unreportData(cname,pname);
00289                     ok &= this->reportData(cname, pname);
00290                 }
00291                 else {
00292                     log(Error) << "Expected \"Component\", \"Port\" or \"Data\", got "
00293                                   << compName->getName() << endlog();
00294                     ok = false;
00295                 }
00296                 ++it;
00297             }
00298         return ok;
00299     }
00300 
00301     bool ReportingComponent::screenComponent( const std::string& comp )
00302     {
00303         Logger::In in("ReportingComponent::screenComponent");
00304         log(Error) << "not implemented." <<comp<<endlog();
00305         return false;
00306     }
00307 
00308     bool ReportingComponent::screenImpl( const std::string& comp, std::ostream& output)
00309     {
00310         Logger::In in("ReportingComponent");
00311         TaskContext* c = this->getPeer(comp);
00312         if ( c == 0) {
00313             log(Error) << "Unknown Component: " <<comp<<endlog();
00314             return false;
00315         }
00316         output << "Screening Component '"<< comp << "' : "<< endl << endl;
00317         PropertyBag* bag = c->properties();
00318         if (bag) {
00319             output << "Properties :" << endl;
00320             for (PropertyBag::iterator it= bag->begin(); it != bag->end(); ++it)
00321                 output << "  " << (*it)->getName() << " : " << (*it)->getDataSource() << endl;
00322         }
00323         ConfigurationInterface::AttributeNames atts = c->provides()->getAttributeNames();
00324         if ( !atts.empty() ) {
00325             output << "Attributes :" << endl;
00326             for (ConfigurationInterface::AttributeNames::iterator it= atts.begin(); it != atts.end(); ++it)
00327                 output << "  " << *it << " : " << c->provides()->getValue(*it)->getDataSource() << endl;
00328         }
00329 
00330         vector<string> ports = c->ports()->getPortNames();
00331         if ( !ports.empty() ) {
00332             output << "Ports :" << endl;
00333             for (vector<string>::iterator it= ports.begin(); it != ports.end(); ++it) {
00334                 output << "  " << *it << " : ";
00335                 if (c->ports()->getPort(*it)->connected() )
00336                     output << "(connected)" << endl;
00337                 else
00338                     output << "(not connected)" << endl;
00339             }
00340         }
00341         return true;
00342     }
00343 
00344     bool ReportingComponent::reportComponent( const std::string& component ) {
00345         Logger::In in("ReportingComponent");
00346         // Users may add own data sources, so avoid duplicates
00347         //std::vector<std::string> sources                = comp->data()->getNames();
00348         TaskContext* comp = this->getPeer(component);
00349         if ( !comp ) {
00350             log(Error) << "Could not report Component " << component <<" : no such peer."<<endlog();
00351             return false;
00352         }
00353         if ( !report_data.value().findValue<string>(component) )
00354             report_data.value().ownProperty( new Property<string>("Component","",component) );
00355         Ports ports   = comp->ports()->getPorts();
00356         for (Ports::iterator it = ports.begin(); it != ports.end() ; ++it) {
00357             log(Debug) << "Checking port " << (*it)->getName()<<"."<<endlog();
00358             this->reportPort( component, (*it)->getName() );
00359         }
00360         return true;
00361     }
00362 
00363 
00364     bool ReportingComponent::unreportComponent( const std::string& component ) {
00365         TaskContext* comp = this->getPeer(component);
00366         if ( !comp ) {
00367             log(Error) << "Could not unreport Component " << component <<" : no such peer."<<endlog();
00368             return false;
00369         }
00370         Ports ports   = comp->ports()->getPorts();
00371         for (Ports::iterator it = ports.begin(); it != ports.end() ; ++it) {
00372             this->unreportDataSource( component + "." + (*it)->getName() );
00373             unreportPort(component, (*it)->getName() );
00374         }
00375         base::PropertyBase* pb = report_data.value().findValue<string>(component);
00376         if (pb)
00377             report_data.value().removeProperty( pb );// pb is deleted by bag
00378         return true;
00379     }
00380 
00381     // report a specific connection.
00382     bool ReportingComponent::reportPort(const std::string& component, const std::string& port ) {
00383         Logger::In in("ReportingComponent");
00384         TaskContext* comp = this->getPeer(component);
00385         if ( this->ports()->getPort(component +"_"+port) ) {
00386             log(Warning) <<"Already reporting "<<component<<"."<<port<<": removing old port first."<<endlog();
00387             this->unreportPort(component,port);
00388         }
00389         if ( !comp ) {
00390             log(Error) << "Could not report Component " << component <<" : no such peer."<<endlog();
00391             return false;
00392         }
00393         std::vector<std::string> strs;
00394         boost::split(strs, port, boost::is_any_of("."));
00395 
00396         // strs could be empty because of a bug in Boost 1.44 (see https://svn.boost.org/trac/boost/ticket/4751)
00397         if (strs.empty()) return false;
00398 
00399         Service::shared_ptr service=comp->provides();
00400         while ( strs.size() != 1 && service) {
00401             service = service->getService( strs.front() );
00402             if (service)
00403                 strs.erase( strs.begin() );
00404         }
00405         if (!service) {
00406             log(Error) <<"No such service: '"<< strs.front() <<"' while looking for port '"<< port<<"'"<<endlog();
00407             return 0;
00408         }
00409         base::PortInterface* porti = 0;
00410         porti = service->getPort(strs.front());
00411         if ( !porti ) {
00412             log(Error) << "Could not report Port " << port
00413                        <<" : no such port on Component "<<component<<"."<<endlog();
00414             return false;
00415         }
00416 
00417         base::InputPortInterface* ipi =  dynamic_cast<base::InputPortInterface*>(porti);
00418         if (ipi) {
00419             log(Error) << "Can not report InputPort "<< porti->getName() <<" of Component " << component <<endlog();
00420             return false;
00421         }
00422             // create new port temporarily
00423         // this port is only created with the purpose of
00424         // creating a connection object.
00425         base::PortInterface* ourport = porti->antiClone();
00426         assert(ourport);
00427         ourport->setName(component + "_" + port);
00428         ipi = dynamic_cast<base::InputPortInterface*> (ourport);
00429         assert(ipi);
00430 
00431         if (report_policy.type == ConnPolicy::DATA ) {
00432             log(Info) << "Not buffering of data flow connections. You may miss samples." <<endlog();
00433         } else {
00434             log(Info) << "Buffering ports with size "<< report_policy.size << ", as set in ReportPolicy property." <<endlog();
00435         }
00436 
00437         if (porti->connectTo(ourport, report_policy ) == false)
00438         {
00439             log(Error) << "Could not connect to OutputPort " << porti->getName() << endlog();
00440             delete ourport; // XXX/TODO We're leaking ourport !
00441             return false;
00442         }
00443 
00444         if (this->reportDataSource(component + "." + port, "Port",
00445                                    ipi->getDataSource(),ipi, true) == false)
00446         {
00447             log(Error) << "Failed reporting port " << port << endlog();
00448             delete ourport;
00449             return false;
00450         }
00451         this->ports()->addEventPort( *ipi );
00452         log(Info) << "Monitoring OutputPort " << port << " : ok." << endlog();
00453         // Add port to ReportData properties if component nor port are listed yet.
00454         if ( !report_data.value().findValue<string>(component) && !report_data.value().findValue<string>( component+"."+port) )
00455             report_data.value().ownProperty(new Property<string>("Port","",component+"."+port));
00456         return true;
00457     }
00458 
00459     bool ReportingComponent::unreportPort(const std::string& component, const std::string& port ) {
00460         base::PortInterface* ourport = this->ports()->getPort(component + "_" + port);
00461         if ( this->unreportDataSource( component + "." + port ) && report_data.value().removeProperty( report_data.value().findValue<string>(component+"."+port))) {
00462             this->ports()->removePort(ourport->getName());
00463             delete ourport; // also deletes datasource.
00464             return true;
00465         }
00466         return false;
00467     }
00468 
00469     // report a specific datasource, property,...
00470     bool ReportingComponent::reportData(const std::string& component,const std::string& dataname)
00471     {
00472         Logger::In in("ReportingComponent");
00473         TaskContext* comp = this->getPeer(component);
00474         if ( !comp ) {
00475             log(Error) << "Could not report Component " << component <<" : no such peer."<<endlog();
00476             return false;
00477         }
00478         // Is it an attribute ?
00479         if ( comp->provides()->getValue( dataname ) ) {
00480             if (this->reportDataSource( component + "." + dataname, "Data",
00481                                         comp->provides()->getValue( dataname )->getDataSource(), 0,  false ) == false) {
00482                 log(Error) << "Failed reporting data " << dataname <<endlog();
00483                 return false;
00484             }
00485         }
00486 
00487         // Is it a property ?
00488         if ( comp->properties() && comp->properties()->find( dataname ) ) {
00489             if (this->reportDataSource( component + "." + dataname, "Data",
00490                                         comp->properties()->find( dataname )->getDataSource(), 0, false ) == false) {
00491                 log(Error) << "Failed reporting data " << dataname <<endlog();
00492                 return false;
00493             }
00494         }
00495         // Ok. we passed.
00496         // Add port to ReportData properties if data not listed yet.
00497         if ( !report_data.value().findValue<string>( component+"."+dataname) )
00498             report_data.value().ownProperty(new Property<string>("Data","",component+"."+dataname));
00499         return true;
00500     }
00501 
00502     bool ReportingComponent::unreportData(const std::string& component,const std::string& datasource) {
00503         return this->unreportDataSource( component +"." + datasource) && report_data.value().removeProperty( report_data.value().findValue<string>(component+"."+datasource));
00504     }
00505 
00506     bool ReportingComponent::reportDataSource(std::string tag, std::string type, base::DataSourceBase::shared_ptr orig, base::InputPortInterface* ipi, bool track)
00507     {
00508         // check for duplicates:
00509         for (Reports::iterator it = root.begin();
00510              it != root.end(); ++it)
00511             if ( it->get<T_QualName>() == tag ) {
00512                 return true;
00513             }
00514 
00515         // creates a copy of the data and an update command to
00516         // update the copy from the original.
00517         base::DataSourceBase::shared_ptr clone = orig->getTypeInfo()->buildValue();
00518         if ( !clone ) {
00519             log(Error) << "Could not report '"<< tag <<"' : unknown type." << endlog();
00520             return false;
00521         }
00522         PropertyBase* prop = 0;
00523         root.push_back( boost::make_tuple( tag, orig, type, prop, ipi, false, track ) );
00524         return true;
00525     }
00526 
00527     bool ReportingComponent::unreportDataSource(std::string tag)
00528     {
00529         for (Reports::iterator it = root.begin();
00530              it != root.end(); ++it)
00531             if ( it->get<T_QualName>() == tag ) {
00532                 root.erase(it);
00533                 return true;
00534             }
00535         return false;
00536     }
00537 
00538     bool ReportingComponent::startHook() {
00539         Logger::In in("ReportingComponent");
00540         if (marshallers.begin() == marshallers.end()) {
00541             log(Error) << "Need at least one marshaller to write reports." <<endlog();
00542             return false;
00543         }
00544 
00545         if(synchronize_with_logging.get())
00546             starttime = Logger::Instance()->getReferenceTime();
00547         else
00548             starttime = os::TimeService::Instance()->getTicks();
00549 
00550         // Get initial data samples
00551         this->copydata();
00552         this->makeReport2();
00553 
00554         // write headers
00555         if (writeHeader.get()) {
00556             // call all header marshallers.
00557             for(Marshallers::iterator it=marshallers.begin(); it != marshallers.end(); ++it) {
00558                 it->first->serialize( report );
00559                 it->first->flush();
00560             }
00561         }
00562 
00563         // write initial values with all value marshallers (uses the forcing above)
00564         if ( getActivity()->isPeriodic() ) {
00565             for(Marshallers::iterator it=marshallers.begin(); it != marshallers.end(); ++it) {
00566                 it->second->serialize( report );
00567                 it->second->flush();
00568             }
00569         }
00570 
00571         // Turn off port triggering in snapshot mode, and vice versa.
00572         // Also clears any old data in the buffers
00573         for(Reports::iterator it = root.begin(); it != root.end(); ++it )
00574             if ( it->get<T_Port>() ) {
00575 #ifndef ORO_SIGNALLING_PORTS
00576                 it->get<T_Port>()->signalInterface( !insnapshot.get() );
00577 #endif
00578                 it->get<T_Port>()->clear();
00579             }
00580 
00581 
00582         snapshotted = false;
00583         return true;
00584     }
00585 
00586     void ReportingComponent::snapshot() {
00587         // this function always copies and reports all data It's run in ownthread, so updateHook will be run later.
00588         if ( getActivity()->isPeriodic() )
00589             return;
00590         snapshotted = true;
00591         updateHook();
00592     }
00593 
00594     bool ReportingComponent::copydata() {
00595         timestamp = os::TimeService::Instance()->secondsSince( starttime );
00596 
00597         // result will become true if more data is to be read.
00598         bool result = false;
00599         // This evaluates the InputPortDataSource evaluate() returns true upon new data.
00600         for(Reports::iterator it = root.begin(); it != root.end(); ++it ) {
00601             it->get<T_NewData>() = (it->get<T_PortDS>())->evaluate(); // stores 'NewData' flag.
00602             // if its a property/attr, get<T_NewData> will always be true, so we override (clear) with get<T_Tracked>.
00603             result = result || ( it->get<T_NewData>() && it->get<T_Tracked>() );
00604         }
00605         return result;
00606     }
00607 
00608     void ReportingComponent::makeReport2()
00609     {
00610         // Uses the port DS itself to make the report.
00611         assert( report.empty() );
00612         // For the timestamp, we need to add a new property object:
00613         report.add( timestamp.getTypeInfo()->buildProperty( timestamp.getName(), "", timestamp.getDataSource() ) );
00614         DataSource<bool>::shared_ptr checker;
00615         for(Reports::iterator it = root.begin(); it != root.end(); ++it ) {
00616             Property<PropertyBag>* subbag = new Property<PropertyBag>( it->get<T_QualName>(), "");
00617             if ( decompose.get() && memberDecomposition( it->get<T_PortDS>(), subbag->value(), checker ) ) {
00618                 report.add( subbag );
00619                 it->get<T_Property>() = subbag;
00620             } else {
00621                 // property or simple value port...
00622                 base::DataSourceBase::shared_ptr converted = it->get<T_PortDS>()->getTypeInfo()->convertType( it->get<T_PortDS>() );
00623                 if ( converted && converted != it->get<T_PortDS>() ) {
00624                     // converted contains another type.
00625                     PropertyBase* convProp = converted->getTypeInfo()->buildProperty(it->get<T_QualName>(), "", converted);
00626                     it->get<T_Property>() = convProp;
00627                     report.add(convProp);
00628                 } else {
00629                     PropertyBase* origProp = it->get<T_PortDS>()->getTypeInfo()->buildProperty(it->get<T_QualName>(), "", it->get<T_PortDS>());
00630                     it->get<T_Property>() = origProp;
00631                     report.add(origProp);
00632                 }
00633                 delete subbag;
00634             }
00635 
00636         }
00637         mchecker = checker;
00638     }
00639         
00640     void ReportingComponent::cleanReport()
00641     {
00642         // Only clones were added to result, so delete them.
00643         deletePropertyBag( report );
00644     }
00645 
00646     void ReportingComponent::updateHook() {
00647         //If not periodic and insnapshot is true, only continue if snapshot is called.
00648         if( !getActivity()->isPeriodic() && insnapshot.get() && !snapshotted)
00649             return;
00650         else
00651             snapshotted = false;
00652 
00653         // if any data sequence got resized, we rebuild the whole bunch.
00654         // otherwise, we need to track every individual array (not impossible though, but still needs an upstream concept).
00655         if ( mchecker && mchecker->get() == false ) {
00656             cleanReport();
00657             makeReport2();
00658         } else
00659             copydata();
00660 
00661         do {
00662             // Step 3: print out the result
00663             // write out to all marshallers
00664             for(Marshallers::iterator it=marshallers.begin(); it != marshallers.end(); ++it) {
00665                 if ( onlyNewData ) {
00666                     // Serialize only changed ports:
00667                     it->second->serialize( *report.begin() ); // TimeStamp.
00668                     for (Reports::const_iterator i = root.begin();
00669                          i != root.end();
00670                          i++ )
00671                         {
00672                             if ( i->get<T_NewData>() )
00673                                 it->second->serialize( i->get<T_Property>() );
00674                         }
00675                 } else {
00676                     // pass on all ports to the marshaller
00677                     it->second->serialize( report );
00678                 }
00679                 it->second->flush();
00680             }
00681         } while( !getActivity()->isPeriodic() && !insnapshot.get() && copydata() ); // repeat if necessary. In periodic mode we always only sample once.
00682     }
00683 
00684     void ReportingComponent::stopHook() {
00685         // tell body marshallers that serialization is done.
00686         for(Marshallers::iterator it=marshallers.begin(); it != marshallers.end(); ++it) {
00687             it->second->flush();
00688         }
00689         cleanReport();
00690     }
00691 
00692 }