当前位置: 首页 > ds >正文

基于dcmtk的dicom工具 第九章 以json文件或sqlite为数据源的worklist服务(附工程源码)

文章目录

  • 前言
  • 一、CWLServer类介绍
    • 1.1 dcmtk中worklist scp的程序流程
    • 1.2 日志函数
    • 1.3. 服务初始化、启动、停止、状态查询、设置日志级别
    • 1.4 设置数据源
    • 5. 主线程与工作线程
  • 二、数据源类
    • 2.1 XgDataSource类
    • 2.2 WlmDataSourceJson类
    • 2.3 WlmDataSourceSqlite类
  • 三、工程源码


前言

worklist服务是为dicom设备提供查询检查登记信息的服务,是RIS系统与放射科检查设备非常重要的沟通桥梁。
基于dcmtk实现worklist服务,支持从json文件或sqlite3数据库中读取登记信息。
本章介绍从json文件中读取登记信息,根据设备发送的查询条件,把登记信息发送到设备。
参考dcmtk源码项目:wlmscpfs,文件:dcmtk-3.6.9\dcmwlm\apps\wlcefs.cc,dcmtk-3.6.9\dcmwlm\apps\wlmscpfs.cc,dcmtk-3.6.9\dcmwlm\libsrc\wlmactmg.cc
程序界面参考基于dcmtk的dicom工具 第三章 图像接受StoreSCP(1),基本一样
用第七章 FindSCU-查询工作列表做客户端测试,效果如下:
在这里插入图片描述


一、CWLServer类介绍

CWLServer类与第四章 图像接受StoreSCP(2)中CStoreServer类结构一致。

1.1 dcmtk中worklist scp的程序流程

dcmtk中scp的流程基本一致,注意与第四章 图像接受StoreSCP(2) storescp的流程对比。

  1. ASC_initializeNetwork初始化网络,整理放入到主线程函数DoNetWorks
  2. acceptAssociation等待连接,整理放入到主线程函数DoNetWorks
  3. 在acceptAssociation中调用ASC_receiveAssociation接受连接,接受到连接后,新建任务放入到线程池由工作线程处理
  4. 服务停止DoNetWorks中调用ASC_dropNetwork关闭网络
  5. 工作线程函数DcmWorkThread->DealAssociation
  6. 重点函数DealAssociation,
    1. 调用ASC_acceptContextsWithPreferredTransferSyntaxes,
      设置接受Verification SOP Class,允许echoscu;
      设置接受抽象语法UID_FINDModalityWorklistInformationModel,允许接受CFind-RQ;
      设置支持的传输语法 transfer Syntaxes,本项目接受所有支持的语法"we accept all supported transfer syntaxes";
    2. 调用acceptUnknownContextsWithPreferredTransferSyntaxes,设置接受其他未知的Storage SOP Class
    3. 调用ASC_getApplicationContextName获取协商结果
    4. 调用ASC_acknowledgeAssociation通知连接成功
    5. 调用processCommands处理命令,支持C-ECHO-RQ 和 DIMSE_C_FIND_RQ两种命令。
    6. processCommands中调用DIMSE_receiveCommand接受命令,根据命令类型分别调用echoSCP和HandleFindSCP处理。
    7. 重点HandleFindSCP中调用DIMSE_findProvider,传入从WlmDataSource派生的类的实例做参数,本章从json中读取检查信息,类名为WlmDataSourceJson,后面详细介绍该类。
    8. 图像接受完成调用ASC_dropSCPAssociation,ASC_destroyAssociation释放连接

1.2 日志函数

参考 第四章 图像接受StoreSCP(2)中CStoreServer类

1.3. 服务初始化、启动、停止、状态查询、设置日志级别

参考 第四章 图像接受StoreSCP(2)中CStoreServer类

1.4 设置数据源

数据源支持json文件和sqlite3数据库文件,在HandleFindSCP函数中根据文件后缀名决定实例化哪个数据源类来处理数据源。

