Orthanc/OrthancServer/Sources/LuaScripting.cpp
2025-06-23 19:07:37 +05:30

1059 lines
28 KiB
C++

/**
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
* Copyright (C) 2017-2023 Osimis S.A., Belgium
* Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
* Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#include "PrecompiledHeadersServer.h"
#include "LuaScripting.h"
#include "OrthancConfiguration.h"
#include "OrthancRestApi/OrthancRestApi.h"
#include "ServerContext.h"
#include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h"
#include "../../OrthancFramework/Sources/HttpServer/StringHttpOutput.h"
#include "../../OrthancFramework/Sources/Logging.h"
#include "../../OrthancFramework/Sources/Lua/LuaFunctionCall.h"
#include <OrthancServerResources.h>
static const char* ON_HEART_BEAT = "OnHeartBeat";
namespace Orthanc
{
class LuaScripting::IEvent : public IDynamicObject
{
public:
virtual void Apply(LuaScripting& lock) = 0;
};
class LuaScripting::OnStoredInstanceEvent : public LuaScripting::IEvent
{
private:
std::string instanceId_;
Json::Value simplifiedTags_;
Json::Value metadata_;
Json::Value origin_;
public:
OnStoredInstanceEvent(const std::string& instanceId,
const Json::Value& simplifiedTags,
const Json::Value& metadata,
const DicomInstanceToStore& instance) :
instanceId_(instanceId),
simplifiedTags_(simplifiedTags),
metadata_(metadata)
{
instance.GetOrigin().Format(origin_);
}
virtual void Apply(LuaScripting& that) ORTHANC_OVERRIDE
{
static const char* NAME = "OnStoredInstance";
LuaScripting::Lock lock(that);
if (lock.GetLua().IsExistingFunction(NAME))
{
that.InitializeJob();
LuaFunctionCall call(lock.GetLua(), NAME);
call.PushString(instanceId_);
call.PushJson(simplifiedTags_);
call.PushJson(metadata_);
call.PushJson(origin_);
call.Execute();
that.SubmitJob();
}
}
};
class LuaScripting::ExecuteEvent : public LuaScripting::IEvent
{
private:
std::string command_;
public:
explicit ExecuteEvent(const std::string& command) :
command_(command)
{
}
virtual void Apply(LuaScripting& that) ORTHANC_OVERRIDE
{
LuaScripting::Lock lock(that);
if (lock.GetLua().IsExistingFunction(command_.c_str()))
{
LuaFunctionCall call(lock.GetLua(), command_.c_str());
call.Execute();
}
}
};
class LuaScripting::StableResourceEvent : public LuaScripting::IEvent
{
private:
ServerIndexChange change_;
class GetInfoOperations : public ServerIndex::IReadOnlyOperations
{
private:
const ServerIndexChange& change_;
bool ok_;
DicomMap tags_;
std::map<MetadataType, std::string> metadata_;
public:
explicit GetInfoOperations(const ServerIndexChange& change) :
change_(change),
ok_(false)
{
}
virtual void Apply(ServerIndex::ReadOnlyTransaction& transaction) ORTHANC_OVERRIDE
{
int64_t internalId;
ResourceType level;
if (transaction.LookupResource(internalId, level, change_.GetPublicId()) &&
level == change_.GetResourceType())
{
transaction.GetMainDicomTags(tags_, internalId);
transaction.GetAllMetadata(metadata_, internalId);
ok_ = true;
}
}
void CallLua(LuaScripting& that,
const char* name) const
{
if (ok_)
{
Json::Value formattedMetadata = Json::objectValue;
for (std::map<MetadataType, std::string>::const_iterator
it = metadata_.begin(); it != metadata_.end(); ++it)
{
std::string key = EnumerationToString(it->first);
formattedMetadata[key] = it->second;
}
{
LuaScripting::Lock lock(that);
if (lock.GetLua().IsExistingFunction(name))
{
that.InitializeJob();
Json::Value json = Json::objectValue;
if (change_.GetResourceType() == ResourceType_Study)
{
DicomMap t;
tags_.ExtractStudyInformation(t); // Discard patient-related tags
FromDcmtkBridge::ToJson(json, t, DicomToJsonFormat_Human);
}
else
{
FromDcmtkBridge::ToJson(json, tags_, DicomToJsonFormat_Human);
}
LuaFunctionCall call(lock.GetLua(), name);
call.PushString(change_.GetPublicId());
call.PushJson(json);
call.PushJson(formattedMetadata);
call.Execute();
that.SubmitJob();
}
}
}
}
};
public:
explicit StableResourceEvent(const ServerIndexChange& change) :
change_(change)
{
}
virtual void Apply(LuaScripting& that) ORTHANC_OVERRIDE
{
const char* name;
switch (change_.GetChangeType())
{
case ChangeType_StablePatient:
name = "OnStablePatient";
break;
case ChangeType_StableStudy:
name = "OnStableStudy";
break;
case ChangeType_StableSeries:
name = "OnStableSeries";
break;
default:
throw OrthancException(ErrorCode_InternalError);
}
{
// Avoid unnecessary calls to the database if there's no Lua callback
LuaScripting::Lock lock(that);
if (!lock.GetLua().IsExistingFunction(name))
{
return;
}
}
GetInfoOperations operations(change_);
that.context_.GetIndex().Apply(operations);
operations.CallLua(that, name);
}
};
class LuaScripting::LuaJobEvent : public LuaScripting::IEvent
{
private:
JobEvent event_;
public:
explicit LuaJobEvent(const JobEvent& event) :
event_(event)
{
}
virtual void Apply(LuaScripting& that) ORTHANC_OVERRIDE
{
std::string functionName;
switch (event_.GetEventType())
{
case JobEventType_Failure:
functionName = "OnJobFailure";
break;
case JobEventType_Submitted:
functionName = "OnJobSubmitted";
break;
case JobEventType_Success:
functionName = "OnJobSuccess";
break;
default:
throw OrthancException(ErrorCode_InternalError);
}
{
LuaScripting::Lock lock(that);
if (lock.GetLua().IsExistingFunction(functionName.c_str()))
{
LuaFunctionCall call(lock.GetLua(), functionName.c_str());
call.PushString(event_.GetJobId());
call.Execute();
}
}
}
};
class LuaScripting::DeleteEvent : public LuaScripting::IEvent
{
private:
ResourceType level_;
std::string publicId_;
public:
DeleteEvent(ResourceType level,
const std::string& publicId) :
level_(level),
publicId_(publicId)
{
}
virtual void Apply(LuaScripting& that) ORTHANC_OVERRIDE
{
std::string functionName;
switch (level_)
{
case ResourceType_Patient:
functionName = "OnDeletedPatient";
break;
case ResourceType_Study:
functionName = "OnDeletedStudy";
break;
case ResourceType_Series:
functionName = "OnDeletedSeries";
break;
case ResourceType_Instance:
functionName = "OnDeletedInstance";
break;
default:
throw OrthancException(ErrorCode_InternalError);
}
{
LuaScripting::Lock lock(that);
if (lock.GetLua().IsExistingFunction(functionName.c_str()))
{
LuaFunctionCall call(lock.GetLua(), functionName.c_str());
call.PushString(publicId_);
call.Execute();
}
}
}
};
class LuaScripting::UpdateEvent : public LuaScripting::IEvent
{
private:
ResourceType level_;
std::string publicId_;
public:
UpdateEvent(ResourceType level,
const std::string& publicId) :
level_(level),
publicId_(publicId)
{
}
virtual void Apply(LuaScripting& that) ORTHANC_OVERRIDE
{
std::string functionName;
switch (level_)
{
case ResourceType_Patient:
functionName = "OnUpdatedPatient";
break;
case ResourceType_Study:
functionName = "OnUpdatedStudy";
break;
case ResourceType_Series:
functionName = "OnUpdatedSeries";
break;
case ResourceType_Instance:
functionName = "OnUpdatedInstance";
break;
default:
throw OrthancException(ErrorCode_InternalError);
}
{
LuaScripting::Lock lock(that);
if (lock.GetLua().IsExistingFunction(functionName.c_str()))
{
LuaFunctionCall call(lock.GetLua(), functionName.c_str());
call.PushString(publicId_);
call.Execute();
}
}
}
};
ServerContext* LuaScripting::GetServerContext(lua_State *state)
{
const void* value = LuaContext::GetGlobalVariable(state, "_ServerContext");
return const_cast<ServerContext*>(reinterpret_cast<const ServerContext*>(value));
}
// Syntax in Lua: RestApiGet(uri, builtin)
int LuaScripting::RestApiGet(lua_State *state)
{
ServerContext* serverContext = GetServerContext(state);
if (serverContext == NULL)
{
LOG(ERROR) << "Lua: The Orthanc API is unavailable";
lua_pushnil(state);
return 1;
}
// Check the types of the arguments
int nArgs = lua_gettop(state);
if (nArgs < 1 || nArgs > 3 ||
!lua_isstring(state, 1) || // URI
(nArgs >= 2 && !lua_isboolean(state, 2))) // Restrict to built-in API?
{
LOG(ERROR) << "Lua: Bad parameters to RestApiGet()";
lua_pushnil(state);
return 1;
}
const char* uri = lua_tostring(state, 1);
bool builtin = (nArgs == 2 ? lua_toboolean(state, 2) != 0 : false);
std::map<std::string, std::string> headers;
LuaContext::GetDictionaryArgument(headers, state, 3, true /* HTTP header key to lower case */);
try
{
std::string result;
if (IHttpHandler::SimpleGet(result, NULL, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin),
RequestOrigin_Lua, uri, headers) == HttpStatus_200_Ok)
{
lua_pushlstring(state, result.c_str(), result.size());
return 1;
}
}
catch (OrthancException& e)
{
LOG(ERROR) << "Lua: " << e.What();
}
LOG(ERROR) << "Lua: Error in RestApiGet() for URI: " << uri;
lua_pushnil(state);
return 1;
}
int LuaScripting::RestApiPostOrPut(lua_State *state,
bool isPost)
{
ServerContext* serverContext = GetServerContext(state);
if (serverContext == NULL)
{
LOG(ERROR) << "Lua: The Orthanc API is unavailable";
lua_pushnil(state);
return 1;
}
// Check the types of the arguments
int nArgs = lua_gettop(state);
if (nArgs < 2 || nArgs > 4 ||
!lua_isstring(state, 1) || // URI
!lua_isstring(state, 2) || // Body
(nArgs >= 3 && !lua_isboolean(state, 3))) // Restrict to built-in API?
{
LOG(ERROR) << "Lua: Bad parameters to " << (isPost ? "RestApiPost()" : "RestApiPut()");
lua_pushnil(state);
return 1;
}
const char* uri = lua_tostring(state, 1);
size_t bodySize = 0;
const char* bodyData = lua_tolstring(state, 2, &bodySize);
bool builtin = (nArgs == 3 ? lua_toboolean(state, 3) != 0 : false);
std::map<std::string, std::string> headers;
LuaContext::GetDictionaryArgument(headers, state, 4, true /* HTTP header key to lower case */);
try
{
std::string result;
if (isPost ?
IHttpHandler::SimplePost(result, NULL,
serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin),
RequestOrigin_Lua, uri, bodyData, bodySize, headers) == HttpStatus_200_Ok :
IHttpHandler::SimplePut(result, NULL,
serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin),
RequestOrigin_Lua, uri, bodyData, bodySize, headers) == HttpStatus_200_Ok)
{
lua_pushlstring(state, result.c_str(), result.size());
return 1;
}
}
catch (OrthancException& e)
{
LOG(ERROR) << "Lua: " << e.What();
}
LOG(ERROR) << "Lua: Error in " << (isPost ? "RestApiPost()" : "RestApiPut()") << " for URI: " << uri;
lua_pushnil(state);
return 1;
}
// Syntax in Lua: RestApiPost(uri, body, builtin)
int LuaScripting::RestApiPost(lua_State *state)
{
return RestApiPostOrPut(state, true);
}
// Syntax in Lua: RestApiPut(uri, body, builtin)
int LuaScripting::RestApiPut(lua_State *state)
{
return RestApiPostOrPut(state, false);
}
// Syntax in Lua: RestApiDelete(uri, builtin)
int LuaScripting::RestApiDelete(lua_State *state)
{
ServerContext* serverContext = GetServerContext(state);
if (serverContext == NULL)
{
LOG(ERROR) << "Lua: The Orthanc API is unavailable";
lua_pushnil(state);
return 1;
}
// Check the types of the arguments
int nArgs = lua_gettop(state);
if (nArgs < 1 || nArgs > 3 ||
!lua_isstring(state, 1) || // URI
(nArgs >= 2 && !lua_isboolean(state, 2))) // Restrict to built-in API?
{
LOG(ERROR) << "Lua: Bad parameters to RestApiDelete()";
lua_pushnil(state);
return 1;
}
const char* uri = lua_tostring(state, 1);
bool builtin = (nArgs == 2 ? lua_toboolean(state, 2) != 0 : false);
std::map<std::string, std::string> headers;
LuaContext::GetDictionaryArgument(headers, state, 3, true /* HTTP header key to lower case */);
try
{
if (IHttpHandler::SimpleDelete(NULL, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin),
RequestOrigin_Lua, uri, headers) == HttpStatus_200_Ok)
{
lua_pushboolean(state, 1);
return 1;
}
}
catch (OrthancException& e)
{
LOG(ERROR) << "Lua: " << e.What();
}
LOG(ERROR) << "Lua: Error in RestApiDelete() for URI: " << uri;
lua_pushnil(state);
return 1;
}
// Syntax in Lua: GetOrthancConfiguration()
int LuaScripting::GetOrthancConfiguration(lua_State *state)
{
Json::Value configuration;
{
OrthancConfiguration::ReaderLock lock;
configuration = lock.GetJson();
}
LuaContext::GetLuaContext(state).PushJson(configuration);
return 1;
}
size_t LuaScripting::ParseOperation(LuaJobManager::Lock& lock,
const std::string& operation,
const Json::Value& parameters)
{
if (operation == "delete")
{
LOG(INFO) << "Lua script to delete resource " << parameters["Resource"].asString();
return lock.AddDeleteResourceOperation(context_);
}
if (operation == "store-scu")
{
std::string localAet;
if (parameters.isMember("LocalAet"))
{
localAet = parameters["LocalAet"].asString();
}
else
{
localAet = context_.GetDefaultLocalApplicationEntityTitle();
}
std::string name = parameters["Modality"].asString();
RemoteModalityParameters modality;
{
OrthancConfiguration::ReaderLock configLock;
modality = configLock.GetConfiguration().GetModalityUsingSymbolicName(name);
}
// This is not a C-MOVE: No need to call "StoreScuCommand::SetMoveOriginator()"
return lock.AddStoreScuOperation(context_, localAet, modality);
}
if (operation == "store-peer")
{
OrthancConfiguration::ReaderLock configLock;
std::string name = parameters["Peer"].asString();
WebServiceParameters peer;
if (configLock.GetConfiguration().LookupOrthancPeer(peer, name))
{
return lock.AddStorePeerOperation(peer);
}
else
{
throw OrthancException(ErrorCode_UnknownResource,
"No peer with symbolic name: " + name);
}
}
if (operation == "modify")
{
std::unique_ptr<DicomModification> modification(new DicomModification);
modification->ParseModifyRequest(parameters);
return lock.AddModifyInstanceOperation(context_, modification.release());
}
if (operation == "call-system")
{
LOG(INFO) << "Lua script to call system command on " << parameters["Resource"].asString();
const Json::Value& argsIn = parameters["Arguments"];
if (argsIn.type() != Json::arrayValue)
{
throw OrthancException(ErrorCode_BadParameterType);
}
std::vector<std::string> args;
args.reserve(argsIn.size());
for (Json::Value::ArrayIndex i = 0; i < argsIn.size(); ++i)
{
// http://jsoncpp.sourceforge.net/namespace_json.html#7d654b75c16a57007925868e38212b4e
switch (argsIn[i].type())
{
case Json::stringValue:
args.push_back(argsIn[i].asString());
break;
case Json::intValue:
args.push_back(boost::lexical_cast<std::string>(argsIn[i].asInt()));
break;
case Json::uintValue:
args.push_back(boost::lexical_cast<std::string>(argsIn[i].asUInt()));
break;
case Json::realValue:
args.push_back(boost::lexical_cast<std::string>(argsIn[i].asFloat()));
break;
default:
throw OrthancException(ErrorCode_BadParameterType);
}
}
std::string command = parameters["Command"].asString();
std::vector<std::string> postArgs;
return lock.AddSystemCallOperation(command, args, postArgs);
}
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
void LuaScripting::InitializeJob()
{
lua_.Execute("_InitializeJob()");
}
void LuaScripting::SubmitJob()
{
Json::Value operations;
LuaFunctionCall call2(lua_, "_AccessJob");
call2.ExecuteToJson(operations, false);
if (operations.type() != Json::arrayValue)
{
throw OrthancException(ErrorCode_InternalError);
}
LuaJobManager::Lock lock(jobManager_, context_.GetJobsEngine());
bool isFirst = true;
size_t previous = 0; // Dummy initialization to avoid warning
for (Json::Value::ArrayIndex i = 0; i < operations.size(); ++i)
{
if (operations[i].type() != Json::objectValue ||
!operations[i].isMember("Operation"))
{
throw OrthancException(ErrorCode_InternalError);
}
const Json::Value& parameters = operations[i];
if (!parameters.isMember("Resource"))
{
throw OrthancException(ErrorCode_InternalError);
}
std::string operation = parameters["Operation"].asString();
size_t index = ParseOperation(lock, operation, operations[i]);
std::string resource = parameters["Resource"].asString();
if (!resource.empty())
{
lock.AddDicomInstanceInput(index, context_, resource);
}
else if (!isFirst)
{
lock.Connect(previous, index);
}
isFirst = false;
previous = index;
}
}
LuaScripting::LuaScripting(ServerContext& context) :
context_(context),
state_(State_Setup),
heartBeatPeriod_(0)
{
lua_.SetGlobalVariable("_ServerContext", &context);
lua_.RegisterFunction("RestApiGet", RestApiGet);
lua_.RegisterFunction("RestApiPost", RestApiPost);
lua_.RegisterFunction("RestApiPut", RestApiPut);
lua_.RegisterFunction("RestApiDelete", RestApiDelete);
lua_.RegisterFunction("GetOrthancConfiguration", GetOrthancConfiguration);
LOG(INFO) << "Initializing Lua for the event handler";
LoadGlobalConfiguration();
}
LuaScripting::~LuaScripting()
{
if (state_ == State_Running)
{
LOG(ERROR) << "INTERNAL ERROR: LuaScripting::Stop() should be invoked manually to avoid mess in the destruction order!";
Stop();
}
}
void LuaScripting::HeartBeatThread(LuaScripting* that)
{
Logging::SetCurrentThreadName("LUA-HEARTBEAT");
static const unsigned int GRANULARITY = 100; // In milliseconds
const boost::posix_time::time_duration PERIODICITY =
boost::posix_time::seconds(that->heartBeatPeriod_);
boost::posix_time::ptime next =
boost::posix_time::microsec_clock::universal_time() + PERIODICITY;
bool shouldStop = false;
while (!shouldStop)
{
boost::this_thread::sleep(boost::posix_time::milliseconds(GRANULARITY));
if (boost::posix_time::microsec_clock::universal_time() >= next)
{
LuaScripting::Lock lock(*that);
if (lock.GetLua().IsExistingFunction(ON_HEART_BEAT))
{
LuaFunctionCall call(lock.GetLua(), ON_HEART_BEAT);
call.Execute();
}
next = boost::posix_time::microsec_clock::universal_time() + PERIODICITY;
}
{
boost::recursive_mutex::scoped_lock lock(that->mutex_);
shouldStop = (that->state_ == State_Done);
}
}
}
void LuaScripting::EventThread(LuaScripting* that)
{
Logging::SetCurrentThreadName("LUA-EVENTS");
for (;;)
{
std::unique_ptr<IDynamicObject> event(that->pendingEvents_.Dequeue(100));
if (event.get() == NULL)
{
// The event queue is empty, check whether we should stop
boost::recursive_mutex::scoped_lock lock(that->mutex_);
if (that->state_ != State_Running)
{
return;
}
}
else
{
try
{
dynamic_cast<IEvent&>(*event).Apply(*that);
}
catch (OrthancException& e)
{
LOG(ERROR) << "Error while processing Lua events: " << e.What();
}
}
that->jobManager_.GetDicomConnectionManager().CloseIfInactive();
}
}
void LuaScripting::Start()
{
boost::recursive_mutex::scoped_lock lock(mutex_);
if (state_ != State_Setup ||
eventThread_.joinable() /* already started */)
{
throw OrthancException(ErrorCode_BadSequenceOfCalls);
}
else
{
LOG(INFO) << "Starting the Lua engine";
eventThread_ = boost::thread(EventThread, this);
LuaScripting::Lock luaLock(*this);
if (heartBeatPeriod_ > 0 && luaLock.GetLua().IsExistingFunction(ON_HEART_BEAT))
{
LOG(INFO) << "Starting the Lua HeartBeat thread with a period of " << heartBeatPeriod_ << " seconds";
heartBeatThread_ = boost::thread(HeartBeatThread, this);
}
state_ = State_Running;
}
}
void LuaScripting::Stop()
{
{
boost::recursive_mutex::scoped_lock lock(mutex_);
if (state_ != State_Running)
{
throw OrthancException(ErrorCode_BadSequenceOfCalls);
}
state_ = State_Done;
}
jobManager_.AwakeTrailingSleep();
if (eventThread_.joinable())
{
LOG(INFO) << "Stopping the Lua engine";
eventThread_.join();
if (heartBeatThread_.joinable())
{
heartBeatThread_.join();
}
LOG(INFO) << "The Lua engine has stopped";
}
}
void LuaScripting::SignalStoredInstance(const std::string& publicId,
const DicomInstanceToStore& instance,
const Json::Value& simplifiedTags)
{
Json::Value metadata = Json::objectValue;
for (ServerIndex::MetadataMap::const_iterator
it = instance.GetMetadata().begin();
it != instance.GetMetadata().end(); ++it)
{
if (it->first.first == ResourceType_Instance)
{
metadata[EnumerationToString(it->first.second)] = it->second;
}
}
pendingEvents_.Enqueue(new OnStoredInstanceEvent(publicId, simplifiedTags, metadata, instance));
}
void LuaScripting::SignalChange(const ServerIndexChange& change)
{
if (change.GetChangeType() == ChangeType_StablePatient ||
change.GetChangeType() == ChangeType_StableStudy ||
change.GetChangeType() == ChangeType_StableSeries)
{
pendingEvents_.Enqueue(new StableResourceEvent(change));
}
else if (change.GetChangeType() == ChangeType_Deleted)
{
pendingEvents_.Enqueue(new DeleteEvent(change.GetResourceType(), change.GetPublicId()));
}
else if (change.GetChangeType() == ChangeType_UpdatedAttachment ||
change.GetChangeType() == ChangeType_UpdatedMetadata)
{
pendingEvents_.Enqueue(new UpdateEvent(change.GetResourceType(), change.GetPublicId()));
}
}
bool LuaScripting::FilterIncomingInstance(const DicomInstanceToStore& instance,
const Json::Value& simplified)
{
static const char* NAME = "ReceivedInstanceFilter";
boost::recursive_mutex::scoped_lock lock(mutex_);
if (lua_.IsExistingFunction(NAME))
{
LuaFunctionCall call(lua_, NAME);
call.PushJson(simplified);
Json::Value origin;
instance.GetOrigin().Format(origin);
call.PushJson(origin);
Json::Value info = Json::objectValue;
info["HasPixelData"] = instance.HasPixelData();
DicomTransferSyntax s;
if (instance.LookupTransferSyntax(s))
{
info["TransferSyntaxUID"] = GetTransferSyntaxUid(s);
}
call.PushJson(info);
if (!call.ExecutePredicate())
{
return false;
}
}
return true;
}
bool LuaScripting::FilterIncomingCStoreInstance(uint16_t& dimseStatus,
const DicomInstanceToStore& instance,
const Json::Value& simplified)
{
static const char* NAME = "ReceivedCStoreInstanceFilter";
boost::recursive_mutex::scoped_lock lock(mutex_);
if (lua_.IsExistingFunction(NAME))
{
LuaFunctionCall call(lua_, NAME);
call.PushJson(simplified);
Json::Value origin;
instance.GetOrigin().Format(origin);
call.PushJson(origin);
Json::Value info = Json::objectValue;
info["HasPixelData"] = instance.HasPixelData();
DicomTransferSyntax s;
if (instance.LookupTransferSyntax(s))
{
info["TransferSyntaxUID"] = GetTransferSyntaxUid(s);
}
call.PushJson(info);
int result;
call.ExecuteToInt(result);
return static_cast<uint16_t>(result);
}
return true;
}
void LuaScripting::Execute(const std::string& command)
{
pendingEvents_.Enqueue(new ExecuteEvent(command));
}
void LuaScripting::LoadGlobalConfiguration()
{
OrthancConfiguration::ReaderLock configLock;
{
std::string command;
Orthanc::ServerResources::GetFileResource(command, Orthanc::ServerResources::LUA_TOOLBOX);
lua_.Execute(command);
}
std::list<std::string> luaScripts;
configLock.GetConfiguration().GetListOfStringsParameter(luaScripts, "LuaScripts");
heartBeatPeriod_ = configLock.GetConfiguration().GetIntegerParameter("LuaHeartBeatPeriod", 0);
LuaScripting::Lock lock(*this);
for (std::list<std::string>::const_iterator
it = luaScripts.begin(); it != luaScripts.end(); ++it)
{
std::string path = configLock.GetConfiguration().InterpretStringParameterAsPath(*it);
LOG(INFO) << "Installing the Lua scripts from: " << path;
std::string script;
SystemToolbox::ReadFile(script, path);
lock.GetLua().Execute(script);
}
}
void LuaScripting::SignalJobEvent(const JobEvent& event)
{
// Lua has its own event thread and queue to dissociate it completely from the main JobEventsThread
pendingEvents_.Enqueue(new LuaJobEvent(event));
}
}