Mercurial > hg > orthanc
comparison OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp @ 5296:5053a10da5a2
Fix orphan files remaining in storage when working with MaximumStorageSize
author | Alain Mazy <am@osimis.io> |
---|---|
date | Mon, 22 May 2023 15:00:01 +0200 |
parents | f22c8fac764b |
children | c78138dc3889 |
comparison
equal
deleted
inserted
replaced
5294:e0e2aee4453e | 5296:5053a10da5a2 |
---|---|
3096 return storeStatus_; | 3096 return storeStatus_; |
3097 } | 3097 } |
3098 | 3098 |
3099 virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE | 3099 virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE |
3100 { | 3100 { |
3101 try | 3101 IDatabaseWrapper::CreateInstanceResult status; |
3102 { | 3102 int64_t instanceId; |
3103 IDatabaseWrapper::CreateInstanceResult status; | 3103 |
3104 int64_t instanceId; | 3104 bool isNewInstance = transaction.CreateInstance(status, instanceId, hashPatient_, |
3105 | 3105 hashStudy_, hashSeries_, hashInstance_); |
3106 bool isNewInstance = transaction.CreateInstance(status, instanceId, hashPatient_, | 3106 |
3107 hashStudy_, hashSeries_, hashInstance_); | 3107 if (isReconstruct_ && isNewInstance) |
3108 | 3108 { |
3109 if (isReconstruct_ && isNewInstance) | 3109 // In case of reconstruct, we just want to modify the attachments and some metadata like the TransferSyntex |
3110 { | 3110 // The DicomTags and many metadata have already been updated before we get here in ReconstructInstance |
3111 // In case of reconstruct, we just want to modify the attachments and some metadata like the TransferSyntex | 3111 throw OrthancException(ErrorCode_InternalError, "New instance while reconstructing; this should not happen."); |
3112 // The DicomTags and many metadata have already been updated before we get here in ReconstructInstance | 3112 } |
3113 throw OrthancException(ErrorCode_InternalError, "New instance while reconstructing; this should not happen."); | 3113 |
3114 } | 3114 // Check whether this instance is already stored |
3115 | 3115 if (!isNewInstance && !isReconstruct_) |
3116 // Check whether this instance is already stored | 3116 { |
3117 if (!isNewInstance && !isReconstruct_) | 3117 // The instance already exists |
3118 { | 3118 if (overwrite_) |
3119 // The instance already exists | 3119 { |
3120 if (overwrite_) | 3120 // Overwrite the old instance |
3121 LOG(INFO) << "Overwriting instance: " << hashInstance_; | |
3122 transaction.DeleteResource(instanceId); | |
3123 | |
3124 // Re-create the instance, now that the old one is removed | |
3125 if (!transaction.CreateInstance(status, instanceId, hashPatient_, | |
3126 hashStudy_, hashSeries_, hashInstance_)) | |
3121 { | 3127 { |
3122 // Overwrite the old instance | 3128 throw OrthancException(ErrorCode_InternalError, "No new instance while overwriting; this should not happen."); |
3123 LOG(INFO) << "Overwriting instance: " << hashInstance_; | |
3124 transaction.DeleteResource(instanceId); | |
3125 | |
3126 // Re-create the instance, now that the old one is removed | |
3127 if (!transaction.CreateInstance(status, instanceId, hashPatient_, | |
3128 hashStudy_, hashSeries_, hashInstance_)) | |
3129 { | |
3130 throw OrthancException(ErrorCode_InternalError, "No new instance while overwriting; this should not happen."); | |
3131 } | |
3132 } | 3129 } |
3133 else | 3130 } |
3131 else | |
3132 { | |
3133 // Do nothing if the instance already exists and overwriting is disabled | |
3134 transaction.GetAllMetadata(instanceMetadata_, instanceId); | |
3135 storeStatus_ = StoreStatus_AlreadyStored; | |
3136 return; | |
3137 } | |
3138 } | |
3139 | |
3140 | |
3141 if (!isReconstruct_) // don't signal new resources if this is a reconstruction | |
3142 { | |
3143 // Warn about the creation of new resources. The order must be | |
3144 // from instance to patient. | |
3145 | |
3146 // NB: In theory, could be sped up by grouping the underlying | |
3147 // calls to "transaction.LogChange()". However, this would only have an | |
3148 // impact when new patient/study/series get created, which | |
3149 // occurs far less often that creating new instances. The | |
3150 // positive impact looks marginal in practice. | |
3151 transaction.LogChange(instanceId, ChangeType_NewInstance, ResourceType_Instance, hashInstance_); | |
3152 | |
3153 if (status.isNewSeries_) | |
3154 { | |
3155 transaction.LogChange(status.seriesId_, ChangeType_NewSeries, ResourceType_Series, hashSeries_); | |
3156 } | |
3157 | |
3158 if (status.isNewStudy_) | |
3159 { | |
3160 transaction.LogChange(status.studyId_, ChangeType_NewStudy, ResourceType_Study, hashStudy_); | |
3161 } | |
3162 | |
3163 if (status.isNewPatient_) | |
3164 { | |
3165 transaction.LogChange(status.patientId_, ChangeType_NewPatient, ResourceType_Patient, hashPatient_); | |
3166 } | |
3167 } | |
3168 | |
3169 // Ensure there is enough room in the storage for the new instance | |
3170 uint64_t instanceSize = 0; | |
3171 for (Attachments::const_iterator it = attachments_.begin(); | |
3172 it != attachments_.end(); ++it) | |
3173 { | |
3174 instanceSize += it->GetCompressedSize(); | |
3175 } | |
3176 | |
3177 if (!isReconstruct_) // reconstruction should not affect recycling | |
3178 { | |
3179 if (maximumStorageMode_ == MaxStorageMode_Reject) | |
3180 { | |
3181 if (transaction.HasReachedMaxStorageSize(maximumStorageSize_, instanceSize)) | |
3134 { | 3182 { |
3135 // Do nothing if the instance already exists and overwriting is disabled | 3183 storeStatus_ = StoreStatus_StorageFull; |
3136 transaction.GetAllMetadata(instanceMetadata_, instanceId); | 3184 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum storage size reached"); // throw to cancel the transaction |
3137 storeStatus_ = StoreStatus_AlreadyStored; | |
3138 return; | |
3139 } | 3185 } |
3140 } | 3186 if (transaction.HasReachedMaxPatientCount(maximumPatientCount_, hashPatient_)) |
3141 | 3187 { |
3142 | 3188 storeStatus_ = StoreStatus_StorageFull; |
3143 if (!isReconstruct_) // don't signal new resources if this is a reconstruction | 3189 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum patient count reached"); // throw to cancel the transaction |
3144 { | 3190 } |
3145 // Warn about the creation of new resources. The order must be | 3191 } |
3146 // from instance to patient. | 3192 else |
3147 | 3193 { |
3148 // NB: In theory, could be sped up by grouping the underlying | 3194 transaction.Recycle(maximumStorageSize_, maximumPatientCount_, |
3149 // calls to "transaction.LogChange()". However, this would only have an | 3195 instanceSize, hashPatient_ /* don't consider the current patient for recycling */); |
3150 // impact when new patient/study/series get created, which | 3196 } |
3151 // occurs far less often that creating new instances. The | 3197 } |
3152 // positive impact looks marginal in practice. | 3198 |
3153 transaction.LogChange(instanceId, ChangeType_NewInstance, ResourceType_Instance, hashInstance_); | 3199 // Attach the files to the newly created instance |
3200 for (Attachments::const_iterator it = attachments_.begin(); | |
3201 it != attachments_.end(); ++it) | |
3202 { | |
3203 if (isReconstruct_) | |
3204 { | |
3205 // we are replacing attachments during a reconstruction | |
3206 transaction.DeleteAttachment(instanceId, it->GetContentType()); | |
3207 } | |
3208 | |
3209 transaction.AddAttachment(instanceId, *it, 0 /* this is the first revision */); | |
3210 } | |
3211 | |
3212 if (!isReconstruct_) | |
3213 { | |
3214 ResourcesContent content(true /* new resource, metadata can be set */); | |
3215 | |
3216 // Attach the user-specified metadata (in case of reconstruction, metadata_ contains all past metadata, including the system ones we want to keep) | |
3217 for (MetadataMap::const_iterator | |
3218 it = metadata_.begin(); it != metadata_.end(); ++it) | |
3219 { | |
3220 switch (it->first.first) | |
3221 { | |
3222 case ResourceType_Patient: | |
3223 content.AddMetadata(status.patientId_, it->first.second, it->second); | |
3224 break; | |
3225 | |
3226 case ResourceType_Study: | |
3227 content.AddMetadata(status.studyId_, it->first.second, it->second); | |
3228 break; | |
3229 | |
3230 case ResourceType_Series: | |
3231 content.AddMetadata(status.seriesId_, it->first.second, it->second); | |
3232 break; | |
3233 | |
3234 case ResourceType_Instance: | |
3235 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3236 it->first.second, it->second); | |
3237 break; | |
3238 | |
3239 default: | |
3240 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
3241 } | |
3242 } | |
3243 | |
3244 if (!isReconstruct_) | |
3245 { | |
3246 // Populate the tags of the newly-created resources | |
3247 content.AddResource(instanceId, ResourceType_Instance, dicomSummary_); | |
3248 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance)); // New in Orthanc 1.11.0 | |
3249 SetMainDicomSequenceMetadata(content, instanceId, dicomSummary_, ResourceType_Instance); // new in Orthanc 1.11.1 | |
3154 | 3250 |
3155 if (status.isNewSeries_) | 3251 if (status.isNewSeries_) |
3156 { | 3252 { |
3157 transaction.LogChange(status.seriesId_, ChangeType_NewSeries, ResourceType_Series, hashSeries_); | 3253 content.AddResource(status.seriesId_, ResourceType_Series, dicomSummary_); |
3254 content.AddMetadata(status.seriesId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series)); // New in Orthanc 1.11.0 | |
3255 SetMainDicomSequenceMetadata(content, status.seriesId_, dicomSummary_, ResourceType_Series); // new in Orthanc 1.11.1 | |
3158 } | 3256 } |
3159 | 3257 |
3160 if (status.isNewStudy_) | 3258 if (status.isNewStudy_) |
3161 { | 3259 { |
3162 transaction.LogChange(status.studyId_, ChangeType_NewStudy, ResourceType_Study, hashStudy_); | 3260 content.AddResource(status.studyId_, ResourceType_Study, dicomSummary_); |
3261 content.AddMetadata(status.studyId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study)); // New in Orthanc 1.11.0 | |
3262 SetMainDicomSequenceMetadata(content, status.studyId_, dicomSummary_, ResourceType_Study); // new in Orthanc 1.11.1 | |
3163 } | 3263 } |
3164 | 3264 |
3165 if (status.isNewPatient_) | 3265 if (status.isNewPatient_) |
3166 { | 3266 { |
3167 transaction.LogChange(status.patientId_, ChangeType_NewPatient, ResourceType_Patient, hashPatient_); | 3267 content.AddResource(status.patientId_, ResourceType_Patient, dicomSummary_); |
3268 content.AddMetadata(status.patientId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient)); // New in Orthanc 1.11.0 | |
3269 SetMainDicomSequenceMetadata(content, status.patientId_, dicomSummary_, ResourceType_Patient); // new in Orthanc 1.11.1 | |
3168 } | 3270 } |
3169 } | 3271 |
3272 // Attach the auto-computed metadata for the patient/study/series levels | |
3273 std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */); | |
3274 content.AddMetadata(status.seriesId_, MetadataType_LastUpdate, now); | |
3275 content.AddMetadata(status.studyId_, MetadataType_LastUpdate, now); | |
3276 content.AddMetadata(status.patientId_, MetadataType_LastUpdate, now); | |
3277 | |
3278 if (status.isNewSeries_) | |
3279 { | |
3280 if (hasExpectedInstances_) | |
3281 { | |
3282 content.AddMetadata(status.seriesId_, MetadataType_Series_ExpectedNumberOfInstances, | |
3283 boost::lexical_cast<std::string>(expectedInstances_)); | |
3284 } | |
3285 | |
3286 // New in Orthanc 1.9.0 | |
3287 content.AddMetadata(status.seriesId_, MetadataType_RemoteAet, | |
3288 origin_.GetRemoteAetC()); | |
3289 } | |
3290 // Attach the auto-computed metadata for the instance level, | |
3291 // reflecting these additions into the input metadata map | |
3292 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3293 MetadataType_Instance_ReceptionDate, now); | |
3294 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_RemoteAet, | |
3295 origin_.GetRemoteAetC()); | |
3296 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_Instance_Origin, | |
3297 EnumerationToString(origin_.GetRequestOrigin())); | |
3298 | |
3299 std::string s; | |
3300 | |
3301 if (origin_.LookupRemoteIp(s)) | |
3302 { | |
3303 // New in Orthanc 1.4.0 | |
3304 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3305 MetadataType_Instance_RemoteIp, s); | |
3306 } | |
3307 | |
3308 if (origin_.LookupCalledAet(s)) | |
3309 { | |
3310 // New in Orthanc 1.4.0 | |
3311 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3312 MetadataType_Instance_CalledAet, s); | |
3313 } | |
3314 | |
3315 if (origin_.LookupHttpUsername(s)) | |
3316 { | |
3317 // New in Orthanc 1.4.0 | |
3318 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3319 MetadataType_Instance_HttpUsername, s); | |
3320 } | |
3321 } | |
3322 | |
3323 // Following metadatas are also updated if reconstructing the instance. | |
3324 // They might be missing since they have been introduced along Orthanc versions. | |
3325 | |
3326 if (hasTransferSyntax_) | |
3327 { | |
3328 // New in Orthanc 1.2.0 | |
3329 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3330 MetadataType_Instance_TransferSyntax, | |
3331 GetTransferSyntaxUid(transferSyntax_)); | |
3332 } | |
3333 | |
3334 if (hasPixelDataOffset_) | |
3335 { | |
3336 // New in Orthanc 1.9.1 | |
3337 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3338 MetadataType_Instance_PixelDataOffset, | |
3339 boost::lexical_cast<std::string>(pixelDataOffset_)); | |
3340 } | |
3170 | 3341 |
3171 // Ensure there is enough room in the storage for the new instance | 3342 const DicomValue* value; |
3172 uint64_t instanceSize = 0; | 3343 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_SOP_CLASS_UID)) != NULL && |
3173 for (Attachments::const_iterator it = attachments_.begin(); | 3344 !value->IsNull() && |
3174 it != attachments_.end(); ++it) | 3345 !value->IsBinary()) |
3175 { | 3346 { |
3176 instanceSize += it->GetCompressedSize(); | 3347 SetInstanceMetadata(content, instanceMetadata_, instanceId, |
3177 } | 3348 MetadataType_Instance_SopClassUid, value->GetContent()); |
3178 | 3349 } |
3179 if (!isReconstruct_) // reconstruction should not affect recycling | 3350 |
3180 { | 3351 |
3181 if (maximumStorageMode_ == MaxStorageMode_Reject) | 3352 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || |
3182 { | 3353 (value = dicomSummary_.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) |
3183 if (transaction.HasReachedMaxStorageSize(maximumStorageSize_, instanceSize)) | 3354 { |
3184 { | 3355 if (!value->IsNull() && |
3185 storeStatus_ = StoreStatus_StorageFull; | |
3186 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum storage size reached"); // throw to cancel the transaction | |
3187 } | |
3188 if (transaction.HasReachedMaxPatientCount(maximumPatientCount_, hashPatient_)) | |
3189 { | |
3190 storeStatus_ = StoreStatus_StorageFull; | |
3191 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum patient count reached"); // throw to cancel the transaction | |
3192 } | |
3193 } | |
3194 else | |
3195 { | |
3196 transaction.Recycle(maximumStorageSize_, maximumPatientCount_, | |
3197 instanceSize, hashPatient_ /* don't consider the current patient for recycling */); | |
3198 } | |
3199 } | |
3200 | |
3201 // Attach the files to the newly created instance | |
3202 for (Attachments::const_iterator it = attachments_.begin(); | |
3203 it != attachments_.end(); ++it) | |
3204 { | |
3205 if (isReconstruct_) | |
3206 { | |
3207 // we are replacing attachments during a reconstruction | |
3208 transaction.DeleteAttachment(instanceId, it->GetContentType()); | |
3209 } | |
3210 | |
3211 transaction.AddAttachment(instanceId, *it, 0 /* this is the first revision */); | |
3212 } | |
3213 | |
3214 if (!isReconstruct_) | |
3215 { | |
3216 ResourcesContent content(true /* new resource, metadata can be set */); | |
3217 | |
3218 // Attach the user-specified metadata (in case of reconstruction, metadata_ contains all past metadata, including the system ones we want to keep) | |
3219 for (MetadataMap::const_iterator | |
3220 it = metadata_.begin(); it != metadata_.end(); ++it) | |
3221 { | |
3222 switch (it->first.first) | |
3223 { | |
3224 case ResourceType_Patient: | |
3225 content.AddMetadata(status.patientId_, it->first.second, it->second); | |
3226 break; | |
3227 | |
3228 case ResourceType_Study: | |
3229 content.AddMetadata(status.studyId_, it->first.second, it->second); | |
3230 break; | |
3231 | |
3232 case ResourceType_Series: | |
3233 content.AddMetadata(status.seriesId_, it->first.second, it->second); | |
3234 break; | |
3235 | |
3236 case ResourceType_Instance: | |
3237 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3238 it->first.second, it->second); | |
3239 break; | |
3240 | |
3241 default: | |
3242 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
3243 } | |
3244 } | |
3245 | |
3246 if (!isReconstruct_) | |
3247 { | |
3248 // Populate the tags of the newly-created resources | |
3249 content.AddResource(instanceId, ResourceType_Instance, dicomSummary_); | |
3250 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance)); // New in Orthanc 1.11.0 | |
3251 SetMainDicomSequenceMetadata(content, instanceId, dicomSummary_, ResourceType_Instance); // new in Orthanc 1.11.1 | |
3252 | |
3253 if (status.isNewSeries_) | |
3254 { | |
3255 content.AddResource(status.seriesId_, ResourceType_Series, dicomSummary_); | |
3256 content.AddMetadata(status.seriesId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series)); // New in Orthanc 1.11.0 | |
3257 SetMainDicomSequenceMetadata(content, status.seriesId_, dicomSummary_, ResourceType_Series); // new in Orthanc 1.11.1 | |
3258 } | |
3259 | |
3260 if (status.isNewStudy_) | |
3261 { | |
3262 content.AddResource(status.studyId_, ResourceType_Study, dicomSummary_); | |
3263 content.AddMetadata(status.studyId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study)); // New in Orthanc 1.11.0 | |
3264 SetMainDicomSequenceMetadata(content, status.studyId_, dicomSummary_, ResourceType_Study); // new in Orthanc 1.11.1 | |
3265 } | |
3266 | |
3267 if (status.isNewPatient_) | |
3268 { | |
3269 content.AddResource(status.patientId_, ResourceType_Patient, dicomSummary_); | |
3270 content.AddMetadata(status.patientId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient)); // New in Orthanc 1.11.0 | |
3271 SetMainDicomSequenceMetadata(content, status.patientId_, dicomSummary_, ResourceType_Patient); // new in Orthanc 1.11.1 | |
3272 } | |
3273 | |
3274 // Attach the auto-computed metadata for the patient/study/series levels | |
3275 std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */); | |
3276 content.AddMetadata(status.seriesId_, MetadataType_LastUpdate, now); | |
3277 content.AddMetadata(status.studyId_, MetadataType_LastUpdate, now); | |
3278 content.AddMetadata(status.patientId_, MetadataType_LastUpdate, now); | |
3279 | |
3280 if (status.isNewSeries_) | |
3281 { | |
3282 if (hasExpectedInstances_) | |
3283 { | |
3284 content.AddMetadata(status.seriesId_, MetadataType_Series_ExpectedNumberOfInstances, | |
3285 boost::lexical_cast<std::string>(expectedInstances_)); | |
3286 } | |
3287 | |
3288 // New in Orthanc 1.9.0 | |
3289 content.AddMetadata(status.seriesId_, MetadataType_RemoteAet, | |
3290 origin_.GetRemoteAetC()); | |
3291 } | |
3292 // Attach the auto-computed metadata for the instance level, | |
3293 // reflecting these additions into the input metadata map | |
3294 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3295 MetadataType_Instance_ReceptionDate, now); | |
3296 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_RemoteAet, | |
3297 origin_.GetRemoteAetC()); | |
3298 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_Instance_Origin, | |
3299 EnumerationToString(origin_.GetRequestOrigin())); | |
3300 | |
3301 std::string s; | |
3302 | |
3303 if (origin_.LookupRemoteIp(s)) | |
3304 { | |
3305 // New in Orthanc 1.4.0 | |
3306 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3307 MetadataType_Instance_RemoteIp, s); | |
3308 } | |
3309 | |
3310 if (origin_.LookupCalledAet(s)) | |
3311 { | |
3312 // New in Orthanc 1.4.0 | |
3313 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3314 MetadataType_Instance_CalledAet, s); | |
3315 } | |
3316 | |
3317 if (origin_.LookupHttpUsername(s)) | |
3318 { | |
3319 // New in Orthanc 1.4.0 | |
3320 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3321 MetadataType_Instance_HttpUsername, s); | |
3322 } | |
3323 } | |
3324 | |
3325 // Following metadatas are also updated if reconstructing the instance. | |
3326 // They might be missing since they have been introduced along Orthanc versions. | |
3327 | |
3328 if (hasTransferSyntax_) | |
3329 { | |
3330 // New in Orthanc 1.2.0 | |
3331 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3332 MetadataType_Instance_TransferSyntax, | |
3333 GetTransferSyntaxUid(transferSyntax_)); | |
3334 } | |
3335 | |
3336 if (hasPixelDataOffset_) | |
3337 { | |
3338 // New in Orthanc 1.9.1 | |
3339 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3340 MetadataType_Instance_PixelDataOffset, | |
3341 boost::lexical_cast<std::string>(pixelDataOffset_)); | |
3342 } | |
3343 | |
3344 const DicomValue* value; | |
3345 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_SOP_CLASS_UID)) != NULL && | |
3346 !value->IsNull() && | |
3347 !value->IsBinary()) | 3356 !value->IsBinary()) |
3348 { | 3357 { |
3349 SetInstanceMetadata(content, instanceMetadata_, instanceId, | 3358 SetInstanceMetadata(content, instanceMetadata_, instanceId, |
3350 MetadataType_Instance_SopClassUid, value->GetContent()); | 3359 MetadataType_Instance_IndexInSeries, Toolbox::StripSpaces(value->GetContent())); |
3351 } | 3360 } |
3352 | 3361 } |
3353 | 3362 |
3354 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || | 3363 |
3355 (value = dicomSummary_.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) | 3364 transaction.SetResourcesContent(content); |
3356 { | 3365 } |
3357 if (!value->IsNull() && | 3366 |
3358 !value->IsBinary()) | 3367 |
3359 { | 3368 // Check whether the series of this new instance is now completed |
3360 SetInstanceMetadata(content, instanceMetadata_, instanceId, | 3369 int64_t expectedNumberOfInstances; |
3361 MetadataType_Instance_IndexInSeries, Toolbox::StripSpaces(value->GetContent())); | 3370 if (ComputeExpectedNumberOfInstances(expectedNumberOfInstances, dicomSummary_)) |
3362 } | 3371 { |
3363 } | 3372 SeriesStatus seriesStatus = transaction.GetSeriesStatus(status.seriesId_, expectedNumberOfInstances); |
3364 | 3373 if (seriesStatus == SeriesStatus_Complete) |
3374 { | |
3375 transaction.LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries_); | |
3376 } | |
3377 } | |
3365 | 3378 |
3366 transaction.SetResourcesContent(content); | 3379 transaction.LogChange(status.seriesId_, ChangeType_NewChildInstance, ResourceType_Series, hashSeries_); |
3367 } | 3380 transaction.LogChange(status.studyId_, ChangeType_NewChildInstance, ResourceType_Study, hashStudy_); |
3368 | 3381 transaction.LogChange(status.patientId_, ChangeType_NewChildInstance, ResourceType_Patient, hashPatient_); |
3369 | 3382 |
3370 // Check whether the series of this new instance is now completed | 3383 // Mark the parent resources of this instance as unstable |
3371 int64_t expectedNumberOfInstances; | 3384 transaction.GetTransactionContext().MarkAsUnstable(status.seriesId_, ResourceType_Series, hashSeries_); |
3372 if (ComputeExpectedNumberOfInstances(expectedNumberOfInstances, dicomSummary_)) | 3385 transaction.GetTransactionContext().MarkAsUnstable(status.studyId_, ResourceType_Study, hashStudy_); |
3373 { | 3386 transaction.GetTransactionContext().MarkAsUnstable(status.patientId_, ResourceType_Patient, hashPatient_); |
3374 SeriesStatus seriesStatus = transaction.GetSeriesStatus(status.seriesId_, expectedNumberOfInstances); | 3387 transaction.GetTransactionContext().SignalAttachmentsAdded(instanceSize); |
3375 if (seriesStatus == SeriesStatus_Complete) | 3388 |
3376 { | 3389 storeStatus_ = StoreStatus_Success; |
3377 transaction.LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries_); | |
3378 } | |
3379 } | |
3380 | |
3381 transaction.LogChange(status.seriesId_, ChangeType_NewChildInstance, ResourceType_Series, hashSeries_); | |
3382 transaction.LogChange(status.studyId_, ChangeType_NewChildInstance, ResourceType_Study, hashStudy_); | |
3383 transaction.LogChange(status.patientId_, ChangeType_NewChildInstance, ResourceType_Patient, hashPatient_); | |
3384 | |
3385 // Mark the parent resources of this instance as unstable | |
3386 transaction.GetTransactionContext().MarkAsUnstable(status.seriesId_, ResourceType_Series, hashSeries_); | |
3387 transaction.GetTransactionContext().MarkAsUnstable(status.studyId_, ResourceType_Study, hashStudy_); | |
3388 transaction.GetTransactionContext().MarkAsUnstable(status.patientId_, ResourceType_Patient, hashPatient_); | |
3389 transaction.GetTransactionContext().SignalAttachmentsAdded(instanceSize); | |
3390 | |
3391 storeStatus_ = StoreStatus_Success; | |
3392 } | |
3393 catch (OrthancException& e) | |
3394 { | |
3395 if (e.GetErrorCode() == ErrorCode_DatabaseCannotSerialize) | |
3396 { | |
3397 throw; // the transaction has failed -> do not commit the current transaction (and retry) | |
3398 } | |
3399 else | |
3400 { | |
3401 LOG(ERROR) << "EXCEPTION [" << e.What() << " - " << e.GetDetails() << "]"; | |
3402 | |
3403 if (e.GetErrorCode() == ErrorCode_FullStorage) | |
3404 { | |
3405 throw; // do not commit the current transaction | |
3406 } | |
3407 | |
3408 // this is an expected failure, exit normaly and commit the current transaction | |
3409 storeStatus_ = StoreStatus_Failure; | |
3410 } | |
3411 } | |
3412 } | 3390 } |
3413 }; | 3391 }; |
3414 | 3392 |
3415 | 3393 |
3416 Operations operations(instanceMetadata, dicomSummary, attachments, metadata, origin, | 3394 Operations operations(instanceMetadata, dicomSummary, attachments, metadata, origin, |
3417 overwrite, hasTransferSyntax, transferSyntax, hasPixelDataOffset, | 3395 overwrite, hasTransferSyntax, transferSyntax, hasPixelDataOffset, |
3418 pixelDataOffset, maximumStorageMode, maximumStorageSize, maximumPatients, isReconstruct); | 3396 pixelDataOffset, maximumStorageMode, maximumStorageSize, maximumPatients, isReconstruct); |
3419 Apply(operations); | 3397 |
3420 return operations.GetStoreStatus(); | 3398 try |
3399 { | |
3400 Apply(operations); | |
3401 return operations.GetStoreStatus(); | |
3402 } | |
3403 catch (OrthancException& e) | |
3404 { | |
3405 if (e.GetErrorCode() == ErrorCode_FullStorage) | |
3406 { | |
3407 return StoreStatus_StorageFull; | |
3408 } | |
3409 else | |
3410 { | |
3411 // the transaction has failed -> do not commit the current transaction (and retry) | |
3412 throw; | |
3413 } | |
3414 } | |
3421 } | 3415 } |
3422 | 3416 |
3423 | 3417 |
3424 StoreStatus StatelessDatabaseOperations::AddAttachment(int64_t& newRevision, | 3418 StoreStatus StatelessDatabaseOperations::AddAttachment(int64_t& newRevision, |
3425 const FileInfo& attachment, | 3419 const FileInfo& attachment, |
3473 { | 3467 { |
3474 ResourceType resourceType; | 3468 ResourceType resourceType; |
3475 int64_t resourceId; | 3469 int64_t resourceId; |
3476 if (!transaction.LookupResource(resourceId, resourceType, publicId_)) | 3470 if (!transaction.LookupResource(resourceId, resourceType, publicId_)) |
3477 { | 3471 { |
3478 status_ = StoreStatus_Failure; // Inexistent resource | 3472 throw OrthancException(ErrorCode_InexistentItem, HttpStatus_404_NotFound); |
3479 } | 3473 } |
3480 else | 3474 else |
3481 { | 3475 { |
3482 // Possibly remove previous attachment | 3476 // Possibly remove previous attachment |
3483 { | 3477 { |