Skip to content
This repository has been archived by the owner on Nov 6, 2021. It is now read-only.

Commit

Permalink
go-netdicom: Match the SOP class list for C-{FIND,GET,MOVE} to dcmtk's
Browse files Browse the repository at this point in the history
Summary:
This may solve the problem where some dcm4chee servers bail out on C-GET
requests.

 More auto-generated stringer definitions.

Handle the combination of (VR=UN, length=undefined). It's converted to mean (VR=SQ, length=undefined).

Reviewers: jbredno

Reviewed By: jbredno

Differential Revision: https://phabricator.grailbio.com/D7827

fbshipit-source-id: 1490f59
  • Loading branch information
Yaz Saito authored and grailbot committed Nov 14, 2017
1 parent 0d850ab commit bb972d5
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 111 deletions.
1 change: 1 addition & 0 deletions dimse/dimse.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dimse

//go:generate ./generate_dimse_messages.py
//go:generate stringer -type StatusCode

// Implements message types defined in P3.7.
//
Expand Down
34 changes: 34 additions & 0 deletions dimse/statuscode_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions pdu/abortreasontype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pdu/presentationcontextresult_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pdu/rejectreasontype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pdu/rejectresulttype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pdu/sourcetype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pdu/type_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions qrlevel_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 37 additions & 46 deletions sampleclient/sampleclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ var (
storeFlag = flag.String("store", "", "If set, issue C-STORE to copy this file to the remote server")
aeTitleFlag = flag.String("ae-title", "testclient", "AE title of the client")
remoteAETitleFlag = flag.String("remote-ae-title", "testserver", "AE title of the server")
findFlag = flag.String("find", "", "If nonempty, issue a C-FIND.")
getFlag = flag.String("get", "", "If nonempty, issue a C-GET.")
findFlag = flag.Bool("find", false, "Issue a C-FIND.")
getFlag = flag.Bool("get", false, "Issue a C-GET.")
seriesFlag = flag.String("series", "", "Study series UID to retrieve in C-{FIND,GET}.")
studyFlag = flag.String("study", "", "Study instance UID to retrieve in C-{FIND,GET}.")
)