class CWLServer
{
public:CWLServer();~CWLServer();...// 支持从json文件、sqlite3数据库void SetSource(std::string source);int GetState() {return m_state;}
private:std::string m_source;
}void CWLServer::SetSource(std::string source)
{m_source = source;
}OFCondition CWLServer::HandleFindSCP(T_ASC_Association* pAssoc, T_DIMSE_C_FindRQ *request, T_ASC_PresentationContextID presID)
{// Create callback data which needs to be passed to DIMSE_findProvider later.WlmFindContextType context;context.priorStatus = WLM_PENDING;// 根据文件后缀名决定实例化哪个数据源类来处理数据源XgDataSource* pWlmDataSource = nullptr;std::string ext;ext = m_source.substr(m_source.rfind('.'));if (ext == ".json") {pWlmDataSource = new WlmDataSourceJson(this);}else {pWlmDataSource = new WlmDataSourceSqlite(this);}...if (pWlmDataSource){delete pWlmDataSource;pWlmDataSource = NULL;}return cond;
}

5. 主线程与工作线程

参考 第四章 图像接受StoreSCP(2)中CStoreServer类

二、数据源类

在1.1 dcmtk中worklist scp的程序流程中,6.7 HandleFindSCP中调用DIMSE_findProvider,传入从WlmDataSource派生的类的实例做参数。
需要从WlmDataSource派生类来从数据源中加载登记信息。只需要重写以下五个函数,函数作用看注释。

	OFCondition ConnectToDataSource();   // 连接数据源OFCondition DisconnectFromDataSource();  // 关闭数据库OFBool IsCalledApplicationEntityTitleSupported();  // 白名单判断客户端是否允许查询WlmDataSourceStatusType StartFindRequest(const DcmDataset &findRequestIdentifiers);  // 查询数据DcmDataset *NextFindResponse(WlmDataSourceStatusType &rStatus);   // 发送数据

因为要实现从json文件和sqlite3数据库中读取登记信息,且IsCalledApplicationEntityTitleSupported,NextFindResponse两个函数可能共用,所以把能共用的代码提取公共类XgDataSource,这个类从WlmDataSource派生。XgDataSource中重写IsCalledApplicationEntityTitleSupported,NextFindResponse两个函数。

  1. 从XgDataSource中派生WlmDataSourceJson类,重写ConnectToDataSource,DisconnectFromDataSource,StartFindRequest三个函数,实现从json文件中读取登记信息。
  2. 从XgDataSource中派生WlmDataSourceSqlite类,重写ConnectToDataSource,DisconnectFromDataSource,StartFindRequest三个函数,实现从sqlite3数据库文件中读取登记信息。

2.1 XgDataSource类

重点是重写的NextFindResponse函数,发送数据到客户端,需要发送哪些字段,
参考DICOM3.0协议第四章第190页, PS 3.4 Service Class Specifications -> K.6.1.2.2
Modality Worklist Attributes -> Table K.6-1
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

头文件:

#pragma once#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dctk.h" 
#include "dcmtk/dcmwlm/wlds.h"
#include "dcmtk/dcmwlm/wlfsim.h"
#include "Defines.h"class CWLServer;class XgDataSource : public WlmDataSource
{
public:XgDataSource() {};XgDataSource(CWLServer* server) : m_pServer(server) {};virtual ~XgDataSource() {};void SetParams(std::string peerAET, std::string peerIP, std::string dataSource) {m_peerAET = peerAET;m_peerIP = peerIP;m_source = dataSource;}protected:// 必须实现的父类虚函数OFBool IsCalledApplicationEntityTitleSupported() override;DcmDataset *NextFindResponse(WlmDataSourceStatusType &rStatus) override;void HandleExistentButEmptyReferencedStudyOrPatientSequenceAttributes(DcmDataset *dataset, const DcmTagKey &sequenceTagKey);protected:CWLServer* m_pServer;std::list<WLINFO>		m_matchingDatasets;std::string m_peerAET;std::string m_peerIP;std::string m_source;
};

源文件:

