comparison OrthancServer/Sources/LuaScripting.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents OrthancServer/LuaScripting.cpp@058b5ade8acd
children 05b8fd21089c
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34 #include "PrecompiledHeadersServer.h"
35 #include "LuaScripting.h"
36
37 #include "OrthancConfiguration.h"
38 #include "OrthancRestApi/OrthancRestApi.h"
39 #include "ServerContext.h"
40
41 #include "../Core/HttpServer/StringHttpOutput.h"
42 #include "../Core/Logging.h"
43 #include "../Core/Lua/LuaFunctionCall.h"
44
45 #include <OrthancServerResources.h>
46
47
48 namespace Orthanc
49 {
50 class LuaScripting::IEvent : public IDynamicObject
51 {
52 public:
53 virtual void Apply(LuaScripting& lock) = 0;
54 };
55
56
57 class LuaScripting::OnStoredInstanceEvent : public LuaScripting::IEvent
58 {
59 private:
60 std::string instanceId_;
61 Json::Value simplifiedTags_;
62 Json::Value metadata_;
63 Json::Value origin_;
64
65 public:
66 OnStoredInstanceEvent(const std::string& instanceId,
67 const Json::Value& simplifiedTags,
68 const Json::Value& metadata,
69 const DicomInstanceToStore& instance) :
70 instanceId_(instanceId),
71 simplifiedTags_(simplifiedTags),
72 metadata_(metadata)
73 {
74 instance.GetOrigin().Format(origin_);
75 }
76
77 virtual void Apply(LuaScripting& that)
78 {
79 static const char* NAME = "OnStoredInstance";
80
81 LuaScripting::Lock lock(that);
82
83 if (lock.GetLua().IsExistingFunction(NAME))
84 {
85 that.InitializeJob();
86
87 LuaFunctionCall call(lock.GetLua(), NAME);
88 call.PushString(instanceId_);
89 call.PushJson(simplifiedTags_);
90 call.PushJson(metadata_);
91 call.PushJson(origin_);
92 call.Execute();
93
94 that.SubmitJob();
95 }
96 }
97 };
98
99
100 class LuaScripting::ExecuteEvent : public LuaScripting::IEvent
101 {
102 private:
103 std::string command_;
104
105 public:
106 ExecuteEvent(const std::string& command) :
107 command_(command)
108 {
109 }
110
111 virtual void Apply(LuaScripting& that)
112 {
113 LuaScripting::Lock lock(that);
114
115 if (lock.GetLua().IsExistingFunction(command_.c_str()))
116 {
117 LuaFunctionCall call(lock.GetLua(), command_.c_str());
118 call.Execute();
119 }
120 }
121 };
122
123
124 class LuaScripting::StableResourceEvent : public LuaScripting::IEvent
125 {
126 private:
127 ServerIndexChange change_;
128
129 public:
130 StableResourceEvent(const ServerIndexChange& change) :
131 change_(change)
132 {
133 }
134
135 virtual void Apply(LuaScripting& that)
136 {
137 const char* name;
138
139 switch (change_.GetChangeType())
140 {
141 case ChangeType_StablePatient:
142 name = "OnStablePatient";
143 break;
144
145 case ChangeType_StableStudy:
146 name = "OnStableStudy";
147 break;
148
149 case ChangeType_StableSeries:
150 name = "OnStableSeries";
151 break;
152
153 default:
154 throw OrthancException(ErrorCode_InternalError);
155 }
156
157 {
158 // Avoid unnecessary calls to the database if there's no Lua callback
159 LuaScripting::Lock lock(that);
160
161 if (!lock.GetLua().IsExistingFunction(name))
162 {
163 return;
164 }
165 }
166
167 Json::Value tags;
168
169 if (that.context_.GetIndex().LookupResource(tags, change_.GetPublicId(), change_.GetResourceType()))
170 {
171 std::map<MetadataType, std::string> metadata;
172 that.context_.GetIndex().GetAllMetadata(metadata, change_.GetPublicId());
173
174 Json::Value formattedMetadata = Json::objectValue;
175
176 for (std::map<MetadataType, std::string>::const_iterator
177 it = metadata.begin(); it != metadata.end(); ++it)
178 {
179 std::string key = EnumerationToString(it->first);
180 formattedMetadata[key] = it->second;
181 }
182
183 {
184 LuaScripting::Lock lock(that);
185
186 if (lock.GetLua().IsExistingFunction(name))
187 {
188 that.InitializeJob();
189
190 LuaFunctionCall call(lock.GetLua(), name);
191 call.PushString(change_.GetPublicId());
192 call.PushJson(tags["MainDicomTags"]);
193 call.PushJson(formattedMetadata);
194 call.Execute();
195
196 that.SubmitJob();
197 }
198 }
199 }
200 }
201 };
202
203
204 class LuaScripting::JobEvent : public LuaScripting::IEvent
205 {
206 public:
207 enum Type
208 {
209 Type_Failure,
210 Type_Submitted,
211 Type_Success
212 };
213
214 private:
215 Type type_;
216 std::string jobId_;
217
218 public:
219 JobEvent(Type type,
220 const std::string& jobId) :
221 type_(type),
222 jobId_(jobId)
223 {
224 }
225
226 virtual void Apply(LuaScripting& that)
227 {
228 std::string functionName;
229
230 switch (type_)
231 {
232 case Type_Failure:
233 functionName = "OnJobFailure";
234 break;
235
236 case Type_Submitted:
237 functionName = "OnJobSubmitted";
238 break;
239
240 case Type_Success:
241 functionName = "OnJobSuccess";
242 break;
243
244 default:
245 throw OrthancException(ErrorCode_InternalError);
246 }
247
248 {
249 LuaScripting::Lock lock(that);
250
251 if (lock.GetLua().IsExistingFunction(functionName.c_str()))
252 {
253 LuaFunctionCall call(lock.GetLua(), functionName.c_str());
254 call.PushString(jobId_);
255 call.Execute();
256 }
257 }
258 }
259 };
260
261
262 class LuaScripting::DeleteEvent : public LuaScripting::IEvent
263 {
264 private:
265 ResourceType level_;
266 std::string publicId_;
267
268 public:
269 DeleteEvent(ResourceType level,
270 const std::string& publicId) :
271 level_(level),
272 publicId_(publicId)
273 {
274 }
275
276 virtual void Apply(LuaScripting& that)
277 {
278 std::string functionName;
279
280 switch (level_)
281 {
282 case ResourceType_Patient:
283 functionName = "OnDeletedPatient";
284 break;
285
286 case ResourceType_Study:
287 functionName = "OnDeletedStudy";
288 break;
289
290 case ResourceType_Series:
291 functionName = "OnDeletedSeries";
292 break;
293
294 case ResourceType_Instance:
295 functionName = "OnDeletedInstance";
296 break;
297
298 default:
299 throw OrthancException(ErrorCode_InternalError);
300 }
301
302 {
303 LuaScripting::Lock lock(that);
304
305 if (lock.GetLua().IsExistingFunction(functionName.c_str()))
306 {
307 LuaFunctionCall call(lock.GetLua(), functionName.c_str());
308 call.PushString(publicId_);
309 call.Execute();
310 }
311 }
312 }
313 };
314
315
316 class LuaScripting::UpdateEvent : public LuaScripting::IEvent
317 {
318 private:
319 ResourceType level_;
320 std::string publicId_;
321
322 public:
323 UpdateEvent(ResourceType level,
324 const std::string& publicId) :
325 level_(level),
326 publicId_(publicId)
327 {
328 }
329
330 virtual void Apply(LuaScripting& that)
331 {
332 std::string functionName;
333
334 switch (level_)
335 {
336 case ResourceType_Patient:
337 functionName = "OnUpdatedPatient";
338 break;
339
340 case ResourceType_Study:
341 functionName = "OnUpdatedStudy";
342 break;
343
344 case ResourceType_Series:
345 functionName = "OnUpdatedSeries";
346 break;
347
348 case ResourceType_Instance:
349 functionName = "OnUpdatedInstance";
350 break;
351
352 default:
353 throw OrthancException(ErrorCode_InternalError);
354 }
355
356 {
357 LuaScripting::Lock lock(that);
358
359 if (lock.GetLua().IsExistingFunction(functionName.c_str()))
360 {
361 LuaFunctionCall call(lock.GetLua(), functionName.c_str());
362 call.PushString(publicId_);
363 call.Execute();
364 }
365 }
366 }
367 };
368
369
370 ServerContext* LuaScripting::GetServerContext(lua_State *state)
371 {
372 const void* value = LuaContext::GetGlobalVariable(state, "_ServerContext");
373 return const_cast<ServerContext*>(reinterpret_cast<const ServerContext*>(value));
374 }
375
376
377 // Syntax in Lua: RestApiGet(uri, builtin)
378 int LuaScripting::RestApiGet(lua_State *state)
379 {
380 ServerContext* serverContext = GetServerContext(state);
381 if (serverContext == NULL)
382 {
383 LOG(ERROR) << "Lua: The Orthanc API is unavailable";
384 lua_pushnil(state);
385 return 1;
386 }
387
388 // Check the types of the arguments
389 int nArgs = lua_gettop(state);
390 if (nArgs < 1 || nArgs > 3 ||
391 !lua_isstring(state, 1) || // URI
392 (nArgs >= 2 && !lua_isboolean(state, 2))) // Restrict to built-in API?
393 {
394 LOG(ERROR) << "Lua: Bad parameters to RestApiGet()";
395 lua_pushnil(state);
396 return 1;
397 }
398
399 const char* uri = lua_tostring(state, 1);
400 bool builtin = (nArgs == 2 ? lua_toboolean(state, 2) != 0 : false);
401
402 std::map<std::string, std::string> headers;
403 LuaContext::GetDictionaryArgument(headers, state, 3, true /* HTTP header key to lower case */);
404
405 try
406 {
407 std::string result;
408 if (HttpToolbox::SimpleGet(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin),
409 RequestOrigin_Lua, uri, headers))
410 {
411 lua_pushlstring(state, result.c_str(), result.size());
412 return 1;
413 }
414 }
415 catch (OrthancException& e)
416 {
417 LOG(ERROR) << "Lua: " << e.What();
418 }
419
420 LOG(ERROR) << "Lua: Error in RestApiGet() for URI: " << uri;
421 lua_pushnil(state);
422 return 1;
423 }
424
425
426 int LuaScripting::RestApiPostOrPut(lua_State *state,
427 bool isPost)
428 {
429 ServerContext* serverContext = GetServerContext(state);
430 if (serverContext == NULL)
431 {
432 LOG(ERROR) << "Lua: The Orthanc API is unavailable";
433 lua_pushnil(state);
434 return 1;
435 }
436
437 // Check the types of the arguments
438 int nArgs = lua_gettop(state);
439 if (nArgs < 2 || nArgs > 4 ||
440 !lua_isstring(state, 1) || // URI
441 !lua_isstring(state, 2) || // Body
442 (nArgs >= 3 && !lua_isboolean(state, 3))) // Restrict to built-in API?
443 {
444 LOG(ERROR) << "Lua: Bad parameters to " << (isPost ? "RestApiPost()" : "RestApiPut()");
445 lua_pushnil(state);
446 return 1;
447 }
448
449 const char* uri = lua_tostring(state, 1);
450 size_t bodySize = 0;
451 const char* bodyData = lua_tolstring(state, 2, &bodySize);
452 bool builtin = (nArgs == 3 ? lua_toboolean(state, 3) != 0 : false);
453
454 std::map<std::string, std::string> headers;
455 LuaContext::GetDictionaryArgument(headers, state, 4, true /* HTTP header key to lower case */);
456
457 try
458 {
459 std::string result;
460 if (isPost ?
461 HttpToolbox::SimplePost(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin),
462 RequestOrigin_Lua, uri, bodyData, bodySize, headers) :
463 HttpToolbox::SimplePut(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin),
464 RequestOrigin_Lua, uri, bodyData, bodySize, headers))
465 {
466 lua_pushlstring(state, result.c_str(), result.size());
467 return 1;
468 }
469 }
470 catch (OrthancException& e)
471 {
472 LOG(ERROR) << "Lua: " << e.What();
473 }
474
475 LOG(ERROR) << "Lua: Error in " << (isPost ? "RestApiPost()" : "RestApiPut()") << " for URI: " << uri;
476 lua_pushnil(state);
477 return 1;
478 }
479
480
481 // Syntax in Lua: RestApiPost(uri, body, builtin)
482 int LuaScripting::RestApiPost(lua_State *state)
483 {
484 return RestApiPostOrPut(state, true);
485 }
486
487
488 // Syntax in Lua: RestApiPut(uri, body, builtin)
489 int LuaScripting::RestApiPut(lua_State *state)
490 {
491 return RestApiPostOrPut(state, false);
492 }
493
494
495 // Syntax in Lua: RestApiDelete(uri, builtin)
496 int LuaScripting::RestApiDelete(lua_State *state)
497 {
498 ServerContext* serverContext = GetServerContext(state);
499 if (serverContext == NULL)
500 {
501 LOG(ERROR) << "Lua: The Orthanc API is unavailable";
502 lua_pushnil(state);
503 return 1;
504 }
505
506 // Check the types of the arguments
507 int nArgs = lua_gettop(state);
508 if (nArgs < 1 || nArgs > 3 ||
509 !lua_isstring(state, 1) || // URI
510 (nArgs >= 2 && !lua_isboolean(state, 2))) // Restrict to built-in API?
511 {
512 LOG(ERROR) << "Lua: Bad parameters to RestApiDelete()";
513 lua_pushnil(state);
514 return 1;
515 }
516
517 const char* uri = lua_tostring(state, 1);
518 bool builtin = (nArgs == 2 ? lua_toboolean(state, 2) != 0 : false);
519
520 std::map<std::string, std::string> headers;
521 LuaContext::GetDictionaryArgument(headers, state, 3, true /* HTTP header key to lower case */);
522
523 try
524 {
525 if (HttpToolbox::SimpleDelete(serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin),
526 RequestOrigin_Lua, uri, headers))
527 {
528 lua_pushboolean(state, 1);
529 return 1;
530 }
531 }
532 catch (OrthancException& e)
533 {
534 LOG(ERROR) << "Lua: " << e.What();
535 }
536
537 LOG(ERROR) << "Lua: Error in RestApiDelete() for URI: " << uri;
538 lua_pushnil(state);
539
540 return 1;
541 }
542
543
544 // Syntax in Lua: GetOrthancConfiguration()
545 int LuaScripting::GetOrthancConfiguration(lua_State *state)
546 {
547 Json::Value configuration;
548
549 {
550 OrthancConfiguration::ReaderLock lock;
551 configuration = lock.GetJson();
552 }
553
554 LuaContext::GetLuaContext(state).PushJson(configuration);
555
556 return 1;
557 }
558
559
560 size_t LuaScripting::ParseOperation(LuaJobManager::Lock& lock,
561 const std::string& operation,
562 const Json::Value& parameters)
563 {
564 if (operation == "delete")
565 {
566 LOG(INFO) << "Lua script to delete resource " << parameters["Resource"].asString();
567 return lock.AddDeleteResourceOperation(context_);
568 }
569
570 if (operation == "store-scu")
571 {
572 std::string localAet;
573 if (parameters.isMember("LocalAet"))
574 {
575 localAet = parameters["LocalAet"].asString();
576 }
577 else
578 {
579 localAet = context_.GetDefaultLocalApplicationEntityTitle();
580 }
581
582 std::string name = parameters["Modality"].asString();
583 RemoteModalityParameters modality;
584
585 {
586 OrthancConfiguration::ReaderLock configLock;
587 modality = configLock.GetConfiguration().GetModalityUsingSymbolicName(name);
588 }
589
590 // This is not a C-MOVE: No need to call "StoreScuCommand::SetMoveOriginator()"
591 return lock.AddStoreScuOperation(context_, localAet, modality);
592 }
593
594 if (operation == "store-peer")
595 {
596 OrthancConfiguration::ReaderLock configLock;
597 std::string name = parameters["Peer"].asString();
598
599 WebServiceParameters peer;
600 if (configLock.GetConfiguration().LookupOrthancPeer(peer, name))
601 {
602 return lock.AddStorePeerOperation(peer);
603 }
604 else
605 {
606 throw OrthancException(ErrorCode_UnknownResource,
607 "No peer with symbolic name: " + name);
608 }
609 }
610
611 if (operation == "modify")
612 {
613 std::unique_ptr<DicomModification> modification(new DicomModification);
614 modification->ParseModifyRequest(parameters);
615
616 return lock.AddModifyInstanceOperation(context_, modification.release());
617 }
618
619 if (operation == "call-system")
620 {
621 LOG(INFO) << "Lua script to call system command on " << parameters["Resource"].asString();
622
623 const Json::Value& argsIn = parameters["Arguments"];
624 if (argsIn.type() != Json::arrayValue)
625 {
626 throw OrthancException(ErrorCode_BadParameterType);
627 }
628
629 std::vector<std::string> args;
630 args.reserve(argsIn.size());
631 for (Json::Value::ArrayIndex i = 0; i < argsIn.size(); ++i)
632 {
633 // http://jsoncpp.sourceforge.net/namespace_json.html#7d654b75c16a57007925868e38212b4e
634 switch (argsIn[i].type())
635 {
636 case Json::stringValue:
637 args.push_back(argsIn[i].asString());
638 break;
639
640 case Json::intValue:
641 args.push_back(boost::lexical_cast<std::string>(argsIn[i].asInt()));
642 break;
643
644 case Json::uintValue:
645 args.push_back(boost::lexical_cast<std::string>(argsIn[i].asUInt()));
646 break;
647
648 case Json::realValue:
649 args.push_back(boost::lexical_cast<std::string>(argsIn[i].asFloat()));
650 break;
651
652 default:
653 throw OrthancException(ErrorCode_BadParameterType);
654 }
655 }
656
657 std::string command = parameters["Command"].asString();
658 std::vector<std::string> postArgs;
659
660 return lock.AddSystemCallOperation(command, args, postArgs);
661 }
662
663 throw OrthancException(ErrorCode_ParameterOutOfRange);
664 }
665
666
667 void LuaScripting::InitializeJob()
668 {
669 lua_.Execute("_InitializeJob()");
670 }
671
672
673 void LuaScripting::SubmitJob()
674 {
675 Json::Value operations;
676 LuaFunctionCall call2(lua_, "_AccessJob");
677 call2.ExecuteToJson(operations, false);
678
679 if (operations.type() != Json::arrayValue)
680 {
681 throw OrthancException(ErrorCode_InternalError);
682 }
683
684 LuaJobManager::Lock lock(jobManager_, context_.GetJobsEngine());
685
686 bool isFirst = true;
687 size_t previous = 0; // Dummy initialization to avoid warning
688
689 for (Json::Value::ArrayIndex i = 0; i < operations.size(); ++i)
690 {
691 if (operations[i].type() != Json::objectValue ||
692 !operations[i].isMember("Operation"))
693 {
694 throw OrthancException(ErrorCode_InternalError);
695 }
696
697 const Json::Value& parameters = operations[i];
698 if (!parameters.isMember("Resource"))
699 {
700 throw OrthancException(ErrorCode_InternalError);
701 }
702
703 std::string operation = parameters["Operation"].asString();
704 size_t index = ParseOperation(lock, operation, operations[i]);
705
706 std::string resource = parameters["Resource"].asString();
707 if (!resource.empty())
708 {
709 lock.AddDicomInstanceInput(index, context_, resource);
710 }
711 else if (!isFirst)
712 {
713 lock.Connect(previous, index);
714 }
715
716 isFirst = false;
717 previous = index;
718 }
719 }
720
721
722 LuaScripting::LuaScripting(ServerContext& context) :
723 context_(context),
724 state_(State_Setup)
725 {
726 lua_.SetGlobalVariable("_ServerContext", &context);
727 lua_.RegisterFunction("RestApiGet", RestApiGet);
728 lua_.RegisterFunction("RestApiPost", RestApiPost);
729 lua_.RegisterFunction("RestApiPut", RestApiPut);
730 lua_.RegisterFunction("RestApiDelete", RestApiDelete);
731 lua_.RegisterFunction("GetOrthancConfiguration", GetOrthancConfiguration);
732
733 LOG(INFO) << "Initializing Lua for the event handler";
734 LoadGlobalConfiguration();
735 }
736
737
738 LuaScripting::~LuaScripting()
739 {
740 if (state_ == State_Running)
741 {
742 LOG(ERROR) << "INTERNAL ERROR: LuaScripting::Stop() should be invoked manually to avoid mess in the destruction order!";
743 Stop();
744 }
745 }
746
747
748 void LuaScripting::EventThread(LuaScripting* that)
749 {
750 for (;;)
751 {
752 std::unique_ptr<IDynamicObject> event(that->pendingEvents_.Dequeue(100));
753
754 if (event.get() == NULL)
755 {
756 // The event queue is empty, check whether we should stop
757 boost::recursive_mutex::scoped_lock lock(that->mutex_);
758
759 if (that->state_ != State_Running)
760 {
761 return;
762 }
763 }
764 else
765 {
766 try
767 {
768 dynamic_cast<IEvent&>(*event).Apply(*that);
769 }
770 catch (OrthancException& e)
771 {
772 LOG(ERROR) << "Error while processing Lua events: " << e.What();
773 }
774 }
775
776 that->jobManager_.GetDicomConnectionManager().CloseIfInactive();
777 }
778 }
779
780
781 void LuaScripting::Start()
782 {
783 boost::recursive_mutex::scoped_lock lock(mutex_);
784
785 if (state_ != State_Setup ||
786 eventThread_.joinable() /* already started */)
787 {
788 throw OrthancException(ErrorCode_BadSequenceOfCalls);
789 }
790 else
791 {
792 LOG(INFO) << "Starting the Lua engine";
793 eventThread_ = boost::thread(EventThread, this);
794 state_ = State_Running;
795 }
796 }
797
798
799 void LuaScripting::Stop()
800 {
801 {
802 boost::recursive_mutex::scoped_lock lock(mutex_);
803
804 if (state_ != State_Running)
805 {
806 throw OrthancException(ErrorCode_BadSequenceOfCalls);
807 }
808
809 state_ = State_Done;
810 }
811
812 jobManager_.AwakeTrailingSleep();
813
814 if (eventThread_.joinable())
815 {
816 LOG(INFO) << "Stopping the Lua engine";
817 eventThread_.join();
818 LOG(INFO) << "The Lua engine has stopped";
819 }
820 }
821
822
823 void LuaScripting::SignalStoredInstance(const std::string& publicId,
824 const DicomInstanceToStore& instance,
825 const Json::Value& simplifiedTags)
826 {
827 Json::Value metadata = Json::objectValue;
828
829 for (ServerIndex::MetadataMap::const_iterator
830 it = instance.GetMetadata().begin();
831 it != instance.GetMetadata().end(); ++it)
832 {
833 if (it->first.first == ResourceType_Instance)
834 {
835 metadata[EnumerationToString(it->first.second)] = it->second;
836 }
837 }
838
839 pendingEvents_.Enqueue(new OnStoredInstanceEvent(publicId, simplifiedTags, metadata, instance));
840 }
841
842
843 void LuaScripting::SignalChange(const ServerIndexChange& change)
844 {
845 if (change.GetChangeType() == ChangeType_StablePatient ||
846 change.GetChangeType() == ChangeType_StableStudy ||
847 change.GetChangeType() == ChangeType_StableSeries)
848 {
849 pendingEvents_.Enqueue(new StableResourceEvent(change));
850 }
851 else if (change.GetChangeType() == ChangeType_Deleted)
852 {
853 pendingEvents_.Enqueue(new DeleteEvent(change.GetResourceType(), change.GetPublicId()));
854 }
855 else if (change.GetChangeType() == ChangeType_UpdatedAttachment ||
856 change.GetChangeType() == ChangeType_UpdatedMetadata)
857 {
858 pendingEvents_.Enqueue(new UpdateEvent(change.GetResourceType(), change.GetPublicId()));
859 }
860 }
861
862
863 bool LuaScripting::FilterIncomingInstance(const DicomInstanceToStore& instance,
864 const Json::Value& simplified)
865 {
866 static const char* NAME = "ReceivedInstanceFilter";
867
868 boost::recursive_mutex::scoped_lock lock(mutex_);
869
870 if (lua_.IsExistingFunction(NAME))
871 {
872 LuaFunctionCall call(lua_, NAME);
873 call.PushJson(simplified);
874
875 Json::Value origin;
876 instance.GetOrigin().Format(origin);
877 call.PushJson(origin);
878
879 Json::Value info = Json::objectValue;
880 info["HasPixelData"] = instance.HasPixelData();
881
882 std::string s;
883 if (instance.LookupTransferSyntax(s))
884 {
885 info["TransferSyntaxUID"] = s;
886 }
887
888 call.PushJson(info);
889
890 if (!call.ExecutePredicate())
891 {
892 return false;
893 }
894 }
895
896 return true;
897 }
898
899
900 void LuaScripting::Execute(const std::string& command)
901 {
902 pendingEvents_.Enqueue(new ExecuteEvent(command));
903 }
904
905
906 void LuaScripting::LoadGlobalConfiguration()
907 {
908 OrthancConfiguration::ReaderLock configLock;
909
910 {
911 std::string command;
912 Orthanc::ServerResources::GetFileResource(command, Orthanc::ServerResources::LUA_TOOLBOX);
913 lua_.Execute(command);
914 }
915
916 std::list<std::string> luaScripts;
917 configLock.GetConfiguration().GetListOfStringsParameter(luaScripts, "LuaScripts");
918
919 LuaScripting::Lock lock(*this);
920
921 for (std::list<std::string>::const_iterator
922 it = luaScripts.begin(); it != luaScripts.end(); ++it)
923 {
924 std::string path = configLock.GetConfiguration().InterpretStringParameterAsPath(*it);
925 LOG(INFO) << "Installing the Lua scripts from: " << path;
926 std::string script;
927 SystemToolbox::ReadFile(script, path);
928
929 lock.GetLua().Execute(script);
930 }
931 }
932
933
934 void LuaScripting::SignalJobSubmitted(const std::string& jobId)
935 {
936 pendingEvents_.Enqueue(new JobEvent(JobEvent::Type_Submitted, jobId));
937 }
938
939
940 void LuaScripting::SignalJobSuccess(const std::string& jobId)
941 {
942 pendingEvents_.Enqueue(new JobEvent(JobEvent::Type_Success, jobId));
943 }
944
945
946 void LuaScripting::SignalJobFailure(const std::string& jobId)
947 {
948 pendingEvents_.Enqueue(new JobEvent(JobEvent::Type_Failure, jobId));
949 }
950 }