Основы DICOM с использованием .NET и C#: понимание проверки DICOM (C-ECHO)

Введение

В этом материале я расскажу о чрезвычайно важной и очень полезной функции сети DICOM, как проверка возможности соединения DICOM между устройствами.

Вещи, которые многие из нас считают само собой разумеющимися, такие как назначение процедуры сканирования при входе в клинику, архивирование изображений, полученных с помощью таких методов, как КТ или МРТ, позднее извлечение и просмотр этих отсканированных изображений, отчет о диагнозе и печать нужных изображений на пленку не могли бы появиться без сетевых служб DICOM обеспечивающих связь между устройствами в больничной сети, даже если они произведены разными производителями. Если вы хотя бы немного знакомы с компьютерными сетями, вы должны знать популярный термин под названием «Пинг» . Это утилита, которая так или иначе реализована в большинстве операционных систем и позволяет выявлять неполадки, связанные с компьютерной сетью. Используя эту утилиту, можно проверить доступность определенного хоста или сети, а также измерить общее время прохождения туда и обратно тестовых сообщений. Хотя это позволяет нам проверить, доступен ли конкретный хост или нет, это не говорит нам, например, поддерживает ли устройство DICOM или включены ли его функции DICOM. Здесь на помощь приходит Служба проверки DICOM. Но прежде чем идти дальше, нам нужно разобраться в некотором техническом жаргоне, который понадобится, чтобы лучше понять эту тему.

Внутри любой сети DICOM может существовать множество приложений, работающих на различных устройствах, которые потребляют, а также предоставляют услуги друг другу (вспомните объяснение пользователей классов обслуживания и поставщиков классов обслуживания из материала "Основы DICOM с использованием .NET и C#: понимание файла DICOM"). Например, может существовать блок сканирования, такой как КТ, МРТ или УЗИ, сервер PACS, который обеспечивает хранение и извлечение отсканированных изображений, может быть несколько принтеров DICOM, которые печатают изображения и т.д. Все эти устройства могут запрашивать услуги или предоставлять услуги друг другу. На некоторых из этих устройств фактически может работать несколько программных приложений.

Например, на одном компьютере могут быть установлены два разных программного обеспечения DICOM: одно может быть программным обеспечением рабочей станции для просмотра, которое обеспечивает такие функции, как просмотр, а также запрос и извлечение изображений из удаленного архива. На том же компьютере может быть другое программное обеспечение, которое, возможно, отвечает за печать изображений DICOM. Оба эти приложения могут прослушивать связь с другими устройствами в сети, обмениваясь данными через совершенно разные порты (обратите внимание, что один объект приложения также может использовать два разных порта — один для передачи, а другой для приема). Каждое из этих приложений (независимо от того, работают они на отдельных устройствах или нет) известно на жаргоне DICOM как «Объект приложения» или сокращенно «AE» (Application Entity). Когда любые два из этих объектов взаимодействуют друг с другом в сети DICOM, объект, который является потребителем запрашиваемой услуги, называется пользователем класса обслуживания или SCU. Поставщик услуги известен как поставщик класса обслуживания или SCP. DICOM предусматривает, что каждый из этих объектов должен быть однозначно идентифицирован путем присвоения им уникального имени, называемого «Заголовком объекта приложения» (Application Entity Title - часто сокращенно «AET»). Заголовки часто кодируются цифрами и только заглавными буквами (подробнее об этом позже).

При устранении неполадок существующего соединения или настройке нового соединения между двумя устройствами используют предварительную проверку связи между двумя объектами, чтобы убедиться, что они могут говорить друг с другом на одном языке DICOM. Эта проверка осуществляется с помощью службы DICOM, называемой Службой проверки (также известной как C-Echo или неофициально называемой DICOM Ping). Эта проверка необходима, поскольку простой проверки доступности сети или физического соединения между двумя устройствами недостаточно, и они оба должны «говорить» на базовом уровне DICOM, чтобы иметь возможность ответить, чтобы понять, о чем спрашивают, прежде чем они смогут ответить «Да» или «Нет». Дальнейшая связь между двумя объектами может происходить только после установления этой первоначальной проверки. DICOM реализует эту первоначальную проверку через службу проверки DICOM, которая идентифицируется своим собственным уникальным идентификатором SOP (1.2.840.10008.1.1).

Во время этой операции проверки между двумя устройствами происходит обмен командными объектами, называемыми DIMSE. Эти DIMSE (расшифровываются как “DICOM Message Service Element” - «Элемент службы сообщений DICOM») кодируются по тому же знакомому шаблону элементов DICOM, который мы уже видели при работе с файлами DICOM, и имеют форму объектов запроса и ответа. Эти команды могут нести или не нести полезную нагрузку в зависимости от выполняемой операции. В случае операции сохранения изображения CT передается, например, команда DIMSE (C-STORE-RQ), за которой следует изображение CT (в форме IOD); затем удаленная сторона может ответить командой DIMSE (C-STORE-RSP), которая предоставит статус операции. Для операции проверки нет других данных, кроме посылки самой команды ("C-ECHO-RQ" в данном случае), а удаленная сторона может ответить обратно объектом "C-ECHO-RSP".

