comparison OrthancServer/LuaScripting.cpp @ 2616:2f3007bf0708 jobs

event queues in Lua, serialization of sequence of operations
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 22 May 2018 12:25:37 +0200
parents f7a84b551ee4
children ef5b45884972
comparison
equal deleted inserted replaced
2614:3200223f9ade 2616:2f3007bf0708
45 #include <EmbeddedResources.h> 45 #include <EmbeddedResources.h>
46 46
47 47
48 namespace Orthanc 48 namespace Orthanc
49 { 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.GetOriginInformation(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 Json::Value tags, metadata;
158 if (that.context_.GetIndex().LookupResource(tags, change_.GetPublicId(), change_.GetResourceType()) &&
159 that.context_.GetIndex().GetMetadata(metadata, change_.GetPublicId()))
160 {
161 LuaScripting::Lock lock(that);
162
163 if (lock.GetLua().IsExistingFunction(name))
164 {
165 that.InitializeJob();
166
167 LuaFunctionCall call(lock.GetLua(), name);
168 call.PushString(change_.GetPublicId());
169 call.PushJson(tags["MainDicomTags"]);
170 call.PushJson(metadata);
171 call.Execute();
172
173 that.SubmitJob();
174 }
175 }
176 }
177 };
178
179
50 ServerContext* LuaScripting::GetServerContext(lua_State *state) 180 ServerContext* LuaScripting::GetServerContext(lua_State *state)
51 { 181 {
52 const void* value = LuaContext::GetGlobalVariable(state, "_ServerContext"); 182 const void* value = LuaContext::GetGlobalVariable(state, "_ServerContext");
53 return const_cast<ServerContext*>(reinterpret_cast<const ServerContext*>(value)); 183 return const_cast<ServerContext*>(reinterpret_cast<const ServerContext*>(value));
54 } 184 }
204 { 334 {
205 LOG(ERROR) << "Lua: " << e.What(); 335 LOG(ERROR) << "Lua: " << e.What();
206 } 336 }
207 337
208 LOG(ERROR) << "Lua: Error in RestApiDelete() for URI: " << uri; 338 LOG(ERROR) << "Lua: Error in RestApiDelete() for URI: " << uri;
209 lua_pushnil(state); 339 lua_pushnil(state);
210 340
211 return 1; 341 return 1;
212 } 342 }
213 343
214 344
323 { 453 {
324 lua_.Execute("_InitializeJob()"); 454 lua_.Execute("_InitializeJob()");
325 } 455 }
326 456
327 457
328 void LuaScripting::SubmitJob(const std::string& description) 458 void LuaScripting::SubmitJob()
329 { 459 {
330 Json::Value operations; 460 Json::Value operations;
331 LuaFunctionCall call2(lua_, "_AccessJob"); 461 LuaFunctionCall call2(lua_, "_AccessJob");
332 call2.ExecuteToJson(operations, false); 462 call2.ExecuteToJson(operations, false);
333 463
372 previous = index; 502 previous = index;
373 } 503 }
374 } 504 }
375 505
376 506
377 LuaScripting::LuaScripting(ServerContext& context) : context_(context) 507 LuaScripting::LuaScripting(ServerContext& context) :
508 context_(context),
509 continue_(true)
378 { 510 {
379 lua_.SetGlobalVariable("_ServerContext", &context); 511 lua_.SetGlobalVariable("_ServerContext", &context);
380 lua_.RegisterFunction("RestApiGet", RestApiGet); 512 lua_.RegisterFunction("RestApiGet", RestApiGet);
381 lua_.RegisterFunction("RestApiPost", RestApiPost); 513 lua_.RegisterFunction("RestApiPost", RestApiPost);
382 lua_.RegisterFunction("RestApiPut", RestApiPut); 514 lua_.RegisterFunction("RestApiPut", RestApiPut);
383 lua_.RegisterFunction("RestApiDelete", RestApiDelete); 515 lua_.RegisterFunction("RestApiDelete", RestApiDelete);
384 lua_.RegisterFunction("GetOrthancConfiguration", GetOrthancConfiguration); 516 lua_.RegisterFunction("GetOrthancConfiguration", GetOrthancConfiguration);
385 517 }
386 lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); 518
387 } 519
388 520 LuaScripting::~LuaScripting()
389 521 {
390 void LuaScripting::ApplyOnStoredInstance(const std::string& instanceId, 522 if (continue_)
391 const Json::Value& simplifiedTags, 523 {
392 const Json::Value& metadata, 524 LOG(ERROR) << "INTERNAL ERROR: LuaScripting::Stop() should be invoked manually to avoid mess in the destruction order!";
393 const DicomInstanceToStore& instance) 525 Stop();
394 { 526 }
395 static const char* NAME = "OnStoredInstance"; 527 }
528
529
530 void LuaScripting::EventThread(LuaScripting* that)
531 {
532 for (;;)
533 {
534 std::auto_ptr<IDynamicObject> event(that->pendingEvents_.Dequeue(100));
535
536 if (event.get() == NULL)
537 {
538 // The event queue is empty, check whether we should stop
539 boost::mutex::scoped_lock lock(that->mutex_);
540
541 if (!that->continue_)
542 {
543 return;
544 }
545 }
546 else
547 {
548 try
549 {
550 dynamic_cast<IEvent&>(*event).Apply(*that);
551 }
552 catch (OrthancException& e)
553 {
554 LOG(ERROR) << "Error while processing Lua events: " << e.What();
555 }
556 }
557 }
558 }
559
560
561 void LuaScripting::Start()
562 {
563 boost::mutex::scoped_lock lock(mutex_);
564
565 if (!continue_ ||
566 eventThread_.joinable() /* already started */)
567 {
568 throw OrthancException(ErrorCode_BadSequenceOfCalls);
569 }
570 else
571 {
572 LOG(INFO) << "Starting the Lua engine";
573 eventThread_ = boost::thread(EventThread, this);
574 }
575 }
576
577
578 void LuaScripting::Stop()
579 {
580 {
581 boost::mutex::scoped_lock lock(mutex_);
582
583 if (!continue_)
584 {
585 throw OrthancException(ErrorCode_BadSequenceOfCalls);
586 }
587
588 continue_ = false;
589 }
590
591 if (eventThread_.joinable())
592 {
593 LOG(INFO) << "Stopping the Lua engine";
594 eventThread_.join();
595 LOG(INFO) << "The Lua engine has stopped";
596 }
597 }
598
599
600 void LuaScripting::SignalStoredInstance(const std::string& publicId,
601 DicomInstanceToStore& instance,
602 const Json::Value& simplifiedTags)
603 {
604 Json::Value metadata = Json::objectValue;
605
606 for (ServerIndex::MetadataMap::const_iterator
607 it = instance.GetMetadata().begin();
608 it != instance.GetMetadata().end(); ++it)
609 {
610 if (it->first.first == ResourceType_Instance)
611 {
612 metadata[EnumerationToString(it->first.second)] = it->second;
613 }
614 }
615
616 pendingEvents_.Enqueue(new OnStoredInstanceEvent(publicId, simplifiedTags, metadata, instance));
617 }
618
619
620 void LuaScripting::SignalChange(const ServerIndexChange& change)
621 {
622 if (change.GetChangeType() == ChangeType_StablePatient ||
623 change.GetChangeType() == ChangeType_StableStudy ||
624 change.GetChangeType() == ChangeType_StableSeries)
625 {
626 pendingEvents_.Enqueue(new StableResourceEvent(change));
627 }
628 }
629
630
631 bool LuaScripting::FilterIncomingInstance(const DicomInstanceToStore& instance,
632 const Json::Value& simplified)
633 {
634 static const char* NAME = "ReceivedInstanceFilter";
635
636 boost::mutex::scoped_lock lock(mutex_);
396 637
397 if (lua_.IsExistingFunction(NAME)) 638 if (lua_.IsExistingFunction(NAME))
398 { 639 {
399 InitializeJob();
400
401 LuaFunctionCall call(lua_, NAME); 640 LuaFunctionCall call(lua_, NAME);
402 call.PushString(instanceId); 641 call.PushJson(simplified);
403 call.PushJson(simplifiedTags);
404 call.PushJson(metadata);
405 642
406 Json::Value origin; 643 Json::Value origin;
407 instance.GetOriginInformation(origin); 644 instance.GetOriginInformation(origin);
408 call.PushJson(origin); 645 call.PushJson(origin);
409 646
410 call.Execute();
411
412 SubmitJob(std::string("Lua script: ") + NAME);
413 }
414 }
415
416
417 void LuaScripting::SignalStoredInstance(const std::string& publicId,
418 DicomInstanceToStore& instance,
419 const Json::Value& simplifiedTags)
420 {
421 boost::recursive_mutex::scoped_lock lock(mutex_);
422
423 Json::Value metadata = Json::objectValue;
424
425 for (ServerIndex::MetadataMap::const_iterator
426 it = instance.GetMetadata().begin();
427 it != instance.GetMetadata().end(); ++it)
428 {
429 if (it->first.first == ResourceType_Instance)
430 {
431 metadata[EnumerationToString(it->first.second)] = it->second;
432 }
433 }
434
435 ApplyOnStoredInstance(publicId, simplifiedTags, metadata, instance);
436 }
437
438
439
440 void LuaScripting::OnStableResource(const ServerIndexChange& change)
441 {
442 const char* name;
443
444 switch (change.GetChangeType())
445 {
446 case ChangeType_StablePatient:
447 name = "OnStablePatient";
448 break;
449
450 case ChangeType_StableStudy:
451 name = "OnStableStudy";
452 break;
453
454 case ChangeType_StableSeries:
455 name = "OnStableSeries";
456 break;
457
458 default:
459 throw OrthancException(ErrorCode_InternalError);
460 }
461
462
463 Json::Value tags, metadata;
464 if (context_.GetIndex().LookupResource(tags, change.GetPublicId(), change.GetResourceType()) &&
465 context_.GetIndex().GetMetadata(metadata, change.GetPublicId()))
466 {
467 boost::recursive_mutex::scoped_lock lock(mutex_);
468
469 if (lua_.IsExistingFunction(name))
470 {
471 InitializeJob();
472
473 LuaFunctionCall call(lua_, name);
474 call.PushString(change.GetPublicId());
475 call.PushJson(tags["MainDicomTags"]);
476 call.PushJson(metadata);
477 call.Execute();
478
479 SubmitJob(std::string("Lua script: ") + name);
480 }
481 }
482 }
483
484
485
486 void LuaScripting::SignalChange(const ServerIndexChange& change)
487 {
488 if (change.GetChangeType() == ChangeType_StablePatient ||
489 change.GetChangeType() == ChangeType_StableStudy ||
490 change.GetChangeType() == ChangeType_StableSeries)
491 {
492 OnStableResource(change);
493 }
494 }
495
496
497 bool LuaScripting::FilterIncomingInstance(const DicomInstanceToStore& instance,
498 const Json::Value& simplified)
499 {
500 static const char* NAME = "ReceivedInstanceFilter";
501
502 boost::recursive_mutex::scoped_lock lock(mutex_);
503
504 if (lua_.IsExistingFunction(NAME))
505 {
506 LuaFunctionCall call(lua_, NAME);
507 call.PushJson(simplified);
508
509 Json::Value origin;
510 instance.GetOriginInformation(origin);
511 call.PushJson(origin);
512
513 if (!call.ExecutePredicate()) 647 if (!call.ExecutePredicate())
514 { 648 {
515 return false; 649 return false;
516 } 650 }
517 } 651 }
520 } 654 }
521 655
522 656
523 void LuaScripting::Execute(const std::string& command) 657 void LuaScripting::Execute(const std::string& command)
524 { 658 {
525 LuaScripting::Locker locker(*this); 659 pendingEvents_.Enqueue(new ExecuteEvent(command));
526 660 }
527 if (locker.GetLua().IsExistingFunction(command.c_str())) 661
528 { 662
529 LuaFunctionCall call(locker.GetLua(), command.c_str()); 663 void LuaScripting::LoadGlobalConfiguration()
530 call.Execute(); 664 {
665 lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX);
666
667 std::list<std::string> luaScripts;
668 Configuration::GetGlobalListOfStringsParameter(luaScripts, "LuaScripts");
669
670 LuaScripting::Lock lock(*this);
671
672 for (std::list<std::string>::const_iterator
673 it = luaScripts.begin(); it != luaScripts.end(); ++it)
674 {
675 std::string path = Configuration::InterpretStringParameterAsPath(*it);
676 LOG(INFO) << "Installing the Lua scripts from: " << path;
677 std::string script;
678 SystemToolbox::ReadFile(script, path);
679
680 lock.GetLua().Execute(script);
531 } 681 }
532 } 682 }
533 } 683 }