Mercurial > hg > orthanc
comparison OrthancServer/ServerContext.cpp @ 1433:461e7554bff7
refactoring: LuaScripting
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 30 Jun 2015 15:09:34 +0200 |
parents | d710ea64f0fd |
children | f9cd40166269 |
comparison
equal
deleted
inserted
replaced
1432:0ac74fa21db8 | 1433:461e7554bff7 |
---|---|
32 | 32 |
33 #include "PrecompiledHeadersServer.h" | 33 #include "PrecompiledHeadersServer.h" |
34 #include "ServerContext.h" | 34 #include "ServerContext.h" |
35 | 35 |
36 #include "../Core/HttpServer/FilesystemHttpSender.h" | 36 #include "../Core/HttpServer/FilesystemHttpSender.h" |
37 #include "../Core/Lua/LuaFunctionCall.h" | |
38 #include "FromDcmtkBridge.h" | 37 #include "FromDcmtkBridge.h" |
39 #include "ServerToolbox.h" | 38 #include "ServerToolbox.h" |
40 #include "OrthancInitialization.h" | 39 #include "OrthancInitialization.h" |
41 | 40 |
42 #include <glog/logging.h> | 41 #include <glog/logging.h> |
52 #include "OrthancRestApi/OrthancRestApi.h" | 51 #include "OrthancRestApi/OrthancRestApi.h" |
53 #include "../Plugins/Engine/OrthancPlugins.h" | 52 #include "../Plugins/Engine/OrthancPlugins.h" |
54 | 53 |
55 | 54 |
56 #define ENABLE_DICOM_CACHE 1 | 55 #define ENABLE_DICOM_CACHE 1 |
57 | |
58 static const char* RECEIVED_INSTANCE_FILTER = "ReceivedInstanceFilter"; | |
59 static const char* ON_STORED_INSTANCE = "OnStoredInstance"; | |
60 | 56 |
61 static const size_t DICOM_CACHE_SIZE = 2; | 57 static const size_t DICOM_CACHE_SIZE = 2; |
62 | 58 |
63 /** | 59 /** |
64 * IMPORTANT: We make the assumption that the same instance of | 60 * IMPORTANT: We make the assumption that the same instance of |
75 index_(*this, database), | 71 index_(*this, database), |
76 compressionEnabled_(false), | 72 compressionEnabled_(false), |
77 provider_(*this), | 73 provider_(*this), |
78 dicomCache_(provider_, DICOM_CACHE_SIZE), | 74 dicomCache_(provider_, DICOM_CACHE_SIZE), |
79 scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)), | 75 scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)), |
76 lua_(*this), | |
80 plugins_(NULL), | 77 plugins_(NULL), |
81 pluginsManager_(NULL), | 78 pluginsManager_(NULL), |
82 queryRetrieveArchive_(Configuration::GetGlobalIntegerParameter("QueryRetrieveSize", 10)), | 79 queryRetrieveArchive_(Configuration::GetGlobalIntegerParameter("QueryRetrieveSize", 10)), |
83 defaultLocalAet_(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")) | 80 defaultLocalAet_(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")) |
84 { | 81 { |
85 uint64_t s = Configuration::GetGlobalIntegerParameter("DicomAssociationCloseDelay", 5); // In seconds | 82 uint64_t s = Configuration::GetGlobalIntegerParameter("DicomAssociationCloseDelay", 5); // In seconds |
86 scu_.SetMillisecondsBeforeClose(s * 1000); // Milliseconds are expected here | 83 scu_.SetMillisecondsBeforeClose(s * 1000); // Milliseconds are expected here |
87 | 84 |
88 lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); | 85 listeners_.push_back(ServerListener(lua_, "Lua")); |
89 lua_.SetHttpProxy(Configuration::GetGlobalStringParameter("HttpProxy", "")); | |
90 } | 86 } |
91 | 87 |
92 void ServerContext::SetCompressionEnabled(bool enabled) | 88 void ServerContext::SetCompressionEnabled(bool enabled) |
93 { | 89 { |
94 if (enabled) | 90 if (enabled) |
104 { | 100 { |
105 accessor_.Remove(fileUuid, type); | 101 accessor_.Remove(fileUuid, type); |
106 } | 102 } |
107 | 103 |
108 | 104 |
109 bool ServerContext::ApplyReceivedInstanceFilter(const Json::Value& simplified, | |
110 const std::string& remoteAet) | |
111 { | |
112 LuaContextLocker locker(*this); | |
113 | |
114 if (locker.GetLua().IsExistingFunction(RECEIVED_INSTANCE_FILTER)) | |
115 { | |
116 LuaFunctionCall call(locker.GetLua(), RECEIVED_INSTANCE_FILTER); | |
117 call.PushJson(simplified); | |
118 call.PushString(remoteAet); | |
119 | |
120 if (!call.ExecutePredicate()) | |
121 { | |
122 return false; | |
123 } | |
124 } | |
125 | |
126 return true; | |
127 } | |
128 | |
129 | |
130 static IServerCommand* ParseOperation(ServerContext& context, | |
131 const std::string& operation, | |
132 const Json::Value& parameters) | |
133 { | |
134 if (operation == "delete") | |
135 { | |
136 LOG(INFO) << "Lua script to delete instance " << parameters["Instance"].asString(); | |
137 return new DeleteInstanceCommand(context); | |
138 } | |
139 | |
140 if (operation == "store-scu") | |
141 { | |
142 std::string localAet; | |
143 if (parameters.isMember("LocalAet")) | |
144 { | |
145 localAet = parameters["LocalAet"].asString(); | |
146 } | |
147 else | |
148 { | |
149 localAet = context.GetDefaultLocalApplicationEntityTitle(); | |
150 } | |
151 | |
152 std::string modality = parameters["Modality"].asString(); | |
153 LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString() | |
154 << " to modality " << modality << " using Store-SCU"; | |
155 return new StoreScuCommand(context, localAet, | |
156 Configuration::GetModalityUsingSymbolicName(modality), true); | |
157 } | |
158 | |
159 if (operation == "store-peer") | |
160 { | |
161 std::string peer = parameters["Peer"].asString(); | |
162 LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString() | |
163 << " to peer " << peer << " using HTTP"; | |
164 | |
165 OrthancPeerParameters parameters; | |
166 Configuration::GetOrthancPeer(parameters, peer); | |
167 return new StorePeerCommand(context, parameters, true); | |
168 } | |
169 | |
170 if (operation == "modify") | |
171 { | |
172 LOG(INFO) << "Lua script to modify instance " << parameters["Instance"].asString(); | |
173 DicomModification modification; | |
174 OrthancRestApi::ParseModifyRequest(modification, parameters); | |
175 | |
176 std::auto_ptr<ModifyInstanceCommand> command(new ModifyInstanceCommand(context, modification)); | |
177 return command.release(); | |
178 } | |
179 | |
180 if (operation == "call-system") | |
181 { | |
182 LOG(INFO) << "Lua script to call system command on " << parameters["Instance"].asString(); | |
183 | |
184 const Json::Value& argsIn = parameters["Arguments"]; | |
185 if (argsIn.type() != Json::arrayValue) | |
186 { | |
187 throw OrthancException(ErrorCode_BadParameterType); | |
188 } | |
189 | |
190 std::vector<std::string> args; | |
191 args.reserve(argsIn.size()); | |
192 for (Json::Value::ArrayIndex i = 0; i < argsIn.size(); ++i) | |
193 { | |
194 // http://jsoncpp.sourceforge.net/namespace_json.html#7d654b75c16a57007925868e38212b4e | |
195 switch (argsIn[i].type()) | |
196 { | |
197 case Json::stringValue: | |
198 args.push_back(argsIn[i].asString()); | |
199 break; | |
200 | |
201 case Json::intValue: | |
202 args.push_back(boost::lexical_cast<std::string>(argsIn[i].asInt())); | |
203 break; | |
204 | |
205 case Json::uintValue: | |
206 args.push_back(boost::lexical_cast<std::string>(argsIn[i].asUInt())); | |
207 break; | |
208 | |
209 case Json::realValue: | |
210 args.push_back(boost::lexical_cast<std::string>(argsIn[i].asFloat())); | |
211 break; | |
212 | |
213 default: | |
214 throw OrthancException(ErrorCode_BadParameterType); | |
215 } | |
216 } | |
217 | |
218 return new CallSystemCommand(context, parameters["Command"].asString(), args); | |
219 } | |
220 | |
221 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
222 } | |
223 | |
224 | |
225 void ServerContext::ApplyLuaOnStoredInstance(const std::string& instanceId, | |
226 const Json::Value& simplifiedDicom, | |
227 const Json::Value& metadata, | |
228 const std::string& remoteAet, | |
229 const std::string& calledAet) | |
230 { | |
231 LuaContextLocker locker(*this); | |
232 | |
233 if (locker.GetLua().IsExistingFunction(ON_STORED_INSTANCE)) | |
234 { | |
235 locker.GetLua().Execute("_InitializeJob()"); | |
236 | |
237 LuaFunctionCall call(locker.GetLua(), ON_STORED_INSTANCE); | |
238 call.PushString(instanceId); | |
239 call.PushJson(simplifiedDicom); | |
240 call.PushJson(metadata); | |
241 call.PushJson(remoteAet); | |
242 call.PushJson(calledAet); | |
243 call.Execute(); | |
244 | |
245 Json::Value operations; | |
246 LuaFunctionCall call2(locker.GetLua(), "_AccessJob"); | |
247 call2.ExecuteToJson(operations); | |
248 | |
249 if (operations.type() != Json::arrayValue) | |
250 { | |
251 throw OrthancException(ErrorCode_InternalError); | |
252 } | |
253 | |
254 ServerJob job; | |
255 ServerCommandInstance* previousCommand = NULL; | |
256 | |
257 for (Json::Value::ArrayIndex i = 0; i < operations.size(); ++i) | |
258 { | |
259 if (operations[i].type() != Json::objectValue || | |
260 !operations[i].isMember("Operation")) | |
261 { | |
262 throw OrthancException(ErrorCode_InternalError); | |
263 } | |
264 | |
265 const Json::Value& parameters = operations[i]; | |
266 std::string operation = parameters["Operation"].asString(); | |
267 | |
268 ServerCommandInstance& command = job.AddCommand(ParseOperation(*this, operation, operations[i])); | |
269 | |
270 if (!parameters.isMember("Instance")) | |
271 { | |
272 throw OrthancException(ErrorCode_InternalError); | |
273 } | |
274 | |
275 std::string instance = parameters["Instance"].asString(); | |
276 if (instance.empty()) | |
277 { | |
278 previousCommand->ConnectOutput(command); | |
279 } | |
280 else | |
281 { | |
282 command.AddInput(instance); | |
283 } | |
284 | |
285 previousCommand = &command; | |
286 } | |
287 | |
288 job.SetDescription(std::string("Lua script: ") + ON_STORED_INSTANCE); | |
289 scheduler_.Submit(job); | |
290 } | |
291 } | |
292 | |
293 | |
294 StoreStatus ServerContext::Store(std::string& resultPublicId, | 105 StoreStatus ServerContext::Store(std::string& resultPublicId, |
295 DicomInstanceToStore& dicom) | 106 DicomInstanceToStore& dicom) |
296 { | 107 { |
297 try | 108 try |
298 { | 109 { |
301 | 112 |
302 Json::Value simplified; | 113 Json::Value simplified; |
303 SimplifyTags(simplified, dicom.GetJson()); | 114 SimplifyTags(simplified, dicom.GetJson()); |
304 | 115 |
305 // Test if the instance must be filtered out | 116 // Test if the instance must be filtered out |
306 if (!ApplyReceivedInstanceFilter(simplified, dicom.GetRemoteAet())) | 117 bool accepted = true; |
118 | |
119 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it) | |
120 { | |
121 try | |
122 { | |
123 if (!it->GetListener().FilterIncomingInstance(simplified, dicom.GetRemoteAet())) | |
124 { | |
125 accepted = false; | |
126 break; | |
127 } | |
128 } | |
129 catch (OrthancException& e) | |
130 { | |
131 LOG(ERROR) << "Error in the " << it->GetDescription() | |
132 << " callback while receiving an instance: " << e.What(); | |
133 throw; | |
134 } | |
135 } | |
136 | |
137 if (!accepted) | |
307 { | 138 { |
308 LOG(INFO) << "An incoming instance has been discarded by the filter"; | 139 LOG(INFO) << "An incoming instance has been discarded by the filter"; |
309 return StoreStatus_FilteredOut; | 140 return StoreStatus_FilteredOut; |
310 } | 141 } |
311 | 142 |
328 typedef std::map<MetadataType, std::string> InstanceMetadata; | 159 typedef std::map<MetadataType, std::string> InstanceMetadata; |
329 InstanceMetadata instanceMetadata; | 160 InstanceMetadata instanceMetadata; |
330 StoreStatus status = index_.Store(instanceMetadata, dicom.GetSummary(), attachments, | 161 StoreStatus status = index_.Store(instanceMetadata, dicom.GetSummary(), attachments, |
331 dicom.GetRemoteAet(), dicom.GetMetadata()); | 162 dicom.GetRemoteAet(), dicom.GetMetadata()); |
332 | 163 |
164 // Only keep the metadata for the "instance" level | |
333 dicom.GetMetadata().clear(); | 165 dicom.GetMetadata().clear(); |
334 | 166 |
335 for (InstanceMetadata::const_iterator it = instanceMetadata.begin(); | 167 for (InstanceMetadata::const_iterator it = instanceMetadata.begin(); |
336 it != instanceMetadata.end(); ++it) | 168 it != instanceMetadata.end(); ++it) |
337 { | 169 { |
365 } | 197 } |
366 | 198 |
367 if (status == StoreStatus_Success || | 199 if (status == StoreStatus_Success || |
368 status == StoreStatus_AlreadyStored) | 200 status == StoreStatus_AlreadyStored) |
369 { | 201 { |
370 Json::Value metadata = Json::objectValue; | 202 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it) |
371 for (std::map<MetadataType, std::string>::const_iterator | |
372 it = instanceMetadata.begin(); | |
373 it != instanceMetadata.end(); ++it) | |
374 { | |
375 metadata[EnumerationToString(it->first)] = it->second; | |
376 } | |
377 | |
378 try | |
379 { | |
380 ApplyLuaOnStoredInstance(resultPublicId, simplified, metadata, | |
381 dicom.GetRemoteAet(), dicom.GetCalledAet()); | |
382 } | |
383 catch (OrthancException& e) | |
384 { | |
385 LOG(ERROR) << "Error in " << ON_STORED_INSTANCE << " callback (Lua): " << e.What(); | |
386 } | |
387 | |
388 if (plugins_ != NULL) | |
389 { | 203 { |
390 try | 204 try |
391 { | 205 { |
392 plugins_->SignalStoredInstance(dicom, resultPublicId); | 206 it->GetListener().SignalStoredInstance(resultPublicId, dicom, simplified); |
393 } | 207 } |
394 catch (OrthancException& e) | 208 catch (OrthancException& e) |
395 { | 209 { |
396 LOG(ERROR) << "Error in " << ON_STORED_INSTANCE << " callback (plugins): " << e.What(); | 210 LOG(ERROR) << "Error in the " << it->GetDescription() |
211 << " callback while receiving an instance: " << e.What(); | |
397 } | 212 } |
398 } | 213 } |
399 } | 214 } |
400 | 215 |
401 return status; | 216 return status; |
544 } | 359 } |
545 | 360 |
546 | 361 |
547 void ServerContext::SignalChange(const ServerIndexChange& change) | 362 void ServerContext::SignalChange(const ServerIndexChange& change) |
548 { | 363 { |
549 if (plugins_ != NULL) | 364 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it) |
550 { | 365 { |
551 try | 366 try |
552 { | 367 { |
553 plugins_->SignalChange(change); | 368 it->GetListener().SignalChange(change); |
554 } | 369 } |
555 catch (OrthancException& e) | 370 catch (OrthancException& e) |
556 { | 371 { |
557 LOG(ERROR) << "Error in OnChangeCallback (plugins): " << e.What(); | 372 LOG(ERROR) << "Error in the " << it->GetDescription() |
373 << " callback while signaling a change: " << e.What(); | |
558 } | 374 } |
559 } | 375 } |
560 } | 376 } |
561 | 377 |
562 | 378 |