573 {
575 multimodal_pacs_server server(port);
576 REQUIRE(server.initialize());
577 REQUIRE(server.start());
578
579 SECTION("Scenario 1: Complete patient journey - CT and MR") {
580
581
582
584 const std::string
patient_name =
"JOURNEY^PATIENT^COMPLETE";
586
587
589 ct_worklist.set_string(tags::study_instance_uid, vr_type::UI, study_uid);
590 server.add_worklist_item(ct_worklist);
591
593 mr_worklist.set_string(tags::study_instance_uid, vr_type::UI, study_uid);
594 server.add_worklist_item(mr_worklist);
595
596
597
599 std::vector<dicom_dataset> ct_images;
600 for (int i = 0; i < 3; ++i) {
602 ct.set_string(tags::patient_id, vr_type::LO, patient_id);
603 ct.set_string(tags::patient_name, vr_type::PN, patient_name);
604 ct.set_string(tags::series_instance_uid, vr_type::UI, ct_series_uid);
605 ct.set_string(tags::sop_instance_uid, vr_type::UI,
generate_uid());
606 ct.set_string(tags::instance_number, vr_type::IS, std::to_string(i + 1));
607 ct_images.push_back(std::move(
ct));
608 }
609
610
611 for (
const auto&
image : ct_images) {
612 REQUIRE(store_to_pacs(
image,
"127.0.0.1", port, server.ae_title(),
"CT_SCANNER"));
613 }
614
615
616
618 std::vector<dicom_dataset> mr_images;
619 for (int i = 0; i < 2; ++i) {
621 mr.set_string(tags::patient_id, vr_type::LO, patient_id);
622 mr.set_string(tags::patient_name, vr_type::PN, patient_name);
623 mr.set_string(tags::series_instance_uid, vr_type::UI, mr_series_uid);
624 mr.set_string(tags::sop_instance_uid, vr_type::UI,
generate_uid());
625 mr.set_string(tags::instance_number, vr_type::IS, std::to_string(i + 1));
626 mr_images.push_back(std::move(
mr));
627 }
628
629
630 for (
const auto&
image : mr_images) {
631 REQUIRE(store_to_pacs(
image,
"127.0.0.1", port, server.ae_title(),
"MR_SCANNER"));
632 }
633
634
635 auto verifier = server.get_verifier();
636
637
638 REQUIRE(verifier.verify_patient_exists(patient_id));
639
640
641 REQUIRE(verifier.verify_modalities_in_study(study_uid, {"CT", "MR"}));
642
643
644 REQUIRE(verifier.verify_series_count(study_uid, 2));
645
646
647 REQUIRE(verifier.verify_image_count(ct_series_uid, 3));
648 REQUIRE(verifier.verify_image_count(mr_series_uid, 2));
649
650
651 REQUIRE(verifier.verify_unique_uids(study_uid));
652
653
654 REQUIRE(server.stored_count() == 5);
655 REQUIRE(server.error_count() == 0);
656 }
657
658 SECTION("Scenario 2: Interventional workflow - XA cine acquisition") {
659
660
661
662 const std::string
patient_id =
"INTERVENT001";
663 const std::string
patient_name =
"INTERVENTIONAL^PATIENT";
665
666
668 xa_cine.set_string(tags::patient_id, vr_type::LO, patient_id);
669 xa_cine.set_string(tags::patient_name, vr_type::PN, patient_name);
670 xa_cine.set_string(tags::series_description, vr_type::LO, "Coronary Angiography Run 1");
671
672 auto xa_series_uid_opt = xa_cine.get_string(tags::series_instance_uid);
673 REQUIRE(!xa_series_uid_opt.empty());
674 auto xa_series_uid = std::string(xa_series_uid_opt);
675
676
677 REQUIRE(store_to_pacs(xa_cine, "127.0.0.1", port, server.ae_title(), "XA_CATH_LAB"));
678
679
681 xa_cine_2.set_string(tags::patient_id, vr_type::LO, patient_id);
682 xa_cine_2.set_string(tags::patient_name, vr_type::PN, patient_name);
683 xa_cine_2.set_string(tags::series_description, vr_type::LO, "Coronary Angiography Run 2");
684
685
686 REQUIRE(store_to_pacs(xa_cine_2, "127.0.0.1", port, server.ae_title(), "XA_CATH_LAB"));
687
688
689 auto verifier = server.get_verifier();
690
691 REQUIRE(verifier.verify_patient_exists(patient_id));
692 REQUIRE(verifier.verify_modalities_in_study(study_uid, {"XA"}));
693 REQUIRE(verifier.verify_series_count(study_uid, 2));
694 REQUIRE(verifier.verify_unique_uids(study_uid));
695 }
696
697 SECTION("Scenario 3: Emergency multi-modality - Trauma workflow") {
698
699
700
702 const std::string
patient_name =
"TRAUMA^PATIENT^EMERGENCY";
704
705
707 for (int i = 0; i < 5; ++i) {
709 ct.set_string(tags::patient_id, vr_type::LO, patient_id);
710 ct.set_string(tags::patient_name, vr_type::PN, patient_name);
711 ct.set_string(tags::series_instance_uid, vr_type::UI, initial_ct_series);
712 ct.set_string(tags::series_description, vr_type::LO,
"Initial Trauma CT");
713 ct.set_string(tags::sop_instance_uid, vr_type::UI,
generate_uid());
714 REQUIRE(store_to_pacs(
ct,
"127.0.0.1", port, server.ae_title(),
"CT_EMERGENCY"));
715 }
716
717
719 xa_intervention.set_string(tags::patient_id, vr_type::LO, patient_id);
720 xa_intervention.set_string(tags::patient_name, vr_type::PN, patient_name);
721 xa_intervention.set_string(tags::series_description, vr_type::LO, "Emergency Embolization");
722 REQUIRE(store_to_pacs(xa_intervention, "127.0.0.1", port, server.ae_title(), "XA_EMERGENCY"));
723
724
726 for (int i = 0; i < 3; ++i) {
728 ct.set_string(tags::patient_id, vr_type::LO, patient_id);
729 ct.set_string(tags::patient_name, vr_type::PN, patient_name);
730 ct.set_string(tags::series_instance_uid, vr_type::UI, followup_ct_series);
731 ct.set_string(tags::series_description, vr_type::LO,
"Follow-up CT");
732 ct.set_string(tags::sop_instance_uid, vr_type::UI,
generate_uid());
733 REQUIRE(store_to_pacs(
ct,
"127.0.0.1", port, server.ae_title(),
"CT_EMERGENCY"));
734 }
735
736
737 auto verifier = server.get_verifier();
738
739 REQUIRE(verifier.verify_patient_exists(patient_id));
740 REQUIRE(verifier.verify_modalities_in_study(study_uid, {"CT", "XA"}));
741 REQUIRE(verifier.verify_series_count(study_uid, 3));
742 REQUIRE(verifier.verify_image_count(initial_ct_series, 5));
743 REQUIRE(verifier.verify_image_count(followup_ct_series, 3));
744 REQUIRE(verifier.verify_unique_uids(study_uid));
745
746
747 size_t total_instances = verifier.get_instance_count(study_uid);
748 REQUIRE(total_instances == 9);
749 }
750
751 SECTION("Scenario 4: Concurrent modality operations") {
752
753
754
756 const std::string
patient_id =
"CONCURRENT001";
758
759 std::vector<dicom_dataset> all_datasets;
760 std::vector<std::string> series_uids;
761
762
763 const std::vector<std::pair<std::string, int>> modality_counts = {
764 {"CT", 5},
765 {"MR", 4},
766 {"XA", 2},
767 {"US", 3}
768 };
769
770 for (const auto& [modality, count] : modality_counts) {
772 series_uids.push_back(series_uid);
773
774 for (int i = 0; i < count; ++i) {
776 if (modality == "CT") {
778 } else if (modality == "MR") {
780 } else if (modality == "XA") {
782 } else if (modality == "US") {
784 }
785
786 ds.
set_string(tags::patient_id, vr_type::LO, patient_id);
787 ds.
set_string(tags::patient_name, vr_type::PN, patient_name);
788 ds.
set_string(tags::series_instance_uid, vr_type::UI, series_uid);
790 all_datasets.push_back(std::move(ds));
791 }
792 }
793
794
795 size_t success = parallel_store(server, all_datasets);
796
797
798 std::this_thread::sleep_for(std::chrono::milliseconds{100});
799
800
801 REQUIRE(
success == all_datasets.size());
802
803
804 auto verifier = server.get_verifier();
805
806 REQUIRE(verifier.verify_patient_exists(patient_id));
807 REQUIRE(verifier.verify_modalities_in_study(study_uid, {"CT", "MR", "XA", "US"}));
808 REQUIRE(verifier.verify_series_count(study_uid, 4));
809 REQUIRE(verifier.verify_unique_uids(study_uid));
810
811
812 REQUIRE(server.error_count() == 0);
813
814
815 size_t expected_total = 5 + 4 + 2 + 3;
816 REQUIRE(verifier.get_instance_count(study_uid) == expected_total);
817 }
818
819 server.stop();
820}
void set_string(dicom_tag tag, encoding::vr_type vr, std::string_view value)
Set a string value for the given tag.
static core::dicom_dataset worklist(const std::string &patient_id="", const std::string &modality="CT")
Generate a worklist item dataset.
static core::dicom_dataset mr(const std::string &study_uid="")
Generate an MR Image dataset.
static core::dicom_dataset ct(const std::string &study_uid="")
Generate a CT Image dataset.
static core::dicom_dataset us(const std::string &study_uid="")
Generate a single-frame US Image dataset.
static core::dicom_dataset xa(const std::string &study_uid="")
Generate a single-frame XA Image dataset.
static core::dicom_dataset xa_cine(uint32_t frames=30, const std::string &study_uid="")
Generate a multi-frame XA cine dataset.
uint16_t find_available_port(uint16_t start=default_test_port, int max_attempts=200)
Find an available port for testing.
std::string generate_uid(const std::string &root="1.2.826.0.1.3680043.9.9999")
Generate a unique UID for testing.
@ image
Image (Instance) level - query instance information.