Mercurial > hg > orthanc
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 } |