60 using namespace network::dimse;
62 if (request.
command() != command_field::n_action_rq) {
65 "Expected N-ACTION-RQ but received " +
73 return "UPS Watch SCP";
81 const std::string& workitem_uid,
82 const std::string& new_state) {
93 const std::string& workitem_uid,
94 const std::string& reason) {
97 if (!reason.empty()) {
107 const std::string& workitem_uid,
108 int progress_percent) {
149 using namespace network::dimse;
153 if (!action_type.has_value()) {
156 "Missing Action Type ID in N-ACTION request");
161 if (sop_instance_uid.empty()) {
165 uint16_t action_id = action_type.value();
171 assoc, context_id, message_id, sop_instance_uid);
175 assoc, context_id, message_id, sop_instance_uid);
179 assoc, context_id, message_id);
183 assoc, context_id, message_id,
184 sop_instance_uid, action_id,
185 status_error_no_such_action_type);
197 const std::string& sop_instance_uid) {
199 using namespace network::dimse;
204 "No subscribe handler configured for UPS Watch SCP");
208 std::string subscriber_ae{assoc.
calling_ae()};
211 std::string workitem_uid;
213 workitem_uid = sop_instance_uid;
217 bool deletion_lock =
false;
219 logger_->debug(
"UPS Watch: Subscribe request from " + subscriber_ae +
220 (workitem_uid.empty() ?
" (global)" :
221 " for workitem " + workitem_uid));
224 if (result.is_err()) {
226 assoc, context_id, message_id,
228 status_error_unable_to_process);
233 logger_->info(
"UPS Watch: Subscription created for " + subscriber_ae);
236 assoc, context_id, message_id,
249 const std::string& sop_instance_uid) {
251 using namespace network::dimse;
256 "No unsubscribe handler configured for UPS Watch SCP");
259 std::string subscriber_ae{assoc.
calling_ae()};
262 std::string workitem_uid;
264 workitem_uid = sop_instance_uid;
267 logger_->debug(
"UPS Watch: Unsubscribe request from " + subscriber_ae +
268 (workitem_uid.empty() ?
" (global)" :
269 " for workitem " + workitem_uid));
272 if (result.is_err()) {
274 assoc, context_id, message_id,
276 status_error_unable_to_process);
281 logger_->info(
"UPS Watch: Subscription removed for " + subscriber_ae);
284 assoc, context_id, message_id,
296 uint16_t message_id) {
298 using namespace network::dimse;
303 "No unsubscribe handler configured for UPS Watch SCP");
306 std::string subscriber_ae{assoc.
calling_ae()};
308 logger_->debug(
"UPS Watch: Suspend global subscription from " +
313 if (result.is_err()) {
315 assoc, context_id, message_id,
318 status_error_unable_to_process);
323 logger_->info(
"UPS Watch: Global subscription suspended for " +
327 assoc, context_id, message_id,
341 const std::string& sop_instance_uid,
342 uint16_t action_type_id,
345 using namespace network::dimse;
347 auto response = make_n_action_rsp(
354 return assoc.
send_dimse(context_id, response);
362 uint16_t event_type_id,
363 const std::string& workitem_uid,
372 if (subscribers_result.is_err()) {
373 logger_->warn(
"UPS Watch: Failed to get subscribers for " +
378 const auto& subscribers = subscribers_result.value();
380 for (
const auto& subscriber_ae : subscribers) {
382 workitem_uid, event_info);
386 if (!subscribers.empty()) {
387 logger_->info(
"UPS Watch: Dispatched event type " +
388 std::to_string(event_type_id) +
" to " +
389 std::to_string(subscribers.size()) +
390 " subscribers for workitem " + workitem_uid);
void set_string(dicom_tag tag, encoding::vr_type vr, std::string_view value)
Set a string value for the given tag.
Result< std::monostate > send_dimse(uint8_t context_id, const dimse::dimse_message &msg)
Send a DIMSE message.
std::string_view calling_ae() const noexcept
Get calling AE title.
auto message_id() const noexcept -> uint16_t
Get the message ID.
auto requested_sop_instance_uid() const -> std::string
Get the Requested SOP Instance UID (for N-SET, N-GET, N-ACTION, N-DELETE)
auto action_type_id() const -> std::optional< uint16_t >
Get the Action Type ID (for N-ACTION)
auto affected_sop_instance_uid() const -> std::string
Get the Affected SOP Instance UID.
auto command() const noexcept -> command_field
Get the command field.
std::shared_ptr< di::ILogger > logger_
Logger instance for service logging.
ups_event_callback event_callback_
ups_unsubscribe_handler unsubscribe_handler_
std::atomic< size_t > subscriptions_removed_
void set_subscribe_handler(ups_subscribe_handler handler)
void dispatch_event(uint16_t event_type_id, const std::string &workitem_uid, const core::dicom_dataset &event_info)
ups_watch_scp(std::shared_ptr< di::ILogger > logger=nullptr)
Construct UPS Watch SCP with optional logger.
std::string_view service_name() const noexcept override
Get the service name for logging/debugging.
void notify_state_change(const std::string &workitem_uid, const std::string &new_state)
Notify subscribers of a workitem state change.
void notify_cancel_requested(const std::string &workitem_uid, const std::string &reason)
Notify subscribers of a cancel request.
std::atomic< size_t > events_sent_
network::Result< std::monostate > handle_suspend_global(network::association &assoc, uint8_t context_id, uint16_t message_id)
size_t events_sent() const noexcept
ups_get_subscribers_handler get_subscribers_handler_
void notify_progress(const std::string &workitem_uid, int progress_percent)
Notify subscribers of progress update.
void reset_statistics() noexcept
std::atomic< size_t > subscriptions_created_
ups_subscribe_handler subscribe_handler_
network::Result< std::monostate > handle_subscribe(network::association &assoc, uint8_t context_id, uint16_t message_id, const std::string &sop_instance_uid)
network::Result< std::monostate > send_n_action_response(network::association &assoc, uint8_t context_id, uint16_t message_id, const std::string &sop_instance_uid, uint16_t action_type_id, network::dimse::status_code status)
network::Result< std::monostate > handle_n_action(network::association &assoc, uint8_t context_id, const network::dimse::dimse_message &request)
size_t subscriptions_created() const noexcept
network::Result< std::monostate > handle_message(network::association &assoc, uint8_t context_id, const network::dimse::dimse_message &request) override
Handle an incoming DIMSE message.
void set_event_callback(ups_event_callback callback)
size_t subscriptions_removed() const noexcept
void set_unsubscribe_handler(ups_unsubscribe_handler handler)
network::Result< std::monostate > handle_unsubscribe(network::association &assoc, uint8_t context_id, uint16_t message_id, const std::string &sop_instance_uid)
void set_get_subscribers_handler(ups_get_subscribers_handler handler)
std::vector< std::string > supported_sop_classes() const override
Get the list of SOP Class UIDs supported by this service.
DIMSE command field enumeration.
Compile-time constants for commonly used DICOM tags.
@ DS
Decimal String (16 chars max)
@ CS
Code String (16 chars max, uppercase + digits + space + underscore)
@ LT
Long Text (10240 chars max)
constexpr int ups_unexpected_command
constexpr int ups_invalid_action_type
constexpr int ups_handler_not_set
uint16_t status_code
DIMSE status code type alias.
std::function< network::Result< std::monostate >( const std::string &subscriber_ae, const std::string &workitem_uid, bool deletion_lock)> ups_subscribe_handler
Subscribe handler function type.
constexpr uint16_t ups_event_state_report
Event Type 1: UPS State Report — workitem state changed.
constexpr uint16_t ups_event_progress_report
Event Type 3: UPS Progress Report — progress percentage update.
constexpr std::string_view ups_watch_sop_class_uid
UPS Watch SOP Class UID (PS3.4 Table CC.2-1)
std::function< network::Result< std::monostate >( const std::string &subscriber_ae, const std::string &workitem_uid)> ups_unsubscribe_handler
Unsubscribe handler function type.
constexpr uint16_t ups_watch_action_suspend_global
N-ACTION Type 5: Suspend Global Subscription (PS3.4 CC.2.3.5)
constexpr std::string_view ups_global_subscription_instance_uid
UPS Global Subscription SOP Instance UID Used as the SOP Instance UID for global (non-workitem-specif...
auto to_string(mpps_status status) -> std::string_view
Convert mpps_status to DICOM string representation.
constexpr uint16_t ups_event_cancel_requested
Event Type 2: UPS Cancel Requested — performer should stop processing.
std::function< network::Result< std::vector< std::string > >( const std::string &workitem_uid)> ups_get_subscribers_handler
Get subscribers handler function type.
std::function< void( const std::string &subscriber_ae, uint16_t event_type_id, const std::string &workitem_uid, const core::dicom_dataset &event_info)> ups_event_callback
Event notification callback type.
constexpr uint16_t ups_watch_action_unsubscribe
N-ACTION Type 4: Unsubscribe from Receiving UPS Event Reports (PS3.4 CC.2.3.4)
constexpr uint16_t ups_watch_action_subscribe
N-ACTION Type 3: Subscribe to Receive UPS Event Reports (PS3.4 CC.2.3.3)
VoidResult pacs_void_error(int code, const std::string &message, const std::string &details="")
Create a PACS void error result.
Result<T> type aliases and helpers for PACS system.
DICOM UPS (Unified Procedure Step) Push SCP service.
DICOM UPS Watch SCP service (subscription and event notification)