Instance(ServerIndex& index, const std::string& instanceId) : instanceId_(instanceId), framesCount_(1) { DicomMap instance; if (!index.GetMainDicomTags(instance, instanceId, ResourceType_Instance, ResourceType_Instance)) { throw OrthancException(ErrorCode_UnknownResource); } const DicomValue* frames = instance.TestAndGetValue(DICOM_TAG_NUMBER_OF_FRAMES); if (frames != NULL && !frames->IsNull() && !frames->IsBinary()) { try { framesCount_ = boost::lexical_cast<unsigned int>(frames->GetContent()); } catch (boost::bad_lexical_cast&) { } } std::vector<float> tmp; hasPosition_ = TokenizeVector(tmp, instance, DICOM_TAG_IMAGE_POSITION_PATIENT, 3); if (hasPosition_) { position_[0] = tmp[0]; position_[1] = tmp[1]; position_[2] = tmp[2]; } std::string s; hasIndexInSeries_ = false; try { if (index.LookupMetadata(s, instanceId, MetadataType_Instance_IndexInSeries)) { indexInSeries_ = boost::lexical_cast<size_t>(s); hasIndexInSeries_ = true; } } catch (boost::bad_lexical_cast&) { } }
static void ComputeSeriesCounters(DicomMap& result, ServerIndex& index, const std::string& series, const DicomMap& query) { std::list<std::string> instances; index.GetChildren(instances, series); if (query.HasTag(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES)) { result.SetValue(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES, boost::lexical_cast<std::string>(instances.size()), false); } }
void DatabaseWrapperBase::GetMainDicomTags(DicomMap& map, int64_t id) { map.Clear(); SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE id=?"); s.BindInt64(0, id); while (s.Step()) { map.SetValue(s.ColumnInt(1), s.ColumnInt(2), s.ColumnString(3)); } }
static void ComputeStudyCounters(DicomMap& result, ServerContext& context, const std::string& study, const DicomMap& query) { ServerIndex& index = context.GetIndex(); std::list<std::string> series; index.GetChildren(series, study); if (query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES)) { result.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES, boost::lexical_cast<std::string>(series.size()), false); } if (query.HasTag(DICOM_TAG_MODALITIES_IN_STUDY)) { std::set<std::string> values; ExtractTagFromMainDicomTags(values, index, DICOM_TAG_MODALITY, series, ResourceType_Series); StoreSetOfStrings(result, DICOM_TAG_MODALITIES_IN_STUDY, values); } if (!query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES) && !query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY)) { return; } std::list<std::string> instances; GetChildren(instances, index, series); if (query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES)) { result.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES, boost::lexical_cast<std::string>(instances.size()), false); } if (query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY)) { if (Configuration::GetGlobalBoolParameter("AllowFindSopClassesInStudy", false)) { std::set<std::string> values; ExtractTagFromInstances(values, context, DICOM_TAG_SOP_CLASS_UID, instances); StoreSetOfStrings(result, DICOM_TAG_SOP_CLASSES_IN_STUDY, values); } else { result.SetValue(DICOM_TAG_SOP_CLASSES_IN_STUDY, "", false); LOG(WARNING) << "The handling of \"SOP Classes in Study\" (0008,0062) " << "in C-FIND requests is disabled"; } } }
void LookupResource::ApplyLevel(SetOfResources& candidates, ResourceType level, IDatabaseWrapper& database) const { Levels::const_iterator it = levels_.find(level); if (it != levels_.end()) { it->second->Apply(candidates, database); } if (level == ResourceType_Study && modalitiesInStudy_.get() != NULL) { // There is a constraint on the "ModalitiesInStudy" DICOM // extension. Check out whether one child series has one of the // allowed modalities std::list<int64_t> allStudies, matchingStudies; candidates.Flatten(allStudies); for (std::list<int64_t>::const_iterator study = allStudies.begin(); study != allStudies.end(); ++study) { std::list<int64_t> childrenSeries; database.GetChildrenInternalId(childrenSeries, *study); for (std::list<int64_t>::const_iterator series = childrenSeries.begin(); series != childrenSeries.end(); ++series) { DicomMap tags; database.GetMainDicomTags(tags, *series); const DicomValue* value = tags.TestAndGetValue(DICOM_TAG_MODALITY); if (value != NULL && !value->IsNull() && !value->IsBinary()) { if (modalitiesInStudy_->Match(value->GetContent())) { matchingStudies.push_back(*study); break; } } } } candidates.Intersect(matchingStudies); } }
static void ExtractTagFromMainDicomTags(std::set<std::string>& target, ServerIndex& index, const DicomTag& tag, const std::list<std::string>& resources, ResourceType level) { for (std::list<std::string>::const_iterator it = resources.begin(); it != resources.end(); ++it) { DicomMap tags; if (index.GetMainDicomTags(tags, *it, level, level) && tags.HasTag(tag)) { target.insert(tags.GetValue(tag).GetContent()); } } }
bool DicomFindQuery::FilterMainDicomTags(const std::string& resourceId, ResourceType level, const DicomMap& mainTags) const { std::set<DicomTag> tags; mainTags.GetTags(tags); for (std::set<DicomTag>::const_iterator it = tags.begin(); it != tags.end(); ++it) { Constraints::const_iterator constraint = constraints_.find(*it); if (constraint != constraints_.end() && !constraint->second->Apply(mainTags.GetValue(*it).AsString())) { return false; } } return true; }
static void AddAnswer(DicomFindAnswers& answers, const Json::Value& resource, const DicomArray& query) { DicomMap result; for (size_t i = 0; i < query.GetSize(); i++) { // Fix issue 30 (QR response missing "Query/Retrieve Level" (008,0052)) if (query.GetElement(i).GetTag() == DICOM_TAG_QUERY_RETRIEVE_LEVEL) { result.SetValue(query.GetElement(i).GetTag(), query.GetElement(i).GetValue()); } else if (query.GetElement(i).GetTag() == DICOM_TAG_SPECIFIC_CHARACTER_SET) { } else { std::string tag = query.GetElement(i).GetTag().Format(); std::string value; if (resource.isMember(tag)) { value = resource.get(tag, Json::arrayValue).get("Value", "").asString(); result.SetValue(query.GetElement(i).GetTag(), value); } else { result.SetValue(query.GetElement(i).GetTag(), ""); } } } if (result.GetSize() == 0) { LOG(WARNING) << "The C-FIND request does not return any DICOM tag"; } else { answers.Add(result); } }
TEST(DicomImageInformation, ExtractPixelFormat2) { // Delphine CT DicomMap m; m.SetValue(DICOM_TAG_ROWS, "24"); m.SetValue(DICOM_TAG_COLUMNS, "16"); m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16"); m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1"); m.SetValue(DICOM_TAG_BITS_STORED, "16"); m.SetValue(DICOM_TAG_HIGH_BIT, "15"); m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "1"); m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2"); DicomImageInformation info(m); PixelFormat format; ASSERT_TRUE(info.ExtractPixelFormat(format)); ASSERT_EQ(PixelFormat_SignedGrayscale16, format); }
TEST(DicomImageInformation, ExtractPixelFormat1) { // Cardiac/MR* DicomMap m; m.SetValue(DICOM_TAG_ROWS, "24", false); m.SetValue(DICOM_TAG_COLUMNS, "16", false); m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16", false); m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1", false); m.SetValue(DICOM_TAG_BITS_STORED, "12", false); m.SetValue(DICOM_TAG_HIGH_BIT, "11", false); m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "0", false); m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2", false); DicomImageInformation info(m); PixelFormat format; ASSERT_TRUE(info.ExtractPixelFormat(format, false)); ASSERT_EQ(PixelFormat_Grayscale16, format); }
void StoreMainDicomTags(IDatabaseWrapper& database, int64_t resource, ResourceType level, const DicomMap& dicomSummary) { // WARNING: The database should be locked with a transaction! StoreIdentifiers(database, resource, level, dicomSummary); DicomMap tags; switch (level) { case ResourceType_Patient: dicomSummary.ExtractPatientInformation(tags); break; case ResourceType_Study: // Duplicate the patient tags at the study level (new in Orthanc 0.9.5 - db v6) dicomSummary.ExtractPatientInformation(tags); StoreMainDicomTagsInternal(database, resource, tags); dicomSummary.ExtractStudyInformation(tags); break; case ResourceType_Series: dicomSummary.ExtractSeriesInformation(tags); break; case ResourceType_Instance: dicomSummary.ExtractInstanceInformation(tags); break; default: throw OrthancException(ErrorCode_InternalError); } StoreMainDicomTagsInternal(database, resource, tags); }
static std::string ValueAsString(const DicomMap& summary, const DicomTag& tag) { const DicomValue& value = summary.GetValue(tag); if (value.IsNull()) { return "(null)"; } else { return value.GetContent(); } }
static void ComputePatientCounters(DicomMap& result, ServerIndex& index, const std::string& patient, const DicomMap& query) { std::list<std::string> studies; index.GetChildren(studies, patient); if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES)) { result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES, boost::lexical_cast<std::string>(studies.size()), false); } if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES) && !query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES)) { return; } std::list<std::string> series; GetChildren(series, index, studies); studies.clear(); // This information is useless below if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES)) { result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES, boost::lexical_cast<std::string>(series.size()), false); } if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES)) { return; } std::list<std::string> instances; GetChildren(instances, index, series); if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES)) { result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES, boost::lexical_cast<std::string>(instances.size()), false); } }
static bool Match(const DicomMap& tags, const DicomTag& tag, const IFindConstraint& constraint) { const DicomValue* value = tags.TestAndGetValue(tag); if (value == NULL || value->IsNull() || value->IsBinary()) { return false; } else { return constraint.Match(value->GetContent()); } }
static bool TokenizeVector(std::vector<float>& result, const DicomMap& map, const DicomTag& tag, unsigned int expectedSize) { const DicomValue* value = map.TestAndGetValue(tag); if (value == NULL || value->IsNull() || value->IsBinary()) { return false; } else { return TokenizeVector(result, value->GetContent(), expectedSize); } }
static void StoreIdentifiers(IDatabaseWrapper& database, int64_t resource, ResourceType level, const DicomMap& map) { const DicomTag* tags; size_t size; LoadIdentifiers(tags, size, level); for (size_t i = 0; i < size; i++) { const DicomValue* value = map.TestAndGetValue(tags[i]); if (value != NULL && !value->IsNull() && !value->IsBinary()) { std::string s = NormalizeIdentifier(value->GetContent()); database.SetIdentifierTag(resource, tags[i], s); } } }
TEST(DicomMap, FindTemplates) { DicomMap m; DicomMap::SetupFindPatientTemplate(m); ASSERT_TRUE(m.HasTag(DICOM_TAG_PATIENT_ID)); DicomMap::SetupFindStudyTemplate(m); ASSERT_TRUE(m.HasTag(DICOM_TAG_STUDY_INSTANCE_UID)); ASSERT_TRUE(m.HasTag(DICOM_TAG_ACCESSION_NUMBER)); DicomMap::SetupFindSeriesTemplate(m); ASSERT_TRUE(m.HasTag(DICOM_TAG_SERIES_INSTANCE_UID)); DicomMap::SetupFindInstanceTemplate(m); ASSERT_TRUE(m.HasTag(DICOM_TAG_SOP_INSTANCE_UID)); }
static void StoreSetOfStrings(DicomMap& result, const DicomTag& tag, const std::set<std::string>& values) { bool isFirst = true; std::string s; for (std::set<std::string>::const_iterator it = values.begin(); it != values.end(); ++it) { if (isFirst) { isFirst = false; } else { s += "\\"; } s += *it; } result.SetValue(tag, s, false); }
TEST(ServerIndex, AttachmentRecycling) { const std::string path = "UnitTestsStorage"; Toolbox::RemoveFile(path + "/index"); FilesystemStorage storage(path); DatabaseWrapper db; // The SQLite DB is in memory ServerContext context(db); context.SetStorageArea(storage); ServerIndex& index = context.GetIndex(); index.SetMaximumStorageSize(10); Json::Value tmp; index.ComputeStatistics(tmp); ASSERT_EQ(0, tmp["CountPatients"].asInt()); ASSERT_EQ(0, boost::lexical_cast<int>(tmp["TotalDiskSize"].asString())); ServerIndex::Attachments attachments; std::vector<std::string> ids; for (int i = 0; i < 10; i++) { std::string id = boost::lexical_cast<std::string>(i); DicomMap instance; instance.SetValue(DICOM_TAG_PATIENT_ID, "patient-" + id); instance.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "study-" + id); instance.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "series-" + id); instance.SetValue(DICOM_TAG_SOP_INSTANCE_UID, "instance-" + id); std::map<MetadataType, std::string> instanceMetadata; ServerIndex::MetadataMap metadata; ASSERT_EQ(StoreStatus_Success, index.Store(instanceMetadata, instance, attachments, "", metadata)); ASSERT_EQ(2, instanceMetadata.size()); ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_RemoteAet) != instanceMetadata.end()); ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_ReceptionDate) != instanceMetadata.end()); DicomInstanceHasher hasher(instance); ids.push_back(hasher.HashPatient()); ids.push_back(hasher.HashStudy()); ids.push_back(hasher.HashSeries()); ids.push_back(hasher.HashInstance()); } index.ComputeStatistics(tmp); ASSERT_EQ(10, tmp["CountPatients"].asInt()); ASSERT_EQ(0, boost::lexical_cast<int>(tmp["TotalDiskSize"].asString())); for (size_t i = 0; i < ids.size(); i++) { FileInfo info(Toolbox::GenerateUuid(), FileContentType_Dicom, 1, "md5"); index.AddAttachment(info, ids[i]); index.ComputeStatistics(tmp); ASSERT_GE(10, boost::lexical_cast<int>(tmp["TotalDiskSize"].asString())); } // Because the DB is in memory, the SQLite index must not have been created ASSERT_THROW(Toolbox::GetFileSize(path + "/index"), OrthancException); context.Stop(); }
TEST(DicomMap, Tags) { DicomMap m; ASSERT_FALSE(m.HasTag(DICOM_TAG_PATIENT_NAME)); ASSERT_FALSE(m.HasTag(0x0010, 0x0010)); m.SetValue(0x0010, 0x0010, "PatientName"); ASSERT_TRUE(m.HasTag(DICOM_TAG_PATIENT_NAME)); ASSERT_TRUE(m.HasTag(0x0010, 0x0010)); ASSERT_FALSE(m.HasTag(DICOM_TAG_PATIENT_ID)); m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID"); ASSERT_TRUE(m.HasTag(0x0010, 0x0020)); m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID2"); ASSERT_EQ("PatientID2", m.GetValue(0x0010, 0x0020).AsString()); m.Remove(DICOM_TAG_PATIENT_ID); ASSERT_THROW(m.GetValue(0x0010, 0x0020), OrthancException); std::auto_ptr<DicomMap> mm(m.Clone()); ASSERT_EQ("PatientName", mm->GetValue(DICOM_TAG_PATIENT_NAME).AsString()); m.SetValue(DICOM_TAG_PATIENT_ID, "Hello"); ASSERT_THROW(mm->GetValue(DICOM_TAG_PATIENT_ID), OrthancException); mm->CopyTagIfExists(m, DICOM_TAG_PATIENT_ID); ASSERT_EQ("Hello", mm->GetValue(DICOM_TAG_PATIENT_ID).AsString()); DicomNullValue v; ASSERT_TRUE(v.IsNull()); }
void LogMissingRequiredTag(const DicomMap& summary) { std::string s, t; if (summary.HasTag(DICOM_TAG_PATIENT_ID)) { if (t.size() > 0) t += ", "; t += "PatientID=" + ValueAsString(summary, DICOM_TAG_PATIENT_ID); } else { if (s.size() > 0) s += ", "; s += "PatientID"; } if (summary.HasTag(DICOM_TAG_STUDY_INSTANCE_UID)) { if (t.size() > 0) t += ", "; t += "StudyInstanceUID=" + ValueAsString(summary, DICOM_TAG_STUDY_INSTANCE_UID); } else { if (s.size() > 0) s += ", "; s += "StudyInstanceUID"; } if (summary.HasTag(DICOM_TAG_SERIES_INSTANCE_UID)) { if (t.size() > 0) t += ", "; t += "SeriesInstanceUID=" + ValueAsString(summary, DICOM_TAG_SERIES_INSTANCE_UID); } else { if (s.size() > 0) s += ", "; s += "SeriesInstanceUID"; } if (summary.HasTag(DICOM_TAG_SOP_INSTANCE_UID)) { if (t.size() > 0) t += ", "; t += "SOPInstanceUID=" + ValueAsString(summary, DICOM_TAG_SOP_INSTANCE_UID); } else { if (s.size() > 0) s += ", "; s += "SOPInstanceUID"; } if (t.size() == 0) { LOG(ERROR) << "Store has failed because all the required tags (" << s << ") are missing (is it a DICOMDIR file?)"; } else { LOG(ERROR) << "Store has failed because required tags (" << s << ") are missing for the following instance: " << t; } }
bool OrthancFindRequestHandler::Handle(DicomFindAnswers& answers, const DicomMap& input, const std::string& callingAETitle) { /** * Ensure that the calling modality is known to Orthanc. **/ RemoteModalityParameters modality; if (!Configuration::LookupDicomModalityUsingAETitle(modality, callingAETitle)) { throw OrthancException("Unknown modality"); } // ModalityManufacturer manufacturer = modality.GetManufacturer(); bool caseSensitivePN = Configuration::GetGlobalBoolParameter("CaseSensitivePN", false); /** * Retrieve the query level. **/ const DicomValue* levelTmp = input.TestAndGetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL); if (levelTmp == NULL) { throw OrthancException(ErrorCode_BadRequest); } ResourceType level = StringToResourceType(levelTmp->AsString().c_str()); if (level != ResourceType_Patient && level != ResourceType_Study && level != ResourceType_Series && level != ResourceType_Instance) { throw OrthancException(ErrorCode_NotImplemented); } DicomArray query(input); LOG(INFO) << "DICOM C-Find request at level: " << EnumerationToString(level); for (size_t i = 0; i < query.GetSize(); i++) { if (!query.GetElement(i).GetValue().IsNull()) { LOG(INFO) << " " << query.GetElement(i).GetTag() << " " << FromDcmtkBridge::GetName(query.GetElement(i).GetTag()) << " = " << query.GetElement(i).GetValue().AsString(); } } /** * Build up the query object. **/ CFindQuery findQuery(answers, context_.GetIndex(), query); findQuery.SetLevel(level); for (size_t i = 0; i < query.GetSize(); i++) { const DicomTag tag = query.GetElement(i).GetTag(); if (query.GetElement(i).GetValue().IsNull() || tag == DICOM_TAG_QUERY_RETRIEVE_LEVEL || tag == DICOM_TAG_SPECIFIC_CHARACTER_SET) { continue; } std::string value = query.GetElement(i).GetValue().AsString(); if (value.size() == 0) { // An empty string corresponds to a "*" wildcard constraint, so we ignore it continue; } if (tag == DICOM_TAG_MODALITIES_IN_STUDY) { findQuery.SetModalitiesInStudy(value); } else { findQuery.SetConstraint(tag, value, caseSensitivePN); } } /** * Run the query. **/ ResourceFinder finder(context_); switch (level) { case ResourceType_Patient: case ResourceType_Study: case ResourceType_Series: finder.SetMaxResults(maxResults_); break; case ResourceType_Instance: finder.SetMaxResults(maxInstances_); break; default: throw OrthancException(ErrorCode_InternalError); } std::list<std::string> tmp; bool finished = finder.Apply(tmp, findQuery); LOG(INFO) << "Number of matching resources: " << tmp.size(); return finished; }
static void AddAnswer(DicomFindAnswers& answers, const Json::Value& resource, const DicomArray& query, const std::list<DicomTag>& sequencesToReturn, const DicomMap* counters) { DicomMap result; for (size_t i = 0; i < query.GetSize(); i++) { if (query.GetElement(i).GetTag() == DICOM_TAG_QUERY_RETRIEVE_LEVEL) { // Fix issue 30 on Google Code (QR response missing "Query/Retrieve Level" (008,0052)) result.SetValue(query.GetElement(i).GetTag(), query.GetElement(i).GetValue()); } else if (query.GetElement(i).GetTag() == DICOM_TAG_SPECIFIC_CHARACTER_SET) { // Do not include the encoding, this is handled by class ParsedDicomFile } else { std::string tag = query.GetElement(i).GetTag().Format(); std::string value; if (resource.isMember(tag)) { value = resource.get(tag, Json::arrayValue).get("Value", "").asString(); result.SetValue(query.GetElement(i).GetTag(), value, false); } else { result.SetValue(query.GetElement(i).GetTag(), "", false); } } } if (counters != NULL) { DicomArray tmp(*counters); for (size_t i = 0; i < tmp.GetSize(); i++) { result.SetValue(tmp.GetElement(i).GetTag(), tmp.GetElement(i).GetValue().GetContent(), false); } } if (result.GetSize() == 0 && sequencesToReturn.empty()) { LOG(WARNING) << "The C-FIND request does not return any DICOM tag"; } else if (sequencesToReturn.empty()) { answers.Add(result); } else { ParsedDicomFile dicom(result); for (std::list<DicomTag>::const_iterator tag = sequencesToReturn.begin(); tag != sequencesToReturn.end(); ++tag) { const Json::Value& source = resource[tag->Format()]; if (source.type() == Json::objectValue && source.isMember("Type") && source.isMember("Value") && source["Type"].asString() == "Sequence" && source["Value"].type() == Json::arrayValue) { Json::Value content = Json::arrayValue; for (Json::Value::ArrayIndex i = 0; i < source["Value"].size(); i++) { Json::Value item; Toolbox::SimplifyTags(item, source["Value"][i], DicomToJsonFormat_Short); content.append(item); } dicom.Replace(*tag, content, false, DicomReplaceMode_InsertIfAbsent); } } answers.Add(dicom); } }
bool OrthancFindRequestHandler::ApplyLuaFilter(DicomMap& target, const DicomMap& source, const std::string& remoteIp, const std::string& remoteAet, const std::string& calledAet) { Json::Value output; { LuaScripting::Locker locker(context_.GetLua()); static const char* NAME = "IncomingFindRequestFilter"; if (!locker.GetLua().IsExistingFunction(NAME)) { return false; } Json::Value tmp = Json::objectValue; DicomArray a(source); for (size_t i = 0; i < a.GetSize(); i++) { const DicomValue& v = a.GetElement(i).GetValue(); std::string s = (v.IsNull() || v.IsBinary()) ? "" : v.GetContent(); tmp[a.GetElement(i).GetTag().Format()] = s; } Json::Value origin = Json::objectValue; origin["RemoteIp"] = remoteIp; origin["RemoteAet"] = remoteAet; origin["CalledAet"] = calledAet; LuaFunctionCall call(locker.GetLua(), NAME); call.PushJson(tmp); call.PushJson(origin); call.ExecuteToJson(output, true); } // The Lua context is released at this point if (output.type() != Json::objectValue) { LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table"; throw OrthancException(ErrorCode_LuaBadOutput); } Json::Value::Members members = output.getMemberNames(); for (size_t i = 0; i < members.size(); i++) { if (output[members[i]].type() != Json::stringValue) { LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table mapping names of DICOM tags to strings"; throw OrthancException(ErrorCode_LuaBadOutput); } DicomTag tag(FromDcmtkBridge::ParseTag(members[i])); target.SetValue(tag, output[members[i]].asString(), false); } return true; }