-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #65 in TELIMA/karnak from feat/update_from_dev to …
…release * commit 'b551265ceec9094b546c23cb59c987c9fb4c9e6a': feat: - set refresh activity to 1000 ms - change to service some classes dealing with transfer in order to autowired constructors and use database repositories - replace map collecting transfer status by an update of a new column in destination table - add new column transfer_in_progress in liquibase - ScheduledService executor which delay the set status back to false feat: - set refresh activity to 500 ms feat: - set refresh activity to 200 ms - set id as key in the loading image map - handle status to false for transferOther feat: - set refresh activity to 500 ms - with stream scu not working for large series: connection not released: simplify use finally to update end of transfer => use same method for stow and dicom feat: - set refresh activity to 1 second feat: - check status when uploading files DICOM/STOW - add column activity for Destination - depending on status set visible/invisible the loading spinner
- Loading branch information
Showing
16 changed files
with
468 additions
and
146 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
191 changes: 191 additions & 0 deletions
191
src/main/java/org/karnak/backend/service/CStoreSCPService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
/* | ||
* Copyright (c) 2021 Karnak Team and other contributors. | ||
* | ||
* This program and the accompanying materials are made available under the terms of the Eclipse | ||
* Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0, or the Apache | ||
* License, Version 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.0. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 | ||
*/ | ||
package org.karnak.backend.service; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.ScheduledExecutorService; | ||
import java.util.concurrent.ScheduledFuture; | ||
import java.util.concurrent.TimeUnit; | ||
import org.dcm4che3.data.Attributes; | ||
import org.dcm4che3.data.Tag; | ||
import org.dcm4che3.data.VR; | ||
import org.dcm4che3.net.Association; | ||
import org.dcm4che3.net.PDVInputStream; | ||
import org.dcm4che3.net.Status; | ||
import org.dcm4che3.net.pdu.PresentationContext; | ||
import org.dcm4che3.net.service.BasicCStoreSCP; | ||
import org.dcm4che3.net.service.DicomServiceException; | ||
import org.karnak.backend.data.entity.DestinationEntity; | ||
import org.karnak.backend.data.repo.DestinationRepo; | ||
import org.karnak.backend.dicom.ForwardDestination; | ||
import org.karnak.backend.dicom.ForwardDicomNode; | ||
import org.karnak.backend.dicom.ForwardUtil; | ||
import org.karnak.backend.dicom.ForwardUtil.Params; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Service; | ||
import org.weasis.dicom.param.DicomNode; | ||
|
||
@Service | ||
public class CStoreSCPService extends BasicCStoreSCP { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(CStoreSCPService.class); | ||
|
||
// Service | ||
private final DestinationRepo destinationRepo; | ||
|
||
private Map<ForwardDicomNode, List<ForwardDestination>> destinations; | ||
private volatile int priority; | ||
private volatile int status = 0; | ||
|
||
// Scheduled service for updating status transfer in progress | ||
private ScheduledFuture isDelayOver; | ||
private final ScheduledExecutorService executorService = | ||
Executors.newSingleThreadScheduledExecutor(); | ||
|
||
@Autowired | ||
public CStoreSCPService(final DestinationRepo destinationRepo) { | ||
super("*"); | ||
this.destinationRepo = destinationRepo; | ||
} | ||
|
||
public void init(Map<ForwardDicomNode, List<ForwardDestination>> destinations) { | ||
this.destinations = destinations; | ||
} | ||
|
||
@Override | ||
protected void store( | ||
Association as, PresentationContext pc, Attributes rq, PDVInputStream data, Attributes rsp) | ||
throws IOException { | ||
Optional<ForwardDicomNode> sourceNode = | ||
destinations.keySet().stream() | ||
.filter(n -> n.getForwardAETitle().equals(as.getCalledAET())) | ||
.findFirst(); | ||
if (sourceNode.isEmpty()) { | ||
throw new IllegalStateException("Cannot find the forward AeTitle " + as.getCalledAET()); | ||
} | ||
ForwardDicomNode fwdNode = sourceNode.get(); | ||
List<ForwardDestination> destList = destinations.get(fwdNode); | ||
if (destList == null || destList.isEmpty()) { | ||
throw new IllegalStateException("No DICOM destinations for " + fwdNode); | ||
} | ||
|
||
DicomNode callingNode = DicomNode.buildRemoteDicomNode(as); | ||
Set<DicomNode> srcNodes = fwdNode.getAcceptedSourceNodes(); | ||
boolean valid = | ||
srcNodes.isEmpty() | ||
|| srcNodes.stream() | ||
.anyMatch( | ||
n -> | ||
n.getAet().equals(callingNode.getAet()) | ||
&& (!n.isValidateHostname() | ||
|| n.equalsHostname(callingNode.getHostname()))); | ||
if (!valid) { | ||
rsp.setInt(Tag.Status, VR.US, Status.NotAuthorized); | ||
LOGGER.error( | ||
"Refused: not authorized (124H). Source node: {}. SopUID: {}", | ||
callingNode, | ||
rq.getString(Tag.AffectedSOPInstanceUID)); | ||
return; | ||
} | ||
|
||
rsp.setInt(Tag.Status, VR.US, status); | ||
|
||
try { | ||
Params p = | ||
new Params( | ||
rq.getString(Tag.AffectedSOPInstanceUID), | ||
rq.getString(Tag.AffectedSOPClassUID), | ||
pc.getTransferSyntax(), | ||
priority, | ||
data, | ||
as); | ||
|
||
// Update transfer status of destinations | ||
updateTransferStatus(destList); | ||
|
||
ForwardUtil.storeMultipleDestination(fwdNode, destList, p); | ||
|
||
} catch (Exception e) { | ||
throw new DicomServiceException(Status.ProcessingFailure, e); | ||
} | ||
} | ||
|
||
/** | ||
* Update transfer status: if there is a transfer in progress set status to true and schedule a | ||
* thread which will set back status to false in a certain delay. If a transfer is still in | ||
* progress after the end of the delay, set status to true and an other delay is scheduled. | ||
* | ||
* @param destinations Destinations to update | ||
*/ | ||
private void updateTransferStatus(List<ForwardDestination> destinations) { | ||
// if delay is over or first iteration | ||
if (isDelayOver == null || isDelayOver.isDone()) { | ||
// Set flag transfer in progress | ||
destinations.forEach(d -> updateTransferStatus(d, true)); | ||
// In a certain delay set back transfer in progress to false | ||
isDelayOver = | ||
executorService.schedule( | ||
() -> destinations.forEach(d -> updateTransferStatus(d, false)), 5, TimeUnit.SECONDS); | ||
} | ||
} | ||
|
||
/** | ||
* Update the transfer status of a destination | ||
* | ||
* @param destination Destination to retrieve | ||
* @param status Status to update | ||
*/ | ||
private void updateTransferStatus(ForwardDestination destination, boolean status) { | ||
// Retrieve the destination entity | ||
Optional<DestinationEntity> destinationEntityOptional = | ||
destinationRepo.findById(destination.getId()); | ||
|
||
if (destinationEntityOptional.isPresent()) { | ||
// Update the destination transfer status if destination has been found and destination | ||
// is active | ||
DestinationEntity destinationEntity = destinationEntityOptional.get(); | ||
if (destinationEntity.isActivate()) { | ||
destinationEntity.setTransferInProgress(status); | ||
destinationRepo.save(destinationEntity); | ||
} | ||
} | ||
} | ||
|
||
public Map<ForwardDicomNode, List<ForwardDestination>> getDestinations() { | ||
return destinations; | ||
} | ||
|
||
public void setDestinations(Map<ForwardDicomNode, List<ForwardDestination>> destinations) { | ||
this.destinations = destinations; | ||
} | ||
|
||
public int getPriority() { | ||
return priority; | ||
} | ||
|
||
public void setPriority(int priority) { | ||
this.priority = priority; | ||
} | ||
|
||
public int getStatus() { | ||
return status; | ||
} | ||
|
||
public void setStatus(int status) { | ||
this.status = status; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.