Change program settings while preserving personal preferences

Background

In a healthcare organization, solutions based on Orthanc PACS servers and the Radiant DICOM client were implemented. During the setup, it was determined that each DICOM client must be described in PACS servers as follows:

  • Client Name
  • AE Name (must be unique)
  • TCP port, which is automatically opened on the client side and accepts DICOM studies from the PACS server (i.e., the server pushes them to the client – initiating the connection first)
  • IP address

After setting up Radiant clients, we received the following information for consideration – each client’s software configuration with the parameters mentioned above resulted in filling out the file pacs.xml, which was located in the user profile (path: %APPDATA%RadiantViewerpacs.xml). Notably, the configuration of one client differed from another by at least two parameters (the AE names were different for all, and the port was mostly the same, except for terminal clients operating on the same server – there, ports also had to be assigned differently).

Example of the pacs.xml file per this link:

For about six months everything was fine, the system worked… and then we encountered some "underwater rocks»:

  • We need to integrate several new PACS servers to replace the old ones (where disk space was running out). The PACS servers are in virtual machines, but that's not the topic here;
  • We need to somehow centrally change the unique configurations (with two differing parameters) on 200 machines (their number regularly increased);
  • Considering the pace of growth in the volume of studies, the solution needs to be not a one-time fix, but a scalable and regular implementation (for example, once every 3-5 months).

The solution is below.

Choosing the toolkit for solving the task

Initially, there were attempts to find some solution that would modify the pacs.xml file on the client side, making changes to the list of PACS servers without touching the AE name and TCP port settings. At that time, Windows clients were running both Windows XP and Windows 7 – so attempts were made to write something based on VBScript. But unfortunately, I couldn't handle such a task, due to a complete lack of experience in writing anything complex and intricate in that language. Attempts to find and rewrite something also met with no success (it should be noted that another plan was already in mind, so I didn't spend more than 3-4 hours on VBScript).

Ultimately, I settled on the following solution:

  • Gather all pacs.xml files in one place on any server in the network resource using group policy;
  • Modify the files in bulk (experience with solving such tasks has already been had using Perl);
  • Also update client settings using group policies.

File collection using group policy

The simplest part is that when a client logs into their profile, they execute a certain .bat file with the following content:

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

Thus, files named pacs.xml will accumulate on the server in a hidden resource, containing information about which computer and user the configuration was copied from.

The hardest part was waiting for all users to process this policy.

Changing configurations with a Perl script

We will need Active Perl for Windows from ActiveState, as well as the XML::Writer module, which can be installed using the command ppm install XML-Writer.

The script itself turned out to be quite simple:

use XML::Writer;
 
# Opening the reports folder, processing the list (removing unnecessary items):
	$report_dir = "C:Perl64WORKPACS-xml3";
	opendir(DIR, "$report_dir") or die "Cannot open the reports folder!";
	@report_files = readdir DIR;
	shift (@report_files); # removing the dot from array elements (.)
	shift (@report_files); # removing the two dots from array elements (..)
#	print "@report_files";
	closedir(DIR);
 
# Start processing the files - one at a time. Need to read the AET parameter and port number into variables.
foreach $analiz_file (@report_files) 
{
	$full_path_to_file="C:Perl64WORKPACS-xml3".$analiz_file;
	open (INFO, $full_path_to_file);
 
	while ($line = )
	{
		# Variables $aet and $port contain unique data for each XML file:
		my ($other1, $aet, $other2, $port, $other3) = split /"/, $line, 5;
		# If the line contains 'listener', we've reached the necessary line and can form the new XML:
		if ($other1 =~ 'listener')
			{
				# Forming new XML with the required fields and data:
				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');
 
				# Place the prepared XML into a variable:
				my $xml = $writer->end();
				# Prepare the file for rewriting:
				$rewritexml = $full_path_to_file;
				# Rewrite XML files with new data:
				open (NEWXML, "> $rewritexml");
				print NEWXML $xml;
				close (NEWXML);
				
			}
	}
 
}

The principle of its operation:

  • We open the directory where we have collected the pacs.xml configurations from clients and place the list of files into an array of scalars (@report_files);
  • In a loop, we process each file one by one and read it line by line;
  • Using split, we break each line into 5 parts, using quotes as the delimiter;
  • We find the line containing the word listener and store the unique data for each file into two variables (AE-client name and TCP-port number);
  • After that, we simply create a new XML file, write the unique parameters into it, and insert the required number of PACS servers with their parameters – i.e., the reason why all of this was started)
  • We overwrite the old XML file with the new one.

It should be noted that I actually use this script not fully automatically – essentially, I copy the collected configs to a separate directory and then, by running the script, I change them all at once. After that, there's a selective check – and the configs can be distributed back to the machines.

Distribution of modified pacs.xml files to clients

The simplest solution that came to mind was to make changes to the already functioning .bat file, which collects configurations from clients and add the line:

If exist %APPDATA%RadiantViewerpacs.xml copy /Y srv.test.localpconfigsnew$pacs-%COMPUTERNAME%-%USERNAME%.xml %APPDATA%RadiantViewerpacs.xml

The final .bat file looks like this:

@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

Conclusion

Such a "knee-jerk" solution. We have already tested it twice (in September 2018 and February 2019), and so far, the flight is normal. Of course, it does not update 100% of clients, but close to that value — we handle the rest remotely. The script is this link.

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster