Изменение настроек программ с сохранением персональных параметров +9


Предыстория


В одной медицинской организации внедряли решения на базе PACS-серверов Orthanc и DICOM-клиента Radiant. В ходе настройки выяснили, что каждый DICOM-клиент должен быть описан в PACS-серверах следующим образом:

  • Имя клиента
  • AE-имя (должно быть уникально)
  • TCP-порт, который автоматически открывается на стороне клиента и принимает DICOM-обследования от PACS-сервера (т.е. сервер как бы толкает их в сторону клиента – инициируя соединение первым)
  • IP-адрес

После настройки Radiant клиентов получили следующую информацию к размышлению – у каждого клиента настройка ПО с указанными выше параметрами приводила к заполнению файла pacs.xml, который располагался в профиле пользователя (путь: %APPDATA%\RadiantViewer\pacs.xml). При этом конфиг одного клиента от другого отличался минимум двумя параметрами (AE-имя у всех разное, а порт в основном одинаковый, кроме терминальных клиентов, работающих на одном и том же сервере – там порты тоже приходилось назначать разными).

Пример файла pacs.xml по ссылке:

Примерно полгода все было хорошо, система заработала…и тут до нас дошли «подводные камни»:

  • Нам нужно ввести в строй несколько новых PACS-серверов, которые подменят старые (где стало заканчиваться место на дисках). PACS сервера в виртуальных машинах, но речь не об этом;
  • Нам нужно как-то централизованно изменить уникальные конфигурации (двумя отличающимися параметрами) на 200 машинах (их количество регулярно увеличивалось);
  • Учитывая темпы роста объемов обследований, решение нужно не разовое, а тиражируемое и регулярное (например, 1 раз в 3-5 месяцев).

Решение ниже.

Выбор инструментария для решения задачи


Вначале были попытки найти какое-то решение, которое на стороне клиента изменяло файл pacs.xml, и вносило в него изменения в список PACS-серверов, не трогая настройки AE-имени и TCP-порта. Windows клиенты на тот момент были на базе как Windows XP, так и Windows 7 – поэтому были попытки написать что-то такое на базе VBScript. Но увы – осилить такую задачу не получилось, ввиду полного отсутствия опыта написания чего-либо сложного и комплексного на этом языке. Попытки же найти и переписать также не увенчались успехом (тут надо отметить, что в голове уже был другой план, поэтому я не долбился с VBScript больше 3-4 часов).

В итоге я остановился на следующем решении:

  • Собрать групповой политикой все файлы pacs.xml в одном месте на каком ни будь сервере в сетевом ресурсе;
  • Изменить файлы скопом (опыт решения таких задач уже был – с использованием Perl);
  • Также с помощью групповых политик обновить настройки клиентов.

Сбор файлов с помощью групповой политики


Самая простая часть – при входе клиента в свой профиль он со своими правами выполняет некий .bat файл, в котором прописано:

echo off
If exist %APPDATA%\RadiantViewer\pacs.xml copy %APPDATA%\RadiantViewer\pacs.xml \\srv.test.local\pconfigs$\pacs-%COMPUTERNAME%-%USERNAME%.xml

Таким образом на сервере в скрытом ресурсе будут накапливаться файлы pacs.xml, в имени которых есть информация с какого компьютера и с какого пользователя был скопирован данный конфиг.

Самое сложное было – дождаться, когда у всех пользователей отработает данная политика.

Изменение конфигураций с помощью Perl скрипта


Нам потребуется Active Perl под Windows от компании ActiveState, а также модуль XML::Writer, который можно установить с помощью команды ppm install XML-Writer.

Сам же скрипт получился довольно простой:

use XML::Writer;
 
# Открываем папку с отчетами, обрабатываем ссписок (удаляем лишнее):
	$report_dir = "C:\\Perl64\\WORK\\PACS-xml3\\";
	opendir(DIR, "$report_dir") or die "Не могу открыть папку с отчетами!";
	@report_files = readdir DIR;
	shift (@report_files); # удаляем точку из элементов массива (.)
	shift (@report_files); # удаляем две точки из элементов массива (..)
#	print "@report_files";
	closedir(DIR);
 
