Skip to content

Commit

Permalink
Merge pull request #65 in TELIMA/karnak from feat/update_from_dev to …
Browse files Browse the repository at this point in the history
…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
jdcshug committed Aug 18, 2021
2 parents ce2bb0c + b551265 commit d722098
Show file tree
Hide file tree
Showing 16 changed files with 468 additions and 146 deletions.
2 changes: 2 additions & 0 deletions src/main/java/org/karnak/StartApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class)
@EntityScan("org.karnak.backend.data.entity")
@EnableJpaRepositories("org.karnak.backend.data.repo")
@EnableVaadin(value = "org.karnak")
@EnableScheduling
public class StartApplication implements CommandLineRunner {

private static final Logger log = LoggerFactory.getLogger(StartApplication.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ public class DestinationEntity implements Serializable {
// Transcode Only Uncompressed
private boolean transcodeOnlyUncompressed;

// Flag to know if there are some transfer activities on this destination
private boolean transferInProgress;

public DestinationEntity() {
this(null);
}
Expand Down Expand Up @@ -490,6 +493,14 @@ public void setTranscodeOnlyUncompressed(boolean transcodeOnlyUncompressed) {
this.transcodeOnlyUncompressed = transcodeOnlyUncompressed;
}

public boolean isTransferInProgress() {
return transferInProgress;
}

public void setTransferInProgress(boolean transferInProgress) {
this.transferInProgress = transferInProgress;
}

/**
* Informs if this object matches with the filter as text.
*
Expand Down Expand Up @@ -627,6 +638,7 @@ public boolean equals(Object o) {
&& Objects.equals(savePseudonym, that.savePseudonym)
&& Objects.equals(transferSyntax, that.transferSyntax)
&& Objects.equals(transcodeOnlyUncompressed, that.transcodeOnlyUncompressed)
&& Objects.equals(transferInProgress, that.transferInProgress)
&& Objects.equals(activateNotification, that.activateNotification)
&& Objects.equals(notify, that.notify)
&& Objects.equals(notifyObjectErrorPrefix, that.notifyObjectErrorPrefix)
Expand Down Expand Up @@ -658,6 +670,7 @@ public int hashCode() {
filterBySOPClasses,
transferSyntax,
transcodeOnlyUncompressed,
transferInProgress,
activateNotification,
notify,
notifyObjectErrorPrefix,
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/karnak/backend/dicom/DefacingUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
package org.karnak.backend.dicom;

import java.security.SecureRandom;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.opencv.core.Core.MinMaxLocResult;
Expand All @@ -21,7 +22,8 @@ public class DefacingUtil {
private DefacingUtil() {}

public static int randomY(int minY, int maxY, int bound) {
return (int) Math.floor(Math.random() * (maxY - minY + bound) + minY);
SecureRandom random = new SecureRandom();
return (int) Math.floor(random.nextDouble() * (maxY - minY + bound) + minY);
}

public static double pickRndYPxlColor(int xInit, int minY, int maxY, PlanarImage imgToPick) {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/karnak/backend/dicom/ForwardUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ public static synchronized StoreFromStreamSCU prepareTransfer(
String tsuid = p.getTsuid();
String dstTsuid = destination.getOutputTransferSyntax(tsuid);
StoreFromStreamSCU streamSCU = destination.getStreamSCU();

if (streamSCU.hasAssociation()) {
// Handle dynamically new SOPClassUID
Set<String> tss = streamSCU.getTransferSyntaxesFor(cuid);
Expand Down
191 changes: 191 additions & 0 deletions src/main/java/org/karnak/backend/service/CStoreSCPService.java
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package org.karnak.backend.service;

import java.util.Collection;
import java.util.List;
import org.karnak.backend.data.entity.DestinationEntity;
import org.karnak.backend.data.entity.ForwardNodeEntity;
import org.karnak.backend.data.repo.DestinationRepo;
Expand Down Expand Up @@ -54,7 +55,7 @@ public DestinationService(
/**
* Store given Destination to the backing destinationEntity service.
*
* @param forwardNodeEntity
* @param forwardNodeEntity ForwardNode Entity
* @param destinationEntity the updated or new destinationEntity
*/
public DestinationEntity save(
Expand Down Expand Up @@ -103,4 +104,8 @@ public ApplicationEventPublisher getApplicationEventPublisher() {
public Collection<DestinationEntity> retrieveDestinations(ForwardNodeEntity forwardNodeEntity) {
return forwardNodeService.getAllDestinations(forwardNodeEntity);
}

public List<DestinationEntity> retrieveDestinationsFromIds(List<Long> ids) {
return destinationRepo.findAllById(ids);
}
}
Loading

0 comments on commit d722098

Please sign in to comment.