comparison Samples/Sdl/Loader.cpp @ 795:bc20e4c417ec

refactoring OrthancMultiframeVolumeLoader using LoaderStateMachine
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 28 May 2019 13:02:56 +0200
parents 04f518ebd132
children d3197e0e321d 98a89b116b62
comparison
equal deleted inserted replaced
794:04f518ebd132 795:bc20e4c417ec
987 State(LoaderStateMachine& that) : 987 State(LoaderStateMachine& that) :
988 that_(that) 988 that_(that)
989 { 989 {
990 } 990 }
991 991
992 void Schedule(OracleCommandWithPayload* command) 992 State(const State& currentState) :
993 that_(currentState.that_)
994 {
995 }
996
997 void Schedule(OracleCommandWithPayload* command) const
993 { 998 {
994 that_.Schedule(command); 999 that_.Schedule(command);
995 } 1000 }
1001
1002 template <typename T>
1003 T& GetLoader() const
1004 {
1005 return dynamic_cast<T&>(that_);
1006 }
996 1007
997 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const 1008 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const
998 { 1009 {
999 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); 1010 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
1000 } 1011 }
1011 }; 1022 };
1012 1023
1013 void Schedule(OracleCommandWithPayload* command) 1024 void Schedule(OracleCommandWithPayload* command)
1014 { 1025 {
1015 std::auto_ptr<OracleCommandWithPayload> protection(command); 1026 std::auto_ptr<OracleCommandWithPayload> protection(command);
1027
1028 if (command == NULL)
1029 {
1030 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
1031 }
1016 1032
1017 if (!command->HasPayload()) 1033 if (!command->HasPayload())
1018 { 1034 {
1019 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, 1035 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange,
1020 "The payload must contain the next state"); 1036 "The payload must contain the next state");
1021 } 1037 }
1022 1038
1023 pendingCommands_.push_back(protection.release()); 1039 pendingCommands_.push_back(protection.release());
1040 Step();
1024 } 1041 }
1025 1042
1026 void Start() 1043 void Start()
1027 { 1044 {
1028 if (active_) 1045 if (active_)
1029 { 1046 {
1030 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 1047 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1031 } 1048 }
1049
1050 active_ = true;
1032 1051
1033 for (size_t i = 0; i < simultaneousDownloads_; i++) 1052 for (size_t i = 0; i < simultaneousDownloads_; i++)
1034 { 1053 {
1035 Step(); 1054 Step();
1036 } 1055 }
1037 } 1056 }
1038 1057
1039 private: 1058 private:
1040 void Step() 1059 void Step()
1041 { 1060 {
1042 if (!pendingCommands_.empty()) 1061 if (!pendingCommands_.empty() &&
1062 activeCommands_ < simultaneousDownloads_)
1043 { 1063 {
1044 oracle_.Schedule(*this, pendingCommands_.front()); 1064 oracle_.Schedule(*this, pendingCommands_.front());
1045 pendingCommands_.pop_front(); 1065 pendingCommands_.pop_front();
1046 } 1066
1047 } 1067 activeCommands_++;
1048 1068 }
1049 void Handle(const OrthancRestApiCommand::SuccessMessage& message) 1069 }
1070
1071 void Clear()
1072 {
1073 for (PendingCommands::iterator it = pendingCommands_.begin();
1074 it != pendingCommands_.end(); ++it)
1075 {
1076 delete *it;
1077 }
1078 }
1079
1080 void HandleException(const OracleCommandExceptionMessage& message)
1081 {
1082 LOG(ERROR) << "Error in the state machine, stopping all processing";
1083 Clear();
1084 }
1085
1086 template <typename T>
1087 void Handle(const T& message)
1050 { 1088 {
1051 dynamic_cast<const State&>(message.GetOrigin().GetPayload()).Handle(message); 1089 dynamic_cast<const State&>(message.GetOrigin().GetPayload()).Handle(message);
1052 Step(); 1090 activeCommands_--;
1053 }
1054
1055 void Handle(const GetOrthancImageCommand::SuccessMessage& message)
1056 {
1057 dynamic_cast<const State&>(message.GetOrigin().GetPayload()).Handle(message);
1058 Step();
1059 }
1060
1061 void Handle(const GetOrthancWebViewerJpegCommand::SuccessMessage& message)
1062 {
1063 dynamic_cast<const State&>(message.GetOrigin().GetPayload()).Handle(message);
1064 Step(); 1091 Step();
1065 } 1092 }
1066 1093
1067 typedef std::list<IOracleCommand*> PendingCommands; 1094 typedef std::list<IOracleCommand*> PendingCommands;
1068 1095
1069 IOracle& oracle_; 1096 IOracle& oracle_;
1070 bool active_; 1097 bool active_;
1071 unsigned int simultaneousDownloads_; 1098 unsigned int simultaneousDownloads_;
1072 PendingCommands pendingCommands_; 1099 PendingCommands pendingCommands_;
1100 unsigned int activeCommands_;
1073 1101
1074 public: 1102 public:
1075 LoaderStateMachine(IOracle& oracle, 1103 LoaderStateMachine(IOracle& oracle,
1076 IObservable& oracleObservable) : 1104 IObservable& oracleObservable) :
1077 IObserver(oracleObservable.GetBroker()), 1105 IObserver(oracleObservable.GetBroker()),
1078 oracle_(oracle), 1106 oracle_(oracle),
1079 active_(false), 1107 active_(false),
1080 simultaneousDownloads_(4) 1108 simultaneousDownloads_(4),
1109 activeCommands_(0)
1081 { 1110 {
1082 oracleObservable.RegisterObserverCallback( 1111 oracleObservable.RegisterObserverCallback(
1083 new Callable<LoaderStateMachine, OrthancRestApiCommand::SuccessMessage> 1112 new Callable<LoaderStateMachine, OrthancRestApiCommand::SuccessMessage>
1084 (*this, &LoaderStateMachine::Handle)); 1113 (*this, &LoaderStateMachine::Handle<OrthancRestApiCommand::SuccessMessage>));
1085 1114
1086 oracleObservable.RegisterObserverCallback( 1115 oracleObservable.RegisterObserverCallback(
1087 new Callable<LoaderStateMachine, GetOrthancImageCommand::SuccessMessage> 1116 new Callable<LoaderStateMachine, GetOrthancImageCommand::SuccessMessage>
1088 (*this, &LoaderStateMachine::Handle)); 1117 (*this, &LoaderStateMachine::Handle<GetOrthancImageCommand::SuccessMessage>));
1089 1118
1090 oracleObservable.RegisterObserverCallback( 1119 oracleObservable.RegisterObserverCallback(
1091 new Callable<LoaderStateMachine, GetOrthancWebViewerJpegCommand::SuccessMessage> 1120 new Callable<LoaderStateMachine, GetOrthancWebViewerJpegCommand::SuccessMessage>
1092 (*this, &LoaderStateMachine::Handle)); 1121 (*this, &LoaderStateMachine::Handle<GetOrthancWebViewerJpegCommand::SuccessMessage>));
1122
1123 oracleObservable.RegisterObserverCallback(
1124 new Callable<LoaderStateMachine, OracleCommandExceptionMessage>
1125 (*this, &LoaderStateMachine::HandleException));
1093 } 1126 }
1094 1127
1095 virtual ~LoaderStateMachine() 1128 virtual ~LoaderStateMachine()
1096 { 1129 {
1097 for (PendingCommands::iterator it = pendingCommands_.begin(); 1130 Clear();
1098 it != pendingCommands_.end(); ++it) 1131 }
1099 { 1132
1100 delete *it; 1133 bool IsActive() const
1101 } 1134 {
1102 } 1135 return active_;
1103 1136 }
1104 virtual void SetSimultaneousDownloads(unsigned int count) 1137
1138 void SetSimultaneousDownloads(unsigned int count)
1105 { 1139 {
1106 if (active_) 1140 if (active_)
1107 { 1141 {
1108 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 1142 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1109 } 1143 }
1119 }; 1153 };
1120 1154
1121 1155
1122 1156
1123 class OrthancMultiframeVolumeLoader : 1157 class OrthancMultiframeVolumeLoader :
1124 public IObserver, 1158 public LoaderStateMachine,
1125 public IObservable 1159 public IObservable
1126 { 1160 {
1127 private: 1161 private:
1128 class State : public Orthanc::IDynamicObject
1129 {
1130 private:
1131 OrthancMultiframeVolumeLoader& that_;
1132
1133 protected:
1134 void Schedule(OrthancRestApiCommand* command) const
1135 {
1136 that_.oracle_.Schedule(that_, command);
1137 }
1138
1139 OrthancMultiframeVolumeLoader& GetTarget() const
1140 {
1141 return that_;
1142 }
1143
1144 public:
1145 State(OrthancMultiframeVolumeLoader& that) :
1146 that_(that)
1147 {
1148 }
1149
1150 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const = 0;
1151 };
1152
1153 void Handle(const OrthancRestApiCommand::SuccessMessage& message)
1154 {
1155 dynamic_cast<const State&>(message.GetOrigin().GetPayload()).Handle(message);
1156 }
1157
1158
1159 class LoadRTDoseGeometry : public State 1162 class LoadRTDoseGeometry : public State
1160 { 1163 {
1161 private: 1164 private:
1162 std::auto_ptr<Orthanc::DicomMap> dicom_; 1165 std::auto_ptr<Orthanc::DicomMap> dicom_;
1163 1166
1169 { 1172 {
1170 if (dicom == NULL) 1173 if (dicom == NULL)
1171 { 1174 {
1172 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 1175 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
1173 } 1176 }
1177
1174 } 1178 }
1175 1179
1176 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const 1180 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const
1177 { 1181 {
1178 // Complete the DICOM tags with just-received "Grid Frame Offset Vector" 1182 // Complete the DICOM tags with just-received "Grid Frame Offset Vector"
1179 std::string s = Orthanc::Toolbox::StripSpaces(message.GetAnswer()); 1183 std::string s = Orthanc::Toolbox::StripSpaces(message.GetAnswer());
1180 dicom_->SetValue(Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, s, false); 1184 dicom_->SetValue(Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, s, false);
1181 1185
1182 GetTarget().SetGeometry(*dicom_); 1186 GetLoader<OrthancMultiframeVolumeLoader>().SetGeometry(*dicom_);
1183 } 1187 }
1184 }; 1188 };
1185 1189
1186 1190
1187 static std::string GetSopClassUid(const Orthanc::DicomMap& dicom) 1191 static std::string GetSopClassUid(const Orthanc::DicomMap& dicom)
1207 { 1211 {
1208 } 1212 }
1209 1213
1210 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const 1214 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const
1211 { 1215 {
1216 OrthancMultiframeVolumeLoader& loader = GetLoader<OrthancMultiframeVolumeLoader>();
1217
1212 Json::Value body; 1218 Json::Value body;
1213 message.ParseJsonBody(body); 1219 message.ParseJsonBody(body);
1214 1220
1215 if (body.type() != Json::objectValue) 1221 if (body.type() != Json::objectValue)
1216 { 1222 {
1224 { 1230 {
1225 // Download the "Grid Frame Offset Vector" DICOM tag, that is 1231 // Download the "Grid Frame Offset Vector" DICOM tag, that is
1226 // mandatory for RT-DOSE, but is too long to be returned by default 1232 // mandatory for RT-DOSE, but is too long to be returned by default
1227 1233
1228 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); 1234 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand);
1229 command->SetUri("/instances/" + GetTarget().GetInstanceId() + "/content/" + 1235 command->SetUri("/instances/" + loader.GetInstanceId() + "/content/" +
1230 Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR.Format()); 1236 Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR.Format());
1231 command->SetPayload(new LoadRTDoseGeometry(GetTarget(), dicom.release())); 1237 command->SetPayload(new LoadRTDoseGeometry(loader, dicom.release()));
1232 1238
1233 Schedule(command.release()); 1239 Schedule(command.release());
1234 } 1240 }
1235 else 1241 else
1236 { 1242 {
1237 GetTarget().SetGeometry(*dicom); 1243 loader.SetGeometry(*dicom);
1238 } 1244 }
1239 } 1245 }
1240 }; 1246 };
1241 1247
1242 1248
1249 { 1255 {
1250 } 1256 }
1251 1257
1252 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const 1258 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const
1253 { 1259 {
1254 GetTarget().SetTransferSyntax(message.GetAnswer()); 1260 GetLoader<OrthancMultiframeVolumeLoader>().SetTransferSyntax(message.GetAnswer());
1255 } 1261 }
1256 }; 1262 };
1257 1263
1258 1264
1259 class LoadUncompressedPixelData : public State 1265 class LoadUncompressedPixelData : public State
1264 { 1270 {
1265 } 1271 }
1266 1272
1267 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const 1273 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const
1268 { 1274 {
1269 GetTarget().SetUncompressedPixelData(message.GetAnswer()); 1275 GetLoader<OrthancMultiframeVolumeLoader>().SetUncompressedPixelData(message.GetAnswer());
1270 } 1276 }
1271 }; 1277 };
1272 1278
1273 1279
1274 1280
1275 boost::shared_ptr<DicomVolumeImage> volume_; 1281 boost::shared_ptr<DicomVolumeImage> volume_;
1276 IOracle& oracle_;
1277 bool active_;
1278 std::string instanceId_; 1282 std::string instanceId_;
1279 std::string transferSyntaxUid_; 1283 std::string transferSyntaxUid_;
1280 1284
1281 1285
1282 const std::string& GetInstanceId() const 1286 const std::string& GetInstanceId() const
1283 { 1287 {
1284 if (active_) 1288 if (IsActive())
1285 { 1289 {
1286 return instanceId_; 1290 return instanceId_;
1287 } 1291 }
1288 else 1292 else
1289 { 1293 {
1307 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); 1311 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand);
1308 command->SetHttpHeader("Accept-Encoding", "gzip"); 1312 command->SetHttpHeader("Accept-Encoding", "gzip");
1309 command->SetUri("/instances/" + instanceId_ + "/content/" + 1313 command->SetUri("/instances/" + instanceId_ + "/content/" +
1310 Orthanc::DICOM_TAG_PIXEL_DATA.Format() + "/0"); 1314 Orthanc::DICOM_TAG_PIXEL_DATA.Format() + "/0");
1311 command->SetPayload(new LoadUncompressedPixelData(*this)); 1315 command->SetPayload(new LoadUncompressedPixelData(*this));
1312 oracle_.Schedule(*this, command.release()); 1316 Schedule(command.release());
1313 } 1317 }
1314 else 1318 else
1315 { 1319 {
1316 throw Orthanc::OrthancException( 1320 throw Orthanc::OrthancException(
1317 Orthanc::ErrorCode_NotImplemented, 1321 Orthanc::ErrorCode_NotImplemented,
1449 1453
1450 public: 1454 public:
1451 OrthancMultiframeVolumeLoader(const boost::shared_ptr<DicomVolumeImage>& volume, 1455 OrthancMultiframeVolumeLoader(const boost::shared_ptr<DicomVolumeImage>& volume,
1452 IOracle& oracle, 1456 IOracle& oracle,
1453 IObservable& oracleObservable) : 1457 IObservable& oracleObservable) :
1454 IObserver(oracleObservable.GetBroker()), 1458 LoaderStateMachine(oracle, oracleObservable),
1455 IObservable(oracleObservable.GetBroker()), 1459 IObservable(oracleObservable.GetBroker()),
1456 volume_(volume), 1460 volume_(volume)
1457 oracle_(oracle),
1458 active_(false)
1459 { 1461 {
1460 if (volume.get() == NULL) 1462 if (volume.get() == NULL)
1461 { 1463 {
1462 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 1464 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
1463 } 1465 }
1464
1465 oracleObservable.RegisterObserverCallback(
1466 new Callable<OrthancMultiframeVolumeLoader, OrthancRestApiCommand::SuccessMessage>
1467 (*this, &OrthancMultiframeVolumeLoader::Handle));
1468 } 1466 }
1469 1467
1470 1468
1471 void LoadInstance(const std::string& instanceId) 1469 void LoadInstance(const std::string& instanceId)
1472 { 1470 {
1473 if (active_) 1471 Start();
1474 { 1472
1475 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 1473 instanceId_ = instanceId;
1476 } 1474
1477 else 1475 {
1478 { 1476 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand);
1479 active_ = true; 1477 command->SetHttpHeader("Accept-Encoding", "gzip");
1480 instanceId_ = instanceId; 1478 command->SetUri("/instances/" + instanceId + "/tags");
1481 1479 command->SetPayload(new LoadGeometry(*this));
1482 { 1480 Schedule(command.release());
1483 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); 1481 }
1484 command->SetHttpHeader("Accept-Encoding", "gzip"); 1482
1485 command->SetUri("/instances/" + instanceId + "/tags"); 1483 {
1486 command->SetPayload(new LoadGeometry(*this)); 1484 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand);
1487 oracle_.Schedule(*this, command.release()); 1485 command->SetUri("/instances/" + instanceId + "/metadata/TransferSyntax");
1488 } 1486 command->SetPayload(new LoadTransferSyntax(*this));
1489 1487 Schedule(command.release());
1490 {
1491 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand);
1492 command->SetUri("/instances/" + instanceId + "/metadata/TransferSyntax");
1493 command->SetPayload(new LoadTransferSyntax(*this));
1494 oracle_.Schedule(*this, command.release());
1495 }
1496 } 1488 }
1497 } 1489 }
1498 }; 1490 };
1499 1491
1500 1492
2026 void Handle(const OrthancStone::DicomVolumeImage::GeometryReadyMessage& message) 2018 void Handle(const OrthancStone::DicomVolumeImage::GeometryReadyMessage& message)
2027 { 2019 {
2028 printf("Geometry ready\n"); 2020 printf("Geometry ready\n");
2029 2021
2030 //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); 2022 //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry();
2031 plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); 2023 //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
2032 //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); 2024 plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
2033 plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); 2025 plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
2034 2026
2035 Refresh(); 2027 Refresh();
2036 } 2028 }
2037 2029
2284 2276
2285 2277
2286 // 2017-11-17-Anonymized 2278 // 2017-11-17-Anonymized
2287 //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT 2279 //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT
2288 doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE 2280 doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE
2289 rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT 2281 //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT
2290 2282
2291 // 2015-01-28-Multiframe 2283 // 2015-01-28-Multiframe
2292 //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT 2284 //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT
2293 2285
2294 // Delphine 2286 // Delphine