Buenos días Alberto,

Recientemente hemos estado indagando desarrollando y profundizando con DICOM para gestionar los FIND obteniendo el listado de imágenes médicas, con el objetivo de extraerlas desde los RISPACS hacia el ESB.

Alberto, agradeceríamos si nos puedes orientar y explicar lo siguiente: ¿estaría soportado realizar la Operacion Retrieve, en el ejemplo que nos explicas en el artículo actual?

La necesidad es el hecho de que quizá sería más apropiado en vez de guardar las imágenes DICOM mediante Move/Store; cachearlas, y trasladarlas desde los RISPACS (Sistemas Destinos) al Origen.

En concreto, lo que conocemos es:

🔎Query/Retrieve, or Q/R for short, is the DICOM service for searching images on the PACS and getting a copy of them to the workstation where they can be displayed.

Mientras que:

 📌C-MOVE is a DICOM command that means this: The calling AE (we) ask the called AE (the PACS) to send all the DICOM Instances that match the identifier to the target AE.

Parece que MOVE es enviar imágenes (guardándolas), mientras que Query/Retrieve es cachear (copiar) las imágenes DICOM.

Por favor, Alberto Fuentes; podrías indicarnos documentación referencias código ejemplos respecto a: ¿estaría soportado realizar la Operacion Retrieve, en el ejemplo que nos explicas en el artículo actual?

Muchas gracias de antemano por tu apoyo y respuestas.

Un saludo.

Hola Alberto,

Muchas gracias por tu ayuda

Hemos realizado lo que nos has recomendado:

Mostrar el contenido del DICOM mediante dcmdump:

  ./dcmdump "IImagen PatientID 732831 PatientName Milagros ReasonForStudy 350290  InstitutionName RISPACsCHUIMI   StudyDate 20220930.dcm"

Observamos el InstitutionName cumplimentado

632: (0008,0080) LO #14 [RISPACsCHUIMI] InstitutionName

Además, Alberto, hemos realizado los apartados 2 al 4 que has indicado, para generar un Servicio de Lectura de Ficheros DICOM puro.

De esta manera se muestra el InstitutionName con su valor correspondiente en el ESB:

(0008,0080) InstitutionName RISPACsCHUIMI

Alberto ¿de qué manera nos recomiendas, suguieres, expones continuar indagando?

¿Es el simulador, el cual está manipulando los campos de datos del DICOM en el envío?

Para construir el repositorio BBDD simulada empleamos:

./dcmdir -c ./shared/dicom/CHUIMI/DICOMDIR --fs-id SAMPLEDICOMS --fs-desc ./shared/dicom/CHUIMI/descriptor ./shared/dicom/CHUIMI

Una vez generada, iniciamos el repositorio a partir de la BBDD previa:

./dcmqrscp --ae-config ./shared/dicom/CHUIMI/ae.properties -b VNAPRE:11110 --dicomdir ./shared/dicom/CHUIMI/DICOMDIR  

Y para llevar a cabo el FIND empleamos:

./findscu -b VNAPRE -c ESBPRE@10.136.4.141:19586 -m PatientID=12989575 -m PatientName="Milagros" -r StudyInstanceUID -r PatientName -r StudyDate -r InstitutionName -r ReasonForStudy

Alberto ¿de qué manera nos recomiendas depurar para profundizar en las causas y resolver la situación?

Muchas gracias de antemano

Un saludo

Hola Alberto,

Gracias por indicarme los recursos y sobre todo agradezco que me especificaras en concreto qué propiedades aportan en este caso.

He configurado:

Seguimiento del nivel de detalle = 2

ARTIM = 20

TXTIM = 20

Y ahora sí se recibe la respuesta del Establecer conexión desde el Simulador ( Destino ) hacia la Operacion TCP Dicom ( Receptora )

Lo que sí es cierto, resulta que ocurre la excepción de forma similar, casi igual en el paso [6]; justo antes de recibir la Notificación de Conexión establecida por parte del SIMULADOR dcm4che hacia la Operacion del ESB:

  ERROR <Ens>ErrBPTerminated: Finalizando BP DICOM Move Process # debido a un error: ERROR <Ens>ErrException: <READ>zAcceptPDU+4^EnsLib.DICOM.Adapter.TCP.1 -- - registrado como '-' número - @''
> ERROR <Ens>ErrException: <READ>zAcceptPDU+4^EnsLib.DICOM.Adapter.TCP.1 -- - registrado como '-' número - @''

