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 }