# Начинаем обрабатывать файлы - по одному за раз. Нужно считать параметр AET и номер порта в переменные.
foreach $analiz_file (@report_files) 
{
	$full_path_to_file="C:\\Perl64\\WORK\\PACS-xml3\\".$analiz_file;
	open (INFO, $full_path_to_file);
 
	while ($line = <INFO>)
	{
		# Переменные $aet и $port содержат уникальные данные для каждого XML файла:
		my ($other1, $aet, $other2, $port, $other3) = split /\"/, $line, 5;
		# Если встречается строка listener - то мы дошли до нужной строчки и можно формировать новый XML:
		if ($other1 =~ 'listener')
			{
				# Формируем новый XML c нужными полями и данными:
				my $writer = XML::Writer->new(OUTPUT => 'self', DATA_MODE => 1, DATA_INDENT => 2, );
				$writer->xmlDecl('utf-8');
				$writer->startTag('pacs');
				$writer->startTag('listener', ae => $aet, port => $port);
				$writer->endTag();
				$writer->startTag('hosts');
				$writer->startTag('host', name => 'MRT', ae => 'ORTHANC', ip => 'XX.YY.214.17', ts => '1.2.840.10008.1.2.1', port => '4242', maxassoc => '1', allpres => '0', search => '1', protocol => '1', searchcharset => '', wildcards => '3', carets => '0');
				$writer->endTag();
				$writer->startTag('host', name => 'KT', ae => 'ORTHANC2', ip => 'XX.YY.215.253', ts => '1.2.840.10008.1.2.1', port => '4242', maxassoc => '1', allpres => '0', search => '1', protocol => '1', searchcharset => '', wildcards => '3', carets => '0');
				$writer->endTag();
				$writer->startTag('host', name => 'R', ae => 'ORTHANC3', ip => 'XX.YY.215.252', ts => '1.2.840.10008.1.2.1', port => '4242', maxassoc => '1', allpres => '0', search => '1', protocol => '1', searchcharset => '', wildcards => '3', carets => '0');
				$writer->endTag();
				$writer->startTag('host', name => 'KT-20180501-20180831', ae => 'ORTHANC4', ip => 'XX.YY.215.251', ts => '1.2.840.10008.1.2.1', port => '4242', maxassoc => '1', allpres => '0', search => '1', protocol => '1', searchcharset => '', wildcards => '3', carets => '0');
				$writer->endTag();
				$writer->startTag('host', name => 'KT-20180901-20181130', ae => 'ORTHANC5', ip => 'XX.YY.215.250', ts => '1.2.840.10008.1.2.1', port => '4242', maxassoc => '1', allpres => '0', search => '1', protocol => '1', searchcharset => '', wildcards => '3', carets => '0');
				$writer->endTag();
				$writer->endTag('hosts');
				$writer->startTag('presets');
				$writer->endTag();
				$writer->startTag('lastsearch', dt => '4', mfid => '1048592');
				$writer->endTag();
				$writer->endTag('pacs');
 
				# Помещаем готовый XML в переменную:
				my $xml = $writer->end();
				# Подготавливаем файл для перезаписи:
				$rewritexml = $full_path_to_file;
				# Переписываем XML файлы новыми данными:
				open (NEWXML, ">$rewritexml");
				print NEWXML $xml;
				close (NEWXML);				
			}
	}
 
}

Принцип его работы:

  • Открываем каталог, в котором у нас собраны конфигурации pacs.xml от клиентов и помещаем список файлов в массив скаляров (@report_files);
  • В цикле обрабатываем по одному файлу и считываем его построчно;
  • С помощью split дробим каждую строку на 5 частей, используя кавычки как разделитель;
  • Находим строку с словом listener и помещаем в две переменные уникальные для каждого файла данные (AE-имя клиента и номер TCP-порта);
  • После этого просто формируем новый XML-файл, вписываем в него уникальные параметры и далее вставляем нужное количество PACS-серверов с их параметрами – т.е. то, ради чего все затевалось)
  • Переписываем новый XML-файл поверх старого.

Надо отметить, что на самом деле я использую данный скрипт не полностью автоматически – по сути я копирую собираемые конфиги в отдельный каталог и потом запуская скрипт изменяю их всем скопом. Далее выборочная проверка – и конфиги можно разливать обратно по машинам.

Распространение измененных pacs.xml файлов по клиентам


Самое простое, что пришло в голову – внести изменения в уже работающий .bat файл, который собирает конфигурации с клиентов и добавить строку:

If exist %APPDATA%\RadiantViewer\pacs.xml copy /Y \\srv.test.local\pconfigsnew$\pacs-%COMPUTERNAME%-%USERNAME%.xml %APPDATA%\RadiantViewer\pacs.xml

Итоговый .bat файл выглядит так:

@echo off
If exist %APPDATA%\RadiantViewer\pacs.xml copy %APPDATA%\RadiantViewer\pacs.xml \\srv.test.local\pconfigs$\pacs-%COMPUTERNAME%-%USERNAME%.xml
If exist %APPDATA%\RadiantViewer\pacs.xml copy /Y \\srv.test.local\pconfigsnew$\pacs-%COMPUTERNAME%-%USERNAME%.xml %APPDATA%\RadiantViewer\pacs.xml

Заключение


Такое вот «наколеночное» решение. Опробовали его уже два раза (в сентябре 2018 и в феврале 2019), пока полет нормальный. Конечно обновляет не 100% клиентов, но близко к этому значению — остальных доделываем удаленно. Скрипт по ссылке.




К сожалению, не доступен сервер mySQL