This tutorial shows how to add an XML database to a PACS Server and allow a client to store DICOM datasets on the server in a C# WinForms application using the LEADTOOLS SDK.
Overview | |
---|---|
Summary | This tutorial covers how to add a database and handle C-STORE requests for a PACS server in a WinForms C# Application. |
Completion Time | 60 minutes |
Visual Studio Project | Download tutorial project (18 KB) |
Platform | Windows WinForms C# Application |
IDE | Visual Studio 2019 |
Development License | Download LEADTOOLS |
Get familiar with the basic steps of creating a project and a PACS Server by reviewing the Add References and Set a License and the Create a Simple PACS Server tutorials, before working on the Handle Store Requests in a PACS Server - WinForms C# tutorial.
Start with a copy of the project created in the Create a Simple PACS Server tutorial. If you do not have that project, follow the steps in that tutorial to create it.
The references needed depend upon the purpose of the project. References can be added by one or the other of the following two methods (but not both).
If using NuGet references, this tutorial requires the following NuGet package:
Leadtools.Dicom.Pacs.Scp
If using local DLL references, the following DLLs are needed.
The DLLs are located at <INSTALL_DIR>\LEADTOOLS22\Bin\Dotnet4\x64
:
Leadtools.dll
Leadtools.Dicom.dll
For a complete list of which DLL files are required for your application, refer to Files to be Included With Your Application.
The License unlocks the features needed for the project. It must be set before any toolkit function is called. For details, including tutorials for different platforms, refer to Setting a Runtime License.
There are two types of runtime licenses:
Note
Adding LEADTOOLS NuGet and local references and setting a license are covered in more detail in the Add References and Set a License tutorial.
With the project created, the references added, and the license set, coding can begin.
In the Solution Explorer, Right-click on <Project>.csproj
and select Add -> New Folder. Create a folder called Database
. Right-click on this folder and select Add -> New Item.... Select the Class
option and name the class DicomDB.cs
, then click Add.
The DicomDB
class holds the datasets found in the PACS server, allowing for the insertion of datasets as records after a C-STORE request and saving and loading the database into an external XML file.
Note
Make sure the namespace for the class is
PacsServer
.
Add the using
statements below to the top of the Server
class:
// Using block at the top
using Leadtools.Dicom;
using System;
using System.Data;
using System.IO;
Use the code below to define the public and private properties of the DicomDB
class along with its constructor.
The constructor loads the database from an external XML file, if it already exists. If it does not exist, it creates the database using the DICOM datasets in the server's images folder.
public class DicomDB
{
protected DataSet ds = new DataSet("DICOMSVR"); // System.Data.DataSet instance
protected string dbFileName; // External XML File Name
public bool NeedImport = false;
public string ImageDir;
private object adoDatasetLock = new object();
// Get/Set Properties
public int Count { get { return ds.Tables[0].Rows.Count; } }
public DataSet DB { get { return ds; } }
public DicomDB(string dbFileName)
{
this.dbFileName = dbFileName;
if (!LoadDatabase())
{
CreateDatabase();
NeedImport = true;
}
lock (adoDatasetLock)
{
if (ds.Tables["Images"] != null)
{
ds.Tables["Images"].RowDeleting += new DataRowChangeEventHandler(DicomDB_RowDeleting);
}
}
}
void DicomDB_RowDeleting(object sender, DataRowChangeEventArgs e)
{
string file = e.Row["ReferencedFile"].ToString();
try
{
File.Delete(file);
}
catch
{
}
}
}
Add two new methods to the DicomDB
class named LoadDatabase()
and Save()
. LoadDatabase()
will be called inside the DicomDB()
method as shown in the previous section. The Save()
method will be called throughout the DicomDB
class, as shown in the next sections.
Add the code below to the LoadDatabase
method to load the database from an external XML file.
protected bool LoadDatabase()
{
bool loaded = true;
try
{
ds.ReadXml(dbFileName);
}
catch
{
loaded = false;
}
return loaded;
}
Add the code to the Save()
method to save the database to an external XML file.
public bool Save()
{
bool ret = false;
try
{
ds.WriteXml(dbFileName, XmlWriteMode.WriteSchema);
ret = true;
}
catch
{
}
return ret;
}
Add a new method to the DicomDB
class named CreateDatabase()
. This method will be called inside the DicomDB()
method, as shown in the first section. Use the following code to create the table structure for the DICOM dataset.
private bool CreateDatabase()
{
bool created = true;
lock (adoDatasetLock)
{
try
{
DataTable table;
DataColumn Patients_PatientID, Studies_PatientID;
DataColumn Studies_InstUID, Series_InstUID;
DataColumn Series_ID, Images_ID;
DataColumn[] key = new DataColumn[1];
table = ds.Tables.Add("Patients");
Patients_PatientID = table.Columns.Add("PatientID", typeof(string));
table.Columns.Add("PatientName", typeof(string));
table.Columns.Add("PatientBirthDate", typeof(DateTime));
table.Columns.Add("PatientBirthTime", typeof(DateTime));
table.Columns.Add("PatientSex", typeof(string));
table.Columns.Add("EthnicGroup", typeof(string));
table.Columns.Add("PatientComments", typeof(string));
key[0] = Patients_PatientID;
table.PrimaryKey = key;
table = ds.Tables.Add("Studies");
Studies_InstUID = table.Columns.Add("StudyInstanceUID", typeof(string));
table.Columns.Add("StudyDate", typeof(DateTime));
table.Columns.Add("StudyTime", typeof(DateTime));
table.Columns.Add("AccessionNumber", typeof(string));
table.Columns.Add("StudyID", typeof(string));
table.Columns.Add("PatientName", typeof(string));
Studies_PatientID = table.Columns.Add("PatientID", typeof(string));
table.Columns.Add("StudyDescription", typeof(string));
table.Columns.Add("ReferringDrName", typeof(string));
key[0] = Studies_InstUID;
table.PrimaryKey = key;
ds.Relations.Add("Studies", Patients_PatientID, Studies_PatientID);
table = ds.Tables.Add("Series");
Series_ID = table.Columns.Add("SeriesInstanceUID", typeof(string));
Series_InstUID = table.Columns.Add("StudyInstanceUID", typeof(string));
table.Columns.Add("Modality", typeof(string));
table.Columns.Add("SeriesNumber", typeof(int));
table.Columns.Add("PatientID", typeof(string));
table.Columns.Add("SeriesDate", typeof(DateTime));
key[0] = Series_ID;
table.PrimaryKey = key;
ds.Relations.Add("Series", Studies_InstUID, Series_InstUID);
table = ds.Tables.Add("Images");
table.Columns.Add("SOPInstanceUID", typeof(string));
Images_ID = table.Columns.Add("SeriesInstanceUID", typeof(string));
table.Columns.Add("StudyInstanceUID", typeof(string));
table.Columns.Add("InstanceNumber", typeof(int));
table.Columns.Add("ReferencedFile", typeof(string));
table.Columns.Add("PatientID", typeof(string));
table.Columns.Add("SOPClassUID", typeof(string));
table.Columns.Add("TransferSyntaxUID", typeof(string));
ds.Relations.Add("Images", Series_ID, Images_ID);
Save();
}
catch
{
created = false;
}
}
return created;
}
Add the enumeration below directly after the namespace declaration.
public enum InsertReturn
{
Exists,
Error,
Success
}
Create a new Insert(DicomDataSet dcm, string filename)
method and use the following code to parse a DICOM dataset file and add it to the table structure and the external XML file.
public InsertReturn Insert(DicomDataSet dcm, string filename)
{
string patientID;
string studyInstanceUID;
string seriesInstanceUID;
string sopInstanceUID;
InsertReturn ret = InsertReturn.Success;
patientID = AddPatient(dcm, ref ret);
if (ret != InsertReturn.Success && ret != InsertReturn.Exists)
{
return ret;
}
studyInstanceUID = AddStudy(dcm, patientID, ref ret);
if (ret != InsertReturn.Success && ret != InsertReturn.Exists)
{
return ret;
}
seriesInstanceUID = AddSeries(dcm, studyInstanceUID, patientID, ref ret);
if (ret != InsertReturn.Success && ret != InsertReturn.Exists)
{
return ret;
}
sopInstanceUID = AddImage(dcm, seriesInstanceUID, studyInstanceUID, patientID, filename, ref ret);
if (ret != InsertReturn.Success && ret != InsertReturn.Exists)
{
return ret;
}
if (ret == InsertReturn.Success)
{
Save();
}
return ret;
}
Create a new Insert(DicomDataSet dcm, string filename)
method and add the following code to check if a record already exists in the table.
private bool RecordExists(DataTable table, string filter)
{
DataView dv = new DataView(table);
if (dv != null)
{
dv.RowFilter = filter;
return dv.Count > 0;
}
return false;
}
Right-click on the <Project>.csproj
in the Solution Explorer and select Add -> New Folder. Create a folder called DicomCommon. Right-click on this folder and select Add -> New Item.... Select the Class
option and name the class Utils.cs
, then click Add.
Note
For this and other related PACS tutorials, the
Utils
class holds a number of utility methods for parsing data from DICOM dataset files.
Use the code below to add a GetStringValue()
method, which returns the string value of a DICOM tag:
using Leadtools.Dicom;
namespace PacsServer
{
class Utils
{
public static string GetStringValue(DicomDataSet dcm, long tag)
{
DicomElement element;
element = dcm.FindFirstElement(null, tag, true);
if (element != null)
{
if (dcm.GetElementValueCount(element) > 0)
{
return dcm.GetConvertValue(element);
}
}
return "";
}
}
}
In the DicomDB.cs
file, use the following code to parse and add the Patients table information from a DICOM Dataset to the database.
private string AddPatient(DicomDataSet dcm, ref InsertReturn ret)
{
string patientID = "";
ret = InsertReturn.Success;
patientID = Utils.GetStringValue(dcm, DicomTag.PatientID);
if (patientID.Length == 0)
{
ret = InsertReturn.Error;
return "";
}
lock (adoDatasetLock)
{
if (!RecordExists(ds.Tables["Patients"], "PatientID = '" + patientID + "'"))
{
DataRow dr;
dr = ds.Tables["Patients"].NewRow();
if (dr != null)
{
dr["PatientID"] = patientID;
dr["PatientName"] = Utils.GetStringValue(dcm, DicomTag.PatientName);
dr["PatientSex"] = Utils.GetStringValue(dcm, DicomTag.PatientSex);
dr["EthnicGroup"] = Utils.GetStringValue(dcm, DicomTag.EthnicGroup);
dr["PatientComments"] = Utils.GetStringValue(dcm, DicomTag.PatientComments);
try
{
dr["PatientBirthDate"] = DateTime.Parse(Utils.GetStringValue(dcm, DicomTag.PatientBirthDate));
dr["PatientBirthTime"] = DateTime.Parse(Utils.GetStringValue(dcm, DicomTag.PatientBirthTime));
}
catch
{
}
ds.Tables["Patients"].Rows.Add(dr);
}
}
else
{
ret = InsertReturn.Exists;
}
}
return patientID;
}
In the DicomDB.cs
file, add a new AddStudy(DicomDataSet dcm, string patientID, ref InsertReturn ret)
method to parse and add the Studies table information from a DICOM Dataset to the database.
private string AddStudy(DicomDataSet dcm, string patientID, ref InsertReturn ret)
{
string studyInstanceUID;
string filter;
ret = InsertReturn.Success;
studyInstanceUID = Utils.GetStringValue(dcm, DicomTag.StudyInstanceUID);
if (studyInstanceUID.Length == 0)
{
ret = InsertReturn.Error;
return "";
}
lock (adoDatasetLock)
{
filter = "StudyInstanceUID = '" + studyInstanceUID + "' AND PatientID = '" + patientID + "'";
if (RecordExists(ds.Tables["Studies"], filter))
{
ret = InsertReturn.Exists;
return studyInstanceUID;
}
filter = string.Format("StudyInstanceUID = '{0}'", studyInstanceUID);
if (RecordExists(ds.Tables["Studies"], filter))
{
ret = InsertReturn.Error;
return studyInstanceUID;
}
// Add
try
{
DataRow dr = ds.Tables["Studies"].NewRow();
if (dr != null)
{
dr["StudyInstanceUID"] = studyInstanceUID;
dr["StudyID"] = Utils.GetStringValue(dcm, DicomTag.StudyID);
dr["StudyDescription"] = Utils.GetStringValue(dcm, DicomTag.StudyDescription);
dr["AccessionNumber"] = Utils.GetStringValue(dcm, DicomTag.AccessionNumber);
dr["PatientID"] = patientID;
dr["PatientName"] = Utils.GetStringValue(dcm, DicomTag.PatientName);
dr["ReferringDrName"] = Utils.GetStringValue(dcm, DicomTag.ReferringPhysicianName);
try
{
dr["StudyDate"] = DateTime.Parse(Utils.GetStringValue(dcm, DicomTag.StudyDate));
dr["StudyTime"] = DateTime.Parse(Utils.GetStringValue(dcm, DicomTag.StudyTime));
}
catch
{
}
ds.Tables["Studies"].Rows.Add(dr);
}
}
catch (Exception)
{
ret = InsertReturn.Error;
}
}
return studyInstanceUID;
}
In the DicomDB.cs
file, add a new AddSeries(DicomDataSet dcm, string studyInstanceUID, string patientID, ref InsertReturn ret)
method to parse and add the Series table information from a DICOM Dataset to the database.
private string AddSeries(DicomDataSet dcm, string studyInstanceUID, string patientID, ref InsertReturn ret)
{
string seriesInstanceUID;
string filter;
ret = InsertReturn.Success;
seriesInstanceUID = Utils.GetStringValue(dcm, DicomTag.SeriesInstanceUID);
if (seriesInstanceUID.Length == 0)
{
ret = InsertReturn.Error;
return "";
}
filter = "StudyInstanceUID = '" + studyInstanceUID + "' AND SeriesInstanceUID = '" + seriesInstanceUID + "'";
lock (adoDatasetLock)
{
if (RecordExists(ds.Tables["Series"], filter))
{
ret = InsertReturn.Exists;
return seriesInstanceUID;
}
filter = string.Format("SeriesInstanceUID = '{0}'", seriesInstanceUID);
if (RecordExists(ds.Tables["Series"], filter))
{
ret = InsertReturn.Error;
return seriesInstanceUID;
}
// Add
try
{
DataRow dr;
dr = ds.Tables["Series"].NewRow();
if (dr != null)
{
string temp;
temp = Utils.GetStringValue(dcm, DicomTag.SeriesNumber);
dr["SeriesInstanceUID"] = seriesInstanceUID;
dr["StudyInstanceUID"] = studyInstanceUID;
dr["Modality"] = Utils.GetStringValue(dcm, DicomTag.Modality);
dr["PatientID"] = patientID;
try
{
dr["SeriesDate"] = DateTime.Parse(Utils.GetStringValue(dcm, DicomTag.SeriesDate));
}
catch
{
}
try
{
if (temp.Length > 0)
{
dr["SeriesNumber"] = Convert.ToInt32(temp);
}
}
catch
{
}
ds.Tables["Series"].Rows.Add(dr);
}
}
catch (Exception)
{
ret = InsertReturn.Error;
}
}
return seriesInstanceUID;
}
In the DicomDB.cs
file, add a new AddImage(DicomDataSet dcm, string seriesInstanceUID, string studyInstanceUID, string patientID, string filename, ref InsertReturn ret)
method to parse and add the Images table information from a DICOM Dataset to the database.
private string AddImage(DicomDataSet dcm, string seriesInstanceUID, string studyInstanceUID, string patientID, string filename, ref InsertReturn ret)
{
string sopInstanceUID;
string filter;
ret = InsertReturn.Success;
sopInstanceUID = Utils.GetStringValue(dcm, DicomTag.SOPInstanceUID);
if (sopInstanceUID.Length == 0)
{
ret = InsertReturn.Error;
return "";
}
filter = "StudyInstanceUID = '" + studyInstanceUID + "' AND SeriesInstanceUID = '" + seriesInstanceUID + "'";
filter += " AND SOPInstanceUID = '" + sopInstanceUID + "'";
lock (adoDatasetLock)
{
if (RecordExists(ds.Tables["Images"], filter))
{
ret = InsertReturn.Exists;
return sopInstanceUID;
}
filter = string.Format("SOPInstanceUID = '{0}'", sopInstanceUID);
if (RecordExists(ds.Tables["Images"], filter))
{
ret = InsertReturn.Error;
return sopInstanceUID;
}
// Add
try
{
DataRow dr;
dr = ds.Tables["Images"].NewRow();
if (dr != null)
{
string temp;
dr["SOPInstanceUID"] = sopInstanceUID;
dr["SeriesInstanceUID"] = seriesInstanceUID;
dr["StudyInstanceUID"] = studyInstanceUID;
dr["PatientID"] = patientID;
dr["ReferencedFile"] = filename;
temp = Utils.GetStringValue(dcm, DicomTag.SOPClassUID);
if (temp.Length == 0)
{
temp = Utils.GetStringValue(dcm, DicomTag.MediaStorageSOPClassUID);
if (temp.Length == 0)
{
temp = "1.1.1.1";
}
}
dr["SOPClassUID"] = temp;
temp = Utils.GetStringValue(dcm, DicomTag.TransferSyntaxUID);
if (temp.Length == 0)
{
temp = DicomUidType.ImplicitVRLittleEndian;
}
dr["TransferSyntaxUID"] = temp;
temp = Utils.GetStringValue(dcm, DicomTag.InstanceNumber);
if (temp.Length > 0)
{
dr["InstanceNumber"] = Convert.ToInt32(temp);
}
ds.Tables["Images"].Rows.Add(dr);
}
}
catch (Exception)
{
ret = InsertReturn.Error;
}
}
return sopInstanceUID;
}
With the database configured, go to Utilities/Server.cs
and add a DicomDB
class to the properties:
public DicomDB dicomDB;
Add an instance of the DicomDB
class to the form1.cs
file:
private DicomDB dicomDB = new DicomDB(Application.StartupPath + @"\DicomCS.xml");
public DicomDB DicomData { get { return dicomDB; } }
Initialize the server's dicomDB
property using this instance in the InitServer()
method:
dicomServer.dicomDB = dicomDB; // Add to InitServer()
Go to the Utilities\Server.cs
file. Add the below using
statement.
using System.Windows.Forms; // Add to using block
Add an ImageDir
string value, which is the directory used by the server for storing local datasets.
// Images Directory
private string _ImageDir = Application.StartupPath + @"\ImagesCS\";
In the Get/Set Server Properties
section in the Server.cs
file add the ImageDir
property below.
// Add to Get/Set Server Properties
public string ImageDir { get { return _ImageDir; } set { _ImageDir = value; } }
Modify the InitAction()
method to accept a DicomDataSet
instance in its parameters and use it to initialize a DicomAction
class.
public DicomAction InitAction(string actionOp, ProcessType process, Client client, DicomDataSet ds)
{
DicomAction action = new DicomAction(process, this, client, ds);
action.AETitle = client.Association.Calling;
action.ipAddress = client.PeerAddress;
client.Timer.Start();
MainForm.Log(actionOp + ": Received from " + action.AETitle);
return action;
}
Go to the Utilities\Client.cs
file and add the code below for the OnReceiveCStoreRequest()
handler method which is called when a C-STORE request is sent:
protected override void OnReceiveCStoreRequest(byte presentationID, int messageID, string affectedClass, string instance, DicomCommandPriorityType priority, string moveAE, int moveMessageID, DicomDataSet dataSet)
{
action = _server.InitAction("C-STORE-REQUEST", ProcessType.StoreRequest, this, dataSet);
action.PresentationID = presentationID;
action.MessageID = messageID;
action.Class = affectedClass;
action.Instance = instance;
action.Priority = priority;
action.MoveAETitle = moveAE;
action.MoveMessageID = moveMessageID;
action.DoAction();
dataSet.Dispose();
}
Modify the OnReceiveCEchoRequest
to pass null to the modified _server.InitAction()
method, since C-ECHO does not involve a dataset.
protected override void OnReceiveCEchoRequest(byte presentationID, int messageID, string affectedClass)
{
action = _server.InitAction("C-ECHO-REQUEST", ProcessType.EchoRequest, this, null);
action.PresentationID = presentationID;
action.MessageID = messageID;
action.Class = affectedClass;
action.DoAction();
}
Navigate to the Form1.cs
file and add the code below to the InitServer()
method.
// Create Images Folder
if (!Directory.Exists(dicomServer.ImageDir))
Directory.CreateDirectory(dicomServer.ImageDir);
Open the Utilities\DicomAction.cs
file and add the store request to the enumeration directly after the namespace declaration.
public enum ProcessType
{
EchoRequest,
StoreRequest
}
Use the following code to add the properties used for the store request to the DicomAction
class.
// Add to Request Properties
private DicomCommandPriorityType _Priority;
private string _MoveAETitle;
private int _MoveMessageID;
// DICOM DataSet Properties
public string dsFileName;
private DicomDataSet ds = new DicomDataSet();
// Add to Get/Set Properties methods
public DicomCommandPriorityType Priority { get { return _Priority; } set { _Priority = value; } }
public string MoveAETitle { get { return _MoveAETitle; } set { _MoveAETitle = value; } }
public int MoveMessageID { get { return _MoveMessageID; } set { _MoveMessageID = value; } }
public DicomDataSet DS { get { return ds; } }
Modify the DicomAction
constructor to take a DataSet inside its parameters.
public DicomAction(ProcessType process, Server server, Client client, DicomDataSet ds)
{
this.server = server;
this.client = client;
this.process = process;
if (ds != null)
{
this.ds.Copy(ds, null, null);
}
}
Modify the DoAction
method to parse the StoreRequest
process.
public void DoAction()
{
if (client.Association != null)
{
switch (process)
{
// C-ECHO
case ProcessType.EchoRequest:
DoEchoRequest();
break;
// C-STORE
case ProcessType.StoreRequest:
DoStoreRequest();
break;
}
}
}
Add a new DoStoreRequest()
method and add the following code.
private void DoStoreRequest()
{
DicomCommandStatusType status = DicomCommandStatusType.ProcessingFailure;
string msg = "Error saving dataset received from: " + AETitle;
DicomElement element;
if (!IsActionSupported())
{
string name = GetUIDName();
server.MainForm.Log("C-STORE-REQUEST: Abstract syntax (" + name + ") not supported by association");
client.SendCStoreResponse(_PresentationID, _MessageID, _Class, _Instance, DicomCommandStatusType.ClassNotSupported);
return;
}
element = ds.FindFirstElement(null, DicomTag.SOPInstanceUID, true);
if (element != null)
{
string value = ds.GetConvertValue(element);
string file;
InsertReturn ret;
file = server.ImageDir + value + ".dcm";
ret = server.MainForm.DicomData.Insert(ds, file);
switch (ret)
{
case InsertReturn.Success:
DicomExceptionCode dret = SaveDataSet(file);
if (dret == DicomExceptionCode.Success)
{
status = DicomCommandStatusType.Success;
}
else
{
msg = "Error saving dicom file: " + dret.ToString();
status = DicomCommandStatusType.ProcessingFailure;
}
server.MainForm.Log("C-STORE-REQUEST: New file imported: " + file);
break;
case InsertReturn.Exists:
msg = "File (" + file + ") not imported. Record already exists in database";
status = DicomCommandStatusType.DuplicateInstance;
break;
case InsertReturn.Error:
msg = "Error importing file: " + file;
status = DicomCommandStatusType.ProcessingFailure;
break;
}
}
if (status != DicomCommandStatusType.Success)
{
server.MainForm.Log("C-STORE-REQUEST: " + msg);
}
client.SendCStoreResponse(_PresentationID, _MessageID, _Class, _Instance, status);
server.MainForm.Log("C-STORE-RESPONSE: Response sent to " + AETitle);
}
Add a new SaveDataSet(string filename)
method and add the following code.
private DicomExceptionCode SaveDataSet(string filename)
{
string temp;
Utils.SetTag(ds, DicomTag.FillerOrderNumberProcedure, "01");
temp = Utils.GetStringValue(ds, DicomTag.SOPClassUID);
Utils.SetTag(ds, DicomTag.MediaStorageSOPClassUID, temp);
temp = Utils.GetStringValue(ds, DicomTag.SOPInstanceUID);
Utils.SetTag(ds, DicomTag.MediaStorageSOPInstanceUID, temp);
Utils.SetTag(ds, DicomTag.ImplementationClassUID, client.Association.ImplementClass);
Utils.SetTag(ds, DicomTag.ImplementationVersionName, client.Association.ImplementationVersionName);
try
{
ds.Save(filename, DicomDataSetSaveFlags.MetaHeaderPresent | DicomDataSetSaveFlags.GroupLengths);
}
catch (DicomException de)
{
return de.Code;
}
return DicomExceptionCode.Success;
}
Open the DicomCommon/Utils.cs
file and add the using
statement below.
using System.Text; // Add to using block
Add a new SetTag(DicomDataSet dcm, long tag, object tagValue)
method and add the following code.
public static DicomExceptionCode SetTag(DicomDataSet dcm, long tag, object tagValue)
{
DicomExceptionCode ret = DicomExceptionCode.Success;
DicomElement element;
if (tagValue == null)
return DicomExceptionCode.Parameter;
element = dcm.FindFirstElement(null, tag, true);
if (element == null)
{
element = dcm.InsertElement(null, false, tag, DicomVRType.UN, false, 0);
}
if (element == null)
return DicomExceptionCode.Parameter;
try
{
string s = tagValue.ToString();
if (IsAscii(s))
dcm.SetConvertValue(element, s, 1);
else
dcm.SetStringValue(element, s, DicomCharacterSetType.UnicodeInUtf8);
}
catch (DicomException de)
{
ret = de.Code;
}
return ret;
}
Add a new IsAscii(string value)
method and add the following code.
public static bool IsAscii(string value)
{
return Encoding.UTF8.GetByteCount(value) == value.Length;
}
Before running the project, open the Utilities/Server.cs
file and add the syntaxes to support storing the DICOM sample files found in <INSTALL_DIR>\LEADTOOLS22\Resources\Images\DICOM
:
private void BuildInclusionList()
{
_UidInclusionList.Add(DicomUidType.VerificationClass);
// Store Transfer Syntax
_UidInclusionList.Add(DicomUidType.JPEG2000LosslessOnly); // Image1.dcm
_UidInclusionList.Add(DicomUidType.JPEGLosslessNonhier14B); // Image2.dcm
_UidInclusionList.Add(DicomUidType.ImplicitVRLittleEndian); // Image3.dcm
// Store Abstract Syntax
_UidInclusionList.Add(DicomUidType.EnhancedMRImageStorage); // Image1.dcm
_UidInclusionList.Add(DicomUidType.DXImageStoragePresentation); // Image2.dcm
_UidInclusionList.Add(DicomUidType.MRImageStorage); // Image3.dcm
}
Run the project by pressing F5, or by selecting Debug -> Start Debugging.
If the steps were followed correctly, the application runs and allows a PACS client to send a C-STORE request to the server. Upon successful validation, the request is processed allowing the image to be added to the database.
Run the LEADTOOLS Dicom Storage SCU - C# demo to test the server, this demo is found here:
<INSTALL_DIR>\LEADTOOLS22\Bin\DotNet4\x64\DicomStoreDemo_Original.exe
Use File -> Options to configure the connection then File -> Add Dicom... to add the sample DICOM datasets (Image1, Image2, and Image3) to the demo.
Then use File -> Store to send a C-STORE request for each of the datasets to the server.
This tutorial showed how to add a database to a PACS Server and implement handling of C-STORE requests.