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