Error processing SSI file
Show in webframe

Tutorial: Creating a new External Store Add-in

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

Error processing SSI file