comparison Framework/Oracle/WebAssemblyOracle.cpp @ 1245:3d4dc87af04b broker

ParseDicomFromWadoCommand working in wasm
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 07 Jan 2020 11:56:20 +0100
parents b17959d4da06
children c1c83c1fb837
comparison
equal deleted inserted replaced
1244:b17959d4da06 1245:3d4dc87af04b
24 #include "OracleCommandExceptionMessage.h" 24 #include "OracleCommandExceptionMessage.h"
25 #include "SleepOracleCommand.h" 25 #include "SleepOracleCommand.h"
26 26
27 #if ORTHANC_ENABLE_DCMTK == 1 27 #if ORTHANC_ENABLE_DCMTK == 1
28 # include "ParseDicomSuccessMessage.h" 28 # include "ParseDicomSuccessMessage.h"
29 static unsigned int BUCKET_SOP = 1;
29 #endif 30 #endif
30 31
31 #include <Core/OrthancException.h> 32 #include <Core/OrthancException.h>
32 #include <Core/Toolbox.h> 33 #include <Core/Toolbox.h>
33 34
144 const T& GetTypedCommand() const 145 const T& GetTypedCommand() const
145 { 146 {
146 return dynamic_cast<T&>(*command_); 147 return dynamic_cast<T&>(*command_);
147 } 148 }
148 149
150 #if ORTHANC_ENABLE_DCMTK == 1
151 void StoreInCache(const std::string& sopInstanceUid,
152 std::auto_ptr<Orthanc::ParsedDicomFile>& dicom,
153 size_t fileSize)
154 {
155 if (oracle_.dicomCache_.get())
156 {
157 // Store it into the cache for future use
158 oracle_.dicomCache_->Acquire(BUCKET_SOP, sopInstanceUid,
159 dicom.release(), fileSize, true);
160 }
161 }
162 #endif
163
149 static void SuccessCallback(emscripten_fetch_t *fetch) 164 static void SuccessCallback(emscripten_fetch_t *fetch)
150 { 165 {
151 /** 166 /**
152 * Firstly, make a local copy of the fetched information, and 167 * Firstly, make a local copy of the fetched information, and
153 * free data associated with the fetch. 168 * free data associated with the fetch.
165 if (fetch->numBytes > 0) 180 if (fetch->numBytes > 0)
166 { 181 {
167 answer.assign(fetch->data, fetch->numBytes); 182 answer.assign(fetch->data, fetch->numBytes);
168 } 183 }
169 184
185
170 /** 186 /**
171 * TODO - HACK - As of emscripten-1.38.31, the fetch API does 187 * Retrieving the headers of the HTTP answer.
172 * not contain a way to retrieve the HTTP headers of the 188 **/
173 * answer. We make the assumption that the "Content-Type" header 189 HttpHeaders headers;
174 * of the response is the same as the "Accept" header of the 190
175 * query. This should be fixed in future versions of emscripten. 191 #if (__EMSCRIPTEN_major__ < 1 || \
192 (__EMSCRIPTEN_major__ == 1 && __EMSCRIPTEN_minor__ < 38) || \
193 (__EMSCRIPTEN_major__ == 1 && __EMSCRIPTEN_minor__ == 38 && __EMSCRIPTEN_tiny__ < 37))
194 # warning Consider upgrading Emscripten to a version above 1.38.37, incomplete support of Fetch API
195
196 /**
197 * HACK - If emscripten < 1.38.37, the fetch API does not
198 * contain a way to retrieve the HTTP headers of the answer. We
199 * make the assumption that the "Content-Type" header of the
200 * response is the same as the "Accept" header of the
201 * query. This is fixed thanks to the
202 * "emscripten_fetch_get_response_headers()" function that was
203 * added to "fetch.h" at emscripten-1.38.37 on 2019-06-26.
204 *
205 * https://github.com/emscripten-core/emscripten/blob/1.38.37/system/include/emscripten/fetch.h
176 * https://github.com/emscripten-core/emscripten/pull/8486 206 * https://github.com/emscripten-core/emscripten/pull/8486
177 *
178 * TODO - The function "emscripten_fetch_get_response_headers()"
179 * was added to "fetch.h" at emscripten-1.38.37 on 2019-06-26.
180 * https://github.com/emscripten-core/emscripten/blob/1.38.37/system/include/emscripten/fetch.h
181 **/ 207 **/
182
183 HttpHeaders headers;
184 if (fetch->userData != NULL) 208 if (fetch->userData != NULL)
185 { 209 {
186 if (!context->GetExpectedContentType().empty()) 210 if (!context->GetExpectedContentType().empty())
187 { 211 {
188 headers["Content-Type"] = context->GetExpectedContentType(); 212 headers["Content-Type"] = context->GetExpectedContentType();
189 } 213 }
190 } 214 }
215 #else
216 {
217 size_t size = emscripten_fetch_get_response_headers_length(fetch);
218
219 std::string plainHeaders(size + 1, '\0');
220 emscripten_fetch_get_response_headers(fetch, &plainHeaders[0], size + 1);
221
222 std::vector<std::string> tokens;
223 Orthanc::Toolbox::TokenizeString(tokens, plainHeaders, '\n');
224
225 for (size_t i = 0; i < tokens.size(); i++)
226 {
227 size_t p = tokens[i].find(':');
228 if (p != std::string::npos)
229 {
230 std::string key = Orthanc::Toolbox::StripSpaces(tokens[i].substr(0, p));
231 std::string value = Orthanc::Toolbox::StripSpaces(tokens[i].substr(p + 1));
232 headers[key] = value;
233 }
234 }
235 }
236 #endif
191 237
192 LOG(TRACE) << "About to call emscripten_fetch_close"; 238 LOG(TRACE) << "About to call emscripten_fetch_close";
193 emscripten_fetch_close(fetch); 239 emscripten_fetch_close(fetch);
194 LOG(TRACE) << "Successfully called emscripten_fetch_close"; 240 LOG(TRACE) << "Successfully called emscripten_fetch_close";
195 241
240 } 286 }
241 287
242 case IOracleCommand::Type_ParseDicomFromWado: 288 case IOracleCommand::Type_ParseDicomFromWado:
243 { 289 {
244 #if ORTHANC_ENABLE_DCMTK == 1 290 #if ORTHANC_ENABLE_DCMTK == 1
291 const ParseDicomFromWadoCommand& command =
292 context->GetTypedCommand<ParseDicomFromWadoCommand>();
293
245 size_t fileSize; 294 size_t fileSize;
246 std::auto_ptr<Orthanc::ParsedDicomFile> dicom 295 std::auto_ptr<Orthanc::ParsedDicomFile> dicom
247 (ParseDicomSuccessMessage::ParseWadoAnswer(fileSize, answer, headers)); 296 (ParseDicomSuccessMessage::ParseWadoAnswer(fileSize, answer, headers));
248 LOG(WARNING) << "bingo"; 297
298 {
299 ParseDicomSuccessMessage message(command, *dicom, fileSize, true);
300 context->EmitMessage(message);
301 }
302
303 context->StoreInCache(command.GetSopInstanceUid(), dicom, fileSize);
249 #else 304 #else
250 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 305 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
251 #endif 306 #endif
252 break; 307 break;
253 } 308 }
594 void WebAssemblyOracle::Execute(boost::weak_ptr<IObserver> receiver, 649 void WebAssemblyOracle::Execute(boost::weak_ptr<IObserver> receiver,
595 ParseDicomFromWadoCommand* command) 650 ParseDicomFromWadoCommand* command)
596 { 651 {
597 std::auto_ptr<ParseDicomFromWadoCommand> protection(command); 652 std::auto_ptr<ParseDicomFromWadoCommand> protection(command);
598 653
599 // TODO - CACHE 654 #if ORTHANC_ENABLE_DCMTK == 1
600 655 if (dicomCache_.get())
601 656 {
657 ParsedDicomCache::Reader reader(*dicomCache_, BUCKET_SOP, protection->GetSopInstanceUid());
658 if (reader.IsValid() &&
659 reader.HasPixelData())
660 {
661 // Reuse the DICOM file from the cache
662 ParseDicomSuccessMessage message(*protection, reader.GetDicom(),
663 reader.GetFileSize(), reader.HasPixelData());
664 EmitMessage(receiver, message);
665 return;
666 }
667 }
668 #endif
669
602 switch (command->GetRestCommand().GetType()) 670 switch (command->GetRestCommand().GetType())
603 { 671 {
604 case IOracleCommand::Type_Http: 672 case IOracleCommand::Type_Http:
605 { 673 {
606 const HttpCommand& rest = 674 const HttpCommand& rest =
706 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); 774 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
707 } 775 }
708 776
709 return true; 777 return true;
710 } 778 }
779
780
781 void WebAssemblyOracle::SetDicomCacheSize(size_t size)
782 {
783 #if ORTHANC_ENABLE_DCMTK == 1
784 if (size == 0)
785 {
786 dicomCache_.reset();
787 }
788 else
789 {
790 dicomCache_.reset(new ParsedDicomCache(size));
791 }
792 #else
793 LOG(INFO) << "DCMTK support is disabled, the DICOM cache is disabled";
794 #endif
795 }
711 } 796 }