comparison Samples/Sdl/Loader.cpp @ 681:9723fceccb9f

revision counters in DicomVolumeImage
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 16 May 2019 14:26:11 +0200
parents 0eb26f514ac5
children dbc1d8bfc68a
comparison
equal deleted inserted replaced
680:0eb26f514ac5 681:9723fceccb9f
755 755
756 756
757 757
758 758
759 759
760 class NativeOracle : public IOracle
761 {
762 private:
763 class Item : public Orthanc::IDynamicObject
764 {
765 private:
766 const OrthancStone::IObserver& receiver_;
767 std::auto_ptr<IOracleCommand> command_;
768
769 public:
770 Item(const OrthancStone::IObserver& receiver,
771 IOracleCommand* command) :
772 receiver_(receiver),
773 command_(command)
774 {
775 if (command == NULL)
776 {
777 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
778 }
779 }
780
781 const OrthancStone::IObserver& GetReceiver() const
782 {
783 return receiver_;
784 }
785
786 const IOracleCommand& GetCommand() const
787 {
788 assert(command_.get() != NULL);
789 return *command_;
790 }
791 };
792
793
794 enum State
795 {
796 State_Setup,
797 State_Running,
798 State_Stopped
799 };
800
801
802 IMessageEmitter& emitter_;
803 Orthanc::WebServiceParameters orthanc_;
804 Orthanc::SharedMessageQueue queue_;
805 State state_;
806 boost::mutex mutex_;
807 std::vector<boost::thread*> workers_;
808
809
810 void CopyHttpHeaders(Orthanc::HttpClient& client,
811 const HttpHeaders& headers)
812 {
813 for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ )
814 {
815 client.AddHeader(it->first, it->second);
816 }
817 }
818
819
820 void DecodeAnswer(std::string& answer,
821 const HttpHeaders& headers)
822 {
823 Orthanc::HttpCompression contentEncoding = Orthanc::HttpCompression_None;
824
825 for (HttpHeaders::const_iterator it = headers.begin();
826 it != headers.end(); ++it)
827 {
828 std::string s;
829 Orthanc::Toolbox::ToLowerCase(s, it->first);
830
831 if (s == "content-encoding")
832 {
833 if (it->second == "gzip")
834 {
835 contentEncoding = Orthanc::HttpCompression_Gzip;
836 }
837 else
838 {
839 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol,
840 "Unsupported HTTP Content-Encoding: " + it->second);
841 }
842
843 break;
844 }
845 }
846
847 if (contentEncoding == Orthanc::HttpCompression_Gzip)
848 {
849 std::string compressed;
850 answer.swap(compressed);
851
852 Orthanc::GzipCompressor compressor;
853 compressor.Uncompress(answer, compressed.c_str(), compressed.size());
854 }
855 }
856
857
858 void Execute(const OrthancStone::IObserver& receiver,
859 const OrthancRestApiCommand& command)
860 {
861 Orthanc::HttpClient client(orthanc_, command.GetUri());
862 client.SetMethod(command.GetMethod());
863 client.SetTimeout(command.GetTimeout());
864
865 CopyHttpHeaders(client, command.GetHttpHeaders());
866
867 if (command.GetMethod() == Orthanc::HttpMethod_Post ||
868 command.GetMethod() == Orthanc::HttpMethod_Put)
869 {
870 client.SetBody(command.GetBody());
871 }
872
873 std::string answer;
874 HttpHeaders answerHeaders;
875 client.ApplyAndThrowException(answer, answerHeaders);
876
877 DecodeAnswer(answer, answerHeaders);
878
879 OrthancRestApiCommand::SuccessMessage message(command, answerHeaders, answer);
880 emitter_.EmitMessage(receiver, message);
881 }
882
883
884 void Execute(const OrthancStone::IObserver& receiver,
885 const GetOrthancImageCommand& command)
886 {
887 Orthanc::HttpClient client(orthanc_, command.GetUri());
888 client.SetTimeout(command.GetTimeout());
889
890 CopyHttpHeaders(client, command.GetHttpHeaders());
891
892 std::string answer;
893 HttpHeaders answerHeaders;
894 client.ApplyAndThrowException(answer, answerHeaders);
895
896 DecodeAnswer(answer, answerHeaders);
897
898 command.ProcessHttpAnswer(emitter_, receiver, answer, answerHeaders);
899 }
900
901
902 void Execute(const OrthancStone::IObserver& receiver,
903 const GetOrthancWebViewerJpegCommand& command)
904 {
905 Orthanc::HttpClient client(orthanc_, command.GetUri());
906 client.SetTimeout(command.GetTimeout());
907
908 CopyHttpHeaders(client, command.GetHttpHeaders());
909
910 std::string answer;
911 HttpHeaders answerHeaders;
912 client.ApplyAndThrowException(answer, answerHeaders);
913
914 DecodeAnswer(answer, answerHeaders);
915
916 command.ProcessHttpAnswer(emitter_, receiver, answer);
917 }
918
919
920 void Step()
921 {
922 std::auto_ptr<Orthanc::IDynamicObject> object(queue_.Dequeue(100));
923
924 if (object.get() != NULL)
925 {
926 const Item& item = dynamic_cast<Item&>(*object);
927
928 try
929 {
930 switch (item.GetCommand().GetType())
931 {
932 case IOracleCommand::Type_OrthancRestApi:
933 Execute(item.GetReceiver(),
934 dynamic_cast<const OrthancRestApiCommand&>(item.GetCommand()));
935 break;
936
937 case IOracleCommand::Type_GetOrthancImage:
938 Execute(item.GetReceiver(),
939 dynamic_cast<const GetOrthancImageCommand&>(item.GetCommand()));
940 break;
941
942 case IOracleCommand::Type_GetOrthancWebViewerJpeg:
943 Execute(item.GetReceiver(),
944 dynamic_cast<const GetOrthancWebViewerJpegCommand&>(item.GetCommand()));
945 break;
946
947 default:
948 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
949 }
950 }
951 catch (Orthanc::OrthancException& e)
952 {
953 LOG(ERROR) << "Exception within the oracle: " << e.What();
954 emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage(item.GetCommand(), e));
955 }
956 catch (...)
957 {
958 LOG(ERROR) << "Native exception within the oracle";
959 emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage
960 (item.GetCommand(), Orthanc::ErrorCode_InternalError));
961 }
962 }
963 }
964
965
966 static void Worker(NativeOracle* that)
967 {
968 assert(that != NULL);
969
970 for (;;)
971 {
972 {
973 boost::mutex::scoped_lock lock(that->mutex_);
974 if (that->state_ != State_Running)
975 {
976 return;
977 }
978 }
979
980 that->Step();
981 }
982 }
983
984
985 void StopInternal()
986 {
987 {
988 boost::mutex::scoped_lock lock(mutex_);
989
990 if (state_ == State_Setup ||
991 state_ == State_Stopped)
992 {
993 return;
994 }
995 else
996 {
997 state_ = State_Stopped;
998 }
999 }
1000
1001 for (size_t i = 0; i < workers_.size(); i++)
1002 {
1003 if (workers_[i] != NULL)
1004 {
1005 if (workers_[i]->joinable())
1006 {
1007 workers_[i]->join();
1008 }
1009
1010 delete workers_[i];
1011 }
1012 }
1013 }
1014
1015
1016 public:
1017 NativeOracle(IMessageEmitter& emitter) :
1018 emitter_(emitter),
1019 state_(State_Setup),
1020 workers_(4)
1021 {
1022 }
1023
1024 virtual ~NativeOracle()
1025 {
1026 StopInternal();
1027 }
1028
1029 void SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc)
1030 {
1031 boost::mutex::scoped_lock lock(mutex_);
1032
1033 if (state_ != State_Setup)
1034 {
1035 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1036 }
1037 else
1038 {
1039 orthanc_ = orthanc;
1040 }
1041 }
1042
1043 void SetWorkersCount(unsigned int count)
1044 {
1045 boost::mutex::scoped_lock lock(mutex_);
1046
1047 if (count <= 0)
1048 {
1049 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
1050 }
1051 else if (state_ != State_Setup)
1052 {
1053 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1054 }
1055 else
1056 {
1057 workers_.resize(count);
1058 }
1059 }
1060
1061 void Start()
1062 {
1063 boost::mutex::scoped_lock lock(mutex_);
1064
1065 if (state_ != State_Setup)
1066 {
1067 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1068 }
1069 else
1070 {
1071 state_ = State_Running;
1072
1073 for (unsigned int i = 0; i < workers_.size(); i++)
1074 {
1075 workers_[i] = new boost::thread(Worker, this);
1076 }
1077 }
1078 }
1079
1080 void Stop()
1081 {
1082 StopInternal();
1083 }
1084
1085 virtual void Schedule(const OrthancStone::IObserver& receiver,
1086 IOracleCommand* command)
1087 {
1088 queue_.Enqueue(new Item(receiver, command));
1089 }
1090 };
1091
1092
1093
1094 class NativeApplicationContext : public IMessageEmitter
1095 {
1096 private:
1097 boost::shared_mutex mutex_;
1098 OrthancStone::MessageBroker broker_;
1099 OrthancStone::IObservable oracleObservable_;
1100
1101 public:
1102 NativeApplicationContext() :
1103 oracleObservable_(broker_)
1104 {
1105 }
1106
1107
1108 virtual void EmitMessage(const OrthancStone::IObserver& observer,
1109 const OrthancStone::IMessage& message)
1110 {
1111 try
1112 {
1113 boost::unique_lock<boost::shared_mutex> lock(mutex_);
1114 oracleObservable_.EmitMessage(observer, message);
1115 }
1116 catch (Orthanc::OrthancException& e)
1117 {
1118 LOG(ERROR) << "Exception while emitting a message: " << e.What();
1119 }
1120 }
1121
1122
1123 class ReaderLock : public boost::noncopyable
1124 {
1125 private:
1126 NativeApplicationContext& that_;
1127 boost::shared_lock<boost::shared_mutex> lock_;
1128
1129 public:
1130 ReaderLock(NativeApplicationContext& that) :
1131 that_(that),
1132 lock_(that.mutex_)
1133 {
1134 }
1135 };
1136
1137
1138 class WriterLock : public boost::noncopyable
1139 {
1140 private:
1141 NativeApplicationContext& that_;
1142 boost::unique_lock<boost::shared_mutex> lock_;
1143
1144 public:
1145 WriterLock(NativeApplicationContext& that) :
1146 that_(that),
1147 lock_(that.mutex_)
1148 {
1149 }
1150
1151 OrthancStone::MessageBroker& GetBroker()
1152 {
1153 return that_.broker_;
1154 }
1155
1156 OrthancStone::IObservable& GetOracleObservable()
1157 {
1158 return that_.oracleObservable_;
1159 }
1160 };
1161 };
1162
1163
1164
1165 class DicomInstanceParameters : 760 class DicomInstanceParameters :
1166 public Orthanc::IDynamicObject /* to be used as a payload of SlicesSorter */ 761 public Orthanc::IDynamicObject /* to be used as a payload of SlicesSorter */
1167 { 762 {
1168 private: 763 private:
1169 struct Data // Struct to ease the copy constructor 764 struct Data // Struct to ease the copy constructor
1541 class DicomVolumeImage : public boost::noncopyable 1136 class DicomVolumeImage : public boost::noncopyable
1542 { 1137 {
1543 private: 1138 private:
1544 std::auto_ptr<OrthancStone::ImageBuffer3D> image_; 1139 std::auto_ptr<OrthancStone::ImageBuffer3D> image_;
1545 std::vector<DicomInstanceParameters*> slices_; 1140 std::vector<DicomInstanceParameters*> slices_;
1141 uint64_t revision_;
1142 std::vector<uint64_t> slicesRevision_;
1546 1143
1547 void CheckSlice(size_t index, 1144 void CheckSlice(size_t index,
1548 const DicomInstanceParameters& reference) const 1145 const DicomInstanceParameters& reference) const
1549 { 1146 {
1550 const DicomInstanceParameters& slice = *slices_[index]; 1147 const DicomInstanceParameters& slice = *slices_[index];
1612 assert(slices_[i] != NULL); 1209 assert(slices_[i] != NULL);
1613 delete slices_[i]; 1210 delete slices_[i];
1614 } 1211 }
1615 } 1212 }
1616 1213
1214
1215 void CheckSliceIndex(size_t index) const
1216 {
1217 assert(slices_.size() == image_->GetDepth() &&
1218 slices_.size() == slicesRevision_.size());
1219
1220 if (!IsGeometryReady())
1221 {
1222 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1223 }
1224 else if (index >= slices_.size())
1225 {
1226 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
1227 }
1228 }
1229
1617 1230
1618 public: 1231 public:
1619 DicomVolumeImage() 1232 DicomVolumeImage()
1620 { 1233 {
1621 } 1234 }
1643 false /* don't compute range */)); 1256 false /* don't compute range */));
1644 } 1257 }
1645 else 1258 else
1646 { 1259 {
1647 slices_.reserve(slices.GetSlicesCount()); 1260 slices_.reserve(slices.GetSlicesCount());
1261 slicesRevision_.resize(slices.GetSlicesCount());
1648 1262
1649 for (size_t i = 0; i < slices.GetSlicesCount(); i++) 1263 for (size_t i = 0; i < slices.GetSlicesCount(); i++)
1650 { 1264 {
1651 const DicomInstanceParameters& slice = 1265 const DicomInstanceParameters& slice =
1652 dynamic_cast<const DicomInstanceParameters&>(slices.GetSlicePayload(i)); 1266 dynamic_cast<const DicomInstanceParameters&>(slices.GetSlicePayload(i));
1653 slices_.push_back(new DicomInstanceParameters(slice)); 1267 slices_.push_back(new DicomInstanceParameters(slice));
1268 slicesRevision_[i] = 0;
1654 } 1269 }
1655 1270
1656 CheckVolume(); 1271 CheckVolume();
1657 1272
1658 const double spacingZ = slices.ComputeSpacingBetweenSlices(); 1273 const double spacingZ = slices.ComputeSpacingBetweenSlices();
1668 image_->SetAxialGeometry(slices.GetSliceGeometry(0)); 1283 image_->SetAxialGeometry(slices.GetSliceGeometry(0));
1669 image_->SetVoxelDimensions(parameters.GetPixelSpacingX(), parameters.GetPixelSpacingY(), spacingZ); 1284 image_->SetVoxelDimensions(parameters.GetPixelSpacingX(), parameters.GetPixelSpacingY(), spacingZ);
1670 } 1285 }
1671 1286
1672 image_->Clear(); 1287 image_->Clear();
1288
1289 revision_++;
1290 }
1291
1292 uint64_t GetRevision() const
1293 {
1294 return revision_;
1673 } 1295 }
1674 1296
1675 bool IsGeometryReady() const 1297 bool IsGeometryReady() const
1676 { 1298 {
1677 return (image_.get() != NULL); 1299 return (image_.get() != NULL);
1695 { 1317 {
1696 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 1318 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1697 } 1319 }
1698 else 1320 else
1699 { 1321 {
1700 assert(slices_.size() == image_->GetDepth());
1701 return slices_.size(); 1322 return slices_.size();
1702 } 1323 }
1703 } 1324 }
1704 1325
1705 const DicomInstanceParameters& GetSlice(size_t index) const 1326 const DicomInstanceParameters& GetSliceParameters(size_t index) const
1706 { 1327 {
1707 if (!IsGeometryReady()) 1328 CheckSliceIndex(index);
1708 { 1329 return *slices_[index];
1709 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 1330 }
1710 } 1331
1711 else if (index >= slices_.size()) 1332 uint64_t GetSliceRevision(size_t index) const
1712 { 1333 {
1713 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 1334 CheckSliceIndex(index);
1714 } 1335 return slicesRevision_[index];
1715 else
1716 {
1717 assert(slices_.size() == image_->GetDepth());
1718 return *slices_[index];
1719 }
1720 } 1336 }
1721 1337
1722 void SetSliceContent(size_t index, 1338 void SetSliceContent(size_t index,
1723 const Orthanc::ImageAccessor& image) 1339 const Orthanc::ImageAccessor& image)
1724 { 1340 {
1725 if (!IsGeometryReady()) 1341 CheckSliceIndex(index);
1726 { 1342
1727 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1728 }
1729 else if (index >= slices_.size())
1730 {
1731 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
1732 }
1733 else
1734 { 1343 {
1735 OrthancStone::ImageBuffer3D::SliceWriter writer 1344 OrthancStone::ImageBuffer3D::SliceWriter writer
1736 (*image_, OrthancStone::VolumeProjection_Axial, index); 1345 (*image_, OrthancStone::VolumeProjection_Axial, index);
1737 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image); 1346 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image);
1738 } 1347 }
1348
1349 revision_ ++;
1350 slicesRevision_[index] += 1;
1739 } 1351 }
1740 }; 1352 };
1741 1353
1742 1354
1743 1355
1744 class AxialVolumeOrthancLoader : public OrthancStone::IObserver 1356 class VolumeSeriesOrthancLoader : public OrthancStone::IObserver
1745 { 1357 {
1746 private: 1358 private:
1747 class MessageHandler : public Orthanc::IDynamicObject 1359 class MessageHandler : public Orthanc::IDynamicObject
1748 { 1360 {
1749 public: 1361 public:
1804 1416
1805 1417
1806 class LoadSeriesGeometryHandler : public MessageHandler 1418 class LoadSeriesGeometryHandler : public MessageHandler
1807 { 1419 {
1808 private: 1420 private:
1809 AxialVolumeOrthancLoader& that_; 1421 VolumeSeriesOrthancLoader& that_;
1810 1422
1811 public: 1423 public:
1812 LoadSeriesGeometryHandler(AxialVolumeOrthancLoader& that) : 1424 LoadSeriesGeometryHandler(VolumeSeriesOrthancLoader& that) :
1813 that_(that) 1425 that_(that)
1814 { 1426 {
1815 } 1427 }
1816 1428
1817 virtual void Handle(const Json::Value& body) const 1429 virtual void Handle(const Json::Value& body) const
1834 1446
1835 that_.image_.SetGeometry(slices); 1447 that_.image_.SetGeometry(slices);
1836 1448
1837 for (size_t i = 0; i < that_.image_.GetSlicesCount(); i++) 1449 for (size_t i = 0; i < that_.image_.GetSlicesCount(); i++)
1838 { 1450 {
1839 const DicomInstanceParameters& slice = that_.image_.GetSlice(i); 1451 const DicomInstanceParameters& slice = that_.image_.GetSliceParameters(i);
1840 1452
1841 const std::string& instance = slice.GetOrthancInstanceIdentifier(); 1453 const std::string& instance = slice.GetOrthancInstanceIdentifier();
1842 if (instance.empty()) 1454 if (instance.empty())
1843 { 1455 {
1844 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 1456 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
1887 1499
1888 1500
1889 class LoadInstanceGeometryHandler : public MessageHandler 1501 class LoadInstanceGeometryHandler : public MessageHandler
1890 { 1502 {
1891 private: 1503 private:
1892 AxialVolumeOrthancLoader& that_; 1504 VolumeSeriesOrthancLoader& that_;
1893 1505
1894 public: 1506 public:
1895 LoadInstanceGeometryHandler(AxialVolumeOrthancLoader& that) : 1507 LoadInstanceGeometryHandler(VolumeSeriesOrthancLoader& that) :
1896 that_(that) 1508 that_(that)
1897 { 1509 {
1898 } 1510 }
1899 1511
1900 virtual void Handle(const Json::Value& body) const 1512 virtual void Handle(const Json::Value& body) const
1910 IOracle& oracle_; 1522 IOracle& oracle_;
1911 bool active_; 1523 bool active_;
1912 DicomVolumeImage image_; 1524 DicomVolumeImage image_;
1913 1525
1914 public: 1526 public:
1915 AxialVolumeOrthancLoader(IOracle& oracle, 1527 VolumeSeriesOrthancLoader(IOracle& oracle,
1916 OrthancStone::IObservable& oracleObservable) : 1528 OrthancStone::IObservable& oracleObservable) :
1917 IObserver(oracleObservable.GetBroker()), 1529 IObserver(oracleObservable.GetBroker()),
1918 oracle_(oracle), 1530 oracle_(oracle),
1919 active_(false) 1531 active_(false)
1920 { 1532 {
1921 oracleObservable.RegisterObserverCallback( 1533 oracleObservable.RegisterObserverCallback(
1922 new OrthancStone::Callable<AxialVolumeOrthancLoader, OrthancRestApiCommand::SuccessMessage> 1534 new OrthancStone::Callable<VolumeSeriesOrthancLoader, OrthancRestApiCommand::SuccessMessage>
1923 (*this, &AxialVolumeOrthancLoader::Handle)); 1535 (*this, &VolumeSeriesOrthancLoader::Handle));
1924 1536
1925 oracleObservable.RegisterObserverCallback( 1537 oracleObservable.RegisterObserverCallback(
1926 new OrthancStone::Callable<AxialVolumeOrthancLoader, GetOrthancImageCommand::SuccessMessage> 1538 new OrthancStone::Callable<VolumeSeriesOrthancLoader, GetOrthancImageCommand::SuccessMessage>
1927 (*this, &AxialVolumeOrthancLoader::Handle)); 1539 (*this, &VolumeSeriesOrthancLoader::Handle));
1928 1540
1929 oracleObservable.RegisterObserverCallback( 1541 oracleObservable.RegisterObserverCallback(
1930 new OrthancStone::Callable<AxialVolumeOrthancLoader, GetOrthancWebViewerJpegCommand::SuccessMessage> 1542 new OrthancStone::Callable<VolumeSeriesOrthancLoader, GetOrthancWebViewerJpegCommand::SuccessMessage>
1931 (*this, &AxialVolumeOrthancLoader::Handle)); 1543 (*this, &VolumeSeriesOrthancLoader::Handle));
1932 } 1544 }
1933 1545
1934 void LoadSeries(const std::string& seriesId) 1546 void LoadSeries(const std::string& seriesId)
1935 { 1547 {
1936 if (active_) 1548 if (active_)
1967 1579
1968 oracle_.Schedule(*this, command.release()); 1580 oracle_.Schedule(*this, command.release());
1969 } 1581 }
1970 }; 1582 };
1971 1583
1584
1585
1586
1587
1588
1589
1590
1591 class NativeOracle : public IOracle
1592 {
1593 private:
1594 class Item : public Orthanc::IDynamicObject
1595 {
1596 private:
1597 const OrthancStone::IObserver& receiver_;
1598 std::auto_ptr<IOracleCommand> command_;
1599
1600 public:
1601 Item(const OrthancStone::IObserver& receiver,
1602 IOracleCommand* command) :
1603 receiver_(receiver),
1604 command_(command)
1605 {
1606 if (command == NULL)
1607 {
1608 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
1609 }
1610 }
1611
1612 const OrthancStone::IObserver& GetReceiver() const
1613 {
1614 return receiver_;
1615 }
1616
1617 const IOracleCommand& GetCommand() const
1618 {
1619 assert(command_.get() != NULL);
1620 return *command_;
1621 }
1622 };
1623
1624
1625 enum State
1626 {
1627 State_Setup,
1628 State_Running,
1629 State_Stopped
1630 };
1631
1632
1633 IMessageEmitter& emitter_;
1634 Orthanc::WebServiceParameters orthanc_;
1635 Orthanc::SharedMessageQueue queue_;
1636 State state_;
1637 boost::mutex mutex_;
1638 std::vector<boost::thread*> workers_;
1639
1640
1641 void CopyHttpHeaders(Orthanc::HttpClient& client,
1642 const HttpHeaders& headers)
1643 {
1644 for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ )
1645 {
1646 client.AddHeader(it->first, it->second);
1647 }
1648 }
1649
1650
1651 void DecodeAnswer(std::string& answer,
1652 const HttpHeaders& headers)
1653 {
1654 Orthanc::HttpCompression contentEncoding = Orthanc::HttpCompression_None;
1655
1656 for (HttpHeaders::const_iterator it = headers.begin();
1657 it != headers.end(); ++it)
1658 {
1659 std::string s;
1660 Orthanc::Toolbox::ToLowerCase(s, it->first);
1661
1662 if (s == "content-encoding")
1663 {
1664 if (it->second == "gzip")
1665 {
1666 contentEncoding = Orthanc::HttpCompression_Gzip;
1667 }
1668 else
1669 {
1670 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol,
1671 "Unsupported HTTP Content-Encoding: " + it->second);
1672 }
1673
1674 break;
1675 }
1676 }
1677
1678 if (contentEncoding == Orthanc::HttpCompression_Gzip)
1679 {
1680 std::string compressed;
1681 answer.swap(compressed);
1682
1683 Orthanc::GzipCompressor compressor;
1684 compressor.Uncompress(answer, compressed.c_str(), compressed.size());
1685 }
1686 }
1687
1688
1689 void Execute(const OrthancStone::IObserver& receiver,
1690 const OrthancRestApiCommand& command)
1691 {
1692 Orthanc::HttpClient client(orthanc_, command.GetUri());
1693 client.SetMethod(command.GetMethod());
1694 client.SetTimeout(command.GetTimeout());
1695
1696 CopyHttpHeaders(client, command.GetHttpHeaders());
1697
1698 if (command.GetMethod() == Orthanc::HttpMethod_Post ||
1699 command.GetMethod() == Orthanc::HttpMethod_Put)
1700 {
1701 client.SetBody(command.GetBody());
1702 }
1703
1704 std::string answer;
1705 HttpHeaders answerHeaders;
1706 client.ApplyAndThrowException(answer, answerHeaders);
1707
1708 DecodeAnswer(answer, answerHeaders);
1709
1710 OrthancRestApiCommand::SuccessMessage message(command, answerHeaders, answer);
1711 emitter_.EmitMessage(receiver, message);
1712 }
1713
1714
1715 void Execute(const OrthancStone::IObserver& receiver,
1716 const GetOrthancImageCommand& command)
1717 {
1718 Orthanc::HttpClient client(orthanc_, command.GetUri());
1719 client.SetTimeout(command.GetTimeout());
1720
1721 CopyHttpHeaders(client, command.GetHttpHeaders());
1722
1723 std::string answer;
1724 HttpHeaders answerHeaders;
1725 client.ApplyAndThrowException(answer, answerHeaders);
1726
1727 DecodeAnswer(answer, answerHeaders);
1728
1729 command.ProcessHttpAnswer(emitter_, receiver, answer, answerHeaders);
1730 }
1731
1732
1733 void Execute(const OrthancStone::IObserver& receiver,
1734 const GetOrthancWebViewerJpegCommand& command)
1735 {
1736 Orthanc::HttpClient client(orthanc_, command.GetUri());
1737 client.SetTimeout(command.GetTimeout());
1738
1739 CopyHttpHeaders(client, command.GetHttpHeaders());
1740
1741 std::string answer;
1742 HttpHeaders answerHeaders;
1743 client.ApplyAndThrowException(answer, answerHeaders);
1744
1745 DecodeAnswer(answer, answerHeaders);
1746
1747 command.ProcessHttpAnswer(emitter_, receiver, answer);
1748 }
1749
1750
1751 void Step()
1752 {
1753 std::auto_ptr<Orthanc::IDynamicObject> object(queue_.Dequeue(100));
1754
1755 if (object.get() != NULL)
1756 {
1757 const Item& item = dynamic_cast<Item&>(*object);
1758
1759 try
1760 {
1761 switch (item.GetCommand().GetType())
1762 {
1763 case IOracleCommand::Type_OrthancRestApi:
1764 Execute(item.GetReceiver(),
1765 dynamic_cast<const OrthancRestApiCommand&>(item.GetCommand()));
1766 break;
1767
1768 case IOracleCommand::Type_GetOrthancImage:
1769 Execute(item.GetReceiver(),
1770 dynamic_cast<const GetOrthancImageCommand&>(item.GetCommand()));
1771 break;
1772
1773 case IOracleCommand::Type_GetOrthancWebViewerJpeg:
1774 Execute(item.GetReceiver(),
1775 dynamic_cast<const GetOrthancWebViewerJpegCommand&>(item.GetCommand()));
1776 break;
1777
1778 default:
1779 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
1780 }
1781 }
1782 catch (Orthanc::OrthancException& e)
1783 {
1784 LOG(ERROR) << "Exception within the oracle: " << e.What();
1785 emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage(item.GetCommand(), e));
1786 }
1787 catch (...)
1788 {
1789 LOG(ERROR) << "Native exception within the oracle";
1790 emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage
1791 (item.GetCommand(), Orthanc::ErrorCode_InternalError));
1792 }
1793 }
1794 }
1795
1796
1797 static void Worker(NativeOracle* that)
1798 {
1799 assert(that != NULL);
1800
1801 for (;;)
1802 {
1803 {
1804 boost::mutex::scoped_lock lock(that->mutex_);
1805 if (that->state_ != State_Running)
1806 {
1807 return;
1808 }
1809 }
1810
1811 that->Step();
1812 }
1813 }
1814
1815
1816 void StopInternal()
1817 {
1818 {
1819 boost::mutex::scoped_lock lock(mutex_);
1820
1821 if (state_ == State_Setup ||
1822 state_ == State_Stopped)
1823 {
1824 return;
1825 }
1826 else
1827 {
1828 state_ = State_Stopped;
1829 }
1830 }
1831
1832 for (size_t i = 0; i < workers_.size(); i++)
1833 {
1834 if (workers_[i] != NULL)
1835 {
1836 if (workers_[i]->joinable())
1837 {
1838 workers_[i]->join();
1839 }
1840
1841 delete workers_[i];
1842 }
1843 }
1844 }
1845
1846
1847 public:
1848 NativeOracle(IMessageEmitter& emitter) :
1849 emitter_(emitter),
1850 state_(State_Setup),
1851 workers_(4)
1852 {
1853 }
1854
1855 virtual ~NativeOracle()
1856 {
1857 StopInternal();
1858 }
1859
1860 void SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc)
1861 {
1862 boost::mutex::scoped_lock lock(mutex_);
1863
1864 if (state_ != State_Setup)
1865 {
1866 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1867 }
1868 else
1869 {
1870 orthanc_ = orthanc;
1871 }
1872 }
1873
1874 void SetWorkersCount(unsigned int count)
1875 {
1876 boost::mutex::scoped_lock lock(mutex_);
1877
1878 if (count <= 0)
1879 {
1880 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
1881 }
1882 else if (state_ != State_Setup)
1883 {
1884 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1885 }
1886 else
1887 {
1888 workers_.resize(count);
1889 }
1890 }
1891
1892 void Start()
1893 {
1894 boost::mutex::scoped_lock lock(mutex_);
1895
1896 if (state_ != State_Setup)
1897 {
1898 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1899 }
1900 else
1901 {
1902 state_ = State_Running;
1903
1904 for (unsigned int i = 0; i < workers_.size(); i++)
1905 {
1906 workers_[i] = new boost::thread(Worker, this);
1907 }
1908 }
1909 }
1910
1911 void Stop()
1912 {
1913 StopInternal();
1914 }
1915
1916 virtual void Schedule(const OrthancStone::IObserver& receiver,
1917 IOracleCommand* command)
1918 {
1919 queue_.Enqueue(new Item(receiver, command));
1920 }
1921 };
1922
1923
1924 class NativeApplicationContext : public IMessageEmitter
1925 {
1926 private:
1927 boost::shared_mutex mutex_;
1928 OrthancStone::MessageBroker broker_;
1929 OrthancStone::IObservable oracleObservable_;
1930
1931 public:
1932 NativeApplicationContext() :
1933 oracleObservable_(broker_)
1934 {
1935 }
1936
1937
1938 virtual void EmitMessage(const OrthancStone::IObserver& observer,
1939 const OrthancStone::IMessage& message)
1940 {
1941 try
1942 {
1943 boost::unique_lock<boost::shared_mutex> lock(mutex_);
1944 oracleObservable_.EmitMessage(observer, message);
1945 }
1946 catch (Orthanc::OrthancException& e)
1947 {
1948 LOG(ERROR) << "Exception while emitting a message: " << e.What();
1949 }
1950 }
1951
1952
1953 class ReaderLock : public boost::noncopyable
1954 {
1955 private:
1956 NativeApplicationContext& that_;
1957 boost::shared_lock<boost::shared_mutex> lock_;
1958
1959 public:
1960 ReaderLock(NativeApplicationContext& that) :
1961 that_(that),
1962 lock_(that.mutex_)
1963 {
1964 }
1965 };
1966
1967
1968 class WriterLock : public boost::noncopyable
1969 {
1970 private:
1971 NativeApplicationContext& that_;
1972 boost::unique_lock<boost::shared_mutex> lock_;
1973
1974 public:
1975 WriterLock(NativeApplicationContext& that) :
1976 that_(that),
1977 lock_(that.mutex_)
1978 {
1979 }
1980
1981 OrthancStone::MessageBroker& GetBroker()
1982 {
1983 return that_.broker_;
1984 }
1985
1986 OrthancStone::IObservable& GetOracleObservable()
1987 {
1988 return that_.oracleObservable_;
1989 }
1990 };
1991 };
1972 } 1992 }
1973 1993
1974 1994
1975 1995
1976 class Toto : public OrthancStone::IObserver 1996 class Toto : public OrthancStone::IObserver
2035 2055
2036 void Run(Refactoring::NativeApplicationContext& context, 2056 void Run(Refactoring::NativeApplicationContext& context,
2037 Refactoring::IOracle& oracle) 2057 Refactoring::IOracle& oracle)
2038 { 2058 {
2039 std::auto_ptr<Toto> toto; 2059 std::auto_ptr<Toto> toto;
2040 std::auto_ptr<Refactoring::AxialVolumeOrthancLoader> loader1, loader2; 2060 std::auto_ptr<Refactoring::VolumeSeriesOrthancLoader> loader1, loader2;
2041 2061
2042 { 2062 {
2043 Refactoring::NativeApplicationContext::WriterLock lock(context); 2063 Refactoring::NativeApplicationContext::WriterLock lock(context);
2044 toto.reset(new Toto(lock.GetOracleObservable())); 2064 toto.reset(new Toto(lock.GetOracleObservable()));
2045 loader1.reset(new Refactoring::AxialVolumeOrthancLoader(oracle, lock.GetOracleObservable())); 2065 loader1.reset(new Refactoring::VolumeSeriesOrthancLoader(oracle, lock.GetOracleObservable()));
2046 loader2.reset(new Refactoring::AxialVolumeOrthancLoader(oracle, lock.GetOracleObservable())); 2066 loader2.reset(new Refactoring::VolumeSeriesOrthancLoader(oracle, lock.GetOracleObservable()));
2047 } 2067 }
2048 2068
2049 if (1) 2069 if (1)
2050 { 2070 {
2051 Json::Value v = Json::objectValue; 2071 Json::Value v = Json::objectValue;