现在我们能够校验 DICOM 连接性了。我们甚至能够从一个 AE 向另一个发送图像了。但
是我们怎么找出需要发送的数据呢?三个
C-Find SOP
类提供
DICOM
查询的实现方法,如表
28
所示。
查找成像数据不是影像设备特定的,因此
C-Find
不包括许多像
C-Store
那样、基于影像
设备的
SOP
。因此可以认为,
C-Find
与
C-Echo
非常相似(图
40
)。然而,
C-Find
中还有一样
是需要全新介绍的:
DICOM
将所有
C-Find
数据查询分为了三个数据级别:病人、检查、病
人
-
检查。这些级别称作根,并且
C-Find
在这三个根上使用不同的
SOP
来实现数据查询
DICOM
查询根符合
5.6
中介绍的
DICOM
病人
-
检查
-
序列
-
图像的数据等级。
DICOM
用四
个等级来组织数据,并且查询数据与等级限制配合的非常好。比如,如果支持病人查询
/
提
取作为根,那么查询和提取只会将限制在病人
-
检查
-
序列
-
图像这个级别;如果使用检查查询
/
提取作为根,那么查询和提取将会限制在检查
-
序列
-
图像这个级别。在后面的例子中,所有
病人属性(比如病人姓名或者
ID
)将会包含在检查属性中用来识别不同检查。
由于目前的放射工作流是以检查为中心的,因此以检查为根在
DICOM 应用程序中会非常广泛地使用并且通常作为默认设置。病人和病人-检查 SOP 作为查询层级则很少使用。事实上,以病人-检查为查询根最近已经在 DICOM 标准中弃用了,这意味着,在未来的 DICOM
版本中将不会支持这种做法,并且会浅浅地在
DICOM
实现过程中消失。 勿须赘言,C-Find SOP
也符合
DIMSE + IOD
结构,如图
41
所示。
DIMSE
对象(编码来作 为 DICOM
命令对象,组
0000
)传送
C-Find
消息参数,并且附加
IOD
对象(编码来作为
DICOM 数据对象)用来承载查询条件去 C-Find SCP
上找匹配的信息。
DICOM
中数据匹配方面的一些词汇
当
C-Find SCU
发送一个查询请求时,请求会包括希望获得匹配的属性集合。这些属性会
作为查询字段与目标
AE
中的数据进行比较。总的来说,
DICOM
会提供一些设置查询值的方
法:
1.
通配符匹配。我们之前已经有所了解。我们曾在
5.3.2
中第一次提到它,当时我们正在
讨论
DICOM
通配符(
*?
)。不仅仅在
DICOM
中,很多地方都会常常用通配符来匹配字
符串。比如,如果我们知道一个病人名字是以“
Smit
”开头,那么我们可以在
C-Find
请
求中将病人姓名(
Patient Name
)
(0010,0010)
属性里面输入“
Smit
”并且在其后面使用
(*)代表任何字符序列。我们会找到“
Smith
”、“
Smithson
”、甚至“
Smit III the Great
”,
这些都是符合此次通配符查询的。其他
DICOM
通配符,比如问号(?),表示单一位置
字符,但是很少会用到它,因为我们在不了解字符是什么的情况下,也很有可能不知道
字符的数量,因此干脆用星号就好了。
2.
罗列匹配。此技术使用反斜线(\)通配符,这一位置“或”。比如,病人姓名中的
“
Smith\Graham
”将用来查找所有叫
Smith
和
Graham
的病人。当然可以将罗列和通配
符结合使用比如“
Smit*\Grah*
”,特别是当你处理由录入错误风险的数据时。(手工录
入的病人姓名很容易造成错误录入)
3.
整体匹配。这是一种最常见的匹配规则。如果被匹配的属性是零长度的,那么就意味着
匹配所有。比如,如果我们在病人姓名栏中留空,并以此条件执行
C-Find
查询,那么
在目标归档中的所有病人姓名都将符合该查询条件。事实上,这种情况下整体匹配意味
着任何可能的值相当于使用星号(*)。然而,在
C-Find
中提供一个零长度匹配属性与
什么都不提供是完全不同的两个概念。包含零长度属性意味着我们希望(或必须)使用
这个零长度属性进行匹配并且我们希望在
C-Find
响应中获得匹配结果。不使用属性进
行查询意味着我们不希望查询内容获得匹配(可能根本不支持这种做法)并且我们也不
想提取结果属性。比如,如果我们不想提取病人姓名,无论病人姓名中是否有值,我们
需要在
C-Find
请求中去掉病人姓名属性。
4.
范围匹配。这种匹配类型用作有范围的属性,比如日期或时间。在范围匹配中,你能够
规定开始和结束的属性值,通过连字号(-)分隔两个值。任何在该范围内的内容都将
匹配此次查询。比如,日期范围匹配
20000201-20100202
意味着在
2000
年
2
月
1
日和
2000
年
2
月
2
日之间的所有内容。如果开始或结束时间不能确定,可以留空;比如
20000201-
表示“
2000
年
2
月
1
日及之后”。
5.
序列查询。这也许是最复杂的
DICOM
属性匹配形式。这种形式只有在属性的整个序列
(由
SQ VR
组成,见
5.3.10
)用作匹配数据时才会采用。比如,比对影像设备已预约病
人检查时,
DICOM
会使用病人
ID
、检查日期和检查号组成属性序列,序列中的每个属
性既可以是确定值,也可以使用前面所提到的某一个匹配类型。在一个
SQ
序列中的属
性是按照逻辑“与”来组合的(意味着所有属性必须匹配)。与表示“或”的反斜线(\)
通配符正好相反。
6.
单一值匹配。这就是简单地使用准巨额属性值作为匹配参数:使用“
Smith
”用做病人
姓名查询;“
20000201
”用作日期查询等等。很自然地,单一值匹配的属性必须不包含
通配符和范围查询。单一值匹配通产雇佣在各种
ID
和
UID
的查询上,并且特别用在分
级的关键属性(即唯一
ID
,如病人
ID
、 检查实例
UID
等)上(见
5.6.3
)。
在
DICOM
数据匹配中的所有属性既可以是必须的也可以是可选的。必须的属性,人如
其名必须在匹配请求中出现。如果我们不知道应该将这类查询值设置成什么,那么我们可以
简单地将它们设置为空值(整体匹配)。这就表明我们保证会收到匹配值。如果我们有兴趣
使用,那么可以使用可选的属性。
DICOM
标准通常会对每个查询类型(比如在病人、检查、
序列、图像层面上的
C-Find
)规定其必须的和可选的属性,但是你总是需要对你所查询的设
备进行
DICOM
一致性声明的核实,因为他们通常不会顾及那些看起来更一般的
DICOM
规定。
这在某种程度上是可以理解的,因为用在超声扫描仪上的属性肯定与用在
CT
教学归档上的
属性不同。然而,这却总是带来一个实施上的问题,你不得不针对不同设备来设置不同的查
询属性。
最后要说的是,在
DICOM
中几乎所有的匹配都是大小写敏感的。只允许在某些特定的
属性中使用大小写不敏感,比如姓名(
PN VR
),在这些属性中也允许空格和强调符不敏感。
以我个人的看法,我认为大小写敏感根本没能给
DICOM
带来任何好处:首先大小写很有可
能在录入时进行转换,其次这几乎没有人关心。更糟糕的是,一些主流的
PACS
界面会要求
用户只能使用大小写中的一种来如入某些数据:比如,只能用大写来录入病人姓名。很明显
这么做有个重大弊端,除非你录入
SMITH
否则你根本别想找到病人
Smith
。实际上,这种要
求实际上逼着用户在使用
PACS
时按下“大写锁定”,但是这样不可避免地会产生一系列其他
的录入问题。因此,只要可以实现,
DICOM
和
DICOM
应用程序就应该避免大小写敏感问题。
C-Find IOD
DICOM
属性匹配大陆上航行过后,我们就可以在更高的层次上对
C-Find
查询进行深入了
解了。
C-Find IOD
包含用来在
C-Find
服务提供者(如数字归档)那里进行匹配的查询参数。表
29
提供了一个真实的一致性声明的实例。
1
如
5.6.3
所述,在
DICOM
中的所有数据符合病人
-
检查
-
序列
-
图像等级制度,而
C-Find
查
询也不例外;他们必须属于四个级别中的一个。
C-Find
查询层级会反映在(
0008,0052
)属
性中(必须的),内容会是病人(
PATIENT
)、检查(
STUDY
)、序列(
SERIES
)或图像(
IMAGE
)。
这同样说明在
SOP
根之外,这种规则也一直在
C-Find
的各个查询层级中遵从着:
1.
所有较高层次的关键属性值
2
必须是已知的。比如,在开始进行检查层次的查询时,我
们必须知道病人
ID
。
2.
所有当前层次的关键属性将通过
C-Find SCP
返回。比如,在一个检查层次的查询中,“检
查实例
UID
”
(0020,000E)
将总会出现在
C-Find
请求的响应中。这符合规则
1
,即允许我
们处理更低的层级(序列、图像)。
3.
在任何的
C-Find
请求中,我们只能从当前
C-Find
层次来查询和匹配属性。比如,一个
检查层次的查询只能查询检查属性而不能单独查询序列或图像。
请看我们表
29
中的样例
IOD
;它是什么层次的呢?我们会立即发现其中有一个
(0008,0052)
值:这是检查层次。但是即使你不知道这个值是什么,你仍可以去推断:
IOD
里
看起来有病人
ID
(病人层次),但是还没有检查实例
UID
(检查层次)。根据规则
1
和规则
2。
这显然是个检查层次的查询。这个逻辑在定位
DICOM
问题时也很有用。比如,如果在关键
属性中我们决定采用的层次与
(0008,0052)
声明的不同,那么
C-Find
查询一定会失败。
当然,你没有必要一定建立你自己的层次属性表格。每个
DICOM
应用程序的
DICOM
一
致性声明都会针对每个支持的层次(并且用必须的或可选的来标识它们)列出所有支持的属
性。事实上,一个特定的设备真不一定能够支持所有四个等级层次,它们的一致性声明将仅
包含所支持的层次。
了解C-FIND之后接下来看看如何用代码实现
/**
* 查询
*
* @param host IP地址
* @param port 端口号
* @param aeTitle remoteAETitle
* @param queryLevel 查询层级
* @param queryModel 查询模式
* @param relational 联合查询
* @param fuzzy 模糊查询
* @param dataTimeCombine 时间日期合并
* @param sourceAeTitle sourceAeTitle
* @param modality 检查类型
* @param studyPeriod 查询时间范围
* @param scheduledStationAET 检查设备的名称
* @return 查询结果
*/
public DICOMResultBean query(String host, int port, String aeTitle, QueryLevel queryLevel,
FindSCU.InformationModel queryModel, boolean relational, boolean fuzzy,
boolean dataTimeCombine, String sourceAeTitle, String modality,
StudyPeriod studyPeriod, String scheduledStationAET, Search search) {
createConn("findScu", "zoncare");
MyQueryTool queryTool = new MyQueryTool(host, port, aeTitle, queryLevel.getValue(), queryModel, relational, device, sourceAeTitle, conn);
try {
Attributes attributes = getAttributes(modality, studyPeriod, scheduledStationAET, search);
queryTool.addAll(attributes);
queryTool.query("worklist查询测试", fuzzy, dataTimeCombine);
QueryResult result = (QueryResult) queryTool.getResult();
return getDICOMResultBean(result);
} catch (Exception e) {
Log.e("yuhao", "queryMWL: Exception" + e.getMessage());
e.printStackTrace();
}
return null;
}
@NotNull
public static Attributes getAttributes(String modality, StudyPeriod studyPeriod, String scheduledStationAET, Search search) {
VR vr;
Attributes attributes = new Attributes();
if (search == Search.IHE) {
vr = ElementDictionary.vrOf(Tag.AccessionNumber, (String) null);
attributes.setNull(Tag.AccessionNumber, vr);
vr = ElementDictionary.vrOf(Tag.ReferringPhysicianName, (String) null);
attributes.setNull(Tag.ReferringPhysicianName, vr);
vr = ElementDictionary.vrOf(Tag.InstitutionalDepartmentName, (String) null);
attributes.setNull(Tag.InstitutionalDepartmentName, vr);
vr = ElementDictionary.vrOf(Tag.PregnancyStatus, (String) null);
attributes.setNull(Tag.PregnancyStatus, vr);
vr = ElementDictionary.vrOf(Tag.RequestingPhysician, (String) null);
attributes.setNull(Tag.RequestingPhysician, vr);
VR vrReferencedStudySequence = ElementDictionary.vrOf(Tag.ReferencedStudySequence, (String) null);
attributes.setNull(Tag.ReferencedStudySequence, vrReferencedStudySequence);
Sequence sequence = attributes.newSequence(Tag.ReferencedStudySequence, 1);
Attributes attributes1 = new Attributes();
vr = ElementDictionary.vrOf(Tag.ReferencedSOPClassUID, (String) null);
attributes1.setNull(Tag.ReferencedSOPClassUID, vr);
vr = ElementDictionary.vrOf(Tag.ReferencedSOPInstanceUID, (String) null);
attributes1.setNull(Tag.ReferencedSOPInstanceUID, vr);
sequence.add(attributes1);
vr = ElementDictionary.vrOf(Tag.PatientName, (String) null);
attributes.setNull(Tag.PatientName, vr);
vr = ElementDictionary.vrOf(Tag.PatientID, (String) null);
attributes.setNull(Tag.PatientID, vr);
vr = ElementDictionary.vrOf(Tag.PatientBirthDate, (String) null);
attributes.setNull(Tag.PatientBirthDate, vr);
vr = ElementDictionary.vrOf(Tag.PatientSex, (String) null);
attributes.setNull(Tag.PatientSex, vr);
vr = ElementDictionary.vrOf(Tag.PatientSize, (String) null);
attributes.setNull(Tag.PatientSize, vr);
vr = ElementDictionary.vrOf(Tag.PatientWeight, (String) null);
attributes.setNull(Tag.PatientWeight, vr);
vr = ElementDictionary.vrOf(Tag.StudyInstanceUID, (String) null);
attributes.setNull(Tag.StudyInstanceUID, vr);
vr = ElementDictionary.vrOf(Tag.RequestingPhysician, (String) null);
attributes.setNull(Tag.RequestingPhysician, vr);
VR vrRequestedProcedureCodeSequence = ElementDictionary.vrOf(Tag.RequestedProcedureCodeSequence, (String) null);
attributes.setNull(Tag.RequestedProcedureCodeSequence, vrRequestedProcedureCodeSequence);
Sequence sequence2 = attributes.newSequence(Tag.RequestedProcedureCodeSequence, 1);
Attributes attributes2 = new Attributes();
vr = ElementDictionary.vrOf(Tag.CodeValue, (String) null);
attributes2.setNull(Tag.CodeValue, vr);
vr = ElementDictionary.vrOf(Tag.CodingSchemeDesignator, (String) null);
attributes2.setNull(Tag.CodingSchemeDesignator, vr);
vr = ElementDictionary.vrOf(Tag.CodeMeaning, (String) null);
attributes2.setNull(Tag.CodeMeaning, vr);
sequence2.add(attributes2);
vr = ElementDictionary.vrOf(Tag.CurrentPatientLocation, (String) null);
attributes.setNull(Tag.CurrentPatientLocation, vr);
VR vrScheduledProcedureStepSequence = ElementDictionary.vrOf(Tag.ScheduledProcedureStepSequence, (String) null);
attributes.setNull(Tag.ScheduledProcedureStepSequence, vrScheduledProcedureStepSequence);
Sequence sequence3 = attributes.newSequence(Tag.ScheduledProcedureStepSequence, 1);
Attributes attributes3 = new Attributes();
vr = ElementDictionary.vrOf(Tag.Modality, (String) null);
if (modality == null) {
attributes3.setNull(Tag.Modality, vr);
} else {
attributes3.setString(Tag.Modality, vr, modality);
}
vr = ElementDictionary.vrOf(Tag.InstitutionName, (String) null);
attributes3.setNull(Tag.InstitutionName, vr);
vr = ElementDictionary.vrOf(Tag.InstitutionalDepartmentName, (String) null);
attributes3.setNull(Tag.InstitutionalDepartmentName, vr);
vr = ElementDictionary.vrOf(Tag.CurrentPatientLocation, (String) null);
attributes3.setNull(Tag.CurrentPatientLocation, vr);
vr = ElementDictionary.vrOf(Tag.ScheduledStationAETitle, (String) null);
if (scheduledStationAET == null) {
attributes3.setNull(Tag.ScheduledStationAETitle, vr);
} else {
attributes3.setString(Tag.ScheduledStationAETitle, vr, scheduledStationAET);
}
StudyPeriodBean studyPeriodBean = getStudyPeriod(studyPeriod);
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepStartDate, (String) null);
if (studyPeriodBean == null || studyPeriod == StudyPeriod.All) {
attributes3.setNull(Tag.ScheduledProcedureStepStartDate, vr);
} else {
attributes3.setString(Tag.ScheduledProcedureStepStartDate, vr, studyPeriodBean.scheduledProcedureStepStartDate);
}
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepStartTime, (String) null);
if (studyPeriodBean == null || studyPeriod == StudyPeriod.All) {
attributes3.setNull(Tag.ScheduledProcedureStepStartTime, vr);
} else {
attributes3.setString(Tag.ScheduledProcedureStepStartTime, vr, studyPeriodBean.scheduledProcedureStepStartTime);
}
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepEndDate, (String) null);
if (studyPeriodBean == null || studyPeriod == StudyPeriod.All) {
attributes3.setNull(Tag.ScheduledProcedureStepEndDate, vr);
} else {
attributes3.setString(Tag.ScheduledProcedureStepEndDate, vr, studyPeriodBean.scheduledProcedureStepEndDate);
}
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepEndTime, (String) null);
if (studyPeriodBean == null || studyPeriod == StudyPeriod.All) {
attributes3.setNull(Tag.ScheduledProcedureStepEndTime, vr);
} else {
attributes3.setString(Tag.ScheduledProcedureStepEndTime, vr, studyPeriodBean.scheduledProcedureStepEndTime);
}
vr = ElementDictionary.vrOf(Tag.ScheduledPerformingPhysicianName, (String) null);
attributes3.setNull(Tag.ScheduledPerformingPhysicianName, vr);
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepDescription, (String) null);
attributes3.setNull(Tag.ScheduledProcedureStepDescription, vr);
sequence3.add(attributes3);
VR vrScheduledProtocolCodeSequence = ElementDictionary.vrOf(Tag.ScheduledProtocolCodeSequence, (String) null);
attributes3.setNull(Tag.ScheduledProtocolCodeSequence, vrScheduledProtocolCodeSequence);
Sequence sequence4 = attributes3.newSequence(Tag.ScheduledProtocolCodeSequence, 1);
Attributes attributes4 = new Attributes();
vr = ElementDictionary.vrOf(Tag.CodeValue, (String) null);
attributes4.setNull(Tag.CodeValue, vr);
vr = ElementDictionary.vrOf(Tag.CodingSchemeDesignator, (String) null);
attributes4.setNull(Tag.CodingSchemeDesignator, vr);
vr = ElementDictionary.vrOf(Tag.CodeMeaning, (String) null);
attributes4.setNull(Tag.CodeMeaning, vr);
sequence4.add(attributes4);
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepID, (String) null);
attributes3.setNull(Tag.ScheduledProcedureStepID, vr);
vr = ElementDictionary.vrOf(Tag.ScheduledStationName, (String) null);
attributes3.setNull(Tag.ScheduledStationName, vr);
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepLocation, (String) null);
attributes3.setNull(Tag.ScheduledProcedureStepLocation, vr);
vr = ElementDictionary.vrOf(Tag.RequestedProcedureID, (String) null);
attributes.setNull(Tag.RequestedProcedureID, vr);
vr = ElementDictionary.vrOf(Tag.RequestedProcedureComments, (String) null);
attributes.setNull(Tag.RequestedProcedureComments, vr);
} else {
vr = ElementDictionary.vrOf(Tag.AccessionNumber, (String) null);
attributes.setNull(Tag.AccessionNumber, vr);
vr = ElementDictionary.vrOf(Tag.PatientName, (String) null);
attributes.setNull(Tag.PatientName, vr);
vr = ElementDictionary.vrOf(Tag.PatientID, (String) null);
attributes.setNull(Tag.PatientID, vr);
vr = ElementDictionary.vrOf(Tag.PatientBirthDate, (String) null);
attributes.setNull(Tag.PatientBirthDate, vr);
vr = ElementDictionary.vrOf(Tag.PatientSex, (String) null);
attributes.setNull(Tag.PatientSex, vr);
vr = ElementDictionary.vrOf(Tag.CurrentPatientLocation, (String) null);
attributes.setNull(Tag.CurrentPatientLocation, vr);
VR vrScheduledProcedureStepSequence = ElementDictionary.vrOf(Tag.ScheduledProcedureStepSequence, (String) null);
attributes.setNull(Tag.ScheduledProcedureStepSequence, vrScheduledProcedureStepSequence);
Sequence sequence3 = attributes.newSequence(Tag.ScheduledProcedureStepSequence, 1);
Attributes attributes3 = new Attributes();
vr = ElementDictionary.vrOf(Tag.Modality, (String) null);
if (modality == null) {
attributes3.setNull(Tag.Modality, vr);
} else {
attributes3.setString(Tag.Modality, vr, modality);
}
vr = ElementDictionary.vrOf(Tag.InstitutionName, (String) null);
attributes3.setNull(Tag.InstitutionName, vr);
vr = ElementDictionary.vrOf(Tag.InstitutionalDepartmentName, (String) null);
attributes3.setNull(Tag.InstitutionalDepartmentName, vr);
vr = ElementDictionary.vrOf(Tag.CurrentPatientLocation, (String) null);
attributes3.setNull(Tag.CurrentPatientLocation, vr);
vr = ElementDictionary.vrOf(Tag.ScheduledStationAETitle, (String) null);
if (scheduledStationAET == null) {
attributes3.setNull(Tag.ScheduledStationAETitle, vr);
} else {
attributes3.setString(Tag.ScheduledStationAETitle, vr, scheduledStationAET);
}
StudyPeriodBean studyPeriodBean = getStudyPeriod(studyPeriod);
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepStartDate, (String) null);
if (studyPeriodBean == null || studyPeriod == StudyPeriod.All) {
attributes3.setNull(Tag.ScheduledProcedureStepStartDate, vr);
} else {
attributes3.setString(Tag.ScheduledProcedureStepStartDate, vr, studyPeriodBean.scheduledProcedureStepStartDate);
}
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepStartTime, (String) null);
if (studyPeriodBean == null || studyPeriod == StudyPeriod.All) {
attributes3.setNull(Tag.ScheduledProcedureStepStartTime, vr);
} else {
attributes3.setString(Tag.ScheduledProcedureStepStartTime, vr, studyPeriodBean.scheduledProcedureStepStartTime);
}
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepEndDate, (String) null);
if (studyPeriodBean == null || studyPeriod == StudyPeriod.All) {
attributes3.setNull(Tag.ScheduledProcedureStepEndDate, vr);
} else {
attributes3.setString(Tag.ScheduledProcedureStepEndDate, vr, studyPeriodBean.scheduledProcedureStepEndDate);
}
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepEndTime, (String) null);
if (studyPeriodBean == null || studyPeriod == StudyPeriod.All) {
attributes3.setNull(Tag.ScheduledProcedureStepEndTime, vr);
} else {
attributes3.setString(Tag.ScheduledProcedureStepEndTime, vr, studyPeriodBean.scheduledProcedureStepEndTime);
}
vr = ElementDictionary.vrOf(Tag.ScheduledPerformingPhysicianName, (String) null);
attributes3.setNull(Tag.ScheduledPerformingPhysicianName, vr);
vr = ElementDictionary.vrOf(Tag.ScheduledProcedureStepDescription, (String) null);
attributes3.setNull(Tag.ScheduledProcedureStepDescription, vr);
sequence3.add(attributes3);
VR vrScheduledProtocolCodeSequence = ElementDictionary.vrOf(Tag.ScheduledProtocolCodeSequence, (String) null);
attributes3.setNull(Tag.ScheduledProtocolCodeSequence, vrScheduledProtocolCodeSequence);
}
return attributes;
}
public class MyQueryTool {
private String host;
private int port;
private String aeTitle;
private Device device;
private Connection conn;
private String sourceAETitle;
private List<Attributes> response = new ArrayList();
private TestResult result;
private String queryLevel;
private FindSCU.InformationModel queryModel;
private boolean relational;
private int numMatches;
private static String[] IVR_LE_FIRST = new String[]{"1.2.840.10008.1.2", "1.2.840.10008.1.2.1", "1.2.840.10008.1.2.2"};
private Attributes queryatts = new Attributes();
private int expectedMatches = -2147483648;
private long timeFirst = 0L;
public MyQueryTool(String host, int port, String aeTitle, String queryLevel, FindSCU.InformationModel queryModel, boolean relational, Device device, String sourceAETitle, Connection conn) {
this.host = host;
this.port = port;
this.aeTitle = aeTitle;
this.device = device;
this.sourceAETitle = sourceAETitle;
this.conn = conn;
this.queryLevel = queryLevel;
this.queryModel = queryModel;
this.relational = relational;
}
public void query(String testDescription, boolean fuzzy, boolean dataTimeCombined) throws IOException, InterruptedException, IncompatibleConnectionException, GeneralSecurityException {
this.doQuery(testDescription, fuzzy, dataTimeCombined);
}
private void doQuery(String testDescription, boolean fuzzy, boolean combined) throws IOException, InterruptedException, IncompatibleConnectionException, GeneralSecurityException {
this.device.setInstalled(true);
ApplicationEntity ae = new ApplicationEntity(this.sourceAETitle);
this.device.addApplicationEntity(ae);
ae.addConnection(this.conn);
FindSCU main = new FindSCU(ae);
main.getAAssociateRQ().setCalledAET(this.aeTitle);
main.getRemoteConnection().setHostname(this.host);
main.getRemoteConnection().setPort(this.port);
main.getRemoteConnection().setTlsCipherSuites(this.conn.getTlsCipherSuites());
main.getRemoteConnection().setTlsProtocols(this.conn.getTlsProtocols());
ExecutorService executorService = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
main.getDevice().setExecutor(executorService);
main.getDevice().setScheduledExecutor(scheduledExecutorService);
EnumSet<QueryOption> queryOptions = EnumSet.noneOf(QueryOption.class);
if (fuzzy) {
queryOptions.add(QueryOption.FUZZY);
}
if (combined) {
queryOptions.add(QueryOption.DATETIME);
}
if (this.relational) {
queryOptions.add(QueryOption.RELATIONAL);
}
main.setInformationModel(this.queryModel, IVR_LE_FIRST, queryOptions);
if (queryModel != FindSCU.InformationModel.MWL) {
main.addLevel(this.queryLevel);
}
main.getKeys().addAll(this.queryatts);
long timeStart = System.currentTimeMillis();
try {
main.open();
main.query(this.getDimseRSPHandler(main.getAssociation().nextMessageID()));
} finally {
main.close();
executorService.shutdown();
scheduledExecutorService.shutdown();
}
long timeEnd = System.currentTimeMillis();
this.validateMatches(testDescription);
this.init(new QueryResult(testDescription, this.expectedMatches, this.numMatches, timeEnd - timeStart, this.timeFirst - timeStart, this.response));
}
private void validateMatches(String testDescription) {
if (this.expectedMatches >= 0) {
Log.e("MyQueryTool", "test[" + testDescription + "] not returned expected result:" + this.expectedMatches + " but:" + this.numMatches + "," + (this.numMatches == this.expectedMatches));
}
}
public Attributes addQueryTag(int tag, String value) throws Exception {
VR vr = ElementDictionary.vrOf(tag, (String) null);
this.queryatts.setString(tag, vr, value);
return queryatts;
}
public Attributes clearQueryKeys() {
this.queryatts = new Attributes();
return queryatts;
}
public Attributes addAll(Attributes attrs) {
this.queryatts.addAll(attrs);
return queryatts;
}
public Attributes addReturnTag(int tag) throws Exception {
VR vr = ElementDictionary.vrOf(tag, (String) null);
this.queryatts.setNull(tag, vr);
return queryatts;
}
public void setExpectedMatches(int matches) {
this.expectedMatches = matches;
}
private DimseRSPHandler getDimseRSPHandler(int messageID) {
DimseRSPHandler rspHandler = new DimseRSPHandler(messageID) {
public void onDimseRSP(Association as, Attributes cmd, Attributes data) {
super.onDimseRSP(as, cmd, data);
onCFindResponse(cmd, data);
}
};
return rspHandler;
}
protected void onCFindResponse(Attributes cmd, Attributes data) {
if (this.numMatches == 0) {
this.timeFirst = System.currentTimeMillis();
}
int status = cmd.getInt(2304, -1);
if (Status.isPending(status)) {
this.response.add(data);
++this.numMatches;
}
}
public void init(TestResult result) {
this.result = result;
}
public TestResult getResult() {
return this.result;
}
public void setAeTitle(String aeTitle) {
this.aeTitle = aeTitle;
}
public String getQueryLevel() {
return this.queryLevel;
}
}