Lo más acuciante e intrigante es el hecho de que el Simulador dcm4che que replica ser un Sistema ORIGEN (envía al servicio) se queda a la escucha, tras haber enviado el C-MOVE-RQ:

Y el simulador que actúa como DESTINO se queda en el estado de Asociación Establecida, sin embargo no le llega efecitamente el C-MOVE-RQ, lo cual es muy llamativo :

¿ De qué forma nos recomiendan depurar ?

Muchas gracias de antemano por su tiempo, por leerme y responder. Un saludo

Hola Alberto,

Gracias por tu respuesta y tiempo, Alberto

Hemos escrito al inicio del método:

set ^zdebug($i(^zdebug))="["_$zdt($zts,3,,2)_"] "_"before" 

y al final del método:

set ^zdebug($i(^zdebug))="["_$zdt($zts,3,,2)_"] "_"after. tSC="_tSC 

En PREproducción observamos:

zw ^zdebug
^zdebug=2
^zdebug(1)="[2022-06-03 08:52:17.11] before"
^zdebug(2)="[2022-06-03 08:52:17.11] after. tSC=1"

En INTegración ocurre similar:

zw ^zdebug
^zdebug=2
^zdebug(1)="[2022-06-03 08:53:43.77] before"
^zdebug(2)="[2022-06-03 08:53:43.78] after. tSC=1"

¿De qué forma nos recomiendas, Alberto, continuar?

Gracias Alberto por tu asistencia y tiempo

Un saludo

Es de agradecer, Alberto, tu respuesta, y sobre todo las explicaciones y enlaces

A continuación te detallamos la situación, Alberto, para que ustedes nos indiquen, ordenen, recomienden, sugieran vías o formas de continuar:

A continuación, se muestran las trazas del WireShark

Primero observamos la traza Simulada desde Línea de Comandos mediante:

./findscu -b VNAPRE -c ESBPRE@10.136.4.142:19586 -m PatientID="102030" -M StudyRoot

Segundo se muestra la traza Real desde Sistema Origen hacia Sistema Destino:

La principal diferencia radica en la petición, la cual está en 1 único paquete en la Simulada, el cual está titulado como:

1072 … P-DATA, C-FIND-RQ ID=1, C-FIND-RQ-DATA

Sin embargo, en la real, la petición consta de 2 paquetes:

1904 … P-DATA, C-FIND-RQ ID=1

1906 … P-DATA, C-FIND-RQ-DATA

 

Ahondando en el detalle, comparamos la primera de las peticiones titulada como: “P-DATA, C-FIND-RQ ID=1

En la Simulada se observa lo siguiente:

Donde cabe destacar estos 2 contenidos del conjunto de comandos (Command, Last Fragment):

    (0000,0700)          2 Priority                                      0

    (0000,0800)          2 Command Data Set Type                         0

 

Los cuales en la Real son distintos:

    (0000,0700)          2 Priority                                      2

    (0000,0800)          2 Command Data Set Type                         1

 

Además, también es importante recalcar que en la segunda petición reseñada como “P-DATA, C-FIND-RQ-DATA” existen diferencias:

En la Real es más completa:

PDV, C-FIND-RQ-DATA

    PDV Length: 130

    Context: 0x01 (Implicit VR Little Endian: Default Transfer Syntax for DICOM, Study Root Query/Retrieve Information Model - FIND)

    Flags: 0x02 (Data, Last Fragment)

    (0008,0005)          0 Specific Character Set                        <Empty>

    (0008,0020)          0 Study Date                                    <Empty>

    (0008,0030)          0 Study Time                                    <Empty>

    (0008,0050)          0 Accession Number                              <Empty>

    (0008,0052)          6 Query/Retrieve Level                          STUDY

    (0008,0061)          0 Modalities in Study                           <Empty>

    (0008,0090)          0 Referring Physician's Name                    <Empty>

    (0008,1010)          0 Station Name                                  <Empty>

    (0008,1030)          0 Study Description                             <Empty>

    (0010,0010)          0 Patient's Name                                <Empty>

    (0010,0020)          2 Patient ID                                    9

    (0010,0030)          0 Patient's Birth Date                          <Empty>

    (0010,0040)          0 Patient's Sex                                 <Empty>

    (0020,000d)          0 Study Instance UID                            <Empty>

    (0020,0010)          0 Study ID                                      <Empty>

Sin embargo, en la Simulada únicamente existen 2 datos:

