Pozadí
V jedné zdravotnické organizaci se zaváděla řešení založená na PACS serverech Orthanc a DICOM klientovi Radiant. Během nastavení se zjistilo, že každý DICOM klient musí být popsán na PACS serverech následujícím způsobem:
- Jméno klienta
- AE-jméno (musí být unikátní)
- TCP port, který se automaticky otevírá na straně klienta a přijímá DICOM vyšetření od PACS serveru (tj. server je jakoby tlačí směrem k klientovi – zahájí spojení jako první)
- IP adresa
Po nastavení klientů Radiant jsme dostali následující informace k zamyšlení – u každého klienta nastavení softwaru s výše uvedenými parametry vedlo k naplnění souboru pacs.xml, který se nacházel v profilu uživatele (cesta: %APPDATA%RadiantViewerpacs.xml). Přičemž konfigurace jednoho klienta se lišila od druhého minimálně dvěma parametry (AE-jméno mělo každé jiné, port byl většinou stejný, kromě terminálových klientů, které pracovaly na stejném serveru – tam se také musely porty nastavovat různě).
Příklad souboru pacs.xml podle :
Přibližně půl roku bylo vše v pořádku, systém fungoval… a zde nás přepadly "úskalí kameni»:
- Musíme zavést několik nových PACS serverů, které nahradí staré (kde už začínalo docházet místo na discích). PACS servery ve virtuálních strojích, ale o tom teď nemluvíme;
- Musíme nějak centralizovaně změnit unikátní konfigurace (dvěmi odlišnými parametry) na 200 strojích (jejich počet se pravidelně zvyšoval);
- Vzhledem k tempu růstu objemů vyšetření je potřeba řešení, které není jednorázové, ale převoditelné a pravidelné (například jednou za 3-5 měsíců).
Níže uvedené řešení.
Výběr nástrojů pro řešení úkolu
Zpočátku jsme se pokoušeli najít nějaké řešení, které na straně klienta měnilo soubor pacs.xml a vnášelo do něj změny v seznamu PACS serverů, aniž by se dotýkalo nastavení AE-jména a TCP portu. Windows klienti byly tehdy na bázi jak Windows XP, tak Windows 7 – proto jsme se pokoušeli napsat něco na bázi VBScript. Ale bohužel – tuto úlohu se nám nepodařilo zvládnout, vzhledem k naprosté absenci zkušeností v psaní něčeho složitého a komplexního v tomto jazyce. Pokusy o nalezení a přepsání také nebyly úspěšné (tady je třeba poznamenat, že už jsem měl v hlavě jiný plán, takže jsem se s VBScript už díval víc než 3-4 hodiny).
Nakonec jsem se zastavil na následujícím řešení:
- Sběr všech souborů pacs.xml na jednom místě na jakémkoliv serveru v síťovém zdroji pomocí skupinové politiky;
- Změna souborů hromadně (máme zkušenosti s řešením takových úloh – s využitím Perl);
- Také pomocí skupinových politik aktualizovat nastavení klientů.
Sběr souborů pomocí skupinové politiky
Nejjednodušší část – při vstupu klienta do svého profilu s jeho právy se spustí určité .bat soubor, ve kterém je uvedeno:
echo off
If exist %APPDATA%RadiantViewerpacs.xml copy %APPDATA%RadiantViewerpacs.xml srv.test.localpconfigs$pacs-%COMPUTERNAME%-%USERNAME%.xml
Tímto způsobem se na serveru ve skrytém zdroji budou shromažďovat soubory pacs.xml, jejichž názvy obsahují informace o tom, z jakého počítače a od jakého uživatele byl tento konfigurační soubor zkopírován.
Nejtěžší bylo počkat, až tato politika proběhne u všech uživatelů.
Zmnaění konfigurací pomocí Perl skriptu
Budeme potřebovat pro Windows od společnosti ActiveState, a také modul XML::Writer, který lze nainstalovat pomocí příkazu ppm install XML-Writer.
Sám skript se ukázal jako docela jednoduchý:
use XML::Writer;
# Otevřeme složku s reporty, zpracujeme seznam (odstraníme nadbytečné):
$report_dir = "C:Perl64WORKPACS-xml3";
opendir(DIR, "$report_dir") or die "Nemohu otevřít složku s reporty!";
@report_files = readdir DIR;
shift (@report_files); # odstraníme tečku z prvků pole (.)
shift (@report_files); # odstraníme dvě tečky z prvků pole (..)
# print "@report_files";
closedir(DIR);
# Začínáme zpracovávat soubory - po jednom. Je potřeba načíst parametr AET a číslo portu do proměnných.
foreach $analiz_file (@report_files)
{
$full_path_to_file="C:Perl64WORKPACS-xml3".$analiz_file;
open (INFO, $full_path_to_file);
while ($line = )
{
# Proměnné $aet a $port obsahují jedinečné údaje pro každý XML soubor:
my ($other1, $aet, $other2, $port, $other3) = split "/", $line, 5;
# Pokud se setkáme se řetězcem listener - tak jsme došli na potřebný řádek a můžeme vytvořit nový XML:
if ($other1 =~ 'listener')
{
# Vytváříme nový XML s potřebnými poli a údaji:
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');
# Umístíme hotový XML do proměnné:
my $xml = $writer->end();
# Připravíme soubor k přepsání:
$rewritexml = $full_path_to_file;
# Přepisujeme XML soubory novými údaji:
open (NEWXML, "> $rewritexml");
print NEWXML $xml;
close (NEWXML);
}
}
}
Princip jeho fungování:
- Otevíráme katalog, ve kterém máme shromážděnými konfiguracemi pacs.xml od klientů, a umisťujeme seznam souborů do pole skalarů (@report_files);
- V cyklu zpracováváme jeden soubor po druhém a čteme jej po řádcích;
- Pomocí split rozdělujeme každý řádek na 5 částí, používající uvozovky jako oddělovač;
- Najdeme řádek se slovem listener a uložíme do dvou proměnných, unikátní pro každý soubor, data (AE-jméno klienta a číslo TCP-portu);
- Poté jednoduše vytváříme nový XML-soubor, zapisujeme do něj unikátní parametry a dále vkládáme potřebný počet PACS-serverů s jejich parametry – tzn. to, kvůli čemu bylo vše zahájeno)
- Přepisujeme nový XML-soubor na starý.
Musím podotknout, že ve skutečnosti tento skript nepoužívám zcela automaticky – v podstatě kopíruji shromážděné konfigurace do samostatného katalogu a pak spouštím skript, abych je změnil všechny najednou. Následuje výběrová kontrola – a konfigurace lze zase rozdělit po strojích.
Distribuce změněných pacs.xml souborů mezi klienty
Nejjednodušší, co mě napadlo – provést změny v již fungujícím .bat souboru, který sbírá konfigurace z klientů, a přidat řádek:
If exist %APPDATA%RadiantViewerpacs.xml copy /Y srv.test.localpconfigsnew$pacs-%COMPUTERNAME%-%USERNAME%.xml %APPDATA%RadiantViewerpacs.xml
Výsledný .bat soubor vypadá takto:
@echo off
If exist %APPDATA%RadiantViewerpacs.xml copy %APPDATA%RadiantViewerpacs.xml srv.test.localpconfigs$pacs-%COMPUTERNAME%-%USERNAME%.xml
If exist %APPDATA%RadiantViewerpacs.xml copy /Y srv.test.localpconfigsnew$pacs-%COMPUTERNAME%-%USERNAME%.xml %APPDATA%RadiantViewerpacs.xml
Závěr
Takové tedy "na koleně" řešení. Vyzkoušeli jsme to už dvakrát (v září 2018 a v únoru 2019), zatím let normální. Samozřejmě neaktualizuje 100% klientů, ale blízko této hodnoty – ostatní doděláváme na dálku. Skript podle .
Zdroj: habr.com
