1 /* 2 * 3 * Copyright (C) 1993-2017, OFFIS e.V. 4 * All rights reserved. See COPYRIGHT file for details. 5 * 6 * This software and supporting documentation were developed by 7 * 8 * OFFIS e.V. 9 * R&D Division Health 10 * Escherweg 2 11 * D-26121 Oldenburg, Germany 12 * 13 * 14 * Module: dcmqrdb 15 * 16 * Author: Andrew Hewett, Marco Eichelberg 17 * 18 * Purpose: class DcmQueryRetrieveIndexDatabaseHandle 19 * 20 */ 21 22 #ifndef DCMQRDBI_H 23 #define DCMQRDBI_H 24 25 #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ 26 #include "dcmtk/dcmqrdb/dcmqrdba.h" /* for class DcmQueryRetrieveDatabaseHandle */ 27 28 #include "dcmtk/dcmnet/dicom.h" 29 #include "dcmtk/dcmnet/dimse.h" 30 #include "dcmtk/ofstd/offname.h" 31 32 struct StudyDescRecord; 33 struct DB_Private_Handle; 34 struct DB_SmallDcmElmt; 35 struct IdxRecord; 36 struct DB_ElementList; 37 class DcmQueryRetrieveConfig; 38 39 /* ENSURE THAT DBVERSION IS INCREMENTED WHENEVER ONE OF THE INDEX FILE STRUCTS IS MODIFIED */ 40 41 #define DBINDEXFILE "index.dat" 42 #define DBMAGIC "QRDB" 43 #define DBVERSION 5 44 #define DBHEADERSIZE 6 45 46 #if DBVERSION > 0xFF 47 #error maximum database version reached, you have to invent a new mechanism 48 #endif 49 50 #ifndef _WIN32 51 /* we lock image files on all platforms except Win32 where it does not work 52 * due to the different semantics of LockFile/LockFileEx compared to flock. 53 */ 54 #define LOCK_IMAGE_FILES 55 #endif 56 57 /** enumeration describing the levels of the DICOM Q/R information model 58 */ 59 enum DB_LEVEL 60 { 61 /// DICOM Q/R patient level 62 PATIENT_LEVEL, 63 /// DICOM Q/R study level 64 STUDY_LEVEL, 65 /// DICOM Q/R series level 66 SERIE_LEVEL, 67 /// DICOM Q/R instance level 68 IMAGE_LEVEL 69 }; 70 71 /** This enum describes the status of one entry in the database hierarchy. An 72 * entry can describe a study, a series or an instance. A study or series is 73 * new exactly if all subobjects (series and instances) are new. A study or 74 * series contains new subobjecs as long as any subobject (series or instance) 75 * has the status objectIsNew. Instances can never have the status 76 * DVIF_objectContainsNewSubobjects. 77 */ 78 enum DVIFhierarchyStatus 79 { 80 /// object (study, series or instance) in the database is not new 81 DVIF_objectIsNotNew, 82 /// object (study, series or instance) in the database is new 83 DVIF_objectIsNew, 84 /// object (study or series) in the database is not new but contains new subobjects 85 DVIF_objectContainsNewSubobjects 86 }; 87 88 /// upper limit for the number of studies per storage area 89 #define DB_UpperMaxStudies 500 90 91 /// upper limit for the number bytes per study 92 #define DB_UpperMaxBytesPerStudy 0x40000000L 93 94 95 /** This class maintains database handles based on the classical "index.dat" file. 96 * A database handle maintains a connection to a database and encapsulates database support for 97 * store, find and move/get operations. 98 */ 99 class DCMTK_DCMQRDB_EXPORT DcmQueryRetrieveIndexDatabaseHandle: public DcmQueryRetrieveDatabaseHandle 100 { 101 private: 102 /// private undefined copy constructor 103 DcmQueryRetrieveIndexDatabaseHandle(const DcmQueryRetrieveIndexDatabaseHandle& other); 104 105 /// private undefined assignment operator 106 DcmQueryRetrieveIndexDatabaseHandle& operator=(const DcmQueryRetrieveIndexDatabaseHandle& other); 107 108 public: 109 110 /** Constructor. Creates and initializes a index file handle for the given 111 * database storage area (storageArea). 112 * @param storageArea name of storage area, must not be NULL 113 * @param maxStudiesPerStorageArea maximum number of studies for this storage area, 114 * needed to correctly parse the index file. 115 * @param maxBytesPerStudy maximum number of bytes per study, for quota mechanism 116 * @param result upon successful initialization of the database handle, 117 * EC_Normal is returned in this parameter, otherwise an error code is returned. 118 */ 119 DcmQueryRetrieveIndexDatabaseHandle( 120 const char *storageArea, 121 long maxStudiesPerStorageArea, 122 long maxBytesPerStudy, 123 OFCondition& result); 124 125 /** Destructor. Destroys handle, cancels any ongoing 126 * request if necessary, deletes temporary files used for C-STORE and 127 * sub-operations of C-MOVE. 128 */ 129 ~DcmQueryRetrieveIndexDatabaseHandle(); 130 131 /** Configure the DB module to perform (or not perform) checking 132 * of FIND and MOVE request identifiers. Default is no checking. 133 * @param checkFind checking for C-FIND parameters 134 * @param checkMove checking for C-MOVE parameters 135 */ 136 void setIdentifierChecking(OFBool checkFind, OFBool checkMove); 137 138 /** create a filename under which a DICOM object that is currently 139 * being received through a C-STORE operation can be stored. 140 * @param SOPClassUID SOP class UID of DICOM instance 141 * @param SOPInstanceUID SOP instance UID of DICOM instance 142 * @param newImageFileName file name is returned in this parameter. 143 * Memory must be provided by the caller and should be at least MAXPATHLEN+1 144 * characters. The file name generated should be an absolute file name. 145 * @param newImageFileNameLen length of buffer pointed to by newImageFileName 146 * @return EC_Normal upon normal completion, or some other OFCondition code upon failure. 147 */ 148 OFCondition makeNewStoreFileName( 149 const char *SOPClassUID, 150 const char *SOPInstanceUID, 151 char *newImageFileName, 152 size_t newImageFileNameLen); 153 154 /** register the given DICOM object, which has been received through a C-STORE 155 * operation and stored in a file, in the database. 156 * @param SOPClassUID SOP class UID of DICOM instance 157 * @param SOPInstanceUID SOP instance UID of DICOM instance 158 * @param imageFileName file name (full path) of DICOM instance 159 * @param status pointer to DB status object in which a DIMSE status code 160 suitable for use with the C-STORE-RSP message is set. 161 * @param isNew if true, the instance is marked as "new" in the database, 162 * if such a flag is maintained in the database. 163 * @return EC_Normal upon normal completion, or some other OFCondition code upon failure. 164 */ 165 OFCondition storeRequest( 166 const char *SOPClassUID, 167 const char *SOPInstanceUID, 168 const char *imageFileName, 169 DcmQueryRetrieveDatabaseStatus *status, 170 OFBool isNew = OFTrue ); 171 172 /** @copydoc DcmQueryRetrieveDatabaseHandle::startFindRequest() 173 */ 174 OFCondition startFindRequest( 175 const char *SOPClassUID, 176 DcmDataset *findRequestIdentifiers, 177 DcmQueryRetrieveDatabaseStatus *status); 178 179 /** @copydoc DcmQueryRetrieveDatabaseHandle::nextFindResponse() 180 */ 181 OFCondition nextFindResponse( 182 DcmDataset **findResponseIdentifiers, 183 DcmQueryRetrieveDatabaseStatus *status, 184 const DcmQueryRetrieveCharacterSetOptions& characterSetOptions); 185 186 /** cancel the ongoing FIND request, stop and reset every running operation 187 * associated with this request, delete existing temporary files. 188 * @param status pointer to DB status object in which a DIMSE status code 189 * suitable for use with the C-FIND-RSP message is set. 190 * @return EC_Normal upon normal completion, or some other OFCondition code upon failure. 191 */ 192 OFCondition cancelFindRequest(DcmQueryRetrieveDatabaseStatus *status); 193 194 /** initiate MOVE operation using the given SOP class UID (which identifies 195 * the retrieve model) and DICOM dataset containing move request identifiers. 196 * @param SOPClassUID SOP class UID of retrieve service, identifies Q/R model 197 * @param moveRequestIdentifiers dataset containing request identifiers (i.e., the query) 198 * The caller retains responsibility for destroying the 199 * moveRequestIdentifiers when no longer needed. 200 * @param status pointer to DB status object in which a DIMSE status code 201 * suitable for use with the C-MOVE-RSP message is set. Status will be 202 * PENDING if any MOVE responses will be generated or SUCCESS if no MOVE responses will 203 * be generated (SUCCESS indicates the completion of a operation), or 204 * another status code upon failure. 205 * @return EC_Normal upon normal completion, or some other OFCondition code upon failure. 206 */ 207 OFCondition startMoveRequest( 208 const char *SOPClassUID, 209 DcmDataset *moveRequestIdentifiers, 210 DcmQueryRetrieveDatabaseStatus *status); 211 212 /** Constructs the information required for the next available C-MOVE 213 * sub-operation (the image SOP class UID, SOP Instance UID and an 214 * imageFileName containing the requested data). 215 * @param SOPClassUID pointer to string of at least 65 characters into 216 * which the SOP class UID for the next DICOM object to be transferred is copied. 217 * @param SOPClassUIDSize size of SOPClassUID element 218 * @param SOPInstanceUID pointer to string of at least 65 characters into 219 * which the SOP instance UID for the next DICOM object to be transferred is copied. 220 * @param SOPInstanceUIDSize size of SOPInstanceUID element 221 * @param imageFileName pointer to string of at least MAXPATHLEN+1 characters into 222 * which the file path for the next DICOM object to be transferred is copied. 223 * @param imageFileNameSize size of imageFileName element 224 * @param numberOfRemainingSubOperations On return, this parameter will contain 225 * the number of suboperations still remaining for the request 226 * (this number is needed by move responses with PENDING status). 227 * @param status pointer to DB status object in which a DIMSE status code 228 * suitable for use with the C-MOVE-RSP message is set. Status will be 229 * PENDING if more MOVE responses will be generated or SUCCESS if no more 230 * MOVE responses will be generated (SUCCESS indicates the completion of 231 * a operation), or another status code upon failure. 232 * @return EC_Normal upon normal completion, or some other OFCondition code upon failure. 233 */ 234 OFCondition nextMoveResponse( 235 char *SOPClassUID, 236 size_t SOPClassUIDSize, 237 char *SOPInstanceUID, 238 size_t SOPInstanceUIDSize, 239 char *imageFileName, 240 size_t imageFileNameSize, 241 unsigned short *numberOfRemainingSubOperations, 242 DcmQueryRetrieveDatabaseStatus *status); 243 244 /** cancel the ongoing MOVE request, stop and reset every running operation 245 * associated with this request, delete existing temporary files. 246 * @param status pointer to DB status object in which a DIMSE status code 247 * suitable for use with the C-MOVE-RSP message is set. 248 * @return EC_Normal upon normal completion, or some other OFCondition code upon failure. 249 */ 250 OFCondition cancelMoveRequest(DcmQueryRetrieveDatabaseStatus *status); 251 252 /** Prune invalid records from the database. 253 * Records referring to non-existant image files are invalid. 254 */ 255 OFCondition pruneInvalidRecords(); 256 257 // methods not inherited from the base class 258 259 /** enable/disable the DB quota system (default: enabled) which causes images 260 * to be deleted if certain boundaries (number of studies, bytes per study) are exceeded. 261 */ 262 void enableQuotaSystem(OFBool enable); 263 264 /** dump database index file to stdout. 265 * @param storeArea name of storage area, must not be NULL 266 */ 267 static void printIndexFile (char *storeArea); 268 269 /** search for a SOP class and SOP instance UIDs in index file. 270 * @param storeArea name of storage area, must not be NULL 271 * @param sopClassUID SOP Class UID to search for 272 * @param sopInstanceUID SOP Instance UID to search for 273 * @return OFTrue if SOP Class and SOP Instance UIDs are found. otherwise return OFFalse. 274 */ 275 OFBool findSOPInstance(const char *storeArea, const OFString &sopClassUID,const OFString &sopInstanceUID); 276 277 /** deletes the given file only if the quota mechanism is enabled. 278 * The image is not de-registered from the database by this routine. 279 * @param imgFile file name (path) to the file to be deleted. 280 * @return EC_Normal upon normal completion, or some other OFCondition code upon failure. 281 */ 282 OFCondition deleteImageFile(char* imgFile); 283 284 /** create lock on database 285 * @param exclusive exclusive/shared lock flag 286 * @return EC_Normal upon success, an error code otherwise 287 */ 288 OFCondition DB_lock(OFBool exclusive); 289 290 /** release lock on database 291 */ 292 OFCondition DB_unlock(); 293 294 /** Get next Index record that is in use (i.e. references a non-empty a filename) 295 * @param idx pointer to index number, updated upon successful return 296 * @param idxRec pointer to index record structure 297 * @return EC_Normal upon success, an error code otherwise 298 */ 299 OFCondition DB_IdxGetNext(int *idx, IdxRecord *idxRec); 300 301 /** seek to beginning of image records in index file 302 * @param idx initialized to -1 303 * @return EC_Normal upon success, an error code otherwise 304 */ 305 OFCondition DB_IdxInitLoop(int *idx); 306 307 /** read index record at given index 308 * @param idx index 309 * @param idxRec pointer to index record 310 * @return EC_Normal upon success, an error code otherwise 311 */ 312 OFCondition DB_IdxRead(int idx, IdxRecord *idxRec); 313 314 /** get study descriptor record from start of index file 315 * @param pStudyDesc pointer to study record descriptor structure 316 * @return EC_Normal upon success, an error code otherwise 317 */ 318 OFCondition DB_GetStudyDesc(StudyDescRecord *pStudyDesc); 319 320 /** write study descriptor record to start of index file 321 * @param pStudyDesc pointer to study record descriptor structure 322 * @return EC_Normal upon success, an error code otherwise 323 */ 324 OFCondition DB_StudyDescChange(StudyDescRecord *pStudyDesc); 325 326 /** deactivate index record at given index by setting an empty filename 327 * @param idx index 328 * @return EC_Normal upon success, an error code otherwise 329 */ 330 OFCondition DB_IdxRemove(int idx); 331 332 /** clear the "is new" flag for the instance with the given index 333 * @param idx index 334 * @return EC_Normal upon success, an error code otherwise 335 */ 336 OFCondition instanceReviewed(int idx); 337 338 /// return name of storage area 339 const char *getStorageArea() const; 340 341 /// return path to index file 342 const char *getIndexFilename() const; 343 344 345 private: 346 347 /** a private helper class that performs character set conversions on the fly 348 * (if necessary) before matching. 349 */ 350 class CharsetConsideringMatcher; 351 352 /** Determine if a character set is not compatible to UTF-8, i.e.\ if it is 353 * not UTF-8 or ASCII. 354 * @param characterSet the character set to inspect. 355 * @return OFTrue if the character set is neither ASCII nor UTF-8, OFFalse 356 * otherwise. 357 */ 358 static OFBool isConversionToUTF8Necessary(const OFString& characterSet); 359 360 /** Determine if data in the source character set must be converted to 361 * be compatible to the given destination character set. 362 * @param sourceCharacterSet the character set the data is encoded in. 363 * @param destinationCharacterSet the character set that is requested, 364 * e.g. the character set that the SCU understands. 365 * @return OFTrue if the source character set is not equal to and not a 366 * subset of the destination character set, OFFalse otherwise. 367 */ 368 static OFBool isConversionNecessary(const OFString& sourceCharacterSet, 369 const OFString& destinationCharacterSet); 370 371 OFCondition removeDuplicateImage( 372 const char *SOPInstanceUID, const char *StudyInstanceUID, 373 StudyDescRecord *pStudyDesc, const char *newImageFileName); 374 int deleteOldestStudy(StudyDescRecord *pStudyDesc); 375 OFCondition deleteOldestImages(StudyDescRecord *pStudyDesc, int StudyNum, char *StudyUID, long RequiredSize); 376 void makeResponseList(DB_Private_Handle *phandle, IdxRecord *idxRec); 377 int matchStudyUIDInStudyDesc (StudyDescRecord *pStudyDesc, char *StudyUID, int maxStudiesAllowed); 378 OFCondition checkupinStudyDesc(StudyDescRecord *pStudyDesc, char *StudyUID, long imageSize); 379 380 OFCondition hierarchicalCompare ( 381 DB_Private_Handle *phandle, 382 IdxRecord *idxRec, 383 DB_LEVEL level, 384 DB_LEVEL infLevel, 385 int *match, 386 CharsetConsideringMatcher& dbmatch); 387 388 OFCondition testFindRequestList ( 389 DB_ElementList *findRequestList, 390 DB_LEVEL queryLevel, 391 DB_LEVEL infLevel, 392 DB_LEVEL lowestLevel); 393 394 OFCondition testMoveRequestList ( 395 DB_ElementList *findRequestList, 396 DB_LEVEL queryLevel, 397 DB_LEVEL infLevel, 398 DB_LEVEL lowestLevel); 399 400 /// database handle 401 DB_Private_Handle *handle_; 402 403 /// flag indicating whether or not the quota system is enabled 404 OFBool quotaSystemEnabled; 405 406 /// flag indicating whether or not the check function for FIND requests is enabled 407 OFBool doCheckFindIdentifier; 408 409 /// flag indicating whether or not the check function for MOVE requests is enabled 410 OFBool doCheckMoveIdentifier; 411 412 /// helper object for file name creation 413 OFFilenameCreator fnamecreator; 414 415 }; 416 417 418 /** Index database factory class. Instances of this class are able to create database 419 * handles for a given called application entity title. 420 */ 421 class DCMTK_DCMQRDB_EXPORT DcmQueryRetrieveIndexDatabaseHandleFactory: public DcmQueryRetrieveDatabaseHandleFactory 422 { 423 private: 424 /// private undefined copy constructor 425 DcmQueryRetrieveIndexDatabaseHandleFactory(const DcmQueryRetrieveIndexDatabaseHandleFactory& other); 426 427 /// private undefined assignment operator 428 DcmQueryRetrieveIndexDatabaseHandleFactory& operator=(const DcmQueryRetrieveIndexDatabaseHandleFactory& other); 429 430 public: 431 432 /** constructor 433 * @param config system configuration object, must not be NULL. 434 */ 435 DcmQueryRetrieveIndexDatabaseHandleFactory(const DcmQueryRetrieveConfig *config); 436 437 /// destructor 438 virtual ~DcmQueryRetrieveIndexDatabaseHandleFactory(); 439 440 /** this method creates a new database handle instance on the heap and returns 441 * a pointer to it, along with a result that indicates if the instance was 442 * successfully initialized, i.e. connected to the database 443 * @param callingAETitle calling aetitle 444 * @param calledAETitle called aetitle 445 * @param result result returned in this variable 446 * @return pointer to database object, must not be NULL if result is EC_Normal. 447 */ 448 virtual DcmQueryRetrieveDatabaseHandle *createDBHandle( 449 const char *callingAETitle, 450 const char *calledAETitle, 451 OFCondition& result) const; 452 453 private: 454 455 /// pointer to system configuration 456 const DcmQueryRetrieveConfig *config_; 457 }; 458 459 #endif 460