PDV, C-FIND-RQ-DATA

    PDV Length: 30

    Context: 0x01 (Implicit VR Little Endian: Default Transfer Syntax for DICOM, Study Root Query/Retrieve Information Model - FIND)

    Flags: 0x02 (Data, Last Fragment)

    (0008,0052)          6 Query/Retrieve Level                          STUDY

    (0010,0020)          6 Patient ID                                    102030

 

 

Por otro lado, lo que nos genera mayor inquietud, nos pone en vilo, y es extraño, resulta ser las discrepacias entre los resultados de los estudios médicos obtenidos mediante servicio ‘interno’ y mediante ‘simulación por herramienta externa’ hacia el Servicio TCP de DICOM

 

En concreto cuando ejecutamos desde la “Salida” del “Studio” de ESBPRE lo siguiente:

do ##class(DICOM.BS.QueryService).TestFind("102030")

 

Se envía la petición:

Y para cada uno de los 3 estudios médicos respondidos por el Sistema Destino, obtenemos datos completos:

 

Mientras que cuando simulamos ser Sistema Origen, gracias a la herramienta ‘findscu’ de Línea de Comandos, ejecutamos:

./findscu -b VNAPRE -c ESBPRE@10.136.4.142:19586 -m PatientID="102030" -M StudyRoot

Generándose la petición siguiente en la traza:

Si prestamos atención a la imagen anterior, se visualiza que únicamente se remiten 2 filas en el “DataSet”, las cuales corresponden a: ‘QueryRetrieveLevel’ y ‘PatientID’

 

Siendo la respuesta por parte del Sistema Origen con 3 estudios médicos, donde únicamente se incluyen 3 filas en el DataSet:

 

Las cuales corresponden al: ‘QueryRetrieveLevel’, ‘RetrieveAETitle’ y ‘PatientID’

 

De esta forma observamos grandes diferencias entre la respuesta generada por el Sistema Destino, cuando enviamos desde nuestro servicio interno, frente a cuando remitimos la consulta simulando ser sistema origen por TCP de DICOM

 

¿ustedes cómo recomiendan continuar?

 

Muchas gracias, Alberto, por leer, responder, y tomar su tiempo para atendernos

 

Gracias a ustedes por indicarnos, ordenarnos, recomendarnos, sugerirnos, técnicas, documentación, ejemplos; mediante los cuales investigar, depurar, indagar esta cuestión

 

Un saludo

Hola Alberto,

gracias por responder, porque es un apoyo, alivio, auxilio y aporte tus indicaciones, instrucciones y ejemplos

Siguiendo tus indicaciones ubicadas en el ejemplo: https://github.com/intersystems-ib/iris-dicom-sample
Hemos realizado 2 pruebas:

Primera prueba: tratamos de guardar DICOM

Siguiendo el ejemplo 1: https://github.com/intersystems-ib/iris-dicom-sample#receiving-dicom-with-embedded-pdf

Circuito:

Siendo la respuesta del sistema destino:

                ERROR <EnsDICOM>PeerRejectedAssociation: La contraparte ha rechazado la asociación. Fuente: '' Motivo: ''

Además, realizamos una segunda prueba

Segunda prueba: Consultar y Recuperar, Query / Retrieve: https://github.com/intersystems-ib/iris-dicom-sample#query--retrieve-scenario

Siendo la respuesta por parte del sistema destino:

ERROR <Ens>ErrBPTerminated: Finalizando BP DICOM Query Process # debido a un error: ERROR <EnsDICOM>PeerRejectedAssociation: La contraparte ha rechazado la asociación. Fuente: '%1' Motivo: '%2'
> ERROR <EnsDICOM>PeerRejectedAssociation: La contraparte ha rechazado la asociación. Fuente: '%1' Motivo: '%2'

¿ de qué manera, Alberto, nos recomiendan ustedes depurar, buscar, investigar, indagar el porqué de estas 2 excepciones ?

¿ mediante qué mecanismo, o cuál sería tu proceso mental y toma de acciones para organizar, ordenar, entender y sobre todo seguir y dar con la causa de estas excepciones ?

¿ cuál sería la vía correcta para entender, atender, depurar, corregir, y comprender mejor este tipo de integraciones, Alberto ?

Muchas gracias por leer y responder, porque es un gran espaldarazo, auxilio y aporte el hecho de que nos hagas seguimiento y nos vayas indicando, ordenando, explicando, cómo continuar, o en qué ejemplos basarnos

Un saludo

Hola Alberto,

Gracias por responder y explicar

Una duda:

¿el uso de las herramientas que comentas es posible para la version HS2017?

¿cual es la versión mínima IRIS para ello?

Lo preguntamos por dos motivos:

Primero: estamos desarollando en:

"" Cache for UNIX (Red Hat Enterprise Linux for x86-64) 2017.2.1 (Build 801_3_18358U) Tue Jul 24 2018 16:36:10 EDT""

Segundo: se presenta el reto o desafío de emplear el simulador para probar DICOM dcm4che

Gracias por responder

Disculpen, había cometido un error

Necesitamos usar el mecanismo llamado "Attachment" para enviar el fichero como un adjunto en la petición SOAP; y no el mecanismo alternativo, conocido como "MTOM"

Hemos añadido las siguientes líneas en el Cliente Web:

Set file=##class(%Library.FileCharacterStream).%New()
 Set file.Filename="application.zip"
 
 while (fichero.AtEnd '= 0){
     do file.Write($system.Encryption.Base64Decode(fichero.Read()))
 }
 
 Set mim

epart=##class(%Net.MIMEPart).%New()
 Set mimepart.Body=file
 Do mimepart.SetHeader("Content-Disposition", "attachment; name=""application.zip""")

do ..Attachments.Insert(mimepart)

Siguiendo la documentación:

Using SOAP with Attachments: Web Client

¿Qué podría significar que el sistema destino nos devuelva un código http 511?

     ERROR #6242: La solicitud HTTP a SOAP WebService ha devuelto un estado inesperado: 511.

Parece que podría indicar lo siguiente:

"El código de estado de respuesta HTTP 511 Network Authentication Required indica que el cliente necesita autenticarse para obtener acceso a la red."

En estos casos, ¿qué se suele necesitar?

En el LOGSOAP veríamos:

10/15/2021 15:51:56 *********************
Output from Web client with SOAP action = http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
----boundary1980.3529411764705881780.294117647058824--
Content-Type: text/xml; charset="UTF-8"
Content-Transfer-Encoding: 8bit

<?xml version="1.0" encoding="UTF-8" ?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:s='http://www.w3.org/2001/XMLSchema' xmlns:wsa='http://www.w3.org/2005/08/addressing'>
    <SOAP-ENV:Header>
        <wsa:Action>cargarFichero</wsa:Action>
        <wsa:MessageID>7148b7422dc711eca7e1005056b672a4</wsa:MessageID>
        <wsa:ReplyTo>
            <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
        </wsa:ReplyTo>
        <wsa:To>https://regvacu-svc-pre.sns.gob.es/sssns/regvacu-bus-r01/PS/CCAA/Canarias/RegVacu_PS?wsdl</wsa:To>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
        <CargarFicheroVacuRequest xmlns="http://ws.regvacuWs.ms.es/regvacu/ws/FicheroVacuService">
            <fichero>
                <![CDATA[;;;47B7F6BF1C6D7174A920B3...;12010070;]]>
            </fichero>
            <ccaaId>01</ccaaId>
            <tipoFichero>2</tipoFichero>
        </CargarFicheroVacuRequest>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
----boundary1980.3529411764705881780.294117647058824--
Content-Disposition: attachment; name="application.zip"


----boundary1980.3529411764705881780.294117647058824----


10/15/2021 15:51:56 *********************
Input to Web client with SOAP action = http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero

ERROR #6242: La solicitud HTTP a SOAP WebService ha devuelto un estado inesperado: 511.
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
    <soapenv:Body/>
</soapenv:Envelope>**** SOAP client return error. method=cargarFichero, action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
     ERROR #6242: La solicitud HTTP a SOAP WebService ha devuelto un estado inesperado: 511.

¿se implementaría una autenticación desde nuestra Operacion SOAP o Cliente Web, hacia el sistema destino?

¿qué recomiendan ustedes?

¿cómo continuaríamos? ¿cuáles son los siguientes pasos?

Gracias por leernos, y por su ayuda con las respuestas

Gracias Alberto por tu respuesta y por dedicarnos tu tiempo,

Hemos añadido:

do ..SetHttpHeader("Content-Disposition", "attachment; name=""application.zip""")

Activamos el LOGSOAP:

set ^ISCSOAP("Log")="ioshH"

set ^ISCSOAP("LogFile") = "/opt/contenedor/15 10 21 3 Content-Disposition attachment name Cargar Fichero Vacunas.xml"

Observamos que unicamente se incluyen las cabeceras SOAP, y no las HTTP en la traza:

10/15/2021 13:17:59 *********************
Output from Web client with SOAP action = http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
----boundary1237.882352941176471407.7058823529411765--
Content-Type: application/xop+xml; type="text/xml"; charset="UTF-8"
Content-Transfer-Encoding: 8bit
Content-Id: <0.EFBCA6F8.2DB1.11EC.9A62.005056B672A4>

        <?xml version="1.0" encoding="UTF-8" ?>
        <SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:s='http://www.w3.org/2001/XMLSchema' xmlns:wsa='http://www.w3.org/2005/08/addressing'>
            <SOAP-ENV:Header>
                <wsa:Action>cargarFichero</wsa:Action>
                <wsa:MessageID>efbc9f822db111ec9a62005056b672a4</wsa:MessageID>
                <wsa:ReplyTo>
                    <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
                </wsa:ReplyTo>
                <wsa:To>https://regvacu-svc-pre.sns.gob.es/sssns/regvacu-bus-r01/PS/CCAA/Canarias/RegVacu_PS?wsdl</wsa:To>
            </SOAP-ENV:Header>

Siguiendo el consejo que nos indicas, leemos la documentación de la versión que empleamos:

Cache for UNIX (Red Hat Enterprise Linux for x86-64) 2017.2.1 (Build 801_3_18358U) Tue Jul 24 2018 16:36:10 EDT

Y nos sale que la opción "H" en el ^ISCSOAP que nos permitiría observar las cabeceras HTTP, está sin incluir:

Tienes razón, Alberto, el Servicio Web destino, nos sigue respondiendo que faltaría la propiedad name:

10/15/2021 13:18:00 *********************
Input to Web client with SOAP action = http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
--MIME_Boundary
Content-ID: <root.message@cxf.apache.org>
Content-Type: application/xop+xml; type="text/xml"; charset=utf-8
Content-Transfer-Encoding: 8bit

                <?xml version="1.0" encoding="UTF-8"?>
                <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

---------------
Validate Security header: action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero, MethodName=cargarFichero
**** SOAP client return error. method=cargarFichero, action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
     ERROR #6248: La respuesta de SOAP es un error de SOAP: faultcode=Server
faultstring=Name must not be null
faultactor=
detail=

¿Cómo podríamos continuar?

¿De qué forma depuraríamos las cabeceras HTTP del envío del fichero mediante SOAP?

¿Ustedes como nos recomiendan que sigamos?

¿Cuáles serían los siguientes pasos?

Muchas gracias por tus respuestas Alberto, y sobre todo gracias por indicarnos la documentación y ejemplos concretos y prácticos

Gracias por responder Alberto,

es un apoyo, alivio y ayuda, la orientación que nos ofreces

Siguiendo tu criterio, y la explicación de la documentación, hemos añadido:

do ..SetHttpHeader("Content-Disposition","application.zip")

La línea la hemos escrito antes de:

Quit ..WebMethod("cargarFichero","CargarFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero",.fichero,.ccaaId,.tipoFichero)

Al capturar el LOGSOAP observamos que se queda sin incluir la cabecera Content-Disposition y el nombre application.zip en lo que enviamos:

¿Por qué podría ser que al escribir la línea, se quede sin incluir la nueva cabecera Content-Disposition y el nombre del fichero application.zip? 💭🤔
 

Además, en otras pruebas, hemos escrito 3 formas adicionales, para tratar de resolverlo:

set ..ContentType="application/octet-stream; name=nombre"

Mediante la línea anterior, no se ve que añada el parámetro "name"

Probamos con la siguiente línea y generaría excepción Ensemble:

set ..HttpRequest.ContentType="application/octet-stream; name=nombre"

Genera excepcion:      ERROR #5001: <INVALID OREF>zcargarFichero+16^WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP.1

y mediante:

do ..HttpRequest.SetHeader("name","nombre")

Nos da excepción también:

ERROR #5001: <INVALID OREF>zcargarFichero+16^WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP.1

La clase completa que hemos escrito del Servicio Web es:

Class WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP Extends %SOAP.WebClient [ ProcedureBlock ]
{

// Parameter LOCATION = "https://regvacube.sns.gob.es/regvacu/ws/FicheroVacuService";

/// This is the URL used to access the web service.
/// This is the namespace used by the Service
Parameter NAMESPACE = "http://ws.regvacuWs.ms.es/regvacu/ws/FicheroVacuService";

///  20/09/21 Cambiamos a 0, con el objetivo de quitar el xsi:type
Parameter OUTPUTTYPEATTRIBUTE = 0;

/// Determines handling of Security header.
Parameter SECURITYIN = "ALLOW";

/// This is the name of the Service
Parameter SERVICENAME = "FicheroVacuService";

// Parameter SOAPVERSION = 1.2;

Parameter SOAPVERSION = 1.1;

/// This is the SOAP version supported by the service.
Parameter MTOMREQUIRED = 1;

// Method cargarFichero(fichero As %xsd.base64Binary(REQUIRED=1), ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]

// Method cargarFichero(fichero As %GlobalBinaryStream, ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]

/// 15 10 21 Edu explica que el fichero, el .zip con un .csv necesitamos enviarlo SIN CODIFICAR
/// quitamos %GlobalBinaryStream y ponemos %GlobalCharacterStream
///
Method cargarFichero(fichero As %GlobalCharacterStream, ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
  //Header - Addresing
 set addressing = ..crearAddressing()
 
 set addressing.Action = "cargarFichero"
 
 set ..AddressingOut                = addressing
 set ..AddressingOut.mustUnderstand = "1"

 //Firma el XML (mensaje SOAP)
 //do ..crearSignature()
 
 set ..MTOMRequired=1
 
 //24 09 21 para añadir parametro name en cabecera content type
 //set ..ContentType="application/octet-stream; name=nombre"
 
 //28 09 21 probamos a ajustarlo
 //set ..ContentType="application/octet-stream; charset=latin1"
 //set ..ContentType="application/xhtml+xml; charset=latin1"
 
 /*
     27 09 21 con el objetivo de poner parametro name en cabecera content type
     Genera excepcion:      ERROR #5001: <INVALID OREF>zcargarFichero+16^WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP.1
 */
 //set ..HttpRequest.ContentType="application/octet-stream; name=nombre"
 //do ..HttpRequest.SetHeader("name","nombre")
 
 
 /*
 15 10 21 seguimos la indicacion de Alberto Fuentes:
     ➕ añadir la cabecera Content-Disposition en la petición y ahí especificar el nombre del archivo.
     https://es.community.intersystems.com/post/%C2%BFc%C3%B3mo-podr%C3%ADamos-usar-mtom-para-enviar-un-zip-con-un-csv-adentro#comment-169536
     https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSOAP_cli_details#GSOAP_cli_details_http_headers
 */
 do ..SetHttpHeader("Content-Disposition","application.zip")
 
 Quit ..WebMethod("cargarFichero","CargarFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero",.fichero,.ccaaId,.tipoFichero)
}

Method infoFichero(ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), infoFichero As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(REQUIRED=1), Output estado As EsquemasDatos.HistoriaClinica.tns.EstadoFicheroType(REQUIRED=1)) As %xsd.base64Binary(XMLNAME="fichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
 set ..MTOMRequired=1    
 Quit ..WebMethod("infoFichero","InfoFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/infoFichero",.ccaaId,.infoFichero,.estado)
}

Method crearAddressing() As %SOAP.Addressing.Properties
{
    set IPRedSanitaria = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","IPRedSanitaria")
     set puertoRespuestas = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","PuertoRespuestasSSL")
     set ReplyTo = ##class(%SOAP.Addressing.EndpointReference).%New()
     set ReplyTo.Address = "http://www.w3.org/2005/08/addressing/anonymous"
     //set ReplyTo.Address = "https://"_IPRedSanitaria_":"_puertoRespuestas_"/csp/SNS/Servicios.ProgramasAsistenciales.SIFCOv02r00.cls"
     set MessageId = ##class(Util.FuncionesComunes).getUID()
     
     set addressing = ##class(%SOAP.Addressing.Properties).%New()
     set addressing.MessageId = MessageId
     set addressing.Destination = ..Location
     set addressing.ReplyEndpoint = ReplyTo    
          
     Quit addressing
}

}

¿Cómo podríamos indagar, depurar, ajustar, y resolver juntos, esta situación?

Si nos pudieran orientar, se los agradeceríamos 🙇‍♂️🙏🙏

¿Cómo recomendarían ustedes seguir?

¿Qué documentación necesitamos estudiar, leer, entender, para completar esta parte?

Gracias por las respuestas

Hola Alberto, gracias por responder porque nos apoya y orienta lo que nos explicas, en concreto el ejemplo de la documentación y las dos indicaciones a implementar

Hemos escrito el siguiente WebService:

