comparison PalanthirServer/Internals/StoreScp.cpp @ 45:33d67e1ab173

r
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 05 Sep 2012 13:24:59 +0200
parents PalantirServer/Internals/StoreScp.cpp@3959d33612cc
children a15e90e5d6fc
comparison
equal deleted inserted replaced
43:9be852ad33d2 45:33d67e1ab173
1 /**
2 * Palantir - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012 Medical Physics Department, CHU of Liege,
4 * Belgium
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
20
21 #include "StoreScp.h"
22
23 #include "../FromDcmtkBridge.h"
24 #include "../ToDcmtkBridge.h"
25 #include "../../Core/PalantirException.h"
26
27 #include <dcmtk/dcmdata/dcfilefo.h>
28 #include <dcmtk/dcmdata/dcmetinf.h>
29 #include <dcmtk/dcmdata/dcostrmb.h>
30 #include <dcmtk/dcmdata/dcdeftag.h>
31 #include <dcmtk/dcmnet/diutil.h>
32
33
34 namespace Palantir
35 {
36 namespace Internals
37 {
38 extern OFLogger Logger;
39 }
40
41
42 namespace
43 {
44 struct StoreCallbackData
45 {
46 IStoreRequestHandler* handler;
47 const char* distantAET;
48 const char* modality;
49 const char* affectedSOPInstanceUID;
50 uint32_t messageID;
51 };
52
53
54 static int SaveToMemoryBuffer(DcmDataset* dataSet,
55 std::vector<uint8_t>& buffer)
56 {
57 // Determine the transfer syntax which shall be used to write the
58 // information to the file. We always switch to the Little Endian
59 // syntax, with explicit length.
60
61 // http://support.dcmtk.org/docs/dcxfer_8h-source.html
62 E_TransferSyntax xfer = EXS_LittleEndianExplicit;
63 E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength;
64
65 uint32_t s = dataSet->getLength(xfer, encodingType);
66
67 buffer.resize(s);
68 DcmOutputBufferStream ob(&buffer[0], s);
69
70 dataSet->transferInit();
71 OFCondition c = dataSet->write(ob, xfer, encodingType, NULL,
72 /*opt_groupLength*/ EGL_recalcGL,
73 /*opt_paddingType*/ EPD_withoutPadding);
74 dataSet->transferEnd();
75 if (c.good())
76 {
77 return 0;
78 }
79 else
80 {
81 buffer.clear();
82 return -1;
83 }
84
85 #if 0
86 OFCondition cond = cbdata->dcmff->saveFile(fileName.c_str(), xfer,
87 encodingType,
88 /*opt_groupLength*/ EGL_recalcGL,
89 /*opt_paddingType*/ EPD_withoutPadding,
90 OFstatic_cast(Uint32, /*opt_filepad*/ 0),
91 OFstatic_cast(Uint32, /*opt_itempad*/ 0),
92 (opt_useMetaheader) ? EWM_fileformat : EWM_dataset);
93 #endif
94 }
95
96
97 static void
98 storeScpCallback(
99 void *callbackData,
100 T_DIMSE_StoreProgress *progress,
101 T_DIMSE_C_StoreRQ *req,
102 char * /*imageFileName*/, DcmDataset **imageDataSet,
103 T_DIMSE_C_StoreRSP *rsp,
104 DcmDataset **statusDetail)
105 /*
106 * This function.is used to indicate progress when storescp receives instance data over the
107 * network. On the final call to this function (identified by progress->state == DIMSE_StoreEnd)
108 * this function will store the data set which was received over the network to a file.
109 * Earlier calls to this function will simply cause some information to be dumped to stdout.
110 *
111 * Parameters:
112 * callbackData - [in] data for this callback function
113 * progress - [in] The state of progress. (identifies if this is the initial or final call
114 * to this function, or a call in between these two calls.
115 * req - [in] The original store request message.
116 * imageFileName - [in] The path to and name of the file the information shall be written to.
117 * imageDataSet - [in] The data set which shall be stored in the image file
118 * rsp - [inout] the C-STORE-RSP message (will be sent after the call to this function)
119 * statusDetail - [inout] This variable can be used to capture detailed information with regard to
120 * the status information which is captured in the status element (0000,0900). Note
121 * that this function does specify any such information, the pointer will be set to NULL.
122 */
123 {
124 StoreCallbackData *cbdata = OFstatic_cast(StoreCallbackData *, callbackData);
125
126 DIC_UI sopClass;
127 DIC_UI sopInstance;
128
129 // if this is the final call of this function, save the data which was received to a file
130 // (note that we could also save the image somewhere else, put it in database, etc.)
131 if (progress->state == DIMSE_StoreEnd)
132 {
133 OFString tmpStr;
134
135 // do not send status detail information
136 *statusDetail = NULL;
137
138 // Concerning the following line: an appropriate status code is already set in the resp structure,
139 // it need not be success. For example, if the caller has already detected an out of resources problem
140 // then the status will reflect this. The callback function is still called to allow cleanup.
141 //rsp->DimseStatus = STATUS_Success;
142
143 // we want to write the received information to a file only if this information
144 // is present and the options opt_bitPreserving and opt_ignore are not set.
145 if ((imageDataSet != NULL) && (*imageDataSet != NULL))
146 {
147 DicomMap summary;
148 Json::Value dicomJson;
149 std::vector<uint8_t> buffer;
150
151 try
152 {
153 FromDcmtkBridge::Convert(summary, **imageDataSet);
154 FromDcmtkBridge::ToJson(dicomJson, **imageDataSet);
155
156 if (SaveToMemoryBuffer(*imageDataSet, buffer) < 0)
157 {
158 OFLOG_ERROR(Internals::Logger, "cannot write DICOM file to memory");
159 rsp->DimseStatus = STATUS_STORE_Refused_OutOfResources;
160 }
161 }
162 catch (...)
163 {
164 rsp->DimseStatus = STATUS_STORE_Refused_OutOfResources;
165 }
166
167 // check the image to make sure it is consistent, i.e. that its sopClass and sopInstance correspond
168 // to those mentioned in the request. If not, set the status in the response message variable.
169 if ((rsp->DimseStatus == STATUS_Success))
170 {
171 // which SOP class and SOP instance ?
172 if (!DU_findSOPClassAndInstanceInDataSet(*imageDataSet, sopClass, sopInstance, /*opt_correctUIDPadding*/ OFFalse))
173 {
174 //OFLOG_ERROR(Internals::Logger, "bad DICOM file: " << fileName);
175 rsp->DimseStatus = STATUS_STORE_Error_CannotUnderstand;
176 }
177 else if (strcmp(sopClass, req->AffectedSOPClassUID) != 0)
178 {
179 rsp->DimseStatus = STATUS_STORE_Error_DataSetDoesNotMatchSOPClass;
180 }
181 else if (strcmp(sopInstance, req->AffectedSOPInstanceUID) != 0)
182 {
183 rsp->DimseStatus = STATUS_STORE_Error_DataSetDoesNotMatchSOPClass;
184 }
185 else
186 {
187 try
188 {
189 cbdata->handler->Handle(buffer, summary, dicomJson, cbdata->distantAET);
190 }
191 catch (PalantirException& e)
192 {
193 rsp->DimseStatus = STATUS_STORE_Refused_OutOfResources;
194 OFLOG_ERROR(Internals::Logger, "Exception while storing DICOM: " << e.What());
195 }
196 }
197 }
198 }
199 }
200 }
201 }
202
203 /*
204 * This function processes a DIMSE C-STORE-RQ commmand that was
205 * received over the network connection.
206 *
207 * Parameters:
208 * assoc - [in] The association (network connection to another DICOM application).
209 * msg - [in] The DIMSE C-STORE-RQ message that was received.
210 * presID - [in] The ID of the presentation context which was specified in the PDV which contained
211 * the DIMSE command.
212 */
213 OFCondition Internals::storeScp(T_ASC_Association * assoc,
214 T_DIMSE_Message * msg,
215 T_ASC_PresentationContextID presID,
216 IStoreRequestHandler& handler)
217 {
218 OFCondition cond = EC_Normal;
219 T_DIMSE_C_StoreRQ *req;
220
221 // assign the actual information of the C-STORE-RQ command to a local variable
222 req = &msg->msg.CStoreRQ;
223
224 // intialize some variables
225 StoreCallbackData callbackData;
226 callbackData.handler = &handler;
227 callbackData.modality = dcmSOPClassUIDToModality(req->AffectedSOPClassUID, "UNKNOWN");
228 callbackData.affectedSOPInstanceUID = req->AffectedSOPInstanceUID;
229 callbackData.messageID = req->MessageID;
230 if (assoc && assoc->params)
231 {
232 callbackData.distantAET = assoc->params->DULparams.callingAPTitle;
233 }
234 else
235 {
236 callbackData.distantAET = "";
237 }
238
239 DcmFileFormat dcmff;
240
241 // store SourceApplicationEntityTitle in metaheader
242 if (assoc && assoc->params)
243 {
244 const char *aet = assoc->params->DULparams.callingAPTitle;
245 if (aet) dcmff.getMetaInfo()->putAndInsertString(DCM_SourceApplicationEntityTitle, aet);
246 }
247
248 // define an address where the information which will be received over the network will be stored
249 DcmDataset *dset = dcmff.getDataset();
250
251 cond = DIMSE_storeProvider(assoc, presID, req, NULL, /*opt_useMetaheader*/OFFalse, &dset,
252 storeScpCallback, &callbackData,
253 /*opt_blockMode*/ DIMSE_BLOCKING,
254 /*opt_dimse_timeout*/ 0);
255
256 // if some error occured, dump corresponding information and remove the outfile if necessary
257 if (cond.bad())
258 {
259 OFString temp_str;
260 OFLOG_ERROR(Logger, "Store SCP Failed: " << DimseCondition::dump(temp_str, cond));
261 }
262
263 // return return value
264 return cond;
265 }
266 }