ServerContext::StoreResult ServerContext::StoreAfterTranscoding(std::string& resultPublicId,
DicomInstanceToStore& dicom,
StoreInstanceMode mode,
bool isReconstruct)
{
bool overwrite;
switch (mode)
{
case StoreInstanceMode_Default:
overwrite = overwriteInstances_;
break;
case StoreInstanceMode_OverwriteDuplicate:
overwrite = true;
break;
case StoreInstanceMode_IgnoreDuplicate:
overwrite = false;
break;
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
bool hasPixelDataOffset;
uint64_t pixelDataOffset;
hasPixelDataOffset = DicomStreamReader::LookupPixelDataOffset(
pixelDataOffset, dicom.GetBufferData(), dicom.GetBufferSize());
DicomTransferSyntax transferSyntax;
bool hasTransferSyntax = dicom.LookupTransferSyntax(transferSyntax);
DicomMap summary;
dicom.GetSummary(summary); // -> from Orthanc 1.11.1, this includes the leaf nodes and sequences
std::set<DicomTag> allMainDicomTags = DicomMap::GetAllMainDicomTags();
try
{
MetricsRegistry::Timer timer(GetMetricsRegistry(), "orthanc_store_dicom_duration_ms");
StorageAccessor accessor(area_, &storageCache_, GetMetricsRegistry());
DicomInstanceHasher hasher(summary);
resultPublicId = hasher.HashInstance();
Json::Value dicomAsJson;
dicom.GetDicomAsJson(dicomAsJson, allMainDicomTags); // don't crop any main dicom tags
Json::Value simplifiedTags;
Toolbox::SimplifyDicomAsJson(simplifiedTags, dicomAsJson, DicomToJsonFormat_Human);
// Test if the instance must be filtered out
StoreResult result;
if (!isReconstruct) // skip all filters if this is a reconstruction
{
boost::shared_lock<boost::shared_mutex> lock(listenersMutex_);
for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it)
{
try
{
if (!it->GetListener().FilterIncomingInstance(dicom, simplifiedTags))
{
result.SetStatus(StoreStatus_FilteredOut);
result.SetCStoreStatusCode(STATUS_Success); // to keep backward compatibility, we still return 'success'
break;
}
if (dicom.GetOrigin().GetRequestOrigin() == Orthanc::RequestOrigin_DicomProtocol)
{
uint16_t filterResult = STATUS_Success;
if (!it->GetListener().FilterIncomingCStoreInstance(filterResult, dicom, simplifiedTags))
{
// The instance is to be discarded
result.SetStatus(StoreStatus_FilteredOut);
result.SetCStoreStatusCode(filterResult);
break;
}
}
}
catch (OrthancException& e)
{
LOG(ERROR) << "Error in the " << it->GetDescription()
<< " callback while receiving an instance: " << e.What()
<< " (code " << e.GetErrorCode() << ")";
throw;
}
}
}
if (result.GetStatus() == StoreStatus_FilteredOut)
{
LOG(INFO) << "An incoming instance has been discarded by the filter";
return result;
}
// Remove the file from the DicomCache (useful if
// "OverwriteInstances" is set to "true")
dicomCache_.Invalidate(resultPublicId);
PublishDicomCacheMetrics();
// TODO Should we use "gzip" instead?
CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None);
FileInfo dicomInfo = accessor.Write(dicom.GetBufferData(), dicom.GetBufferSize(),
FileContentType_Dicom, compression, storeMD5_);
ServerIndex::Attachments attachments;
attachments.push_back(dicomInfo);
FileInfo dicomUntilPixelData;
ParsedDicomFile pdf(dicom.GetBufferData(), dicom.GetBufferSize());
DicomWebJsonVisitor visitor;
pdf.Apply(visitor);
Json::Value v = visitor.GetResult();
v["00431028"]["BulkDataURI"] = "http://localhost/dicom-web/studies/1.2.840.113619.2.417.3.2831214628.419.1651705382.616/series/1.2.840.113619.2.417.3.2831214628.419.1651705382.622.3/instances/1.2.840.113619.2.417.3.2831214628.419.1651705382.667.1/bulk/00431028";
v["00431028"].removeMember("InlineBinary");
v["7FE00010"]["BulkDataURI"] = "http://localhost/dicom-web/studies/1.2.840.113619.2.417.3.2831214628.419.1651705382.616/series/1.2.840.113619.2.417.3.2831214628.419.1651705382.622.3/instances/1.2.840.113619.2.417.3.2831214628.419.1651705382.667.1/bulk/7fe00010";
v["7FE00010"].removeMember("InlineBinary");
Json::StreamWriterBuilder wbuilder;
wbuilder["indentation"] = ""; // Optional
Json::String str = Json::writeString(wbuilder, v);
if (hasPixelDataOffset &&
(!area_.HasReadRange() ||
compressionEnabled_))
{
dicomUntilPixelData = accessor.Write(dicom.GetBufferData(), pixelDataOffset,
FileContentType_DicomUntilPixelData, compression, storeMD5_);
attachments.push_back(dicomUntilPixelData);
}
typedef std::map<MetadataType, std::string> InstanceMetadata;
InstanceMetadata instanceMetadata;
result.SetStatus(index_.Store(
instanceMetadata, summary, attachments, dicom.GetMetadata(), dicom.GetOrigin(), overwrite,
hasTransferSyntax, transferSyntax, hasPixelDataOffset, pixelDataOffset, isReconstruct));
// Only keep the metadata for the "instance" level
dicom.ClearMetadata();
for (InstanceMetadata::const_iterator it = instanceMetadata.begin();
it != instanceMetadata.end(); ++it)
{
dicom.AddMetadata(ResourceType_Instance, it->first, it->second);
}
if (result.GetStatus() != StoreStatus_Success)
{
accessor.Remove(dicomInfo);
if (dicomUntilPixelData.IsValid())
{
accessor.Remove(dicomUntilPixelData);
}
}
if (!isReconstruct)
{
// skip logs in case of reconstruction
switch (result.GetStatus())
{
case StoreStatus_Success:
LOG(INFO) << "New instance stored";
break;
case StoreStatus_AlreadyStored:
LOG(INFO) << "Already stored";
break;
case StoreStatus_Failure:
LOG(ERROR) << "Store failure";
break;
default:
// This should never happen
break;
}
// skip all signals if this is a reconstruction
if (result.GetStatus() == StoreStatus_Success ||
result.GetStatus() == StoreStatus_AlreadyStored)
{
boost::shared_lock<boost::shared_mutex> lock(listenersMutex_);
for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it)
{
try
{
it->GetListener().SignalStoredInstance(resultPublicId, dicom, simplifiedTags);
}
catch (OrthancException& e)
{
LOG(ERROR) << "Error in the " << it->GetDescription()
<< " callback while receiving an instance: " << e.What()
<< " (code " << e.GetErrorCode() << ")";
}
}
}
}
return result;
}
catch (OrthancException& e)
{
if (e.GetErrorCode() == ErrorCode_InexistentTag)
{
summary.LogMissingTagsForStore();
}
throw;
}
}