Class WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP Extends %SOAP.WebClient [ ProcedureBlock ]
{

// Parameter LOCATION = "https://regvacube.sns.gob.es/regvacu/ws/FicheroVacuService";

/// This is the URL used to access the web service.
/// This is the namespace used by the Service
Parameter NAMESPACE = "http://ws.regvacuWs.ms.es/regvacu/ws/FicheroVacuService";

///  20/09/21 Cambiamos a 0, con el objetivo de quitar el xsi:type
Parameter OUTPUTTYPEATTRIBUTE = 0;

/// Determines handling of Security header.
Parameter SECURITYIN = "ALLOW";

/// This is the name of the Service
Parameter SERVICENAME = "FicheroVacuService";

// Parameter SOAPVERSION = 1.2;

Parameter SOAPVERSION = 1.1;

/// This is the SOAP version supported by the service.
Parameter MTOMREQUIRED = 1;

// Method cargarFichero(fichero As %xsd.base64Binary(REQUIRED=1), ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]

// Method cargarFichero(fichero As %GlobalBinaryStream, ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]

/// 15 10 21 Edu explica que el fichero, el .zip con un .csv necesitamos enviarlo SIN CODIFICAR
/// quitamos %GlobalBinaryStream y ponemos %GlobalCharacterStream
///
Method cargarFichero(fichero As %GlobalCharacterStream, ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
  //Header - Addresing
 set addressing = ..crearAddressing()
 
 set addressing.Action = "cargarFichero"
 
 set ..AddressingOut                = addressing
 set ..AddressingOut.mustUnderstand = "1"

 //Firma el XML (mensaje SOAP)
 //do ..crearSignature()
 
 set ..MTOMRequired=1
 
 //24 09 21 para añadir parametro name en cabecera content type
 //set ..ContentType="application/octet-stream; name=nombre"
 
 //28 09 21 probamos a ajustarlo
 //set ..ContentType="application/octet-stream; charset=latin1"
 set ..ContentType="application/xhtml+xml; charset=latin1"
 
 /*
     27 09 21 con el objetivo de poner parametro name en cabecera content type
     Genera excepcion:      ERROR #5001: <INVALID OREF>zcargarFichero+16^WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP.1
 */
 //set ..HttpRequest.ContentType="application/octet-stream; name=nombre"
 //do ..HttpRequest.SetHeader("name","nombre")
 
 Quit ..WebMethod("cargarFichero","CargarFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero",.fichero,.ccaaId,.tipoFichero)
}

Method infoFichero(ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), infoFichero As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(REQUIRED=1), Output estado As EsquemasDatos.HistoriaClinica.tns.EstadoFicheroType(REQUIRED=1)) As %xsd.base64Binary(XMLNAME="fichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
 set ..MTOMRequired=1    
 Quit ..WebMethod("infoFichero","InfoFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/infoFichero",.ccaaId,.infoFichero,.estado)
}

Method crearAddressing() As %SOAP.Addressing.Properties
{
    set IPRedSanitaria = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","IPRedSanitaria")
     set puertoRespuestas = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","PuertoRespuestasSSL")
     set ReplyTo = ##class(%SOAP.Addressing.EndpointReference).%New()
     set ReplyTo.Address = "http://www.w3.org/2005/08/addressing/anonymous"
     //set ReplyTo.Address = "https://"_IPRedSanitaria_":"_puertoRespuestas_"/csp/SNS/Servicios.ProgramasAsistenciales.SIFCOv02r00.cls"
     set MessageId = ##class(Util.FuncionesComunes).getUID()
     
     set addressing = ##class(%SOAP.Addressing.Properties).%New()
     set addressing.MessageId = MessageId
     set addressing.Destination = ..Location
     set addressing.ReplyEndpoint = ReplyTo    
          
     Quit addressing
}

