C# Worklist 开发实战:从环境搭建到高效调用
·
① 开发环境快速搭建与依赖配置
基于Dicom.Core 4.0.4.0
② WorklistServer
public class WorklistServer
{
private static IDicomServer _server;
private static Timer _itemsLoaderTimer;
protected WorklistServer()
{
}
public static string AETitle { get; set; }
public static IWorklistItemsSource CreateItemsSourceService => new WorklistItemsProvider();
public static List<WorklistItem> CurrentWorklistItems { get; private set; }
public static void Start(int port, string aet)
{
AETitle = aet;
_server = DicomServer.Create<WorklistService>(port);
}
public static void Stop()
{
_itemsLoaderTimer?.Dispose();
_server.Dispose();
}
}
③ WorklistService代码实现
public class WorklistService : DicomService, IDicomServiceProvider, IDicomCEchoProvider, IDicomCFindProvider, IDicomNServiceProvider
{
private static readonly DicomTransferSyntax[] AcceptedTransferSyntaxes = new DicomTransferSyntax[]
{
DicomTransferSyntax.ExplicitVRLittleEndian,
DicomTransferSyntax.ExplicitVRBigEndian,
DicomTransferSyntax.ImplicitVRLittleEndian
};
private IMppsSource _mppsSource;
private IMppsSource MppsSource
{
get
{
if (_mppsSource == null)
{
_mppsSource = new MppsHandler(Logger);
}
return _mppsSource;
}
}
public static IWorklistItemsSource CreateItemsSourceService => new WorklistItemsProvider();
public static List<WorklistItem> CurrentWorklistItems { get; private set; }
public WorklistService(INetworkStream stream, Encoding fallbackEncoding, Dicom.Log.Logger log) : base(stream, fallbackEncoding, log)
{
}
public DicomCEchoResponse OnCEchoRequest(DicomCEchoRequest request)
{
LogWrite.mwlLog($"Received verification request from AE {Association.CallingAE} with IP: {Association.RemoteHost}");
return new DicomCEchoResponse(request, DicomStatus.Success);
}
public IEnumerable<DicomCFindResponse> OnCFindRequest(DicomCFindRequest request)
{
LogWrite.mwlLog("WorklistSCP收到工作列表查询请求\r\n");
foreach (var element in request.Dataset)
{
// 输出标签的键(Tag)和值
LogWrite.mwlLog("Tag: " + element.ToString() + " 数值 " + request.Dataset.GetSingleValueOrDefault(element.Tag, string.Empty));
}
var newWorklistItems = CreateItemsSourceService.GetAllCurrentWorklistItems();
foreach (DicomDataset result in WorklistHandler.FilterWorklistItems(request.Dataset, newWorklistItems))
{
yield return new DicomCFindResponse(request, DicomStatus.Pending) { Dataset = result };
}
yield return new DicomCFindResponse(request, DicomStatus.Success);
}
public void OnConnectionClosed(Exception exception)
{
Clean();
}
public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
{
LogWrite.mwlLog($"Received abort from {source}, reason is {reason}");
}
public System.Threading.Tasks.Task OnReceiveAssociationReleaseRequestAsync()
{
Clean();
return SendAssociationReleaseResponseAsync();
}
public System.Threading.Tasks.Task OnReceiveAssociationRequestAsync(DicomAssociation association)
{
LogWrite.mwlLog($"Received association request from AE: {association.CallingAE} with IP: {association.RemoteHost} ");
if (WorklistServer.AETitle != association.CalledAE)
{
Logger.Error($"Association with {association.CallingAE} rejected since called aet {association.CalledAE} is unknown");
return SendAssociationRejectAsync(DicomRejectResult.Permanent, DicomRejectSource.ServiceUser, DicomRejectReason.CalledAENotRecognized);
}
foreach (var pc in association.PresentationContexts)
{
if (pc.AbstractSyntax == DicomUID.Verification
|| pc.AbstractSyntax == DicomUID.ModalityWorklistInformationModelFIND
|| pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepSOPClass
|| pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepNotificationSOPClass
|| pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepNotificationSOPClass)
{
pc.AcceptTransferSyntaxes(AcceptedTransferSyntaxes);
}
else
{
LogWrite.mwlLog($"Requested abstract syntax {pc.AbstractSyntax} from {association.CallingAE} not supported");
pc.SetResult(DicomPresentationContextResult.RejectAbstractSyntaxNotSupported);
}
}
LogWrite.mwlLog($"Accepted association request from {association.CallingAE}");
return SendAssociationAcceptAsync(association);
}
public void Clean()
{
// cleanup, like cancel outstanding move- or get-jobs
}
public DicomNCreateResponse OnNCreateRequest(DicomNCreateRequest request)
{
if (request.SOPClassUID != DicomUID.ModalityPerformedProcedureStepSOPClass)
{
return new DicomNCreateResponse(request, DicomStatus.SOPClassNotSupported);
}
var affectedSopInstanceUID = request.Command.GetSingleValue<string>(DicomTag.AffectedSOPInstanceUID);
Logger.Log(LogLevel.Info, $"reeiving N-Create with SOPUID {affectedSopInstanceUID}");
var procedureStepId = request.Dataset
.GetSequence(DicomTag.ScheduledStepAttributesSequence)
.First()
.GetSingleValue<string>(DicomTag.ScheduledProcedureStepID);
var ok = MppsSource.SetInProgress(affectedSopInstanceUID, procedureStepId);
return new DicomNCreateResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
}
public DicomNSetResponse OnNSetRequest(DicomNSetRequest request)
{
if (request.SOPClassUID != DicomUID.ModalityPerformedProcedureStepSOPClass)
{
return new DicomNSetResponse(request, DicomStatus.SOPClassNotSupported);
}
var requestedSopInstanceUID = request.Command.GetSingleValue<string>(DicomTag.RequestedSOPInstanceUID);
Logger.Log(LogLevel.Info, $"receiving N-Set with SOPUID {requestedSopInstanceUID}");
var status = request.Dataset.GetSingleValue<string>(DicomTag.PerformedProcedureStepStatus);
if (status == "COMPLETED")
{
var doseDescription = request.Dataset.GetSingleValueOrDefault(DicomTag.CommentsOnRadiationDose, string.Empty);
var listOfInstanceUIDs = new List<string>();
foreach (var seriesDataset in request.Dataset.GetSequence(DicomTag.PerformedSeriesSequence))
{
foreach (var instanceDataset in seriesDataset.GetSequence(DicomTag.ReferencedImageSequence))
{
var instanceUID = instanceDataset.GetSingleValueOrDefault(DicomTag.ReferencedSOPInstanceUID, string.Empty);
if (!string.IsNullOrEmpty(instanceUID)) listOfInstanceUIDs.Add(instanceUID);
}
}
var ok = MppsSource.SetCompleted(requestedSopInstanceUID, doseDescription, listOfInstanceUIDs);
return new DicomNSetResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
}
else if (status == "DISCONTINUED")
{
var ok = MppsSource.SetDiscontinued(requestedSopInstanceUID, string.Empty);
return new DicomNSetResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
}
else
{
return new DicomNSetResponse(request, DicomStatus.InvalidAttributeValue);
}
}
#region not supported methods but that are required because of the interface
public DicomNDeleteResponse OnNDeleteRequest(DicomNDeleteRequest request)
{
Logger.Log(LogLevel.Info, "receiving N-Delete, not supported");
return new DicomNDeleteResponse(request, DicomStatus.UnrecognizedOperation);
}
public DicomNEventReportResponse OnNEventReportRequest(DicomNEventReportRequest request)
{
Logger.Log(LogLevel.Info, "receiving N-Event, not supported");
return new DicomNEventReportResponse(request, DicomStatus.UnrecognizedOperation);
}
public DicomNGetResponse OnNGetRequest(DicomNGetRequest request)
{
Logger.Log(LogLevel.Info, "receiving N-Get, not supported");
return new DicomNGetResponse(request, DicomStatus.UnrecognizedOperation);
}
public DicomNActionResponse OnNActionRequest(DicomNActionRequest request)
{
Logger.Log(LogLevel.Info, "receiving N-Action, not supported");
return new DicomNActionResponse(request, DicomStatus.UnrecognizedOperation);
}
#endregion
}
④ 分步实操:CreateItemsSourceService.GetAllCurrentWorklistItems() 通过配置sql和数据库连接查询数据
public List<WorklistItem> GetAllCurrentWorklistItems()
{
List<WorklistItem> worklistItems = new List<WorklistItem>();
try
{
var appSettings = ConfigurationManager.AppSettings;
BaseType = appSettings["BaseType"] ?? null;
BasedbUrl = appSettings["BasedbUrl"] ?? null;
QuerySql = appSettings["QuerySql"] ?? null;
NameType = appSettings["NameType"] ?? "1";
Encode = appSettings["Encode"] ?? null;
LogWrite.mwlLog("数据库类型:" + BaseType);
LogWrite.mwlLog("数据库链接字符串:" + BasedbUrl);
LogWrite.mwlLog("查询数据库SQL语句:" + QuerySql);
LogWrite.mwlLog("姓名显示形式:" + NameType);
LogWrite.mwlLog("字符集:" + Encode);
bool IsConnected = true;
myds = GetDataValue.GetData(BaseType, BasedbUrl, QuerySql, ref IsConnected);
if (!IsConnected)
{
LogWrite.mwlLog("第三方数据库链接失败!");
return worklistItems;
}
if (myds.Rows.Count == 0)
{
LogWrite.mwlLog("Worklist未查询到数据集合");
return worklistItems;
}
else
{
for (int i = 0; i < myds.Rows.Count; i++)
{
WorklistItem worklistItem = new WorklistItem();
string PatientName = myds.Rows[i]["PatientName"].ToString();
if (NameType.Equals("1"))
{
string[] spell = new string[] { null };
new Hanzi2PinyinConverter().Convert(PatientName, "", out spell);
PatName = PatientName;
}
else if (NameType.Equals("2"))
{
PatName = PinYinConverter.Get(PatientName);
}
else if (NameType.Equals("3"))
{
PatName = PatientName + PinYinConverter.Get(PatientName);
}
worklistItem.Surname = PatName;
worklistItem.PatientID = myds.Rows[i]["PatientID"].ToString();
worklistItem.Modality = myds.Rows[i]["Modality"].ToString();
worklistItem.AccessionNumber = myds.Rows[i]["AccessionNumber"].ToString();
worklistItem.PatientAge = myds.Rows[i]["PatientAge"].ToString();
worklistItem.PatientWeight = myds.Rows[i]["PatientWeight"].ToString();
worklistItem.ExamDateAndTime = DateTime.Parse(myds.Rows[i]["StudyTime"].ToString());
worklistItem.DateOfBirth = DateTime.Parse(myds.Rows[i]["PatientBirthDate"].ToString());
worklistItem.Sex = myds.Rows[i]["Sex"].ToString();
worklistItem.PregnancyStatus = myds.Rows[i]["PregnancyStatus"].ToString();
worklistItem.HospitalName = myds.Rows[i]["InstitutionName"].ToString();
worklistItem.ReferringPhysician = myds.Rows[i]["RequestingPhysician"].ToString();
worklistItem.PerformingPhysician = myds.Rows[i]["PerformingPhysician"].ToString();
worklistItem.StudyUID = myds.Rows[i]["StudyUuid"].ToString();
worklistItem.ProcedureID = myds.Rows[i]["ProcedureID"].ToString();
worklistItem.ProcedureStepID = myds.Rows[i]["ProcedureStepID"].ToString();
worklistItem.ScheduledAET = myds.Rows[i]["ScheduledAET"].ToString();
worklistItem.Encode = Encode;
worklistItem.ExamRoom = myds.Rows[i]["ExamRoom"].ToString();
worklistItem.Forename = "";
worklistItem.AdmissionID = myds.Rows[i]["AdmissionID"].ToString();
worklistItem.ScheduledStationName = myds.Rows[i]["ScheduledStationName"].ToString();
worklistItem.ExamDescription = PinYinConverter.GetFirst(myds.Rows[i]["ExamDescription"].ToString());
worklistItems.Add(worklistItem);
}
}
}
catch (Exception ex)
{
LogWrite.mwlLog("Worklist数据库集合转化异常:" + ex.ToString());
return new List<WorklistItem>();
}
return worklistItems;
}
⑤ 分步实操: WorklistHandler 实现
public class WorklistHandler
{
private static readonly System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("\\d+$");
public static IEnumerable<DicomDataset> FilterWorklistItems(DicomDataset request, List<WorklistItem> allWorklistItems)
{
var exams = allWorklistItems.AsQueryable();
// 从请求中获取字符集
var requestedCharacterSet = "ISO_IR 100"; // 默认值
if (request.Contains(DicomTag.SpecificCharacterSet))
{
var charsets = request.GetValues<string>(DicomTag.SpecificCharacterSet);
if (charsets != null && charsets.Length > 0)
{
requestedCharacterSet = charsets[0];
LogWrite.mwlLog("WorklistSCP请求的字符集: " + requestedCharacterSet);
}
}
if ( request.TryGetSingleValue(DicomTag.PatientID, out string patientId))
{
exams = exams.Where(x => x.PatientID.Equals(patientId));
}
var patName = request.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
if (!string.IsNullOrEmpty(patName))
{
exams = AddNameCondition(exams, patName);
}
DicomDataset procedureStep = null;
if (request.Contains(DicomTag.ScheduledProcedureStepSequence))
{
procedureStep = request.GetSequence(DicomTag.ScheduledProcedureStepSequence).First();
// Required Matching keys
var scheduledStationAET = procedureStep.GetSingleValueOrDefault(DicomTag.ScheduledStationAETitle, string.Empty);
if (!string.IsNullOrEmpty(scheduledStationAET))
{
exams = exams.Where(x => x.ScheduledAET == scheduledStationAET);
}
var performingPhysician = procedureStep.GetSingleValueOrDefault(DicomTag.PerformingPhysicianName, string.Empty);
if (!string.IsNullOrEmpty(performingPhysician))
{
exams = exams.Where(x => x.PerformingPhysician == performingPhysician);
}
var modality = procedureStep.GetSingleValueOrDefault(DicomTag.Modality, string.Empty);
if (!string.IsNullOrEmpty(modality))
{
LogWrite.mwlLog("WorklistSCP从 ScheduledProcedureStep 获取到 Modality:" + modality);
exams = exams.Where(x => x.Modality == modality);
}
// if only date is specified, then using standard matching
// but if both are specified, then MWL defines a combined match
var scheduledProcedureStepStartDateTime = procedureStep.GetSingleValueOrDefault(DicomTag.ScheduledProcedureStepStartDate, string.Empty);
if (!string.IsNullOrEmpty(scheduledProcedureStepStartDateTime))
{
exams = AddDateCondition(exams, scheduledProcedureStepStartDateTime);
}
var procedureStepLocation = procedureStep.GetSingleValueOrDefault(DicomTag.ScheduledProcedureStepLocation, string.Empty);
if (!string.IsNullOrEmpty(procedureStepLocation))
{
exams = exams.Where(x => x.ExamRoom.Equals(procedureStepLocation));
}
var procedureDescription = procedureStep.GetSingleValueOrDefault(DicomTag.ScheduledProcedureStepDescription, string.Empty);
if (!string.IsNullOrEmpty(procedureDescription))
{
exams = exams.Where(x => x.ExamDescription.Equals(procedureDescription));
}
}
var results = exams.ToList();
// Parsing result
foreach (var result in results)
{
var resultingSPS = new DicomDataset();
var resultingSPCS = new DicomDataset();
var resultDataset = new DicomDataset();
var resultingSPSSequence = new DicomSequence(DicomTag.ScheduledProcedureStepSequence, resultingSPS);
if (procedureStep != null)
{
resultDataset.Add(resultingSPSSequence);
}
// 判断是否需要转换中文名
bool needConvertName = true;
string patientName = null;
// 根据请求的字符集设置响应的字符集
switch (requestedCharacterSet.ToUpperInvariant())
{
case "ISO_IR 100": // Latin1
AddIfExistsInRequest(resultDataset, request, DicomTag.SpecificCharacterSet, "ISO_IR 100");
needConvertName = true; // Latin1 不支持中文,需要转换
break;
case "GB18030": // 中文简体
case "GBK": // 中文简体
case "GB2312": // 中文简体
AddIfExistsInRequest(resultDataset, request, DicomTag.SpecificCharacterSet, "GB18030");
needConvertName = false; // GB18030 支持中文,不需要转换
break;
case "ISO_IR 192": // UTF-8
AddIfExistsInRequest(resultDataset, request, DicomTag.SpecificCharacterSet, "ISO_IR 192");
needConvertName = false; // UTF-8 支持中文,不需要转换
break;
default: // 其他未知字符集,使用 Latin1 作为安全选项
AddIfExistsInRequest(resultDataset, request, DicomTag.SpecificCharacterSet, "ISO_IR 100");
needConvertName = true; // 使用拼音
LogWrite.mwlLog("WorklistSCP未知的字符集:" + requestedCharacterSet + ", 使用 Latin1");
break;
}
// 根据字符集决定是否需要转换中文名
if (needConvertName)
{
patientName = ConvertToDeviceName(result.Surname);
LogWrite.mwlLog("WorklistSCP转换患者姓名 - 原始名:" + result.Surname + "转换后: " + patientName + "字符集:" + requestedCharacterSet);
}
else
{
patientName = result.Surname;
LogWrite.mwlLog("WorklistSCP使用原始中文名 - 患者姓名: {PatientName}:" + result.Surname + "字符集:" + requestedCharacterSet);
}
//add results to "main" dataset
//检查号
AddIfExistsInRequest(resultDataset, request, DicomTag.AccessionNumber, result.AccessionNumber); // T2
//医院名称
AddIfExistsInRequest(resultDataset, request, DicomTag.InstitutionName, result.HospitalName);
//推荐医师名称
AddIfExistsInRequest(resultDataset, request, DicomTag.ReferringPhysicianName, result.ReferringPhysician); // T2
//姓名
AddIfExistsInRequest(resultDataset, request, DicomTag.PatientName, patientName); //T1
AddIfExistsInRequest(resultDataset, request, DicomTag.InstitutionalDepartmentName, string.Format("DEPT{0}", "放射科")); //T1
//患者id
AddIfExistsInRequest(resultDataset, request, DicomTag.PatientID, result.PatientID); // T1
//出生日期
AddIfExistsInRequest(resultDataset, request, DicomTag.PatientBirthDate, result.DateOfBirth); // T2
//年龄
AddIfExistsInRequest(resultDataset, request, DicomTag.PatientAge, RegulateAge(result.PatientAge)); // T2
//性别
AddIfExistsInRequest(resultDataset, request, DicomTag.PatientSex, RegulateSex(result.Sex)); //T2
//检查号
AddIfExistsInRequest(resultDataset, request, DicomTag.StudyID, result.AccessionNumber);
//StudyUID
AddIfExistsInRequest(resultDataset, request, DicomTag.StudyInstanceUID, result.StudyUID); // T1
AddIfExistsInRequest(resultDataset, request, DicomTag.SeriesInstanceUID, result.StudyUID); // T1
AddIfExistsInRequest(resultDataset, request, DicomTag.StudyDescription, ""); // T1
AddIfExistsInRequest(resultDataset, request, DicomTag.SpecialNeeds, "CC"); // T1
AddIfExistsInRequest(resultDataset, request, DicomTag.PatientState, ""); // T1
//
AddIfExistsInRequest(resultDataset, request, DicomTag.BodyPartExamined, result.ExamDescription);
//请求医生
AddIfExistsInRequest(resultDataset, request, DicomTag.RequestingPhysician, result.ReferringPhysician); //T2
//请求描述
AddIfExistsInRequest(resultDataset, request, DicomTag.RequestedProcedureDescription, result.ExamDescription); //T1C
//请求过程ID
AddIfExistsInRequest(resultDataset, request, DicomTag.RequestedProcedureID, result.ProcedureID); // T1
// Scheduled Procedure Step sequence T1
// add results to procedure step dataset
// Return if requested
if (procedureStep != null)
{
//设备ae
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.ScheduledStationAETitle, result.ScheduledAET); // T1
//检查时间
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.ScheduledProcedureStepStartDate, result.ExamDateAndTime.ToString("yyyyMMdd")); //T1
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.ScheduledProcedureStepStartTime, result.ExamDateAndTime); //T1
//检查时间
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.StudyDate, GetDate(DateTime.Now)); //T1
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.StudyTime, GetTime(DateTime.Now)); //T1
//检查类型
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.Modality, result.Modality); // T1
//请求医生
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.ScheduledPerformingPhysicianName, result.PerformingPhysician); //T2
//步骤描述
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.ScheduledProcedureStepDescription, result.PerformingPhysician); // T1C
//步骤ID
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.ScheduledProcedureStepID, result.ProcedureStepID); //
//检查项目名称 ////T1
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.ScheduledStationName, result.ScheduledStationName); //T2
AddIfExistsInRequest(resultingSPS, procedureStep, DicomTag.ScheduledProcedureStepLocation, result.ExamRoom); //T2
}
var procedureDescription = procedureStep.GetSingleValueOrDefault(DicomTag.RequestedProcedureCodeSequence, string.Empty);
if (!string.IsNullOrEmpty(procedureDescription))
{
DicomDataset resultingrRPCS = new DicomDataset();
AddIfExistsInRequest(resultingrRPCS, procedureStep, DicomTag.CodeValue, "");
AddIfExistsInRequest(resultingrRPCS, procedureStep, DicomTag.CodingSchemeDesignator, "");
AddIfExistsInRequest(resultingrRPCS, procedureStep, DicomTag.CodingSchemeVersion, "");
AddIfExistsInRequest(resultingrRPCS, procedureStep, DicomTag.CodeMeaning, "");
var resultingRPCSSequence = new DicomSequence(DicomTag.RequestedProcedureCodeSequence, resultingrRPCS);
resultDataset.Add(resultingRPCSSequence);
}
// Put blanks in for unsupported fields which are type 2 (i.e. must have a value even if NULL)
// In a real server, you may wish to support some or all of these, but they are not commonly supported
AddIfExistsInRequest(resultDataset, request, DicomTag.ReferencedStudySequence, new DicomDataset()); // Ref//d Study Sequence
AddIfExistsInRequest(resultDataset, request, DicomTag.Priority, "");
//怀孕状态
AddIfExistsInRequest(resultDataset, request, DicomTag.PregnancyStatus, result.PregnancyStatus);// Transport Arrangements// Priority
AddIfExistsInRequest(resultDataset, request, DicomTag.MedicalAlerts, "");
AddIfExistsInRequest(resultDataset, request, DicomTag.Allergies, "");
AddIfExistsInRequest(resultDataset, request, DicomTag.PatientTransportArrangements, "");
//承认ID
AddIfExistsInRequest(resultDataset, request, DicomTag.AdmissionID, result.AdmissionID); // Admission ID
AddIfExistsInRequest(resultDataset, request, DicomTag.CurrentPatientLocation, ""); // Patient Location
AddIfExistsInRequest(resultDataset, request, DicomTag.ReferencedPatientSequence, new DicomDataset()); // Ref//d Patient Sequence
//体重
AddIfExistsInRequest(resultDataset, request, DicomTag.PatientWeight, result.PatientWeight); // Weight
AddIfExistsInRequest(resultDataset, request, DicomTag.ConfidentialityConstraintOnPatientDataDescription, "");// Confidentiality Constraint
//sop
AddIfExistsInRequest(resultDataset, request, DicomTag.ImplementationClassUID, DicomUIDGenerator.GenerateDerivedFromUUID());
AddIfExistsInRequest(resultDataset, request, DicomTag.TransferSyntaxUID, DicomTS.Explicit_VR_Little_Endian);
// Send Reponse
yield return resultDataset;
}
}
//Splits patient name into 2 separte strings surname and forename and send then to the addstringcondition subroutine.
internal static IQueryable<WorklistItem> AddNameCondition(IQueryable<WorklistItem> exams, string dicomName)
{
if (string.IsNullOrEmpty(dicomName) || dicomName == "*")
{
return exams;
}
var personName = new DicomPersonName(DicomTag.PatientName, dicomName);
if (dicomName.Contains("*"))
{
var firstNameRegex = new Regex("^" + Regex.Escape(personName.First).Replace("\\*", ".*") + "$");
var lastNameRegex = new Regex("^" + Regex.Escape(personName.Last).Replace("\\*", ".*") + "$");
exams = exams.Where(x => firstNameRegex.IsMatch(x.Forename) || lastNameRegex.IsMatch(x.Surname));
}
else
{
exams = exams.Where(x => (x.Forename.Equals(personName.First) && x.Surname.Equals(personName.Last)));
}
return exams;
}
internal static IQueryable<WorklistItem> AddDateCondition(IQueryable<WorklistItem> exams, string dateCondition)
{
if (!string.IsNullOrEmpty(dateCondition) && dateCondition != "*")
{
var range = new DicomDateTime(DicomTag.ScheduledProcedureStepStartDate, dateCondition).Get<DicomDateRange>();
exams = exams.Where(x => range.Contains(x.ExamDateAndTime));
}
return exams;
}
internal static void AddIfExistsInRequest<T>(DicomDataset result, DicomDataset request, DicomTag tag, T value)
{
// Only send items which have been requested
if (request.Contains(tag))
{
if (value == null)
{
value = default;
}
result.AddOrUpdate(tag, value);
}
}
internal class DicomTS
{
public const string Implicit_VR_Little_Endian = "1.2.840.10008.1.2";
public const string Explicit_VR_Little_Endian = "1.2.840.10008.1.2.1";
public const string Explicit_VR_Big_Endian = "1.2.840.10008.1.2.2";
}
protected static object GetDate(DateTime dt)
{
return dt.ToString("yyyyMMdd");
}
protected static object GetTime(DateTime dt)
{
return dt.ToString("HHmmss");
}
public static string RegulateSex(string sex)
{
switch (sex)
{
case "男":
case "M":
case "m":
return "M";
case "女":
case "F":
case "f":
return "F";
default:
return "O";
}
}
protected static string RegulateAge(string age)
{
SortedList<string, string> replaces = new SortedList<string, string>();
replaces.Add("零", "0");
replaces.Add("一", "1");
replaces.Add("二", "2");
replaces.Add("三", "3");
replaces.Add("四", "4");
replaces.Add("五", "5");
replaces.Add("六", "6");
replaces.Add("七", "7");
replaces.Add("八", "8");
replaces.Add("九", "9");
replaces.Add("壹", "1");
replaces.Add("贰", "2");
replaces.Add("叁", "3");
replaces.Add("肆", "4");
replaces.Add("伍", "5");
replaces.Add("陆", "6");
replaces.Add("柒", "7");
replaces.Add("捌", "8");
replaces.Add("玖", "9");
replaces.Add(" ", "");
age = WorklistHandler.Replace(age, replaces);
int? year = WorklistHandler.GetAgeValueByUnit(ref age, new string[] { "年", "岁", "Y", "y" });
int? month = WorklistHandler.GetAgeValueByUnit(ref age, new string[] { "月", "M", "m" });
int? week = WorklistHandler.GetAgeValueByUnit(ref age, new string[] { "周", "星期", "礼拜", "W", "w" });
int? day = WorklistHandler.GetAgeValueByUnit(ref age, new string[] { "日", "天", "D", "d" });
int? hour = WorklistHandler.GetAgeValueByUnit(ref age, new string[] { "时", "小时", "H", "h" });
StringBuilder sb = new StringBuilder();
if (year.HasValue) sb.Append(year.Value.ToString().PadLeft(3, '0')).Append("Y");
if (month.HasValue) sb.Append(month.Value.ToString().PadLeft(3, '0')).Append("M");
if (week.HasValue) sb.Append(week.Value.ToString().PadLeft(3, '0')).Append("W");
if (day.HasValue) sb.Append(day.Value.ToString().PadLeft(3, '0')).Append("D");
if (hour.HasValue) sb.Append(hour.Value.ToString().PadLeft(3, '0')).Append("H");
return sb.Length < 1 ? null : sb.ToString();
}
private static string Replace(string s, SortedList<string, string> replaces)
{
if (s == null) return null;
foreach (KeyValuePair<string, string> kv in replaces)
{
s = s.Replace(kv.Key, kv.Value);
}
return s;
}
private static int? GetAgeValueByUnit(ref string s, string[] units)
{
if (s == null) return null;
foreach (string unit in units)
{
try
{
string[] ss = s.Split(new string[] { unit }, StringSplitOptions.RemoveEmptyEntries);
if (ss.Length < 1) continue;
System.Text.RegularExpressions.Match m = reg.Match(ss[0]);
if (!m.Success) continue;
int v;
if (int.TryParse(m.Value, out v))
{
int start = ss[0].Length - m.Value.Length;
int end = ss[0].Length + unit.Length - 1;
if (start > 0)
{
if (s.Length - end - 1 > 0)
{
s = s.Substring(0, start) + s.Substring(end + 1);
}
else
{
s = s.Substring(0, start);
}
}
else
{
if (s.Length - end - 1 > 0)
{
s = s.Substring(end + 1);
}
else
{
s = "";
}
}
return v;
}
}
catch
{ }
}
return null;
}
private static string ConvertToDeviceName(string chineseName)
{
try
{
if (string.IsNullOrEmpty(chineseName))
return "";
var result = new StringBuilder();
var isFirst = true;
foreach (var c in chineseName)
{
if (PinyinHelper.IsChinese(c))
{
var pinyin = PinyinHelper.GetPinyin(c);
// 首字大写
if (isFirst)
{
result.Append(char.ToUpper(pinyin[0]) + pinyin.Substring(1).ToLower());
isFirst = false;
}
else
{
result.Append(pinyin.ToLower());
}
result.Append('^'); // DICOM中姓名分隔符
}
else
{
// 如果是非文字符,直接添加
result.Append(c);
}
}
// 移除最后一个分隔符(如果存在)
if (result.Length > 0 && result[result.Length - 1] == '^')
{
result.Length--;
}
return result.ToString();
}
catch (Exception ex)
{
LogWrite.mwlLog("WorklistSCP" + ex + "转换患者姓名失败:" + chineseName);
return chineseName; // 转换失败时返回原始名字
}
}
}
⑥ Main 方法
static void Main(string[] args)
{
var appSettings = ConfigurationManager.AppSettings;
String Prot = appSettings[“Port”] ?? “1004”;
String Ae = appSettings[“Ae”] ?? null;
WorklistServer.Start(int.Parse(Prot), Ae);
LogWrite.mwlLog(Prot+ Ae);
Console.WriteLine($"Starting WORKLIST SCP server with AET: "+ Ae + " port "+ Prot);
Console.WriteLine(“Press any key to stop the service”);
Console.Read();
}
⑦ AppSettings核心配置参考
<appSettings>
<!--数据库类型 SQLSERVER /ORACLE /MYSQL/-->
<add key="BaseType" value="SQLSERVER"/>
<!--数据库链接字符串-->
<add key="BasedbUrl" value="server=127.0.0.1,1433; database=pacs; uid=sa;pwd="/>
<!--数据库查询sql-->
<add key="QuerySql" value="SELECT PatientId
,PatientName
,Modality
,Sex
,AccessionNumber
,PatientWeight
,StudyTime
,PatientBirthDate
,PregnancyStatus
,PatientAge
,StudyUuid
,InstitutionName
,RequestingPhysician
,PerformingPhysician
,ProcedureID
,ProcedureStepID
,ScheduledAET
,ExamRoom
,AdmissionID
,ScheduledStationName
,ExamDescription
FROM WRKLIST_XQXQWDDR"/>
<!--姓名显示形式 1中文 2英文 3中文+英文-->
<add key="NameType" value="1"/>
<!--字符集 ISO_IR 192/ISO_IR 100/GB18030可以空/实际以仪器交互字符集为准-->
<add key="Encode" value="ISO_IR 100"/>
<!--服务端口-->
<add key="Port" value="104"/>
<!--服务AE名称-->
<add key="Ae" value="Bluesky"/>
</appSettings>
更多推荐
所有评论(0)