use dicom_toolkit_core::error::DcmResult;
use dicom_toolkit_data::DataSet;
use dicom_toolkit_dict::tags;
use crate::association::Association;
#[derive(Debug, Clone)]
pub struct GetRequest {
pub sop_class_uid: String,
pub query: Vec<u8>,
pub context_id: u8,
pub priority: u16,
}
#[derive(Debug, Clone)]
pub struct GetResponse {
pub status: u16,
pub remaining: Option<u16>,
pub completed: Option<u16>,
pub failed: Option<u16>,
pub warning: Option<u16>,
pub dataset: Option<Vec<u8>>,
}
#[derive(Debug, Clone)]
pub struct ReceivedInstance {
pub sop_class_uid: String,
pub sop_instance_uid: String,
pub dataset: Vec<u8>,
}
#[derive(Debug)]
pub struct GetResult {
pub responses: Vec<GetResponse>,
pub instances: Vec<ReceivedInstance>,
}
pub async fn c_get(assoc: &mut Association, req: GetRequest) -> DcmResult<GetResult> {
let msg_id = next_message_id();
let mut cmd = DataSet::new();
cmd.set_uid(tags::AFFECTED_SOP_CLASS_UID, &req.sop_class_uid);
cmd.set_u16(tags::COMMAND_FIELD, 0x0010); cmd.set_u16(tags::MESSAGE_ID, msg_id);
cmd.set_u16(tags::PRIORITY, req.priority);
cmd.set_u16(tags::COMMAND_DATA_SET_TYPE, 0x0000);
assoc.send_dimse_command(req.context_id, &cmd).await?;
assoc.send_dimse_data(req.context_id, &req.query).await?;
let mut responses = Vec::new();
let mut instances = Vec::new();
loop {
let (ctx_id, rsp_cmd) = assoc.recv_dimse_command().await?;
let command_field = rsp_cmd.get_u16(tags::COMMAND_FIELD).unwrap_or(0);
match command_field {
0x0001 => {
let sop_class = rsp_cmd
.get_string(tags::AFFECTED_SOP_CLASS_UID)
.unwrap_or_default()
.trim_end_matches('\0')
.to_string();
let sop_instance = rsp_cmd
.get_string(tags::AFFECTED_SOP_INSTANCE_UID)
.unwrap_or_default()
.trim_end_matches('\0')
.to_string();
let store_msg_id = rsp_cmd.get_u16(tags::MESSAGE_ID).unwrap_or(1);
let data = assoc.recv_dimse_data().await?;
instances.push(ReceivedInstance {
sop_class_uid: sop_class.clone(),
sop_instance_uid: sop_instance.clone(),
dataset: data,
});
let mut store_rsp = DataSet::new();
store_rsp.set_uid(tags::AFFECTED_SOP_CLASS_UID, &sop_class);
store_rsp.set_u16(tags::COMMAND_FIELD, 0x8001); store_rsp.set_u16(tags::MESSAGE_ID_BEING_RESPONDED_TO, store_msg_id);
store_rsp.set_u16(tags::COMMAND_DATA_SET_TYPE, 0x0101); store_rsp.set_uid(tags::AFFECTED_SOP_INSTANCE_UID, &sop_instance);
store_rsp.set_u16(tags::STATUS, 0x0000); assoc.send_dimse_command(ctx_id, &store_rsp).await?;
}
0x8010 => {
let status = rsp_cmd.get_u16(tags::STATUS).unwrap_or(0xFFFF);
let has_dataset = rsp_cmd
.get_u16(tags::COMMAND_DATA_SET_TYPE)
.map(|v| v != 0x0101)
.unwrap_or(false);
let dataset = if has_dataset {
Some(assoc.recv_dimse_data().await?)
} else {
None
};
responses.push(GetResponse {
status,
remaining: rsp_cmd.get_u16(tags::NUMBER_OF_REMAINING_SUB_OPERATIONS),
completed: rsp_cmd.get_u16(tags::NUMBER_OF_COMPLETED_SUB_OPERATIONS),
failed: rsp_cmd.get_u16(tags::NUMBER_OF_FAILED_SUB_OPERATIONS),
warning: rsp_cmd.get_u16(tags::NUMBER_OF_WARNING_SUB_OPERATIONS),
dataset,
});
let is_pending = status == 0xFF00 || status == 0xFF01;
if !is_pending {
break;
}
}
_ => {
break;
}
}
}
Ok(GetResult {
responses,
instances,
})
}
fn next_message_id() -> u16 {
use std::sync::atomic::{AtomicU16, Ordering};
static ID: AtomicU16 = AtomicU16::new(1);
ID.fetch_add(1, Ordering::Relaxed)
}
#[cfg(test)]
mod tests {
use crate::dimse;
use dicom_toolkit_data::DataSet;
use dicom_toolkit_dict::tags;
#[test]
fn c_get_rq_command_build() {
let mut cmd = DataSet::new();
cmd.set_uid(tags::AFFECTED_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.2.1.3");
cmd.set_u16(tags::COMMAND_FIELD, 0x0010); cmd.set_u16(tags::MESSAGE_ID, 1);
cmd.set_u16(tags::PRIORITY, 0);
cmd.set_u16(tags::COMMAND_DATA_SET_TYPE, 0x0000);
let bytes = dimse::encode_command_dataset(&cmd);
let decoded = dimse::decode_command_dataset(&bytes).unwrap();
assert_eq!(decoded.get_u16(tags::COMMAND_FIELD), Some(0x0010));
assert_eq!(decoded.get_u16(tags::PRIORITY), Some(0));
assert_eq!(decoded.get_u16(tags::COMMAND_DATA_SET_TYPE), Some(0x0000));
}
#[test]
fn c_get_rsp_pending_has_sub_operation_counts() {
let mut rsp = DataSet::new();
rsp.set_u16(tags::COMMAND_FIELD, 0x8010); rsp.set_u16(tags::MESSAGE_ID_BEING_RESPONDED_TO, 1);
rsp.set_u16(tags::COMMAND_DATA_SET_TYPE, 0x0101); rsp.set_u16(tags::STATUS, 0xFF00); rsp.set_u16(tags::NUMBER_OF_REMAINING_SUB_OPERATIONS, 5);
rsp.set_u16(tags::NUMBER_OF_COMPLETED_SUB_OPERATIONS, 2);
rsp.set_u16(tags::NUMBER_OF_FAILED_SUB_OPERATIONS, 0);
rsp.set_u16(tags::NUMBER_OF_WARNING_SUB_OPERATIONS, 0);
let bytes = dimse::encode_command_dataset(&rsp);
let decoded = dimse::decode_command_dataset(&bytes).unwrap();
assert_eq!(decoded.get_u16(tags::STATUS), Some(0xFF00));
assert_eq!(
decoded.get_u16(tags::NUMBER_OF_REMAINING_SUB_OPERATIONS),
Some(5)
);
assert_eq!(
decoded.get_u16(tags::NUMBER_OF_COMPLETED_SUB_OPERATIONS),
Some(2)
);
}
#[test]
fn c_get_rsp_final_success() {
let mut rsp = DataSet::new();
rsp.set_u16(tags::COMMAND_FIELD, 0x8010);
rsp.set_u16(tags::MESSAGE_ID_BEING_RESPONDED_TO, 1);
rsp.set_u16(tags::COMMAND_DATA_SET_TYPE, 0x0101);
rsp.set_u16(tags::STATUS, 0x0000);
rsp.set_u16(tags::NUMBER_OF_COMPLETED_SUB_OPERATIONS, 7);
let bytes = dimse::encode_command_dataset(&rsp);
let decoded = dimse::decode_command_dataset(&bytes).unwrap();
assert_eq!(decoded.get_u16(tags::STATUS), Some(0x0000));
assert_eq!(
decoded.get_u16(tags::NUMBER_OF_COMPLETED_SUB_OPERATIONS),
Some(7)
);
}
}