Также следует знать, что в отличие от других протоколов связи, хотя стандарт DICOM и не требует этого, информация, касающаяся обеих сторон, часто должна быть настроена на каждом объекте при настройке подключения DICOM. Так, например, если вы настраиваете новый сервер DICOM в своей сети, то вы должны не только настроить этот объект с его уникальным заголовком объекта приложения (АЕТ) в сети, но и также указать ему список других объектов, которые могут запрашивать услуги от него, чтобы он реагировал на них при общении. Давайте перейдем к рассмотрению примера кода, чтобы увидеть, как это работает в реальной жизни.

Пример проверки DICOM

Данный материал создан на основе творческого перевода статьи "DICOM Basics using .NET and C# - Understanding DICOM Verification", но далее я буду использовать собственный программный код для иллюстрации работы сервиса  проверки DICOM с помощью библиотеки fo-dicom для .NET и C#. За 10-15 минут мы получим полноценную программу для "прозвонки" dicom-устройств.

Набор инструментов DICOM Fellow Oak предоставляет ряд классов для выполнения сетевых операций DICOM. Эти классы можно найти в пространстве имен FellowOakDicom.Network библиотеки. Все операции DICOM SCU, включая проверку DICOM, выполняются с помощью класса DicomClientFactory . Класс предоставляет простые в использовании методы (как синхронные, так и асинхронные) для вызова ряда операций DICOM на любых удаленных узлах. В приведенном ниже коде я просто создаю экземпляр клиента dicom и добавляю к нему «эхо-запрос».
Итак, создаем в Visual Studio новый Windows Form проект.
На форме я разместил 5 подписей, 4 текстовых бокса и одну кнопку.

Текстовым полям здесь я сразу присвоил значения, чтобы не вводить их каждый раз при тестировании кода. Здесь указан реальный тестовый сервер https://dicomserver.co.uk/, с помощью которого можно протестировать работу своих приложений. Причем для этого сервера что вызывающий AET - Local AET, что вызываемый - Remote AET могут иметь любые значения. В поле Host Name можно вводить как название хоста, так и IP-адрес (для https://dicomserver.co.uk/ это 198.244.176.149), Dicom Port - только целое положительное число. Ещё раз: приведенные значения корректны только для этого сервера. Вообще, как правило, вызывающие заголовки должны быть зарегистрированы на сервере, а сочетание АЕТа, адреса и порта должны однозначно идентифицировать устройство (сервер PACS, например) в сети.
Вернемся к коду. При нажатии на кнопку "Echo" вызывается асинхронная процедура посылки запроса с аргументами, указанными в текстовых полях и ожидается результат в виде логического значения. Результат отображается в виде текста рядом с кнопкой - "Success" или "Failed".

try
            {
                labelRezultEcho.Text = "Wait...";
                labelRezultEcho.ForeColor = Color.Black;
                labelRezultEcho.Visible = true;
                if (await EchoRQ.EchoRSPAsync( textBoxLocalAet.Text,
                                               textBoxRemoteAet.Text,
                                               textBoxHostName.Text,
                                               int.Parse(textBoxDicomPort.Text)
                                              ))
                {
                    labelRezultEcho.Text = "Success !";
                    labelRezultEcho.ForeColor = Color.Green;
                }
                else
                {
                    labelRezultEcho.Text = "Failed !";
                    labelRezultEcho.ForeColor = Color.Red;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error: {ex.Message}");
            }

Процедуру посылки запроса я оформил в отдельном классе, код которого приводится ниже:

using FellowOakDicom.Network.Client;
using FellowOakDicom.Network;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Windows.Forms;

namespace DicomEcho
internal class EchoRQ
    {
        private static bool echoRezult = false;

        public static async Task EchoRSPAsync(string localAet, string remoteAet, string hostName, int dicomPort)
        {
            try
            {
                if (await EchoAsync(localAet, remoteAet, hostName, dicomPort))
                return echoRezult;
                else return false;
            }
            catch
            {
                return false;
            }
        }
        private static async Task  EchoAsync(string localAet, string remoteAet, string hostName, int dicomPort)
        {
            try
            {   //Создание клиента dicom             
                var client = DicomClientFactory.Create(hostName, dicomPort, false, localAet, remoteAet);
                client.NegotiateAsyncOps();
                var requestEcho = new DicomCEchoRequest(); // сохдание экземпляра запроса "эха"
                requestEcho.OnResponseReceived += EchoResponse; // Назначение процедуры обработки ответа на запрос
                await client.AddRequestAsync(requestEcho).ConfigureAwait(true); //Подключение запроса
                await client.SendAsync().ConfigureAwait(true); //Собственно отправка запроса
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
        }

        private static void EchoResponse(DicomCEchoRequest request, DicomCEchoResponse response)
        {
            if (response.Status.ToString() == "Success")
            {
                echoRezult = true;
                return;
            }
            else
            {
                echoRezult = false;
            }
        }
    }
}

Подключить библиотеку fo-dicom можно просто через меню управления пакетами NuGet

Собираем, пользуемся 🙂

 

Scroll to top