#include "pch.h"
#include "XgDataSource.h"#include "Utilities.h"
#include "CWLServer.h"void XgDataSource::HandleExistentButEmptyReferencedStudyOrPatientSequenceAttributes(DcmDataset *dataset, const DcmTagKey &sequenceTagKey)
{DcmElement *sequenceAttribute = NULL, *referencedSOPClassUIDAttribute = NULL, *referencedSOPInstanceUIDAttribute = NULL;// in case the sequence attribute contains exactly one item with an empty// ReferencedSOPClassUID and an empty ReferencedSOPInstanceUID, remove the itemif (dataset->findAndGetElement(sequenceTagKey, sequenceAttribute).good() &&((DcmSequenceOfItems*)sequenceAttribute)->card() == 1 &&((DcmSequenceOfItems*)sequenceAttribute)->getItem(0)->findAndGetElement(DCM_ReferencedSOPClassUID, referencedSOPClassUIDAttribute).good() &&referencedSOPClassUIDAttribute->getLength() == 0 &&((DcmSequenceOfItems*)sequenceAttribute)->getItem(0)->findAndGetElement(DCM_ReferencedSOPInstanceUID, referencedSOPInstanceUIDAttribute, OFFalse).good() &&referencedSOPInstanceUIDAttribute->getLength() == 0){DcmItem *item = ((DcmSequenceOfItems*)sequenceAttribute)->remove(((DcmSequenceOfItems*)sequenceAttribute)->getItem(0));delete item;}
}OFBool XgDataSource::IsCalledApplicationEntityTitleSupported()
{return OFTrue;
}DcmDataset * XgDataSource::NextFindResponse(WlmDataSourceStatusType &rStatus)
{WLINFO wlInfo;if (m_matchingDatasets.size() == 0){//log_debug(LT_DICOM, "find datasource size=0,return success.");DisconnectFromDataSource();rStatus = WLM_SUCCESS;return NULL;}else{//if (m_pWlCfg->bReverseSend)//	wlInfo = m_listWLResult.front();//elsewlInfo = m_matchingDatasets.front();}DcmDataset *pWLDataSet = new DcmDataset();OFCondition cond;//字符集std::string strCharSet = "ISO_IR 100";// 字符集pWLDataSet->putAndInsertString(DCM_SpecificCharacterSet, strCharSet.c_str());// 检查部位/* create Scheduled Procedure Step Sequence (0040,0100) */DcmItem  *pScheduledProcedureStepSequenceItem = NULL;cond = pWLDataSet->findOrCreateSequenceItem(DCM_ScheduledProcedureStepSequence, pScheduledProcedureStepSequenceItem, -2);COleDateTime regDate;regDate.ParseDateTime(wlInfo.RegistyDate.c_str());if (pScheduledProcedureStepSequenceItem && cond.good()){pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_ScheduledStationAETitle, m_peerAET.c_str());if (regDate.GetStatus() == COleDateTime::valid){pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_ScheduledProcedureStepStartDate, regDate.Format(_T("%Y%m%d")));pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_ScheduledProcedureStepStartTime, regDate.Format(_T("%H%M%S")));}if (!wlInfo.Modality.empty()){pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_Modality, wlInfo.Modality.c_str());}// 检查技师pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_ScheduledPerformingPhysicianName, wlInfo.PerformingPhysician.c_str());// 检查部位pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_ScheduledProcedureStepDescription, wlInfo.ExamItem.c_str());pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_ScheduledStationName, "");pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_ScheduledProcedureStepLocation, "");pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_PreMedication, "");pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_ScheduledProcedureStepID, wlInfo.StudyId.c_str());pScheduledProcedureStepSequenceItem->putAndInsertString(DCM_RequestedContrastAgent, "");}pWLDataSet->putAndInsertString(DCM_RequestedProcedureID, wlInfo.StudyId.c_str());pWLDataSet->putAndInsertString(DCM_RequestedProcedureDescription, wlInfo.ExamItem.c_str());/* create Requested Procedure Code Sequence (0032,1064) */DcmItem  *pRequestedProcedureCodeSequencePart = NULL;cond = pWLDataSet->findOrCreateSequenceItem(DCM_RequestedProcedureCodeSequence, pRequestedProcedureCodeSequencePart, -2);if (pRequestedProcedureCodeSequencePart && cond.good()){pRequestedProcedureCodeSequencePart->putAndInsertString(DCM_CodeValue, "0");pRequestedProcedureCodeSequencePart->putAndInsertString(DCM_CodingSchemeDesignator, "0");pRequestedProcedureCodeSequencePart->putAndInsertString(DCM_CodingSchemeVersion, "0");pRequestedProcedureCodeSequencePart->putAndInsertString(DCM_CodeMeaning, wlInfo.ExamItem.c_str());}// 生成Study Instance UIDstd::string studyInsUID;FormatStr(studyInsUID, "1.2.840.122619.2.5.4421578.260.666.%s", wlInfo.StudyId.c_str());pWLDataSet->putAndInsertString(DCM_StudyInstanceUID, studyInsUID.c_str());// 检查信息序列DcmItem  *pReferencedStudySequence = NULL;cond = pWLDataSet->findOrCreateSequenceItem(DCM_ReferencedStudySequence, pReferencedStudySequence, -2);pReferencedStudySequence->putAndInsertString(DCM_ReferencedSOPClassUID, "");pReferencedStudySequence->putAndInsertString(DCM_ReferencedSOPInstanceUID, "");pWLDataSet->putAndInsertString(DCM_RequestedProcedurePriority, "NORMAL");pWLDataSet->putAndInsertString(DCM_PatientTransportArrangements, "");// 申请医生pWLDataSet->putAndInsertString(DCM_RequestingPhysician, wlInfo.RequestPhysician.c_str());pWLDataSet->putAndInsertString(DCM_ReferringPhysicianName, "");// 申请科室pWLDataSet->putAndInsertString(DCM_CurrentPatientLocation, wlInfo.RequestDepartment.c_str());pWLDataSet->putAndInsertString(DCM_InstitutionalDepartmentName, wlInfo.RequestDepartment.c_str());// 床号pWLDataSet->putAndInsertString(DCM_PatientInstitutionResidence, wlInfo.BedNo.c_str());// 患者信息序列DcmItem  *pReferencedPatientSequenceItem = NULL;cond = pWLDataSet->findOrCreateSequenceItem(DCM_ReferencedPatientSequence, pReferencedPatientSequenceItem, -2);pReferencedPatientSequenceItem->putAndInsertString(DCM_ReferencedSOPClassUID, "");pReferencedPatientSequenceItem->putAndInsertString(DCM_ReferencedSOPInstanceUID, "");// 患者姓名pWLDataSet->putAndInsertString(DCM_PatientName, wlInfo.PatientName.c_str());pWLDataSet->putAndInsertString(DCM_PatientID, wlInfo.StudyId.c_str());pWLDataSet->putAndInsertString(DCM_AccessionNumber, wlInfo.AccNumber.c_str());// 住院号/门诊号/体检号pWLDataSet->putAndInsertString(DCM_AdmissionID, wlInfo.MedicalNo.c_str());// 生日pWLDataSet->putAndInsertString(DCM_PatientBirthDate, wlInfo.Birthday.c_str());// 性别pWLDataSet->putAndInsertString(DCM_PatientSex, wlInfo.Sex.c_str());// 身高体重pWLDataSet->putAndInsertString(DCM_PatientWeight, wlInfo.PatWeight.c_str());pWLDataSet->putAndInsertString(DCM_PatientSize, wlInfo.PatHeight.c_str());// 年龄pWLDataSet->putAndInsertString(DCM_PatientAge, wlInfo.StudyAge.c_str());std::string sendStr = "";sendStr += "姓名:" + wlInfo.PatientName + ",";sendStr += "性别:" + wlInfo.Sex + ",";sendStr += "年龄:" + wlInfo.StudyAge + ",";sendStr += "生日:" + wlInfo.Birthday + ",";sendStr += "日期:" + wlInfo.RegistyDate + ",";sendStr += "检查号:" + wlInfo.StudyId + ",";sendStr += "字符集:" + strCharSet;m_pServer->log_info("发送到[%s]:[%s]", m_peerAET.c_str(), sendStr.c_str());HandleExistentButEmptyReferencedStudyOrPatientSequenceAttributes(pWLDataSet, DCM_ReferencedStudySequence);HandleExistentButEmptyReferencedStudyOrPatientSequenceAttributes(pWLDataSet, DCM_ReferencedPatientSequence);// 最后一条记录打印debug日志if (m_matchingDatasets.size() == 1){std::ostringstream msg;msg.str("");m_pServer->log_debug("Last Find SCP Response:");pWLDataSet->print(msg);std::string tmp;tmp = msg.str().c_str();Replace(tmp, "\n", "\r\n");m_pServer->log_debug("=============================");m_pServer->log_debug(tmp.c_str());m_pServer->log_debug("=============================");}m_matchingDatasets.pop_front();rStatus = WLM_PENDING;return pWLDataSet;
}

2.2 WlmDataSourceJson类

WlmDataSourceJson类从XgDataSource类派生,重写ConnectToDataSource,DisconnectFromDataSource,StartFindRequest三个函数,实现从json文件中读取登记信息。
读写json文件请参考我的文章c++ nlohmann/json读写json文件
头文件:

#pragma once
#include "XgDataSource.h"class WlmDataSourceJson : public XgDataSource
{
public:WlmDataSourceJson();WlmDataSourceJson(CWLServer* server);~WlmDataSourceJson();// 测试数据static void GenTestData();static int ReadData(std::list<WLINFO>& data);protected:// 必须实现的父类虚函数OFCondition ConnectToDataSource() override;OFCondition DisconnectFromDataSource() override;WlmDataSourceStatusType StartFindRequest(const DcmDataset &findRequestIdentifiers) override;private:std::list<WLINFO>		m_fullDatasets;
};

源文件:

#include "pch.h"
#include "WlmDataSourceJson.h"
#include "Utilities.h"
#include "CWLServer.h"
#include "json.hpp"
using json = nlohmann::json;#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "netapi32.lib")#pragma comment(lib, "dcmnet.lib")
#pragma comment(lib, "dcmdata.lib")
#pragma comment(lib, "oflog.lib")
#pragma comment(lib, "ofstd.lib")
#pragma comment(lib, "dcmtls.lib")
#pragma comment(lib, "oficonv.lib")
#pragma comment(lib,"dcmwlm.lib")#ifdef _DEBUG
#pragma comment(lib,"zlib_d.lib")
#else
#pragma comment(lib,"zlib_o.lib")
#endifWlmDataSourceJson::WlmDataSourceJson(): XgDataSource()
{}WlmDataSourceJson::WlmDataSourceJson(CWLServer* server): XgDataSource(server)
{}WlmDataSourceJson::~WlmDataSourceJson()
{}void WlmDataSourceJson::GenTestData()
{std::vector<nlohmann::json> v = {{ {"studyId", 1}, {"patName", u8"测试json1"}, {"gender", u8"男"}, {"age", "46Y"}, {"regDate", "2025-07-18 09:01:01"}, {"modality", "CT"}, {"examItem", u8"胸部X线计算机体层(CT)平扫"} },{ {"studyId", 2}, {"patName", "jsonName2"}, {"gender", u8"男"}, {"age", "75Y"}, {"regDate", "2025-07-18 09:01:01"}, {"modality", "CT"}, {"examItem", u8"颅脑X线计算机体层(CT)平扫"} },{ {"studyId", 3}, {"patName", "jsonName3"}, {"gender", u8"女"}, {"age", "66Y"}, {"regDate", "2025-07-18 09:01:01"}, {"modality", "CT"}, {"examItem", u8"胸部X线计算机体层(CT)平扫"} }};nlohmann::json j_data(v);std::string appDir = GetAppPath();std::string fn = appDir + "wl.json";std::ofstream o(fn);//o << std::setw(4) << j_data << std::endl;o << j_data.dump(4) << std::endl;
}int WlmDataSourceJson::ReadData(std::list<WLINFO>& data)
{std::string appDir = GetAppPath();std::string fn = appDir + "wl.json";std::ifstream f(fn);json j_data = json::parse(f);if (!j_data.is_array()) {return 0;}for (const auto& item : j_data) {WLINFO info;int id = item["studyId"];info.StudyId = info.AccNumber = info.PatientId = std::to_string(id);info.PatientName = UTF8toA(item["patName"]);info.Sex = UTF8toA(item["gender"]);info.StudyAge = item["age"];info.RegistyDate = item["regDate"];info.Modality = item["modality"];info.ExamItem = UTF8toA(item["examItem"]);data.push_back(info);}return data.size();
}OFCondition WlmDataSourceJson::ConnectToDataSource()
{try{std::ifstream f(m_source);json j_data = json::parse(f);if (!j_data.is_array()) {return makeDcmnetCondition(0, OF_error, "解析json失败!");}for (const auto& item : j_data) {WLINFO info;int id = item["studyId"];info.StudyId = info.AccNumber = info.PatientId = std::to_string(id);info.PatientName = UTF8toA(item["patName"]);info.Sex = UTF8toA(item["gender"]);info.StudyAge = item["age"];info.RegistyDate = item["regDate"];info.Modality = item["modality"];info.ExamItem = UTF8toA(item["examItem"]);m_fullDatasets.push_back(info);}}catch (const std::exception&){return makeDcmnetCondition(0, OF_error, "解析json失败!");}return EC_Normal;
}OFCondition WlmDataSourceJson::DisconnectFromDataSource()
{return EC_Normal;
}WlmDataSourceStatusType WlmDataSourceJson::StartFindRequest(const DcmDataset &findRequestIdentifiers)
{// 从数据源(文件或数据库)中查找匹配的登记信息if (ConnectToDataSource().bad()){m_pServer->log_error("查询工作列表, 连接数据源失败!");return WLM_FAILED_IDENTIFIER_DOES_NOT_MATCH_SOP_CLASS;}ClearDataset(identifiers);delete identifiers;identifiers = new DcmDataset(findRequestIdentifiers);identifiers->computeGroupLengthAndPadding(EGL_withoutGL, EPD_withoutPadding);//查询条件OFCondition cond;OFString PatName;OFString PatId;OFString StartDate;OFString StartTime;OFString EndDate;OFString EndTime;OFString Sex;OFString Modality;OFString charset;identifiers->findAndGetOFString(DCM_SpecificCharacterSet, charset);identifiers->findAndGetOFString(DCM_PatientName, PatName);identifiers->findAndGetOFString(DCM_PatientID, PatId);identifiers->findAndGetOFString(DCM_PatientSex, Sex);if (Sex == "M") Sex = "男";if (Sex == "F") Sex = "女";DcmItem *pItem;identifiers->findOrCreateSequenceItem(DCM_ScheduledProcedureStepSequence, pItem, -1);DcmElement *elm = NULL;DcmSequenceOfItems *seq = NULL;if (pItem){pItem->findAndGetOFString(DCM_ScheduledProcedureStepStartDate, StartDate);pItem->findAndGetOFString(DCM_ScheduledProcedureStepStartTime, StartTime);pItem->findAndGetOFString(DCM_ScheduledProcedureStepEndDate, EndDate);pItem->findAndGetOFString(DCM_ScheduledProcedureStepEndTime, EndTime);}std::copy_if(m_fullDatasets.begin(), m_fullDatasets.end(), std::back_inserter(m_matchingDatasets),[&](WLINFO item) {bool bOk = true;if (!PatName.empty() && item.PatientName.find(PatName)==std::string::npos) {bOk = false;}if (!PatId.empty() && item.PatientId != PatId) {bOk = false;}if (!Sex.empty() && item.Sex != Sex) {bOk = false;}if (!StartDate.empty() && !EndDate.empty()) {// 简化比较, 正常要转化为日期再比较if (item.RegistyDate < StartDate || item.RegistyDate > EndDate) {bOk = false;}}return bOk;}); m_pServer->log_info("查询到 %d 条检查记录", m_matchingDatasets.size());if (m_matchingDatasets.size() > 0){return WLM_PENDING;}return WLM_SUCCESS;}

2.3 WlmDataSourceSqlite类

WlmDataSourceSqlite类从XgDataSource类派生,重写ConnectToDataSource,DisconnectFromDataSource,StartFindRequest三个函数,实现从sqlitle3数据库文件中读取登记信息。
读写sqlite数据库请参考我的文章vs2017 c++ 使用sqlite3数据库

头文件:

#pragma once
#include "XgDataSource.h"struct sqlite3;class WlmDataSourceSqlite : public XgDataSource
{
public:WlmDataSourceSqlite();WlmDataSourceSqlite(CWLServer* server);~WlmDataSourceSqlite();protected:// 必须实现的父类虚函数OFCondition ConnectToDataSource() override;OFCondition DisconnectFromDataSource() override;WlmDataSourceStatusType StartFindRequest(const DcmDataset &findRequestIdentifiers) override;private:sqlite3* m_db;};

源文件:

#include "pch.h"
#include "WlmDataSourceSqlite.h"
#include "CWLServer.h"
#include "sqlite3.h"
#include "Utilities.h"#pragma comment(lib, "sqlite3.lib")WlmDataSourceSqlite::WlmDataSourceSqlite(): XgDataSource(), m_db(nullptr)
{}WlmDataSourceSqlite::WlmDataSourceSqlite(CWLServer* server): XgDataSource(server), m_db(nullptr)
{}WlmDataSourceSqlite::~WlmDataSourceSqlite()
{DisconnectFromDataSource();
}OFCondition WlmDataSourceSqlite::ConnectToDataSource()
{//std::string dbfn = GetAppPath() + "wl.db";std::string dbfn = m_source;int rc = sqlite3_open(dbfn.c_str(), &m_db);if (SQLITE_OK != rc) {m_pServer->log_error("open sqlite failed.%s:%d", __FUNCTION__, __LINE__);return makeDcmnetCondition(0, OF_error, "open sqlite failed");}return EC_Normal;
}OFCondition WlmDataSourceSqlite::DisconnectFromDataSource()
{int rc = SQLITE_ERROR;if (m_db) {rc = sqlite3_close_v2(m_db);if (rc == SQLITE_OK) m_db = nullptr;}return EC_Normal;
}WlmDataSourceStatusType WlmDataSourceSqlite::StartFindRequest(const DcmDataset &findRequestIdentifiers)
{// 从数据源(文件或数据库)中查找匹配的登记信息if (ConnectToDataSource().bad()){m_pServer->log_error("查询工作列表, 连接数据源失败!");return WLM_FAILED_IDENTIFIER_DOES_NOT_MATCH_SOP_CLASS;}ClearDataset(identifiers);delete identifiers;identifiers = new DcmDataset(findRequestIdentifiers);identifiers->computeGroupLengthAndPadding(EGL_withoutGL, EPD_withoutPadding);//查询条件OFCondition cond;OFString PatName;OFString PatId;OFString StartDate;OFString StartTime;OFString EndDate;OFString EndTime;OFString Sex;OFString Modality;OFString charset;identifiers->findAndGetOFString(DCM_SpecificCharacterSet, charset);identifiers->findAndGetOFString(DCM_PatientName, PatName);identifiers->findAndGetOFString(DCM_PatientID, PatId);identifiers->findAndGetOFString(DCM_PatientSex, Sex);if (Sex == "M") Sex = "男";if (Sex == "F") Sex = "女";DcmItem *pItem;identifiers->findOrCreateSequenceItem(DCM_ScheduledProcedureStepSequence, pItem, -1);DcmElement *elm = NULL;DcmSequenceOfItems *seq = NULL;if (pItem){pItem->findAndGetOFString(DCM_ScheduledProcedureStepStartDate, StartDate);pItem->findAndGetOFString(DCM_ScheduledProcedureStepStartTime, StartTime);pItem->findAndGetOFString(DCM_ScheduledProcedureStepEndDate, EndDate);pItem->findAndGetOFString(DCM_ScheduledProcedureStepEndTime, EndTime);}std::string sql;sql = "select studyId, patId, patName, gender, age, modality, IFNULL(requestPhysician, ''), regDate, IFNULL(examItem, '') from Registry where 1=1";if (!PatName.empty()) {sql += " and patName like '%" + PatName + "%'";}if (!PatId.empty()) {sql += " and patId = '" + PatId + "'";}if (!Sex.empty()) {sql += " and gender = '" + Sex + "'";}if (!StartDate.empty() && !EndDate.empty()) {std::string start, end;start = StartDate.substr(0, 4) + "-" + StartDate.substr(4, 2) + "-" + StartDate.substr(6, 2);start += " 00:00:00";end = EndDate.substr(0, 4) + "-" + EndDate.substr(4, 2) + "-" + EndDate.substr(6, 2);end += " 23:59:59";sql += " and regDate between '" + start + "' and '" + end + "'";}sql = AtoUTF8(sql);int nCols = -1;int nRows = -1;char** pResult = NULL;char* errMsg = NULL;int index = 0;const int ret = sqlite3_get_table(m_db, sql.c_str(), &pResult, &nRows, &nCols, &errMsg);index = nCols;for (int i = 0; i < nRows; i++){WLINFO info;int rowStart = (i + 1) * nCols;info.StudyId = info.AccNumber = pResult[rowStart + 0];info.PatientId = pResult[rowStart + 1];info.PatientName = UTF8toA(pResult[rowStart + 2]);info.Sex = UTF8toA(pResult[rowStart + 3]);info.StudyAge = UTF8toA(pResult[rowStart + 4]);info.Modality = pResult[rowStart + 5];info.RequestPhysician = UTF8toA(pResult[rowStart + 6]);info.RegistyDate = pResult[rowStart + 7];info.ExamItem = UTF8toA(pResult[rowStart + 8]);m_matchingDatasets.push_back(info);}m_pServer->log_info("查询到 %d 条检查记录", m_matchingDatasets.size());if (m_matchingDatasets.size() > 0){return WLM_PENDING;}return WLM_SUCCESS;
}

三、工程源码

下载WorklistServer.exe
下载工程源码

http://www.xdnf.cn/news/16732.html

相关文章:

  • Qt 移动应用性能优化策略
  • 复现cacti的RCE(CVE-2022-46169)
  • TDengine 中 TDgpt 异常检测的机器学习算法
  • Leetcode——41. 缺失的第一个正数
  • 数学建模——非线性规划
  • 大文档免费翻译方法分享
  • 政策合规性前端设计:工业数据安全的可视化技术规范与落地实践
  • C语言进阶(指针2.函数指针和指针函数,二级指针,指针数组和数组指针,void*指针)
  • 数据结构 排序(2)---选择排序
  • 使用鼠标在Canvas上绘制矩形
  • PDF转Word免费工具!批量处理PDF压缩,合并, OCR识别, 去水印, 签名等全功能详解
  • Shader开发(四)计算机图形学中的颜色定义
  • Java 大视界 -- Java 大数据机器学习模型在金融信用评级模型优化与信用风险动态管理中的应用(371)
  • Day23-二叉树的层序遍历(广度优先搜素)
  • [明道云]-基础教学2-工作表字段 vs 控件:选哪种?
  • Redis 跨主机连接超时分析:从网络波动到架构优化
  • 个人健康管理小程序(消息订阅、Echarts图形化分析)
  • TGD第八篇:二维应用——图像边缘检测
  • ftp加ssl,升级ftps
  • 三维扫描相机:工业自动化的智慧之眼——迁移科技赋能智能制造新纪元
  • 从东南亚出发:小程序容器技术如何助力 App 快速打入全球市场?
  • LeetCode 1616.分割两个字符串得到回文串
  • PHP性能优化与高并发处理:从基础到高级实践
  • 直播间里的酒旅新故事:内容正在重构消费链路
  • 设计模式:状态模式 State
  • 配置daemon.json使得 Docker 容器能够使用服务器GPU【验证成功】
  • 设计模式十三:代理模式(Proxy Pattern)
  • mac 字体遍历demo
  • 网络原理 - TCP/IP(一)
  • 大数据集分页优化:LIMIT OFFSET的替代方案