File indexing completed on 2026-08-05 09:39:47
0001
0002
0003
0004
0005 #pragma once
0006
0007 #include <JANA/JObject.h>
0008 #include <JANA/JException.h>
0009 #include <JANA/JFactoryT.h>
0010 #include <JANA/JFactorySet.h>
0011 #include <JANA/JLogger.h>
0012 #include <JANA/JVersion.h>
0013
0014 #include <JANA/Components/JLightweightDatabundle.h>
0015 #if JANA2_HAVE_PODIO
0016 #include <JANA/Components/JPodioDatabundle.h>
0017 #endif
0018
0019 #include <JANA/Utils/JEventLevel.h>
0020 #include <JANA/Utils/JTypeInfo.h>
0021 #include <JANA/Utils/JCpuInfo.h>
0022 #include <JANA/Utils/JCallGraphRecorder.h>
0023 #include <JANA/Utils/JCallGraphEntryMaker.h>
0024 #include <JANA/Utils/JInspector.h>
0025
0026 #include <typeindex>
0027 #include <cstdint>
0028 #include <vector>
0029 #include <memory>
0030 #include <atomic>
0031
0032
0033 class JApplication;
0034 class JEventSource;
0035
0036
0037 class JEvent : public std::enable_shared_from_this<JEvent> {
0038
0039 private:
0040 JApplication* mApplication = nullptr;
0041 int32_t mRunNumber = 0;
0042 uint64_t mEventNumber = 0;
0043 mutable JFactorySet mFactorySet;
0044 mutable JCallGraphRecorder mCallGraph;
0045 mutable JInspector mInspector;
0046 mutable std::string mEventStamp;
0047 bool mUseDefaultTags = false;
0048 std::map<std::string, std::string> mDefaultTags;
0049 JEventSource* mEventSource = nullptr;
0050 bool mIsBarrierEvent = false;
0051 bool mIsWarmedUp = false;
0052
0053
0054 std::vector<std::pair<JEventLevel, std::pair<JEvent*, uint64_t>>> mParents;
0055 std::atomic_int mReferenceCount {0};
0056 int64_t mEventIndex = -1;
0057
0058 void MakeEventStamp() const;
0059
0060 public:
0061 JEvent();
0062 explicit JEvent(JApplication* app);
0063 virtual ~JEvent();
0064
0065 void SetRunNumber(int32_t aRunNumber){mRunNumber = aRunNumber; MakeEventStamp(); }
0066 void SetEventNumber(uint64_t aEventNumber){mEventNumber = aEventNumber; MakeEventStamp(); }
0067 void SetJApplication(JApplication* app){mApplication = app;}
0068 void SetJEventSource(JEventSource* aSource){mEventSource = aSource;}
0069 void SetDefaultTags(std::map<std::string, std::string> aDefaultTags){mDefaultTags=aDefaultTags; mUseDefaultTags = !mDefaultTags.empty();}
0070 void SetSequential(bool isSequential) {mIsBarrierEvent = isSequential;}
0071
0072 JFactorySet* GetFactorySet() const { return &mFactorySet; }
0073 int32_t GetRunNumber() const {return mRunNumber;}
0074 uint64_t GetEventNumber() const {return mEventNumber;}
0075 JApplication* GetJApplication() const {return mApplication;}
0076 JEventSource* GetJEventSource() const {return mEventSource; }
0077 JCallGraphRecorder* GetJCallGraphRecorder() const {return &mCallGraph;}
0078 JInspector* GetJInspector() const {return &mInspector;}
0079 void Inspect() const { mInspector.Loop();}
0080 bool GetSequential() const {return mIsBarrierEvent;}
0081 bool IsWarmedUp() { return mIsWarmedUp; }
0082
0083
0084 JEventLevel GetLevel() const { return mFactorySet.GetLevel(); }
0085 void SetLevel(JEventLevel level) { mFactorySet.SetLevel(level); }
0086 void SetEventIndex(int event_index) { mEventIndex = event_index; }
0087 int64_t GetEventIndex() const { return mEventIndex; }
0088 const std::string& GetEventStamp() const;
0089
0090 bool HasParent(JEventLevel level) const;
0091 const JEvent& GetParent(JEventLevel level) const;
0092 void SetParent(JEvent* parent);
0093 JEvent* ReleaseParent(JEventLevel level);
0094 std::vector<JEvent*> ReleaseAllParents();
0095 int GetChildCount();
0096 uint64_t GetParentNumber(JEventLevel level) const;
0097 void SetParentNumber(JEventLevel level, uint64_t number);
0098
0099
0100 void Clear(bool processed_successfully=true);
0101 void Finish();
0102
0103 JFactory* GetFactory(const std::string& object_name, const std::string& tag) const;
0104 std::vector<JFactory*> GetAllFactories() const;
0105
0106
0107 template<class T> JFactoryT<T>* GetFactory(const std::string& tag = "", bool throw_on_missing=false) const;
0108 template<class T> JLightweightDatabundleT<T>* GetLightweightDatabundle(const std::string& tag, bool throw_on_missing, bool call_factory_create) const;
0109 template<class T> std::vector<JFactoryT<T>*> GetFactoryAll(bool throw_on_missing = false) const;
0110
0111
0112 template<class T> JFactoryT<T>* GetSingle(const T* &t, const char *tag="", bool exception_if_not_one=true) const;
0113 template<class T> JFactoryT<T>* Get(const T** item, const std::string& tag="") const;
0114 template<class T> JFactoryT<T>* Get(std::vector<const T*> &vec, const std::string& tag = "", bool strict=true) const;
0115 template<class T> void GetAll(std::vector<const T*> &vec) const;
0116
0117
0118 template<class T> const T* GetSingle(const std::string& tag = "") const;
0119 template<class T> const T* GetSingleStrict(const std::string& tag = "") const;
0120 template<class T> std::vector<const T*> Get(const std::string& tag = "", bool strict=true) const;
0121 template<class T> typename JFactoryT<T>::PairType GetIterators(const std::string& aTag = "") const;
0122 template<class T> std::vector<const T*> GetAll() const;
0123 template<class T> std::map<std::pair<std::string,std::string>,std::vector<T*>> GetAllChildren() const;
0124
0125
0126 template <class T> JFactoryT<T>* Insert(T* item, const std::string& aTag = "") const;
0127 template <class T> JFactoryT<T>* Insert(const std::vector<T*>& items, const std::string& tag = "") const;
0128
0129
0130 #if JANA2_HAVE_PODIO
0131 std::vector<std::string> GetAllCollectionNames() const;
0132 const podio::CollectionBase* GetCollectionBase(std::string name, bool throw_on_missing=true) const;
0133 template <typename T> const typename T::collection_type* GetCollection(std::string name, bool throw_on_missing=true) const;
0134 template <typename T> void InsertCollection(typename T::collection_type&& collection, std::string name);
0135 template <typename T> void InsertCollectionAlreadyInFrame(const podio::CollectionBase* collection, std::string name);
0136 #endif
0137
0138
0139 };
0140
0141
0142
0143
0144 template<class T>
0145 inline JFactoryT<T>* JEvent::GetFactory(const std::string& tag, bool throw_on_missing) const
0146 {
0147 std::string resolved_tag = tag;
0148 if (mUseDefaultTags && tag.empty()) {
0149 auto defaultTag = mDefaultTags.find(JTypeInfo::demangle<T>());
0150 if (defaultTag != mDefaultTags.end()) resolved_tag = defaultTag->second;
0151 }
0152 auto* databundle = mFactorySet.GetDatabundle(std::type_index(typeid(T)), resolved_tag);
0153 if (databundle == nullptr) {
0154 if (throw_on_missing) {
0155 JException ex("Could not find databundle with type_index=" + JTypeInfo::demangle<T>() + " and tag=" + tag);
0156 ex.show_stacktrace = false;
0157 mFactorySet.Print();
0158 throw ex;
0159 }
0160 return nullptr;
0161 };
0162 auto* factory = databundle->GetFactory();
0163 if (factory == nullptr) {
0164 if (throw_on_missing) {
0165 JException ex("No factory provided for databundle with type_index=" + JTypeInfo::demangle<T>() + " and tag=" + tag);
0166 ex.show_stacktrace = false;
0167 mFactorySet.Print();
0168 throw ex;
0169 }
0170 return nullptr;
0171 };
0172 auto* typed_factory = dynamic_cast<JFactoryT<T>*>(databundle->GetFactory());
0173 if (typed_factory == nullptr) {
0174 if (throw_on_missing) {
0175 JException ex("Factory does not inherit from JFactoryT<T> for databundle with type_index=" + JTypeInfo::demangle<T>() + " and tag=" + tag);
0176 ex.show_stacktrace = false;
0177 mFactorySet.Print();
0178 throw ex;
0179 }
0180 return nullptr;
0181 };
0182 return typed_factory;
0183 }
0184
0185 template<class T>
0186 JLightweightDatabundleT<T>* JEvent::GetLightweightDatabundle(const std::string& tag, bool throw_on_missing, bool call_factory_create) const {
0187 std::string resolved_tag = tag;
0188 if (mUseDefaultTags && tag.empty()) {
0189 auto defaultTag = mDefaultTags.find(JTypeInfo::demangle<T>());
0190 if (defaultTag != mDefaultTags.end()) resolved_tag = defaultTag->second;
0191 }
0192 auto* databundle = mFactorySet.GetDatabundle(std::type_index(typeid(T)), resolved_tag);
0193 if (databundle == nullptr) {
0194 if (throw_on_missing) {
0195 JException ex("Could not find databundle with type_index=" + JTypeInfo::demangle<T>() + " and tag=" + tag);
0196 ex.show_stacktrace = false;
0197 mFactorySet.Print();
0198 throw ex;
0199 }
0200 return nullptr;
0201 };
0202 auto* typed_databundle = dynamic_cast<JLightweightDatabundleT<T>*>(databundle);
0203 if (typed_databundle == nullptr) {
0204 if (throw_on_missing) {
0205 JException ex("Databundle with shortname '%s' does not inherit from JLightweightDatabundleT<%s>", tag.c_str(), JTypeInfo::demangle<T>().c_str());
0206 ex.show_stacktrace = false;
0207 mFactorySet.Print();
0208 throw ex;
0209 }
0210 return nullptr;
0211 }
0212 auto factory = databundle->GetFactory();
0213 if (call_factory_create && factory != nullptr) {
0214
0215 JCallGraphEntryMaker cg_entry(mCallGraph, factory);
0216 factory->Create(*this);
0217 }
0218 return typed_databundle;
0219 }
0220
0221
0222
0223
0224
0225 template<class T>
0226 inline std::vector<JFactoryT<T>*> JEvent::GetFactoryAll(bool throw_on_missing) const {
0227 std::vector<JFactoryT<T>*> factories;
0228 for (auto* factory : mFactorySet.GetAllFactories()) {
0229 auto typed_factory = dynamic_cast<JFactoryT<T>*>(factory);
0230 if (typed_factory != nullptr) {
0231 factories.push_back(typed_factory);
0232 }
0233 }
0234 if (factories.size() == 0) {
0235 if (throw_on_missing) {
0236 JException ex("Could not find any JFactoryT<" + JTypeInfo::demangle<T>() + "> (from any tag)");
0237 throw ex;
0238 }
0239 };
0240 return factories;
0241 }
0242
0243
0244
0245
0246 template<class T>
0247 JFactoryT<T>* JEvent::GetSingle(const T* &t, const char *tag, bool exception_if_not_one) const {
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260 auto* databundle = GetLightweightDatabundle<T>(tag, true, true);
0261 if (databundle->GetSize() != 1) {
0262 t = nullptr;
0263 if (exception_if_not_one) {
0264 throw JException("GetSingle<%s>: Databundle has wrong number of items: 1 expected, %d found.", JTypeInfo::demangle<T>().c_str(), databundle->GetSize());
0265 }
0266 }
0267 else {
0268 t = databundle->GetData().at(0);
0269 }
0270 return dynamic_cast<JFactoryT<T>*>(databundle->GetFactory());
0271 }
0272
0273
0274
0275
0276
0277
0278
0279
0280 template<class T>
0281 JFactoryT<T>* JEvent::Get(const T** destination, const std::string& tag) const {
0282 auto* databundle = GetLightweightDatabundle<T>(tag, true, true);
0283 if (databundle->GetSize() == 0) {
0284 *destination = nullptr;
0285 }
0286 else {
0287 *destination = databundle->GetData().at(0);
0288 }
0289 return dynamic_cast<JFactoryT<T>*>(databundle->GetFactory());
0290 }
0291
0292
0293 template<class T>
0294 JFactoryT<T>* JEvent::Get(std::vector<const T*>& destination, const std::string& tag, bool strict) const
0295 {
0296 auto* databundle = GetLightweightDatabundle<T>(tag, strict, true);
0297 if (databundle != nullptr) {
0298 for (auto* item : databundle->GetData()) {
0299 destination.push_back(item);
0300 }
0301 }
0302 return dynamic_cast<JFactoryT<T>*>(databundle->GetFactory());
0303 }
0304
0305
0306
0307 template<class T>
0308 void JEvent::GetAll(std::vector<const T*>& destination) const {
0309 auto factories = GetFactoryAll<T>(true);
0310 for (auto factory : factories) {
0311 auto iterators = factory->CreateAndGetData(*this);
0312 for (auto it = iterators.first; it != iterators.second; it++) {
0313 destination.push_back(*it);
0314 }
0315 }
0316 }
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329 template<class T> const T* JEvent::GetSingle(const std::string& tag) const {
0330
0331 auto databundle = GetLightweightDatabundle<T>(tag, true, true);
0332 if (databundle->GetSize() == 0) {
0333 return nullptr;
0334 }
0335 return databundle->GetData().at(0);
0336 }
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346 template<class T> const T* JEvent::GetSingleStrict(const std::string& tag) const {
0347
0348 auto databundle = GetLightweightDatabundle<T>(tag, true, true);
0349 if (databundle->GetSize() == 0) {
0350 JException ex("GetSingle failed due to missing %d", NAME_OF(T));
0351 ex.show_stacktrace = false;
0352 throw ex;
0353 return nullptr;
0354 }
0355 else if (databundle->GetSize() > 1) {
0356 JException ex("GetSingle failed due to too many %d", NAME_OF(T));
0357 ex.show_stacktrace = false;
0358 throw ex;
0359 }
0360 return databundle->GetData().at(0);
0361 }
0362
0363 template<class T>
0364 std::vector<const T*> JEvent::Get(const std::string& tag, bool strict) const {
0365
0366 auto databundle = GetLightweightDatabundle<T>(tag, strict, true);
0367 std::vector<const T*> vec;
0368 if (databundle != nullptr) {
0369 for (auto x: databundle->GetData()) {
0370 vec.push_back(x);
0371 }
0372 }
0373 return vec;
0374 }
0375
0376 template<class T>
0377 typename JFactoryT<T>::PairType JEvent::GetIterators(const std::string& tag) const {
0378
0379 auto databundle = GetLightweightDatabundle<T>(tag, true, true);
0380 auto& data = databundle->GetData();
0381 return std::make_pair(data.cbegin(), data.cend());
0382 }
0383
0384
0385 template<class T>
0386 std::vector<const T*> JEvent::GetAll() const {
0387
0388 std::vector<const T*> results;
0389
0390 for (auto databundle : mFactorySet.GetDatabundles(std::type_index(typeid(T)))) {
0391 auto fac = databundle->GetFactory();
0392 if (fac != nullptr) {
0393 fac->Create(*this);
0394 }
0395 auto typed_databundle = dynamic_cast<JLightweightDatabundleT<T>*>(databundle);
0396 if (typed_databundle != nullptr) {
0397 for (auto* item : typed_databundle->GetData()) {
0398 results.push_back(item);
0399 }
0400 }
0401 }
0402 return results;
0403 }
0404
0405
0406
0407
0408
0409
0410 template<class S>
0411 std::map<std::pair<std::string, std::string>, std::vector<S*>> JEvent::GetAllChildren() const {
0412 std::map<std::pair<std::string, std::string>, std::vector<S*>> results;
0413 for (JDatabundle* databundle: mFactorySet.GetAllDatabundles()) {
0414 auto val = databundle->GetAs<S>();
0415 if (!val.empty()) {
0416 auto key = std::make_pair(databundle->GetTypeName(),
0417 databundle->HasShortName() ? databundle->GetShortName() : databundle->GetUniqueName());
0418 results.insert(std::make_pair(key, val));
0419 }
0420 }
0421 return results;
0422 }
0423
0424
0425
0426
0427
0428
0429
0430 template <class T>
0431 inline JFactoryT<T>* JEvent::Insert(T* item, const std::string& tag) const {
0432
0433 auto* databundle = GetLightweightDatabundle<T>(tag, false, false);
0434
0435 if (databundle == nullptr) {
0436 auto* factory = new JFactoryT<T>;
0437 factory->SetTag(tag);
0438 factory->SetLevel(mFactorySet.GetLevel());
0439 mFactorySet.Add(factory);
0440 databundle = GetLightweightDatabundle<T>(tag, false, false);
0441 }
0442
0443 databundle->SetStatus(JDatabundle::Status::Inserted);
0444 databundle->GetData().push_back(item);
0445
0446 auto* factory = databundle->GetFactory();
0447 if (factory != nullptr) {
0448 factory->SetStatus(JFactory::Status::Inserted);
0449 factory->SetInsertOrigin( mCallGraph.GetInsertDataOrigin() );
0450 return dynamic_cast<JFactoryT<T>*>(factory);
0451 }
0452 throw JException("Attempted to call JEvent::Insert without an underlying JFactoryT. Hint: Did you previously use Output<T>?");
0453
0454 }
0455
0456 template <class T>
0457 inline JFactoryT<T>* JEvent::Insert(const std::vector<T*>& items, const std::string& tag) const {
0458
0459 auto* databundle = GetLightweightDatabundle<T>(tag, false, false);
0460
0461 if (databundle == nullptr) {
0462 auto* factory = new JFactoryT<T>;
0463 factory->SetTag(tag);
0464 factory->SetLevel(mFactorySet.GetLevel());
0465 mFactorySet.Add(factory);
0466 databundle = GetLightweightDatabundle<T>(tag, false, false);
0467 }
0468
0469 databundle->SetStatus(JDatabundle::Status::Inserted);
0470 databundle->GetData() = items;
0471
0472 auto* factory = databundle->GetFactory();
0473 if (factory != nullptr) {
0474 factory->SetStatus(JFactory::Status::Inserted);
0475 factory->SetInsertOrigin( mCallGraph.GetInsertDataOrigin() );
0476 return dynamic_cast<JFactoryT<T>*>(factory);
0477 }
0478 throw JException("Attempted to call JEvent::Insert without an underlying JFactoryT. Hint: Did you previously use Output<T>?");
0479
0480 }
0481
0482
0483
0484 #if JANA2_HAVE_PODIO
0485
0486 inline std::vector<std::string> JEvent::GetAllCollectionNames() const {
0487 std::vector<std::string> unique_names;
0488 for (auto databundle : mFactorySet.GetAllDatabundles()) {
0489 if (dynamic_cast<JPodioDatabundle*>(databundle) != nullptr) {
0490 unique_names.push_back(databundle->GetUniqueName());
0491 }
0492 }
0493 return unique_names;
0494 }
0495
0496 inline const podio::CollectionBase* JEvent::GetCollectionBase(std::string unique_name, bool throw_on_missing) const {
0497 auto* bundle = mFactorySet.GetDatabundle(unique_name);
0498 if (bundle == nullptr) {
0499 if (throw_on_missing) {
0500 throw JException("Missing databundle with uniquename '%s'", unique_name.c_str());
0501 }
0502 return nullptr;
0503 }
0504
0505 auto* typed_bundle = dynamic_cast<JPodioDatabundle*>(bundle);
0506 if (typed_bundle == nullptr) {
0507 if (throw_on_missing) {
0508 throw JException("Databundle with uniquename '%s' is not a JPodioDatabundle", unique_name.c_str());
0509 }
0510 return nullptr;
0511 }
0512
0513 if (typed_bundle->GetStatus() == JDatabundle::Status::Empty || typed_bundle->GetStatus() == JDatabundle::Status::Excepted) {
0514 auto* fac = typed_bundle->GetFactory();
0515 if (fac != nullptr) {
0516 JCallGraphEntryMaker cg_entry(mCallGraph, fac);
0517 fac->Create(*this);
0518 }
0519 }
0520
0521 return typed_bundle->GetCollection();
0522 }
0523
0524
0525 template <typename T>
0526 const typename T::collection_type* JEvent::GetCollection(std::string name, bool throw_on_missing) const {
0527
0528 auto collection = GetCollectionBase(name, throw_on_missing);
0529 auto* typed_collection = dynamic_cast<const typename T::collection_type*>(collection);
0530 if (throw_on_missing && typed_collection == nullptr) {
0531 throw JException("Databundle with uniquename '%s' does not contain %s", name.c_str(), JTypeInfo::demangle<typename T::collection_type>().c_str());
0532 }
0533 return typed_collection;
0534 }
0535
0536
0537 template <typename T>
0538 void JEvent::InsertCollection(typename T::collection_type&& collection, std::string name) {
0539
0540
0541 if (name.empty()) {
0542 throw JException("JEvent::InsertCollection: Podio collection names must be non-empty!");
0543 }
0544
0545 podio::Frame* frame = nullptr;
0546 auto* bundle = mFactorySet.GetDatabundle("podio::Frame");
0547
0548 if (bundle == nullptr) {
0549
0550 frame = new podio::Frame();
0551 Insert(frame, "");
0552
0553
0554
0555 }
0556 else {
0557 JLightweightDatabundleT<podio::Frame>* typed_bundle = nullptr;
0558 typed_bundle = dynamic_cast<JLightweightDatabundleT<podio::Frame>*>(bundle);
0559 if (typed_bundle == nullptr) {
0560 throw JException("Databundle with unique_name 'podio::Frame' is not a JLightweightDatabundleT");
0561 }
0562 if (typed_bundle->GetSize() == 0) {
0563
0564 typed_bundle->GetData().push_back(new podio::Frame);
0565 typed_bundle->SetStatus(JDatabundle::Status::Inserted);
0566 }
0567 frame = typed_bundle->GetData().at(0);
0568 }
0569
0570 const auto& owned_collection = frame->put(std::move(collection), name);
0571 InsertCollectionAlreadyInFrame<T>(&owned_collection, name);
0572 }
0573
0574
0575 template <typename T>
0576 void JEvent::InsertCollectionAlreadyInFrame(const podio::CollectionBase* collection, std::string unique_name) {
0577
0578
0579
0580
0581 const auto* typed_collection = dynamic_cast<const typename T::collection_type*>(collection);
0582 if (typed_collection == nullptr) {
0583 mFactorySet.Print();
0584 throw JException("Attempted to insert a collection of the wrong type! name='%s', expected type='%s', actual type='%s'",
0585 unique_name.c_str(), JTypeInfo::demangle<typename T::collection_type>().c_str(), collection->getDataTypeName().data());
0586 }
0587
0588
0589 if (mUseDefaultTags && unique_name.empty()) {
0590 auto defaultTag = mDefaultTags.find(JTypeInfo::demangle<T>());
0591 if (defaultTag != mDefaultTags.end()) unique_name = defaultTag->second;
0592 }
0593
0594
0595
0596 JDatabundle* bundle = mFactorySet.GetDatabundle(unique_name);
0597 JPodioDatabundle* typed_bundle = nullptr;
0598
0599 if (bundle == nullptr) {
0600 typed_bundle = new JPodioDatabundle();
0601 typed_bundle->SetUniqueName(unique_name);
0602 typed_bundle->SetTypeIndex(std::type_index(typeid(T)));
0603 typed_bundle->SetTypeName(JTypeInfo::demangle<T>());
0604 mFactorySet.Add(typed_bundle);
0605
0606 }
0607 else {
0608 typed_bundle = dynamic_cast<JPodioDatabundle*>(bundle);
0609 if (typed_bundle == nullptr) {
0610 mFactorySet.Print();
0611 throw JException("Databundle with unique_name='%s' must be a JPodioDatabundle in order to insert a Podio collection", unique_name.c_str());
0612 }
0613 if (typed_bundle->GetStatus() != JDatabundle::Status::Empty) {
0614
0615 mFactorySet.Print();
0616 throw JException("A Podio collection with unique_name='%s' is already present and cannot be overwritten", unique_name.c_str());
0617 }
0618 }
0619
0620 typed_bundle->SetStatus(JDatabundle::Status::Inserted);
0621 typed_bundle->SetCollection(typed_collection);
0622 auto fac = typed_bundle->GetFactory();
0623 if (fac) {
0624 fac->SetStatus(JFactory::Status::Inserted);
0625 }
0626 }
0627
0628 #endif
0629
0630
0631