func newServiceUser(sopClasses []string) *netdicom.ServiceUser {
Expand Down Expand Up @@ -48,46 +50,13 @@ func cStore(inPath string) {
vlog.Infof("C-STORE finished successfully")
}

func cGet1(argStr string) {
su := newServiceUser(sopclass.QRGetClasses)
defer su.Release()
args := []*dicom.Element{
dicom.MustNewElement(dicomtag.QueryRetrieveLevel, "SERIES"),
dicom.MustNewElement(dicomtag.SeriesInstanceUID, "1.2.840.113681.183783719.1496821562.4928.395"),
func generateCFindElements() (netdicom.QRLevel, []*dicom.Element) {
if *seriesFlag != "" {
return netdicom.QRLevelSeries, []*dicom.Element{dicom.MustNewElement(dicomtag.SeriesInstanceUID, *seriesFlag)}
}
n := 0
err := su.CGet(netdicom.QRLevelPatient,
args,
func(transferSyntaxUID, sopClassUID, sopInstanceUID string, data []byte) dimse.Status {
vlog.Infof("%d: C-GET data; transfersyntax=%v, sopclass=%v, sopinstance=%v data %dB",
n, transferSyntaxUID, sopClassUID, sopInstanceUID, len(data))
n++
return dimse.Success
})
vlog.Infof("C-GET finished: %v", err)
}

func cGet(argStr string) {
su := newServiceUser(sopclass.QRGetClasses)
defer su.Release()
args := []*dicom.Element{
dicom.MustNewElement(dicomtag.StudyInstanceUID, "1.2.840.113681.183783719.1496821562.4928.389"),
if *studyFlag != "" {
return netdicom.QRLevelStudy, []*dicom.Element{dicom.MustNewElement(dicomtag.StudyInstanceUID, *studyFlag)}
}
n := 0
err := su.CGet(netdicom.QRLevelStudy,
args,
func(transferSyntaxUID, sopClassUID, sopInstanceUID string, data []byte) dimse.Status {
vlog.Infof("%d: C-GET data; transfersyntax=%v, sopclass=%v, sopinstance=%v data %dB",
n, transferSyntaxUID, sopClassUID, sopInstanceUID, len(data))
n++
return dimse.Success
})
vlog.Infof("C-GET finished: %v", err)
}

func cFind(argStr string) {
su := newServiceUser(sopclass.QRFindClasses)
defer su.Release()
args := []*dicom.Element{
dicom.MustNewElement(dicomtag.SpecificCharacterSet, "ISO_IR 100"),
dicom.MustNewElement(dicomtag.AccessionNumber, ""),
Expand All @@ -106,7 +75,29 @@ func cFind(argStr string) {
dicom.MustNewElement(dicomtag.ScheduledPerformingPhysicianName, ""),
dicom.MustNewElement(dicomtag.ScheduledProcedureStepStatus, ""))),
}
for result := range su.CFind(netdicom.QRLevelStudy, args) {
return netdicom.QRLevelPatient, args
}

func cGet() {
su := newServiceUser(sopclass.QRGetClasses)
defer su.Release()
qrLevel, args := generateCFindElements()
n := 0
err := su.CGet(qrLevel, args,
func(transferSyntaxUID, sopClassUID, sopInstanceUID string, data []byte) dimse.Status {
vlog.Infof("%d: C-GET data; transfersyntax=%v, sopclass=%v, sopinstance=%v data %dB",
n, transferSyntaxUID, sopClassUID, sopInstanceUID, len(data))
n++
return dimse.Success
})
vlog.Infof("C-GET finished: %v", err)
}

func cFind() {
su := newServiceUser(sopclass.QRFindClasses)
defer su.Release()
qrLevel, args := generateCFindElements()
for result := range su.CFind(qrLevel, args) {
if result.Err != nil {
vlog.Errorf("C-FIND error: %v", result.Err)
continue
Expand All @@ -124,11 +115,11 @@ func main() {

if *storeFlag != "" {
cStore(*storeFlag)
} else if *findFlag != "" {
cFind(*findFlag)
} else if *getFlag != "" {
cGet(*getFlag)
} else if *findFlag {
cFind()
} else if *getFlag {
cGet()
} else {
vlog.Fatal("Either -store or -find must be set")
vlog.Fatal("Either -store, -get, or -find must be set")
}
}
15 changes: 12 additions & 3 deletions serviceuser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package netdicom

// This file implements the ServiceUser (i.e., a DICOM DIMSE client) class.

//go:generate stringer -type QRLevel

import (
"fmt"
"net"
Expand Down Expand Up @@ -251,6 +253,9 @@ const (
// QRLevelStudy chooses Study-Root QR model. P3.4, C.3.2
QRLevelStudy

// QRLevelSeries chooses Study-Root QR model, but using "SERIES" QueryRetrieveLevel. P3.4, C.3.2
QRLevelSeries

qrOpCFind qrOpType = iota
qrOpCGet
qrOpCMove
Expand All @@ -277,7 +282,7 @@ func encodeQRPayload(opType qrOpType, qrLevel QRLevel, filter []*dicom.Element,
sopClassUID = dicomuid.PatientRootQRMove
}
qrLevelString = "PATIENT"
case QRLevelStudy:
case QRLevelStudy, QRLevelSeries:
switch opType {
case qrOpCFind:
sopClassUID = dicomuid.StudyRootQRFind
Expand All @@ -287,6 +292,9 @@ func encodeQRPayload(opType qrOpType, qrLevel QRLevel, filter []*dicom.Element,
sopClassUID = dicomuid.StudyRootQRMove
}
qrLevelString = "STUDY"
if qrLevel == QRLevelSeries {
qrLevelString = "SERIES"
}
default:
return contextManagerEntry{}, nil, fmt.Errorf("Invalid C-FIND QR lever: %d", qrLevel)
}
Expand Down Expand Up @@ -450,8 +458,9 @@ func (su *ServiceUser) CGet(qrLevel QRLevel, filter []*dicom.Element,
}
if resp.Status.Status != dimse.StatusPending {
if resp.Status.Status != 0 {
// TODO: report error if status!= 0
panic(resp)
e := fmt.Errorf("Received C-GET error: %+v", resp)
vlog.Error(e)
return e
}
break
}
Expand Down
Loading

0 comments on commit bb972d5

Please sign in to comment.