Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename nevent modules #34

Merged
merged 14 commits into from
Apr 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions LoopbackApplicationEntities.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[
{
"AETitle": "OST",
"IPAddr": "127.0.0.1",
"Port": 11112
},
{
"AETitle": "QRSCP",
"IPAddr": "127.0.0.1",
"Port": 11112
},
{
"AETitle": "TMS",
"IPAddr": "127.0.0.1",
"Port": 11114
},
{
"AETitle": "UPSSCP",
"IPAddr": "127.0.0.1",
"Port": 11114
},
{
"AETitle": "PPVS_SCP",
"IPAddr": "127.0.0.1",
"Port": 11115
},
{
"AETitle": "NEVENT_RECEIVER",
"IPAddr": "127.0.0.1",
"Port": 11115
},
{
"AETitle": "TDD",
"IPAddr": "127.0.0.1",
"Port": 11113
}
]
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ If the required packages are not shown (e.g. pydicom, pynetdicom), you may have
Try:
```console
export VIRTUAL_ENV=$(pyenv virtualenv-prefix)/envs/$(pyenv version | cut -f1 -d ' ')
poetry install
```


Expand All @@ -45,7 +46,7 @@ To generate .dcm files needed by ups enabled findscu
dump2dcm queryfile.dcmdump.txt queryfile.dcm
```

## A sample C-FIND SCU is available from pynetdicom (it has been enhanced with support for UPS): clone from https://github.com/pynetdicom/pynetdicom.git
## A sample C-FIND SCU is available from pynetdicom (it has been enhanced with support for UPS): clone from https://github.com/pydicom/pynetdicom
then use findscu to query the TMS (Treatment Management System):

in pynetdicom/apps
Expand Down Expand Up @@ -129,9 +130,9 @@ python nactionscu.py -T "1.2.826.0.1.3680043.8.498.23133079088775253446636289730
```
the above will request that upsscp (listening at 11114) change the state using the Transaction UID (-T) of the UPS with the shown UID to "COMPLETED". The Transaction UID here is not optional.

## A sample application for receiving notifications (N-EVENT-REPORT-RQ) is provided in neventscp.py
## A sample application for receiving notifications (N-EVENT-REPORT-RQ) is provided in nevent_receiver.py
```console
python neventscp.py --debug
python nevent_receiver.py --debug

```
which listens on port 11115 by default,
Expand All @@ -140,9 +141,9 @@ The application does not take specific actions when receiving an N-EVENT-REPORT



## A sample application for sending notifications is provided in neventscu.py (which can be run against neventscp.py mentioned above)
## A sample application for sending notifications is provided in nevent_sender.py (which can be run against nevent_receiver.py mentioned above)
```console
python neventscu.py 127.0.0.1 11115
python nevent_sender.py 127.0.0.1 11115

```
## A Qt/PySide6 based utility for generating RT Beams Delivery Instructions and Unified Procedure Step content
Expand Down
37 changes: 37 additions & 0 deletions tdwii_plus_examples/ApplicationEntities.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[
{
"AETitle": "OST",
"IPAddr": "127.0.0.1",
"Port": 11112
},
{
"AETitle": "QRSCP",
"IPAddr": "127.0.0.1",
"Port": 11112
},
{
"AETitle": "TMS",
"IPAddr": "127.0.0.1",
"Port": 11114
},
{
"AETitle": "UPSSCP",
"IPAddr": "127.0.0.1",
"Port": 11114
},
{
"AETitle": "PPVS_SCP",
"IPAddr": "127.0.0.1",
"Port": 11115
},
{
"AETitle": "NEVENT_RECEIVER",
"IPAddr": "127.0.0.1",
"Port": 11115
},
{
"AETitle": "TDD",
"IPAddr": "127.0.0.1",
"Port": 11113
}
]
Empty file.
2 changes: 1 addition & 1 deletion tdwii_plus_examples/TDWII_PPVS_subscriber/basescp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Tuple

