This tutorial uses Visual Studio 2010.
Step 1:
Start Visual Studio, and create a new Class Library project.
Name: Leadtools.Medical.ExternalStore.Tutorial.Addin
Location: use C:\LEADTOOLS 19\Examples\PACSFramework\CS\Sample AddIns
Uncheck the Create directory for solution checkbox
Step 2:
In Solution Explorer:
Step 3:
In the Solution Explorer, right-click the References folder and add the following references by using the Browse tab.
The tutorial add-in uses the .NET Framework 4, so browse to the C:\LEADTOOLS 19\Bin\Dotnet4\Win32 for the LEAD assemblies.
After adding the references, right click all the Leadtools references in Solution Explorer, and set the Copy Local property to False.
Add the following System reference. Leave the Copy Local property to True
Compile the project
Step 4:
Module.cs
All the LEADTOOLS PACSFramework addins derive from the abstract class ModuleInit, and implement the interface IProcessBreak
Rename the Class1.cs to be Module.cs
Add the following using statements
Copy Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Leadtools.Dicom.AddIn;
using Leadtools.Dicom.AddIn.Interfaces;
using Leadtools.Medical.Winforms.ExternalStore;
using Leadtools.Dicom.AddIn.Configuration;
using Leadtools.Medical.DataAccessLayer;
using Leadtools.Logging;
Change the class Module so it derives from ModuleInit, an implements IProcessBreak as follows:
Copy Code
public class Module : ModuleInit, IProcessBreak
{
}
Right-click IProcessBreak, and choose Implement Interface
Remove the ‘thrownew NotImplementedException()’from the body of the Break method.
Add the following public constants:
Copy Code
public const string Source = "ExternalStoreTutorial";
public const string FriendlyName = "Tutorial External Store Addin";
public const string TutorialGuid = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE";
Use Tools->Create GUID->New GUID to create your own GUID for the TutorialGuid constant
Add the following public methods:
Copy Code
public static void ExternalStoreSettingsChanged()
{
// TODO: implement later in the tutorial
}
private static void CreateExternalStoreFolder()
{
// TODO: implement later in the tutorial
}
// Stores the configuration settings for this addin
private static ExternalStoreAddinConfigAbstract _tutorialExternalStoreAddinConfig;
public static ExternalStoreAddinConfigAbstract TutorialExternalStoreAddinConfig
{
get {return _tutorialExternalStoreAddinConfig;} // TODO: implement later in the tutorial
set {_tutorialExternalStoreAddinConfig = value;} // TODO: implement later in the tutorial
}
// For mutual exclusion when reading the 'ExternalStoreOptions'
private static readonly object optionsLock = new object();
// Get/Set the 'ExternalStoreOptions'
// ExternalStoreOptions holds options that are common to all ExternalStore addins
private static ExternalStoreOptions _options;
public static ExternalStoreOptions Options
{
get
{
lock (optionsLock)
{
return _options;
}
}
set
{
lock (optionsLock)
{
_options = value;
if (_options != null)
{
ExternalStoreItem item = _options.GetCurrentOption();
if ((item != null) && (item.ExternalStoreAddinConfig != null) && (item.ExternalStoreAddinConfig.Guid == Module.TutorialGuid) )
{
_tutorialExternalStoreAddinConfig = item.ExternalStoreAddinConfig;
CreateExternalStoreFolder();
}
}
}
}
}
Compile the project
Step 5:
TutorialCrud.cs Part I
In the Solution Explorer, right-click Leadtools.Medical.ExternalStore.Tutorial.Addin and add public class named TutorialCrud.cs that implements the ICrud interface.
Add the following using statements.
Copy Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Leadtools.Dicom.AddIn.Interfaces;
using System.IO;
using Leadtools.Medical.Storage.DataAccessLayer;
using Leadtools.Dicom;
using System.Data;
namespace Leadtools.Medical.ExternalStore.Tutorial.Addin
{
public class TutorialCrud : ICrud
{
}
}
Right-click ICrud, and choose Implement Interface.
Compile the project.
Step 6:
TutorialConfiguration.cs
In the Solution Explorer, right-click Leadtools.Medical.ExternalStore.Tutorial.Addin and a public class named TutorialConfiguration.cs
Add the following using statements to the top of the file
Example Title Copy Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Leadtools.Dicom.AddIn.Interfaces;
using System.IO;
using Leadtools.Medical.Storage.DataAccessLayer;
using Leadtools.Dicom;
using System.Data;
namespace Leadtools.Medical.ExternalStore.Tutorial.Addin
{
public class TutorialCrud : ICrud
{
}
}
Make the TutorialConfiguration class public, and add the class attributes Serializable and ExternalStoreConfigurationAttribute
Copy Code
namespace Leadtools.Medical.ExternalStore.Tutorial.Addin
{
[Serializable]
[ExternalStoreConfigurationAttribute]
public class TutorialConfiguration
{
}
}
Copy the unique configuration (class TutorialConfiguration) class shown earlier here (Ctrl-Click) in the tutorial to TutorialConfiguration.cs
After namespaceLeadtools.Medical.ExternalStore.Tutorial.Addin, add a public, serializable class named TutorialExternalStoreAddinConfig that implements the abstract class ExternalStoreAddinConfigAbstract
Copy Code
[Serializable]
public class TutorialExternalStoreAddinConfig : ExternalStoreAddinConfigAbstract
{
}
Right-click ExternalStoreAddinConfigAbstract and choose Implement Abstract Class
Add a [NonSerialized] attribute at the end the class, after the VerifyConfiguration member
Implement the ConfigurationObject override.
Copy Code
// The unique configuration settings for this add-in
// (specified by the TutorialConfiguration class)
private TutorialConfiguration _configurationObject;
public override object ConfigurationObject
{
get { return _configurationObject; }
set { _configurationObject = value as TutorialConfiguration; }
}
Implement theEnableAutoClear, EnableAutoExternalStore, and EnableVerifyRetrieveAfterExternalStore overrides. All getters should return true.
Copy Code
public override bool EnableAutoClear
{
get { return true; }
}
public override bool EnableAutoExternalStore
{
get { return true; }
}
public override bool EnableVerifyRetrieveAfterExternalStore
{
get { return true; }
}
Implement the FriendlyName override
Get: return Module.FriendlyName
Set: do nothing
Copy Code
public override string FriendlyName
{
get
{
return Module.FriendlyName;
}
set
{
}
}
Implement the Guid override
Get: return Module.TutorialGuid
Set: do nothing
Copy Code
public override string Guid
{
get
{
return Module.TutorialGuid;
}
set
{
}
}
Implement the VerifyConfiguration override. This method verifies that the configuration settings for the external store add-in are valid. For this add-in, DICOM datasets are externally stored to a location folder. Verification simply means verifying that location exists, trying to create it if it does not exists, and making sure that the add-in has write permissions to this location.
Copy Code
public override bool VerifyConfiguration(out string errorString)
{
errorString = string.Empty;
string folderPath = _configurationObject.Location;
// Check to see if directory exists
bool exists = Directory.Exists(folderPath);
if (!exists)
{
try
{
Directory.CreateDirectory(folderPath);
}
catch(Exception)
{
errorString = string.Format("Cannot create directory location: '{0}", folderPath);
return false;
}
}
if (!exists)
{
errorString = string.Format("Location does not exist: '{0}", folderPath);
return false;
}
// Check to see if user has write permissions
try
{
// Attempt to get a list of security permissions from the folder.
// This will raise an exception if the path is read only or do not have
// access to view the permissions.
Directory.GetAccessControl(folderPath);
}
catch (UnauthorizedAccessException)
{
errorString = string.Format("No permissions to write to Location: '{0}", folderPath);
return false;
}
return true;
}
Add a class constructor
Copy Code
public TutorialExternalStoreAddinConfig()
{
_crud = null;
_configurationObject = new TutorialConfiguration();
}
Implement the GetCrudInterface override.
Copy Code
public override ICrud GetCrudInterface()
{
if (_crud == null)
{
_crud = new TutorialCrud();
}
_crud.TutorialConfiguration = ConfigurationObject as TutorialConfiguration;
return _crud;
}
Add a ToString() override. This is what will be displayed in the External Store
Copy Code
public override string ToString()
{
return FriendlyName;
}
The project will not compile until you complete the next step (Step 7).
Step 7:
TutorialCrud.cs Part II
Open TutorialCrud.cs
Add the following method above the #regionICrud Members
Copy Code
private TutorialConfiguration _tutorialConfiguration;
public TutorialConfiguration TutorialConfiguration
{
get { return _tutorialConfiguration; }
set { _tutorialConfiguration = value; }
}
Compile the project.
At this point, the external store settings of CSStorageServerManagerDemo.exe will display/verify the add-in settings.
Copy Leadtools.Medical.ExternalStore.Tutorial.Addin.dll to the add-ins folder:
C:\\LEADTOOLS 19\\Bin\\Dotnet4\\Win32\\L19\_PACS\_SCP32\\AddIns
Start CSStorageServerManagerDemo.exe
Login
Click the External Store button
Choose Tutorial External Store Addin from the drop down
Click the Verify button to see that the settings are valid.
Step 8:
TutorialCrud.cs Part III
Open TutorialCrud.cs
Add the following utility function that takes a local store path name, and returns an external store full path name.
Copy Code
// A token is a string value, that together with the external store add-in GUID,
// corresponds to a file stored externally.
// For this tutorial external store add-in, the external store path will be the
// path TutorialConfiguration.Location
// This function takes the local store name, and returns the external store full path name
private string GetTokenFromFilename(string fn)
{
string fileName = Path.GetFileName(fn);
string location = TutorialConfiguration.Location;
string token = Path.Combine(location, fileName);
return token;
}
Implement the ICrud.Delete member
Copy Code
// Since the token is the full path of the externally stored file,
// simply call File.Delete(token)
public Exception Delete(string token)
{
Exception ret = null;
try
{
File.Delete(token);
}
catch (Exception ex)
{
ret = ex;
}
return ret;
}
Implement the ICrud.DeleteDicom member
Copy Code
// Use the helper method (from Leadtools.Medical.Storage.DataAccessLayer.dll)
// which takes a DataRow, and returns a token
// string token = RegisteredDataRows.InstanceInfo.StoreToken(DataRow row)
// Then call the existing ICrud.Delete() method
public Exception DeleteDicom(DataRow row)
{
string token = RegisteredDataRows.InstanceInfo.StoreToken(row);
return Delete(token);
}
Implement the ICrud.Exists member
Copy Code
public Exception Exists(string token, out bool exists)
{
exists = false;
Exception ret = null;
try
{
exists = File.Exists(token);
}
catch (Exception ex)
{
ret = ex;
}
return ret;
}
Implement the ICrud.ExistsDicom member
Copy Code
public Exception ExistsDicom(DataRow row, out bool exists)
{
string token = RegisteredDataRows.InstanceInfo.StoreToken(row);
return Exists(token, out exists);
}
Implement the ICrud.ExternalStoreGuid property
Copy Code
public string ExternalStoreGuid
{
get { return Module.TutorialGuid; }
}
Implement the ICrud.FriendlyName property
Copy Code
public string FriendlyName
{
get { return Module.FriendlyName; }
}
Implement the ICrud.Initialize member. For this add-in, nothing is required.
Copy Code
// No initialization required for the tutorial external store add-in
public void Initialize()
{
// do nothing
}
Implement the ICrud.RetrieveDicom method.
Copy Code
public Exception RetrieveDicom(DataRow row, DicomDataSetLoadFlags loadFlags, out DicomDataSet ds)
{
ds = null;
Exception ret = null;
try
{
ds = new DicomDataSet();
string token = RegisteredDataRows.InstanceInfo.StoreToken(row);
ds.Load(token, loadFlags);
}
catch (Exception ex)
{
ret = ex;
}
return ret;
}
Implement the ICrud.RetrieveFile method.
Copy Code
private static Exception RetrieveFile(string token, string outFile)
{
Exception ret = null;
try
{
File.Copy(token, outFile);
}
catch (Exception ex)
{
ret = ex;
}
return ret;
}
public Exception RetrieveFile(DataRow row, string outFile)
{
string token = RegisteredDataRows.InstanceInfo.StoreToken(row);
return RetrieveFile(token, outFile);
}
Implement the ICrud.SettingsChanged method
Copy Code
// This is called by the PACSFramework when the external store settings change
// for any external store addin
// through the CSStorageServerManager UI
// This method should re-read the external store settings
public void SettingsChanged()
{
Module.ExternalStoreSettingsChanged();
}
Implement the ICrud.Store method
Copy Code
public Exception Store(string filename, bool overwrite, out string token)
{
Exception ret = null;
token = string.Empty;
try
{
string newFileName = GetTokenFromFilename(filename);
token = newFileName;
if (overwrite || !File.Exists(newFileName))
{
File.Copy(filename, newFileName, true);
}
}
catch (Exception ex)
{
ret = ex;
}
return ret;
}
Implement the ICrud.StoreDicom method
Copy Code
private Exception SaveDicom(string filename, DicomDataSet ds, DicomDataSetSaveFlags saveFlags, out string token)
{
Exception ret = null;
string newFileName = GetTokenFromFilename(filename);
token = newFileName;
try
{
string directory = Path.GetDirectoryName(newFileName);
Directory.CreateDirectory(directory);
ds.Save(newFileName, saveFlags);
}
catch (Exception ex)
{
ret = ex;
}
return ret;
}
public Exception StoreDicom(string filename, DicomDataSet ds, DicomDataSetSaveFlags saveFlags, bool overwrite, out string token)
{
token = string.Empty;
Exception ret = null;
try
{
if (overwrite || !File.Exists(filename))
{
SaveDicom(filename, ds, saveFlags, out token);
}
}
catch (Exception ex)
{
ret = ex;
}
return ret;
}
Implement the ICrud.StoreLocal property
Copy Code
// This is equivalent to the current value of the TutorialConfiguration.StoreLocally
public bool StoreLocal
{
get
{
return _tutorialConfiguration.StoreLocally;
}
}
Implement the ICrud.UpdateDicom method
Copy Code
// Patient Updater calls 'UpdateDicom'
// Write the file if it already exists
// If no exists, do nothing.
public Exception UpdateDicom(DataRow row, DicomDataSet dataset, DicomDataSetSaveFlags saveFlags)
{
string token = RegisteredDataRows.InstanceInfo.StoreToken(row);
Exception ret = null;
try
{
if (File.Exists(token))
{
dataset.Save(token, saveFlags);
}
}
catch (Exception ex)
{
ret = ex;
}
return ret;
}
Compile the project.
Step 9:
Module.cs Part II
Open Module.cs
Add a member that will hold the current DICOM listening service directory. This is initialized when the external store add-in loads, and is used to read the external store settings from advanced.config
Copy Code
// Location of the configuration settings for this addin
private static string _serviceDirectory;
Now that the TutorialExternalStoreAddinConfig class has been defined, configuration settings member implementation can be completed.
Example Title Copy Code
// Stores the configuration settings for this addin
private static ExternalStoreAddinConfigAbstract _tutorialExternalStoreAddinConfig = null;
public static ExternalStoreAddinConfigAbstract TutorialExternalStoreAddinConfig
{
get
{
if (_tutorialExternalStoreAddinConfig == null)
{
_tutorialExternalStoreAddinConfig = new TutorialExternalStoreAddinConfig();
}
return _tutorialExternalStoreAddinConfig;
}
set
{
_tutorialExternalStoreAddinConfig = value;
}
}
Implement the ModuleInit.Load override
Copy Code
// Called when the external store addin is loaded by the PACSFramework
// Registers the ICrud interface used by this external store add-in
// If this external store add-in is currently active, this method starts
// the JobManager for this addin
public override void Load(string serviceDirectory, string displayName)
{
// Open 'advanced.config' which contains all settings for
// CSStorageServerManger.exe addins (including this ExternalStore TutorialAddin)
AdvancedSettings settings = AdvancedSettings.Open(serviceDirectory);
_serviceDirectory = serviceDirectory;
try
{
Type[] extraTypes = new[]{typeof(TutorialConfiguration)};
Options = settings.GetAddInCustomData<ExternalStoreOptions>(ExternalStorePresenter._Name, "ExternalStoreOptions", extraTypes);
if (Options == null)
{
Options = new ExternalStoreOptions();
}
ICrud thisCrud = Options.GetCrud(TutorialGuid);
DataAccessServiceLocator.Register(thisCrud, thisCrud.ExternalStoreGuid);
}
catch (Exception e)
{
if (Options == null)
{
Options = new ExternalStoreOptions();
}
Logger.Global.Error(Source, e.Message);
}
ExternalStore.Addin.Module.StartExternalStoreJobs(TutorialExternalStoreAddinConfig, "Tutorial");
}
Finish implementing the ExternalStoreSettingsChanged() method
Copy Code
// When the settings are changed from the CSStorageServerManager.exe UI, this event is fired
// The add-in needs
// * Re-read its settings
// * Store the settings locally (in _tutorialExternalStoreAddinConfig)
// * If the external store folder has changed, create the new external store folder location
public static void ExternalStoreSettingsChanged()
{
AdvancedSettings settings = AdvancedSettings.Open(_serviceDirectory);
if (settings != null)
{
settings.RefreshSettings();
Type[] extraTypes = new Type[] { typeof(TutorialConfiguration) };
Options = settings.GetAddInCustomData<ExternalStoreOptions>(ExternalStorePresenter._Name, "ExternalStoreOptions", extraTypes);
ExternalStore.Addin.Module.StartExternalStoreJobs(TutorialExternalStoreAddinConfig, "Tutorial");
}
}
When the Options member is set, it checks to see if this add-in (Tutorial) is active. If the tutorial add-in is active, create the external store folder if it does not already exist.
Now we can finish the implementation of the CreateExternalStoreFolder member by calling VerifyConfiguration (which creates the folder if it does not exist).
Copy Code
private static void CreateExternalStoreFolder()
{
try
{
// Call VerifyConfiguration -- for this addin, it creates the external store folder
string errorString = string.Empty;
bool verify = TutorialExternalStoreAddinConfig.VerifyConfiguration(out errorString);
if (verify == false)
{
Logger.Global.Error(Source, errorString);
}
}
catch (Exception ex)
{
Logger.Global.Error(Source, ex.Message);
}
}
Step 10.
You have completed the implementation of the tutorial external store add-in.
Copy Leadtools.Medical.ExternalStore.Tutorial.Addin.dll to the add-ins folder:
C:\\LEADTOOLS 19\\Bin\\Dotnet4\\Win32\\L19\_PACS\_SCP32\\AddIns
Start CSStorageServerManagerDemo.exe
Login
Click the External Store button Choose Tutorial External Store Addin from the drop down