Method crearSignature() As %XML.Security.Signature
{
       //Generamos el Binary Security Token a partir del mcertificado
     set x509alias = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","aliasCertMSSSI")
    set pwd = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","pwdCertMSSSI")
    set cred = ##class(%SYS.X509Credentials).GetByAlias(x509alias,pwd)
    set token = ##class(%SOAP.Security.BinarySecurityToken).CreateX509Token(cred)
    
    //Creamos la firma
    //set sig1=##class(%XML.Security.Signature).CreateX509(token,,$$$KeyInfoX509IssuerSerial)
    //set sig2=##class(%XML.Security.Signature).CreateX509(token,$$$SOAPWSIncludeSoapBody,$$$SOAPWSReferenceDirect)
    set sig2=##class(%XML.Security.Signature).CreateX509(token,$$$SOAPWSIncludeDefault,$$$SOAPWSReferenceDirect)
    
    //do sig1.SetSignatureMethod($$$SOAPWSrsasha1)
    do sig2.SetSignatureMethod($$$SOAPWSrsasha1)
    //do sig1.SetDigestMethod($$$SOAPWSsha1)
    do sig2.SetDigestMethod($$$SOAPWSsha1)
    
    //Creamos la referencia al id del token generado a partir de la firma
    //set algorithm=$$$SOAPWSEnvelopedSignature_","_$$$SOAPWSc14n
    set reference=##class(%XML.Security.Reference).Create(token.Id)
    do sig2.AddReference(reference)
    
    //Crear TimeStamp
    Set timestamp=##class(%SOAP.Security.Timestamp).Create()
    
    //Se une
    //do ..SecurityOut.AddElement(sig1)
    do ..SecurityOut.AddToken(token)
    do ..SecurityOut.AddElement(sig2)    
    Do ..SecurityOut.AddToken(timestamp)
}

}

El sistema destino, nos pide por favor, que implementemos un WebService con MTOM y que envie los zip con csv de la siguiente forma:

Al importar el WSDL del sistema destino, se nos generó la cabecera del método "cargarFichero" con "fichero" como un "%xsd.base64Binary"

Siguiendo tus indicaciones, hemos cambiado fichero por "%GlobalCharacterStream"

De esta forma se envía el fichero, el csv, sin codificar, dentro de un CDATA

Siendo la respuesta del sistema destino:

10/15/2021 08:57:24 *********************
Input to Web client with SOAP action = http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
--MIME_Boundary
Content-ID: <root.message@cxf.apache.org>
Content-Type: application/xop+xml; type="text/xml"; charset=utf-8
Content-Transfer-Encoding: 8bit

        <?xml version="1.0" encoding="UTF-8"?>
        <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
            <soap:Body>
                <soap:Fault>
                    <faultcode>soap:Server</faultcode>
                    <faultstring>Name must not be null</faultstring>
                </soap:Fault>
            </soap:Body>
        </soap:Envelope>

--MIME_Boundary--

---------------
Validate Security header: action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero, MethodName=cargarFichero
**** SOAP client return error. method=cargarFichero, action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
     ERROR #6248: La respuesta de SOAP es un error de SOAP: faultcode=Server
faultstring=Name must not be null
faultactor=
detail=

Sin embargo, como observamos en la primera imagen, el sistema destino necesitaría, requeriría, que le enviemos el fichero codificado en binario, ya que pone:

Content-Transfer-Encoding: binary

Cuando adaptamos el fichero para que sea "%GlobalBinaryStream" vemos la siguiente traza, donde sí se codifica en binario:

Al enviar codificado en binario, el sistema destino también responde:

09/28/2021 16:56:43 *********************
Input to Web client with SOAP action = http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
--MIME_Boundary
Content-ID: <root.message@cxf.apache.org>
Content-Type: application/xop+xml; type="text/xml"; charset=utf-8
Content-Transfer-Encoding: 8bit

            <?xml version="1.0" encoding="UTF-8"?>
            <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
                <soap:Body>
                    <soap:Fault>
                        <faultcode>soap:Server</faultcode>
                        <faultstring>Name must not be null</faultstring>
                    </soap:Fault>
                </soap:Body>
            </soap:Envelope>

--MIME_Boundary--

---------------
Validate Security header: action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero, MethodName=cargarFichero
**** SOAP client return error. method=cargarFichero, action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
     ERROR #6248: La respuesta de SOAP es un error de SOAP: faultcode=Server
faultstring=Name must not be null
faultactor=
detail=

En resumen, el sistema destino necesita de nosotros: "añadir el parámetro “name” dentro de la cabecera “Content-Type” al adjuntar el archivo. Vuestro cliente debería generar dicho parámetro para no obtener dicho error."

Siendo la comparativa completa entre lo enviado por ensemble (sin el parámetro "name", por lo tanto es incorrecto) a la izquierda; y lo generado por el SoapUI (con el parámetro name, correcto), a la derecha:


 

Por favor, ustedes ¿podrían indicarnos ejemplos, documentación, proyectos, código, que nos sirva de referencia para indagar, investigar y completar el desarrollo?

Además hemos investigado las siguientes respuestas:

https://community.intersystems.com/post/add-parameter-name-inside-conten...

Muchas gracias por su tiempo, leyendo y respondiendo, gracias