PantelisGeorgiadis / dcmjs-dimse

DICOM DIMSE implementation for Node.js using the dcmjs library
MIT License
73 stars 14 forks source link

Error sending DIMSE: First argument to DataView constructor must be an ArrayBuffer #54

Closed xenonwellz closed 1 year ago

xenonwellz commented 1 year ago
2023-10-10T11:02:23.717Z -- INFO -- SCUSTORE -> C-STORE RQ [HasDataset: true] [id: 1] [pc: 1]
Invalid vr type xs - using US
Invalid vr type lt - using UN
Invalid vr type xs - using US
Invalid vr type lt - using UN
Invalid vr type xs - using US
Invalid vr type lt - using UN
Invalid vr type xs - using US
Invalid vr type lt - using UN
2023-10-10T11:02:23.898Z -- ERROR -- SCUSTORE -> Error sending DIMSE: First argument to DataView constructor must be an ArrayBuffer
2023-10-10T11:02:23.900Z -- INFO -- SCUSTORE -> Abort (A-ABORT) [source: Service user, reason: Not specified]
2023-10-10T11:02:23.993Z -- ERROR -- SCUSTORE -> Connection error: write after end
2023-10-10T11:02:23.993Z -- INFO -- SCUSTORE -> Abort (A-ABORT) [source: Service user, reason: Not specified]
2023-10-10T11:02:24.067Z -- INFO -- SCUSTORE -> Connection closed
2023-10-10T11:02:24.068Z -- INFO -- SCUSTORE -> Connection closed

This hapens for a specific type of file, if i need to trim off a tag to fix this issue i am fine with that.

PantelisGeorgiadis commented 1 year ago

Hello @xenonwellz! Thank you for reporting this! Do you mind sharing an anonymized version of the file that demostrates this behavior? You are using dcmjs-dimse to implement an SCP, right? If yes, what's the client (SCU) sending that file?

xenonwellz commented 1 year ago

Okay sure

xenonwellz commented 1 year ago

Okay sure

This is the anonymizeed dicom file https://drive.google.com/file/d/1rxb0sPlOXAqDRH6R3_MqmBRMq-Z5EPXi/view?usp=sharing

PantelisGeorgiadis commented 1 year ago

Hello @xenonwellz! The issue with the dataset you uploaded is the tags inside the VOILUTSequence (0028,3010), which have no fixed VRs. It is the same as the PixelData, but for this tag, dcmjs internally handles it. We need to figure out how to set the VRs for the LUTDescriptor and the LUTData tags inside the VOILUTSequence. I know that dcmjs has the concept of the _vrMap object, that we could use, but I’m not sure how we can do that for the tags inside a sequence. Maybe someone from the dcmjs team can help us with that (@pieper). Worst case scenario, and only if you are certain that you don’t need that sequence for the rendering purposes, you can omit it, until we can find a proper solution.

xenonwellz commented 1 year ago

Maybe

Just to be clear, you mean I can omit that tag and the file will still be fine ?

Also how can I do that ?

Naturally I have no specific use for the tag.

jimOnAir commented 1 year ago

Hello. It's an issued Here what you can do: define custom dictionary:

export const NAME_DCMJS_DICTIONARY_MAP: Record<string, IDcmJsDictionaryItem> = {
  LUTData: {
    tag: '(0028,3006)',
    vr: EDicomVr.UNSIGNED_SHORT,
    name: 'LUTData',
    vm: '1-n',
    version: EDicomVersion.DICOM,
  },
  LUTDescriptor: {
    tag: '(0028,3002)',
    vr: EDicomVr.UNSIGNED_SHORT,
    name: 'LUTDescriptor',
    vm: '3',
    version: EDicomVersion.DICOM,
  },
  RETIRED_GrayLookupTableData: {
    tag: '(0028,1200)',
    vr: EDicomVr.LONG_TEXT,
    name: 'RETIRED_GrayLookupTableData',
    vm: '1-n',
    version: EDicomVersion.DICOM_RETIRED,
  },
};

Provide it as nameMap parameter for saving file:

  private async datasetToFile(dataset: Dataset, filePath: string) {
    return new Promise<void>((resolve, reject) => {
      try {
        dataset.toFile(filePath, (error: Error | undefined) => {
          if (error) {
            return reject(error);
          }
          resolve();
        },
        NAME_DCMJS_DICTIONARY_MAP,
        { fragmentMultiframe: false });
      } catch (error) {
        reject(error);
      }
    });
  }

or sending C-Store:


    const client = (new Client()) as Client & EventEmitter;
    const request = new CStoreRequest();
....

        const options: TDcmjsScuOptions = this.configService.getStorescuOptions();
        options.datasetWriteOptions = { fragmentMultiframe: false };
        options.datasetNameMap = NAME_DCMJS_DICTIONARY_MAP;

          client.addRequest(request);
          client.send(pacsServer.host, pacsServer.port, pacsServer.sourceAet ?? DEFAULT_SOURCE_AET, pacsServer.destinationAet, options);

        }

Note that you should also add to NAME_DCMJS_DICTIONARY_MAP any private tags you need to keep while sending or saving, because if not, they will be erased otherwise.

PantelisGeorgiadis commented 1 year ago

Thank you @jimOnAir! That's exactly the power of the open source community! I just tested your proposed solution (the C-STORE SCU portion) and it works! @xenonwellz have you managed to fix your issue?

xenonwellz commented 1 year ago

I'm a bit confused, where do I import EDicomVr and EDicomVersion from ?

PantelisGeorgiadis commented 1 year ago

These are types that @jimOnAir defined on top of dcmjs. You can change EDicomVr.UNSIGNED_SHORT with 'US', EDicomVr.LONG_TEXT with 'LT', EDicomVersion.DICOM with 'DICOM' and EDicomVersion.DICOM_RETIRED with 'DICOM/retired'.

xenonwellz commented 1 year ago

It works!!!

Thanks 🐙

PantelisGeorgiadis commented 1 year ago

Hello @xenonwellz! Closing the issue now. Thank you @jimOnAir for your support!