import pydicom.config
from neventscp_handlers import handle_echo
from tdwii_plus_examples.TDWII_PPVS_subscriber.nevent_receiver_handlers import handle_echo
from pynetdicom import (
AE,
ALL_TRANSFER_SYNTAXES,
Expand Down
4 changes: 2 additions & 2 deletions tdwii_plus_examples/TDWII_PPVS_subscriber/echoscp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Tuple
from time import sleep
import pydicom.config
from neventscp_handlers import handle_echo
from tdwii_plus_examples.TDWII_PPVS_subscriber.nevent_receiver_handlers import handle_echo
from pynetdicom import (
AE,
ALL_TRANSFER_SYNTAXES,
Expand All @@ -20,7 +20,7 @@
from pynetdicom.sop_class import Verification
from pynetdicom.utils import set_ae

from basescp import BaseSCP
from tdwii_plus_examples.TDWII_PPVS_subscriber.basescp import BaseSCP

class EchoSCP:
def __init__(self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Tuple
from time import sleep
import pydicom.config
from neventscp_handlers import handle_echo, handle_nevent
from tdwii_plus_examples.TDWII_PPVS_subscriber.nevent_receiver_handlers import handle_echo, handle_nevent
from pynetdicom import (
AE,
ALL_TRANSFER_SYNTAXES,
Expand All @@ -20,8 +20,8 @@
from pynetdicom.sop_class import Verification
from pynetdicom.utils import set_ae

from basescp import BaseSCP
from echoscp import EchoSCP
from tdwii_plus_examples.TDWII_PPVS_subscriber.basescp import BaseSCP
from tdwii_plus_examples.TDWII_PPVS_subscriber.echoscp import EchoSCP

def nevent_cb(**kwargs):
logger = None
Expand Down Expand Up @@ -75,10 +75,10 @@ def nevent_cb(**kwargs):
if logger:
logger.warning(f"Unknown Event Type ID: {event_type_id}")

class NEventSCP(EchoSCP):
class NEventReceiver(EchoSCP):
def __init__(self,
nevent_callback=None,
ae_title:str="NEVENT_SCP",
ae_title:str="NEVENT_RECEIVER",
port:int=11115,
logger=None,
bind_address:str=""
Expand All @@ -104,6 +104,6 @@ def run(self):
BaseSCP.run(self)

if __name__ == '__main__':
my_scp = NEventSCP()
my_scp = NEventReceiver()
my_scp.run()
while True: sleep(100) # sleep forever
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Event handlers for neventscp.py"""
"""Event handlers for nevent_receiver.py"""

from pydicom import dcmread

Expand Down
1 change: 1 addition & 0 deletions tdwii_plus_examples/TDWII_PPVS_subscriber/ppvs_subscriber_widget.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python
# This Python file uses the following encoding: utf-8
import sys
from pathlib import Path
Expand Down
12 changes: 6 additions & 6 deletions tdwii_plus_examples/TDWII_PPVS_subscriber/ppvsscp.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@

from basescp import BaseSCP
from echoscp import EchoSCP
from neventscp import NEventSCP
from tdwii_plus_examples.TDWII_PPVS_subscriber.nevent_receiver import NEventReceiver
from storescp import StoreSCP

class PPVS_SCP(NEventSCP,StoreSCP):
class PPVS_SCP(NEventReceiver,StoreSCP):
def __init__(self,
ae_title:str="PPVS_SCP",
port:int=11112,
port:int=11115,
logger=None,
bind_address:str="",
storage_presentation_contexts=AllStoragePresentationContexts,
Expand All @@ -50,7 +50,7 @@ def __init__(self,
storage_presentation_contexts=storage_presentation_contexts,
transfer_syntaxes=transfer_syntaxes,
store_directory=store_directory)
NEventSCP.__init__(self,
NEventReceiver.__init__(self,
nevent_callback=nevent_callback,
ae_title=ae_title,
port=port,
Expand All @@ -61,15 +61,15 @@ def __init__(self,

def _add_contexts(self):
StoreSCP._add_contexts(self)
NEventSCP._add_contexts(self)
NEventReceiver._add_contexts(self)





def _add_handlers(self):
StoreSCP._add_handlers(self)
NEventSCP._add_handlers(self)
NEventReceiver._add_handlers(self)

def run(self):
# Listen for incoming association requests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
</rect>
</property>
<property name="title">
<string>group_box_aes_and_machine_name</string>
<string>Remote System Configuration</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
Expand Down Expand Up @@ -68,7 +68,7 @@
</rect>
</property>
<property name="title">
<string>ppvs_scp_group_box</string>
<string>PPVS Configuration</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
Expand Down Expand Up @@ -131,7 +131,7 @@
</rect>
</property>
<property name="title">
<string>group_box_cfind_request_and_response</string>
<string>UPS Query Specification</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
Expand Down Expand Up @@ -210,7 +210,7 @@
</rect>
</property>
<property name="title">
<string>group_box_get_reference_data</string>
<string>Reference Data Retrieval</string>
</property>
<widget class="QPushButton" name="get_listed_inputs_push_button">
<property name="geometry">
Expand Down
46 changes: 36 additions & 10 deletions tdwii_plus_examples/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from upsdb import Instance, InvalidIdentifier, add_instance, search
import tdwii_config

_SERVICE_STATUS = {
"SCHEDULED": {
Expand Down Expand Up @@ -55,6 +56,8 @@
_global_subscribers = dict() # AE Title and delection lock boolean "TRUE" or "FALSE" is the text representation
_filtered_subscribers = dict() # AE Title and the Dataset acting as the query filter

REMOTE_AE_CONFIG_FILE = "ApplicationEntities.json"
tdwii_config.load_ae_config(REMOTE_AE_CONFIG_FILE)

def _add_global_subscriber(subscriber_ae_title: str, deletion_lock: bool = False, logger=None):
if subscriber_ae_title not in _global_subscribers.keys():
Expand Down Expand Up @@ -610,7 +613,7 @@ def handle_nset(event, db_path, cli_config, logger):
Parameters
----------
event : pynetdicom.events.Event
The C-GET request :class:`~pynetdicom.events.Event`.
The N-SET request :class:`~pynetdicom.events.Event`.
db_path : str
The database path to use with create_engine().
cli_config : dict
Expand All @@ -623,13 +626,13 @@ def handle_nset(event, db_path, cli_config, logger):
int
The number of sub-operations required to complete the request.
int or pydicom.dataset.Dataset, pydicom.dataset.Dataset or None
The C-GET response's *Status* and if the *Status* is pending then
The N-SET response's *Status* and if the *Status* is pending then
the dataset to be sent, otherwise ``None``.
"""
requestor = event.assoc.requestor
timestamp = event.timestamp.strftime("%Y-%m-%d %H:%M:%S")
addr, port = requestor.address, requestor.port
logger.info(f"Received C-GET request from {addr}:{port} at {timestamp}")
logger.info(f"Received N-SET request from {addr}:{port} at {timestamp}")

model = event.request.AffectedSOPClassUID

Expand All @@ -642,7 +645,7 @@ def handle_nset(event, db_path, cli_config, logger):
matches = search(model, event.identifier, session)
except InvalidIdentifier as exc:
session.rollback()
logger.error("Invalid C-GET Identifier received")
logger.error("Invalid N-SET Identifier received")
logger.error(str(exc))
yield 0xA900, None
return
Expand Down Expand Up @@ -820,19 +823,42 @@ def handle_ncreate(event, storage_dir, db_path, cli_config, logger):
for globalsubscriber in _global_subscribers:
# Request association with subscriber
ae = AE(ae_title=acceptor.ae_title)
# hard code for the moment, deal with configuration of AE's soon
if (not globalsubscriber in tdwii_config.known_ae_ipaddr):
logger.error(f"{globalsubscriber} missing IP Address configuration in {REMOTE_AE_CONFIG_FILE}")
continue

if (not globalsubscriber in tdwii_config.known_ae_port):
logger.error(f"{globalsubscriber} missing Port configuration in {REMOTE_AE_CONFIG_FILE}")
continue

subscriber_ip_addr = tdwii_config.known_ae_ipaddr[globalsubscriber]
subscriber_port = tdwii_config.known_ae_port[globalsubscriber]
assoc = ae.associate(
"127.0.0.1",
11112,
subscriber_ip_addr,
subscriber_port,
contexts=UnifiedProcedurePresentationContexts,
ae_title=globalsubscriber,
max_pdu=16382,
)

if assoc.is_established:
message_id=0
try:
logger.info(f"Send UPS State Report: {ds.SOPInstanceUID}, {ds.ProcedureStepState}")
assoc.send_n_event_report(event_info, event_type, UnifiedProcedureStepPush, ds.SOPInstanceUID)
if (event_type==1):
logger.info(f"Sending UPS State Report: {ds.SOPInstanceUID}, {ds.ProcedureStepState}")
message_id +=1
assoc.send_n_event_report(event_info, event_type, UnifiedProcedureStepPush, ds.SOPInstanceUID, message_id)
elif (event_type==5): # The assignment took place at the time of creation
# notify of creation first, i.e. event type == 1
logger.info(f"Sending UPS State Report: {ds.SOPInstanceUID}, {ds.ProcedureStepState}")
message_id +=1
assoc.send_n_event_report(event_info, 1, UnifiedProcedureStepPush, ds.SOPInstanceUID, message_id)
# if the assignment happened after the creation (e.g. via N-SET or internal change in a TMS)
# then *only* send an N-EVENT-REPORT regarding the UPS Assignment
logger.info(f"Sending UPS Assignment: {ds.ScheduledStationNameCodeSequence}")
message_id +=1
assoc.send_n_event_report(event_info, event_type, UnifiedProcedureStepPush, ds.SOPInstanceUID, message_id)

logger.info(f"Notified global subscriber: {globalsubscriber}")
except InvalidDicomError:
logger.error("Bad DICOM: ")
Expand All @@ -845,6 +871,6 @@ def handle_ncreate(event, storage_dir, db_path, cli_config, logger):
assoc.release()

else:
logger.error(f"Failed to establish assocation with subscriber: {globalsubscriber}")
logger.error(f"Failed to establish association with subscriber: {globalsubscriber}")

return 0x0000, ds
Loading
Loading