Open suoc opened 4 months ago
Hello @suoc! Thanks for reporting this. There seems to be an issue with the presentation context IDs, which your SCP accepts. Instead of accepting the provided, by the SCU IDs, the SCP seems to take the last one (0x19
) and increase it, for every context. Are you somehow altering the presentation context IDs during the association negotiation?
I modified the dcmjs-dimse basic SCP example to reproduce this, as bellow:
const dcmjsDimse = require('dcmjs-dimse');
const { Scp, Server } = dcmjsDimse;
const { CStoreResponse } = dcmjsDimse.responses;
const { PresentationContextResult, Status } = dcmjsDimse.constants;
class ExampleScp extends Scp {
constructor(socket, opts) {
super(socket, opts);
this.association = undefined;
}
associationRequested(association) {
this.association = association;
const contexts = association.getPresentationContexts();
contexts.forEach((c) => {
const context = association.getPresentationContext(c.id);
const tsIds = context.getTransferSyntaxUids();
tsIds.forEach((ts) => {
context.setResult(PresentationContextResult.Accept, ts);
});
});
this.sendAssociationAccept();
}
cStoreRequest(request, callback) {
const response = CStoreResponse.fromRequest(request);
response.setStatus(Status.Success);
callback(response);
}
associationReleaseRequested() {
this.sendAssociationReleaseResponse();
}
}
const server = new Server(ExampleScp);
server.listen(port, { logCommandDatasets: true });
Then I created a basic SCU, using fo-dicom, to emulate sending multiple datasets, as part of the same association. I actually used the files in the datasets
folder:
var client = DicomClientFactory.Create("127.0.0.1", 2104, false, "SCU", "ANY-SCP");
await client.AddRequestAsync(new DicomCStoreRequest("ebe.dcm"));
await client.AddRequestAsync(new DicomCStoreRequest("ele.dcm"));
await client.AddRequestAsync(new DicomCStoreRequest("j2k.dcm"));
await client.AddRequestAsync(new DicomCStoreRequest("pdf.dcm"));
await client.AddRequestAsync(new DicomCStoreRequest("sr.dcm"));
await client.SendAsync();
Bellow are the SCP generated logs:
2024-07-25T08:57:56.873Z -- INFO -- Client connecting from ::ffff:127.0.0.1:56867
2024-07-25T08:57:56.924Z -- INFO -- SCU <- Association request:
Application Context: 1.2.840.10008.3.1.1.1
Implementation Class: 1.3.6.1.4.1.30071.8
Implementation Version: fo-dicom 5.1.2
Maximum PDU Length: 262144
Called AE Title: ANY-SCP
Calling AE Title: SCU
Presentation Contexts: 8
Presentation Context: 1 [Proposed]
Abstract: MrImageStorage
Transfer: ExplicitVRBigEndian
Presentation Context: 3 [Proposed]
Abstract: MrImageStorage
Transfer: ImplicitVRLittleEndian
Presentation Context: 5 [Proposed]
Abstract: CtImageStorage
Transfer: Jpeg2000Lossless
Presentation Context: 7 [Proposed]
Abstract: CtImageStorage
Transfer: ImplicitVRLittleEndian
Presentation Context: 9 [Proposed]
Abstract: EncapsulatedPdfStorage
Transfer: ExplicitVRLittleEndian
Presentation Context: 11 [Proposed]
Abstract: EncapsulatedPdfStorage
Transfer: ImplicitVRLittleEndian
Presentation Context: 13 [Proposed]
Abstract: BasicTextSrStorage
Transfer: ExplicitVRLittleEndian
Presentation Context: 15 [Proposed]
Abstract: BasicTextSrStorage
Transfer: ImplicitVRLittleEndian
2024-07-25T08:57:56.926Z -- INFO -- SCU -> Association accept:
Application Context: 1.2.840.10008.3.1.1.1
Implementation Class: 1.2.826.0.1.3680043.10.854
Implementation Version: DCMJS-DIMSE-V0.1
Maximum PDU Length: 262144
Called AE Title: ANY-SCP
Calling AE Title: SCU
Presentation Contexts: 8
Presentation Context: 1 [Accept]
Abstract: MrImageStorage
Transfer: ExplicitVRBigEndian
Presentation Context: 3 [Accept]
Abstract: MrImageStorage
Transfer: ImplicitVRLittleEndian
Presentation Context: 5 [Accept]
Abstract: CtImageStorage
Transfer: Jpeg2000Lossless
Presentation Context: 7 [Accept]
Abstract: CtImageStorage
Transfer: ImplicitVRLittleEndian
Presentation Context: 9 [Accept]
Abstract: EncapsulatedPdfStorage
Transfer: ExplicitVRLittleEndian
Presentation Context: 11 [Accept]
Abstract: EncapsulatedPdfStorage
Transfer: ImplicitVRLittleEndian
Presentation Context: 13 [Accept]
Abstract: BasicTextSrStorage
Transfer: ExplicitVRLittleEndian
Presentation Context: 15 [Accept]
Abstract: BasicTextSrStorage
Transfer: ImplicitVRLittleEndian
2024-07-25T08:57:56.997Z -- INFO -- SCU <- C-STORE RQ [HasDataset: true]
DIMSE Command Dataset:
==================================================
{"_vrMap":{},"CommandGroupLength":146,"AffectedSOPClassUID":"1.2.840.10008.5.1.4.1.1.4","CommandField":1,"MessageID":1,"Priority":0,"CommandDataSetType":514,"AffectedSOPInstanceUID":"1.2.826.0.1.3680043.2.1143.3638768091140812186843363535159947743"} [id: 1] [pc: 1]
2024-07-25T08:57:57.007Z -- INFO -- SCU -> C-STORE RSP [HasDataset: false]
DIMSE Command Dataset:
==================================================
{"CommandField":32769,"CommandDataSetType":257,"Status":0,"AffectedSOPClassUID":"1.2.840.10008.5.1.4.1.1.4","MessageIDBeingRespondedTo":1,"AffectedSOPInstanceUID":"1.2.826.0.1.3680043.2.1143.3638768091140812186843363535159947743"} [id: 1; status: Success] [pc: 1]
2024-07-25T08:57:57.034Z -- INFO -- SCU <- C-STORE RQ [HasDataset: true]
DIMSE Command Dataset:
==================================================
{"_vrMap":{},"CommandGroupLength":142,"AffectedSOPClassUID":"1.2.840.10008.5.1.4.1.1.4","CommandField":1,"MessageID":2,"Priority":0,"CommandDataSetType":514,"AffectedSOPInstanceUID":"1.3.46.670589.11.7002.9.1582062500000285221701.8.1.1.1.0.0.1"} [id: 2] [pc: 3]
Invalid vr type ox - using OW
2024-07-25T08:57:57.041Z -- INFO -- SCU -> C-STORE RSP [HasDataset: false]
DIMSE Command Dataset:
==================================================
{"CommandField":32769,"CommandDataSetType":257,"Status":0,"AffectedSOPClassUID":"1.2.840.10008.5.1.4.1.1.4","MessageIDBeingRespondedTo":2,"AffectedSOPInstanceUID":"1.3.46.670589.11.7002.9.1582062500000285221701.8.1.1.1.0.0.1"} [id: 2; status: Success] [pc: 3]
2024-07-25T08:57:57.053Z -- INFO -- SCU <- C-STORE RQ [HasDataset: true]
DIMSE Command Dataset:
==================================================
{"_vrMap":{},"CommandGroupLength":116,"AffectedSOPClassUID":"1.2.840.10008.5.1.4.1.1.2","CommandField":1,"MessageID":3,"Priority":0,"CommandDataSetType":514,"AffectedSOPInstanceUID":"1.2.840.113674.950809132337081.100"} [id: 3] [pc: 5]
2024-07-25T08:57:57.056Z -- INFO -- SCU -> C-STORE RSP [HasDataset: false]
DIMSE Command Dataset:
==================================================
{"CommandField":32769,"CommandDataSetType":257,"Status":0,"AffectedSOPClassUID":"1.2.840.10008.5.1.4.1.1.2","MessageIDBeingRespondedTo":3,"AffectedSOPInstanceUID":"1.2.840.113674.950809132337081.100"} [id: 3; status: Success] [pc: 5]
2024-07-25T08:57:57.065Z -- INFO -- SCU <- C-STORE RQ [HasDataset: true]
DIMSE Command Dataset:
==================================================
{"_vrMap":{},"CommandGroupLength":140,"AffectedSOPClassUID":"1.2.840.10008.5.1.4.1.1.104.1","CommandField":1,"MessageID":4,"Priority":0,"CommandDataSetType":514,"AffectedSOPInstanceUID":"1.2.276.0.7230010.3.1.4.368843532.32020.1619168794.980"} [id: 4] [pc: 9]
2024-07-25T08:57:57.069Z -- INFO -- SCU -> C-STORE RSP [HasDataset: false]
DIMSE Command Dataset:
==================================================
{"CommandField":32769,"CommandDataSetType":257,"Status":0,"AffectedSOPClassUID":"1.2.840.10008.5.1.4.1.1.104.1","MessageIDBeingRespondedTo":4,"AffectedSOPInstanceUID":"1.2.276.0.7230010.3.1.4.368843532.32020.1619168794.980"} [id: 4; status: Success] [pc: 9]
2024-07-25T08:57:57.074Z -- INFO -- SCU <- C-STORE RQ [HasDataset: true]
DIMSE Command Dataset:
==================================================
{"_vrMap":{},"CommandGroupLength":138,"AffectedSOPClassUID":"1.2.840.10008.5.1.4.1.1.88.11","CommandField":1,"MessageID":5,"Priority":0,"CommandDataSetType":514,"AffectedSOPInstanceUID":"1.2.276.0.7230010.3.1.4.1787205428.166.1117461927.34"} [id: 5] [pc: 13]
2024-07-25T08:57:57.077Z -- INFO -- SCU -> C-STORE RSP [HasDataset: false]
DIMSE Command Dataset:
==================================================
{"CommandField":32769,"CommandDataSetType":257,"Status":0,"AffectedSOPClassUID":"1.2.840.10008.5.1.4.1.1.88.11","MessageIDBeingRespondedTo":5,"AffectedSOPInstanceUID":"1.2.276.0.7230010.3.1.4.1787205428.166.1117461927.34"} [id: 5; status: Success] [pc: 13]
2024-07-25T08:57:57.133Z -- INFO -- SCU <- Association release request
2024-07-25T08:57:57.133Z -- INFO -- SCU -> Association release response
2024-07-25T08:57:57.181Z -- INFO -- SCU -> Connection closed
As you can see in the logs, the SCP accepted the proper presentation context IDs and the DICOM files were received, as expected. Would you mind checking again how you are handling your presentation IDs and figure why they are increasing?
I am using the scp example in the README document on the project homepage.
I will test with the new method you provided.
Hello @suoc, Did you have the chance to verify this?
This issue has not been resolved, and since the peer device is closed source, I have made no progress through debugging on the receiving end, and no bugs have been found in the dcmjs-dimse source code. However, I have found a workaround for this issue, which is to clear the PresentationContexts when receiving the association request. The code is as follows:
if (association.implementationVersion.indexOf('NMSDICOM') != -1) {
association.clearPresentationContexts();
let pcId = association.addPresentationContext(StorageClass.MrImageStorage);
association.addTransferSyntaxToPresentationContext(pcId, TransferSyntax.ExplicitVRLittleEndian);
pcId = association.addPresentationContext(StorageClass.CtImageStorage);
association.addTransferSyntaxToPresentationContext(pcId, TransferSyntax.ExplicitVRLittleEndian);
pcId = association.addPresentationContext(StorageClass.UltrasoundImageStorage);
association.addTransferSyntaxToPresentationContext(pcId, TransferSyntax.ExplicitVRLittleEndian);
}
Thank you for sharing part of your code @suoc. Is this code part of your SCP or SCU?
This code is part of my scp service
Hello @suoc,
You should never alter the presentation contexts, during an association negotiation, in your SCP code. You should either accept or reject them. The code you sent explains why your presentation context IDs do not match with those sent by the SCU in your logs. The addPresentationContext
function is internally increasing the presentation ID by two, creating a mismatch between the original IDs, sent by the SCU, and those sent back by your SCP, as part of the association acceptance. Rather than adding presentation contexts and transfer syntaxes to the proposed association
object, you should iterate over the contexts and for each context, evaluate the proposed abstract syntax and the transfer syntaxes and accept them or reject them, accordingly.
Example:
associationRequested(association) {
// Get the proposed presentation contexts
const contexts = association.getPresentationContexts();
// Iterate over the proposed presentation contexts
contexts.forEach((c) => {
// Get the proposed presentation context
const context = association.getPresentationContext(c.id);
// Get the abstract syntax of the presentation context and evaluate it
const asUid = context.getAbstractSyntaxUid();
// Get the proposed transfer syntax UIDs and iterate over them
const tsIds = context.getTransferSyntaxUids();
tsIds.forEach((ts) => {
// Evaluate the transfer syntax and accept or reject, accordingly
context.setResult(PresentationContextResult.Accept, ts);
});
});
// Accept the association
this.sendAssociationAccept();
}
If I accept or reject presentation contexts in the normal way, the id will be wrong. I don't know where the problem is.Not all devices have this problem. Currently, it has been found that only the requests sent by this one device will have an id error.
Strange case... really! I tend to believe that it has to do with the SCU implementation but you already mentioned that other SCPs seem to able to handle it. Please let me know if you find anything that could help us debug the issue.
Hello @suoc! Have you managed to gather more information about this issue?
Hello @suoc! Any further information on this issue?
I used wireshark to capture the response results of two dimse tools and got different response content. The response structure of dcmjs-dimse seems to be problematic. This is request:
Response by DCMOBJ:
Response by dcmjs-dimse:
The response of dcmjs-dimse will cause the negotiation on the client side to fail, and the dcm file will not continue to be sent.