Mercurial > hg > orthanc
comparison OrthancServer/ServerContext.cpp @ 1020:1fc112c4b832
integration lua-scripting->mainline
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 10 Jul 2014 11:38:46 +0200 |
parents | 160dfe770618 |
children | 921532f67770 |
comparison
equal
deleted
inserted
replaced
1016:f4bbf13572cd | 1020:1fc112c4b832 |
---|---|
41 | 41 |
42 #include <glog/logging.h> | 42 #include <glog/logging.h> |
43 #include <EmbeddedResources.h> | 43 #include <EmbeddedResources.h> |
44 #include <dcmtk/dcmdata/dcfilefo.h> | 44 #include <dcmtk/dcmdata/dcfilefo.h> |
45 | 45 |
46 | |
47 #include "Scheduler/DeleteInstanceCommand.h" | |
48 #include "Scheduler/ModifyInstanceCommand.h" | |
49 #include "Scheduler/StoreScuCommand.h" | |
50 #include "Scheduler/StorePeerCommand.h" | |
51 #include "OrthancRestApi/OrthancRestApi.h" | |
52 | |
53 | |
54 | |
46 #define ENABLE_DICOM_CACHE 1 | 55 #define ENABLE_DICOM_CACHE 1 |
47 | 56 |
48 static const char* RECEIVED_INSTANCE_FILTER = "ReceivedInstanceFilter"; | 57 static const char* RECEIVED_INSTANCE_FILTER = "ReceivedInstanceFilter"; |
58 static const char* ON_STORED_INSTANCE = "OnStoredInstance"; | |
49 | 59 |
50 static const size_t DICOM_CACHE_SIZE = 2; | 60 static const size_t DICOM_CACHE_SIZE = 2; |
51 | 61 |
52 /** | 62 /** |
53 * IMPORTANT: We make the assumption that the same instance of | 63 * IMPORTANT: We make the assumption that the same instance of |
65 storage_(storagePath.string()), | 75 storage_(storagePath.string()), |
66 index_(*this, indexPath.string()), | 76 index_(*this, indexPath.string()), |
67 accessor_(storage_), | 77 accessor_(storage_), |
68 compressionEnabled_(false), | 78 compressionEnabled_(false), |
69 provider_(*this), | 79 provider_(*this), |
70 dicomCache_(provider_, DICOM_CACHE_SIZE) | 80 dicomCache_(provider_, DICOM_CACHE_SIZE), |
81 scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)) | |
71 { | 82 { |
72 scu_.SetLocalApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")); | 83 scu_.SetLocalApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")); |
73 //scu_.SetMillisecondsBeforeClose(1); // The connection is always released | 84 //scu_.SetMillisecondsBeforeClose(1); // The connection is always released |
74 | 85 |
75 lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); | 86 lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); |
88 void ServerContext::RemoveFile(const std::string& fileUuid) | 99 void ServerContext::RemoveFile(const std::string& fileUuid) |
89 { | 100 { |
90 storage_.Remove(fileUuid); | 101 storage_.Remove(fileUuid); |
91 } | 102 } |
92 | 103 |
93 StoreStatus ServerContext::Store(const char* dicomInstance, | 104 |
94 size_t dicomSize, | 105 bool ServerContext::ApplyReceivedInstanceFilter(const Json::Value& simplified, |
95 const DicomMap& dicomSummary, | 106 const std::string& remoteAet) |
96 const Json::Value& dicomJson, | 107 { |
97 const std::string& remoteAet) | 108 LuaContextLocker locker(*this); |
98 { | 109 |
99 // Test if the instance must be filtered out | 110 if (locker.GetLua().IsExistingFunction(RECEIVED_INSTANCE_FILTER)) |
100 if (lua_.IsExistingFunction(RECEIVED_INSTANCE_FILTER)) | 111 { |
101 { | 112 LuaFunctionCall call(locker.GetLua(), RECEIVED_INSTANCE_FILTER); |
113 call.PushJson(simplified); | |
114 call.PushString(remoteAet); | |
115 | |
116 if (!call.ExecutePredicate()) | |
117 { | |
118 return false; | |
119 } | |
120 } | |
121 | |
122 return true; | |
123 } | |
124 | |
125 | |
126 static IServerCommand* ParseOperation(ServerContext& context, | |
127 const std::string& operation, | |
128 const Json::Value& parameters) | |
129 { | |
130 if (operation == "delete") | |
131 { | |
132 LOG(INFO) << "Lua script to delete instance " << parameters["Instance"].asString(); | |
133 return new DeleteInstanceCommand(context); | |
134 } | |
135 | |
136 if (operation == "store-scu") | |
137 { | |
138 std::string modality = parameters["Modality"].asString(); | |
139 LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString() | |
140 << " to modality " << modality << " using Store-SCU"; | |
141 return new StoreScuCommand(context, Configuration::GetModalityUsingSymbolicName(modality)); | |
142 } | |
143 | |
144 if (operation == "store-peer") | |
145 { | |
146 std::string peer = parameters["Peer"].asString(); | |
147 LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString() | |
148 << " to peer " << peer << " using HTTP"; | |
149 | |
150 OrthancPeerParameters parameters; | |
151 Configuration::GetOrthancPeer(parameters, peer); | |
152 return new StorePeerCommand(context, parameters); | |
153 } | |
154 | |
155 if (operation == "modify") | |
156 { | |
157 LOG(INFO) << "Lua script to modify instance " << parameters["Instance"].asString(); | |
158 std::auto_ptr<ModifyInstanceCommand> command(new ModifyInstanceCommand(context)); | |
159 OrthancRestApi::ParseModifyRequest(command->GetModification(), parameters); | |
160 return command.release(); | |
161 } | |
162 | |
163 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
164 } | |
165 | |
166 | |
167 void ServerContext::ApplyOnStoredInstance(const std::string& instanceId, | |
168 const Json::Value& simplifiedDicom, | |
169 const Json::Value& metadata) | |
170 { | |
171 LuaContextLocker locker(*this); | |
172 | |
173 if (locker.GetLua().IsExistingFunction(ON_STORED_INSTANCE)) | |
174 { | |
175 locker.GetLua().Execute("_InitializeJob()"); | |
176 | |
177 LuaFunctionCall call(locker.GetLua(), ON_STORED_INSTANCE); | |
178 call.PushString(instanceId); | |
179 call.PushJson(simplifiedDicom); | |
180 call.PushJson(metadata); | |
181 call.Execute(); | |
182 | |
183 Json::Value operations; | |
184 LuaFunctionCall call2(locker.GetLua(), "_AccessJob"); | |
185 call2.ExecuteToJson(operations); | |
186 | |
187 if (operations.type() != Json::arrayValue) | |
188 { | |
189 throw OrthancException(ErrorCode_InternalError); | |
190 } | |
191 | |
192 ServerJob job; | |
193 ServerCommandInstance* previousCommand = NULL; | |
194 | |
195 for (Json::Value::ArrayIndex i = 0; i < operations.size(); ++i) | |
196 { | |
197 if (operations[i].type() != Json::objectValue || | |
198 !operations[i].isMember("Operation")) | |
199 { | |
200 throw OrthancException(ErrorCode_InternalError); | |
201 } | |
202 | |
203 const Json::Value& parameters = operations[i]; | |
204 std::string operation = parameters["Operation"].asString(); | |
205 | |
206 ServerCommandInstance& command = job.AddCommand(ParseOperation(*this, operation, operations[i])); | |
207 | |
208 if (!parameters.isMember("Instance")) | |
209 { | |
210 throw OrthancException(ErrorCode_InternalError); | |
211 } | |
212 | |
213 std::string instance = parameters["Instance"].asString(); | |
214 if (instance.empty()) | |
215 { | |
216 previousCommand->ConnectOutput(command); | |
217 } | |
218 else | |
219 { | |
220 command.AddInput(instance); | |
221 } | |
222 | |
223 previousCommand = &command; | |
224 } | |
225 | |
226 job.SetDescription(std::string("Lua script: ") + ON_STORED_INSTANCE); | |
227 scheduler_.Submit(job); | |
228 } | |
229 } | |
230 | |
231 | |
232 StoreStatus ServerContext::Store(std::string& resultPublicId, | |
233 DicomInstanceToStore& dicom) | |
234 { | |
235 try | |
236 { | |
237 DicomInstanceHasher hasher(dicom.GetSummary()); | |
238 resultPublicId = hasher.HashInstance(); | |
239 | |
102 Json::Value simplified; | 240 Json::Value simplified; |
103 SimplifyTags(simplified, dicomJson); | 241 SimplifyTags(simplified, dicom.GetJson()); |
104 | 242 |
105 LuaFunctionCall call(lua_, RECEIVED_INSTANCE_FILTER); | 243 // Test if the instance must be filtered out |
106 call.PushJSON(simplified); | 244 if (!ApplyReceivedInstanceFilter(simplified, dicom.GetRemoteAet())) |
107 call.PushString(remoteAet); | |
108 | |
109 if (!call.ExecutePredicate()) | |
110 { | 245 { |
111 LOG(INFO) << "An incoming instance has been discarded by the filter"; | 246 LOG(INFO) << "An incoming instance has been discarded by the filter"; |
112 return StoreStatus_FilteredOut; | 247 return StoreStatus_FilteredOut; |
113 } | 248 } |
114 } | 249 |
115 | 250 if (compressionEnabled_) |
116 if (compressionEnabled_) | 251 { |
117 { | 252 accessor_.SetCompressionForNextOperations(CompressionType_Zlib); |
118 accessor_.SetCompressionForNextOperations(CompressionType_Zlib); | 253 } |
119 } | 254 else |
120 else | 255 { |
121 { | 256 accessor_.SetCompressionForNextOperations(CompressionType_None); |
122 accessor_.SetCompressionForNextOperations(CompressionType_None); | 257 } |
123 } | 258 |
124 | 259 FileInfo dicomInfo = accessor_.Write(dicom.GetBufferData(), dicom.GetBufferSize(), FileContentType_Dicom); |
125 FileInfo dicomInfo = accessor_.Write(dicomInstance, dicomSize, FileContentType_Dicom); | 260 FileInfo jsonInfo = accessor_.Write(dicom.GetJson().toStyledString(), FileContentType_DicomAsJson); |
126 FileInfo jsonInfo = accessor_.Write(dicomJson.toStyledString(), FileContentType_DicomAsJson); | 261 |
127 | 262 ServerIndex::Attachments attachments; |
128 ServerIndex::Attachments attachments; | 263 attachments.push_back(dicomInfo); |
129 attachments.push_back(dicomInfo); | 264 attachments.push_back(jsonInfo); |
130 attachments.push_back(jsonInfo); | 265 |
131 | 266 std::map<MetadataType, std::string> instanceMetadata; |
132 StoreStatus status = index_.Store(dicomSummary, attachments, remoteAet); | 267 StoreStatus status = index_.Store(instanceMetadata, dicom.GetSummary(), attachments, |
133 | 268 dicom.GetRemoteAet(), dicom.GetMetadata()); |
134 if (status != StoreStatus_Success) | 269 |
135 { | 270 if (status != StoreStatus_Success) |
136 storage_.Remove(dicomInfo.GetUuid()); | 271 { |
137 storage_.Remove(jsonInfo.GetUuid()); | 272 storage_.Remove(dicomInfo.GetUuid()); |
138 } | 273 storage_.Remove(jsonInfo.GetUuid()); |
139 | 274 } |
140 switch (status) | 275 |
141 { | 276 switch (status) |
142 case StoreStatus_Success: | 277 { |
143 LOG(INFO) << "New instance stored"; | 278 case StoreStatus_Success: |
144 break; | 279 LOG(INFO) << "New instance stored"; |
145 | 280 break; |
146 case StoreStatus_AlreadyStored: | 281 |
147 LOG(INFO) << "Already stored"; | 282 case StoreStatus_AlreadyStored: |
148 break; | 283 LOG(INFO) << "Already stored"; |
149 | 284 break; |
150 case StoreStatus_Failure: | 285 |
151 LOG(ERROR) << "Store failure"; | 286 case StoreStatus_Failure: |
152 break; | 287 LOG(ERROR) << "Store failure"; |
153 | 288 break; |
154 default: | 289 |
155 // This should never happen | 290 default: |
156 break; | 291 // This should never happen |
157 } | 292 break; |
158 | 293 } |
159 return status; | 294 |
160 } | 295 if (status == StoreStatus_Success || |
161 | 296 status == StoreStatus_AlreadyStored) |
162 | 297 { |
298 Json::Value metadata = Json::objectValue; | |
299 for (std::map<MetadataType, std::string>::const_iterator | |
300 it = instanceMetadata.begin(); | |
301 it != instanceMetadata.end(); ++it) | |
302 { | |
303 metadata[EnumerationToString(it->first)] = it->second; | |
304 } | |
305 | |
306 try | |
307 { | |
308 ApplyOnStoredInstance(resultPublicId, simplified, metadata); | |
309 } | |
310 catch (OrthancException& e) | |
311 { | |
312 LOG(ERROR) << "Error in OnStoredInstance callback (Lua): " << e.What(); | |
313 } | |
314 } | |
315 | |
316 return status; | |
317 } | |
318 catch (OrthancException& e) | |
319 { | |
320 if (e.GetErrorCode() == ErrorCode_InexistentTag) | |
321 { | |
322 LogMissingRequiredTag(dicom.GetSummary()); | |
323 } | |
324 | |
325 throw; | |
326 } | |
327 } | |
328 | |
329 | |
330 | |
163 void ServerContext::AnswerDicomFile(RestApiOutput& output, | 331 void ServerContext::AnswerDicomFile(RestApiOutput& output, |
164 const std::string& instancePublicId, | 332 const std::string& instancePublicId, |
165 FileContentType content) | 333 FileContentType content) |
166 { | 334 { |
167 FileInfo attachment; | 335 FileInfo attachment; |
247 that_.dicomCacheMutex_.unlock(); | 415 that_.dicomCacheMutex_.unlock(); |
248 #endif | 416 #endif |
249 } | 417 } |
250 | 418 |
251 | 419 |
252 static DcmFileFormat& GetDicom(ParsedDicomFile& file) | |
253 { | |
254 return *reinterpret_cast<DcmFileFormat*>(file.GetDcmtkObject()); | |
255 } | |
256 | |
257 | |
258 StoreStatus ServerContext::Store(std::string& resultPublicId, | |
259 ParsedDicomFile& dicomInstance, | |
260 const char* dicomBuffer, | |
261 size_t dicomSize) | |
262 { | |
263 DicomMap dicomSummary; | |
264 FromDcmtkBridge::Convert(dicomSummary, *GetDicom(dicomInstance).getDataset()); | |
265 | |
266 try | |
267 { | |
268 DicomInstanceHasher hasher(dicomSummary); | |
269 resultPublicId = hasher.HashInstance(); | |
270 | |
271 Json::Value dicomJson; | |
272 FromDcmtkBridge::ToJson(dicomJson, *GetDicom(dicomInstance).getDataset()); | |
273 | |
274 StoreStatus status = StoreStatus_Failure; | |
275 if (dicomSize > 0) | |
276 { | |
277 status = Store(dicomBuffer, dicomSize, dicomSummary, dicomJson, ""); | |
278 } | |
279 | |
280 return status; | |
281 } | |
282 catch (OrthancException& e) | |
283 { | |
284 if (e.GetErrorCode() == ErrorCode_InexistentTag) | |
285 { | |
286 LogMissingRequiredTag(dicomSummary); | |
287 } | |
288 | |
289 throw; | |
290 } | |
291 } | |
292 | |
293 | |
294 StoreStatus ServerContext::Store(std::string& resultPublicId, | |
295 ParsedDicomFile& dicomInstance) | |
296 { | |
297 std::string buffer; | |
298 if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer, GetDicom(dicomInstance).getDataset())) | |
299 { | |
300 throw OrthancException(ErrorCode_InternalError); | |
301 } | |
302 | |
303 if (buffer.size() == 0) | |
304 return Store(resultPublicId, dicomInstance, NULL, 0); | |
305 else | |
306 return Store(resultPublicId, dicomInstance, &buffer[0], buffer.size()); | |
307 } | |
308 | |
309 | |
310 StoreStatus ServerContext::Store(std::string& resultPublicId, | |
311 const char* dicomBuffer, | |
312 size_t dicomSize) | |
313 { | |
314 ParsedDicomFile dicom(dicomBuffer, dicomSize); | |
315 return Store(resultPublicId, dicom, dicomBuffer, dicomSize); | |
316 } | |
317 | |
318 | |
319 StoreStatus ServerContext::Store(std::string& resultPublicId, | |
320 const std::string& dicomContent) | |
321 { | |
322 if (dicomContent.size() == 0) | |
323 { | |
324 return Store(resultPublicId, NULL, 0); | |
325 } | |
326 else | |
327 { | |
328 return Store(resultPublicId, &dicomContent[0], dicomContent.size()); | |
329 } | |
330 } | |
331 | |
332 | |
333 void ServerContext::SetStoreMD5ForAttachments(bool storeMD5) | 420 void ServerContext::SetStoreMD5ForAttachments(bool storeMD5) |
334 { | 421 { |
335 LOG(INFO) << "Storing MD5 for attachments: " << (storeMD5 ? "yes" : "no"); | 422 LOG(INFO) << "Storing MD5 for attachments: " << (storeMD5 ? "yes" : "no"); |
336 accessor_.SetStoreMD5(storeMD5); | 423 accessor_.SetStoreMD5(storeMD5); |
337 } | 424 } |