551 lines
20 KiB
JavaScript
551 lines
20 KiB
JavaScript
const dicomParser = require("dicom-parser");
|
|
|
|
const BaseController = require("./base");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
function getObjectMkdir(url) {
|
|
let index = url.lastIndexOf("/")
|
|
let a = url.substring(0, index);
|
|
return a
|
|
}
|
|
|
|
function getObjectName(url) {
|
|
var value = url
|
|
var str = value.split("/"); //https://进行分割,
|
|
var name = str[str.length - 1]
|
|
return name
|
|
}
|
|
|
|
function parseDicomFile(file, uploadQueues, path) {
|
|
return new Promise(function (resolve, reject) {
|
|
try {
|
|
var data = dicomParser.parseDicom(file)
|
|
var studyUid = data.string('x0020000d')
|
|
var studyIndex = 0
|
|
while (
|
|
studyIndex < uploadQueues.length &&
|
|
uploadQueues[studyIndex].dicomInfo.studyUid !== studyUid
|
|
) {
|
|
++studyIndex
|
|
}
|
|
if (studyIndex >= uploadQueues.length) {
|
|
var date = data.string('x00080020')
|
|
var time = data.string('x00080030')
|
|
var studyTime = ''
|
|
if (date) {
|
|
// date = `${date.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}`
|
|
date = `${date.substring(0, 4)}-${date.substring(4, 6)}-${date.substring(6, 8)}`
|
|
}
|
|
if (time) {
|
|
time = `${time.substring(0, 2)}:${time.substring(2, 4)}:${time.substring(4, 6)}`
|
|
}
|
|
if (date) {
|
|
studyTime = time ? `${date} ${time}` : `${date} 00:00:00`
|
|
}
|
|
uploadQueues.push({
|
|
studyIndex: studyIndex,
|
|
seriesList: [],
|
|
fileList: [],
|
|
dicomInfo: {
|
|
studyUid: studyUid,
|
|
patientId: data.string('x00100020'),
|
|
patientName: data.string('x00100010') ? data.string('x00100010') : '',
|
|
patientAge: data.string('x00101010') ? data.string('x00101010') : '',
|
|
patientSex: data.string('x00100040') ? data.string('x00100040') : '',
|
|
patientBirthDate: data.string('x00100030'),
|
|
hospitalName: data.string('x00080080'),
|
|
accNumber: data.string('x00080050'),
|
|
bodyPart: data.string('x00180015'),
|
|
modality: [],
|
|
institutionName: data.string('x00080080'),
|
|
description: data.string('x00081030'),
|
|
studyTime: studyTime,
|
|
VisitInfo: {},
|
|
SubjectInfo: {}
|
|
},
|
|
|
|
uploadState: {
|
|
selected: false,
|
|
stateCode: '',
|
|
beginUploadTime: {},
|
|
progressValue: 0
|
|
}
|
|
})
|
|
}
|
|
var modality = uploadQueues[studyIndex].dicomInfo.modality
|
|
var currentModality = data.string('x00080060')
|
|
if (!(modality.indexOf(currentModality) > -1)) {
|
|
modality.push(currentModality)
|
|
}
|
|
|
|
var fileList = uploadQueues[studyIndex].fileList
|
|
var instanceUid = data.string('x00080018')
|
|
var instanceIndex = 0
|
|
while (
|
|
instanceIndex < fileList.length &&
|
|
fileList[instanceIndex].instanceUid !== instanceUid
|
|
) {
|
|
++instanceIndex
|
|
}
|
|
if (instanceIndex >= fileList.length) {
|
|
fileList.push({
|
|
instanceUid: instanceUid,
|
|
file: file
|
|
})
|
|
}
|
|
|
|
var seriesUid = data.string('x0020000e')
|
|
var seriesList = uploadQueues[studyIndex].seriesList
|
|
var seriesItem = seriesList.find(function (item) {
|
|
return item.seriesUid === seriesUid
|
|
})
|
|
if (!seriesItem) {
|
|
seriesItem = {
|
|
seriesUid: seriesUid,
|
|
seriesNumber: data.intString('x00200011') || 1,
|
|
modality: data.string('x00080060'),
|
|
description: data.string('x0008103e'),
|
|
instanceList: []
|
|
}
|
|
seriesList.push(seriesItem)
|
|
}
|
|
var instanceList = seriesItem.instanceList
|
|
var instanceItem = instanceList.find(function (item) {
|
|
return item.instanceUid === instanceUid
|
|
})
|
|
if (!instanceItem) {
|
|
instanceItem = {
|
|
instanceUid: instanceUid,
|
|
path: path,
|
|
modality: data.string('x00080060'),
|
|
instanceNumber: data.intString('x00200013') || 1,
|
|
frameCount: data.intString('x00280008') || 1,
|
|
file: file
|
|
}
|
|
instanceList.push(instanceItem)
|
|
}
|
|
resolve(uploadQueues)
|
|
} catch (error) {
|
|
console.log(error)
|
|
resolve()
|
|
}
|
|
})
|
|
}
|
|
|
|
|
|
class PacsController extends BaseController {
|
|
constructor(props) {
|
|
super(props);
|
|
}
|
|
//获取pacs位置
|
|
async getPACS() {
|
|
try {
|
|
console.log(this.ctx.request.query)
|
|
let pacs = await this.ctx.model.Pacs.find(this.ctx.request.query).select("_id aet host port mode description")
|
|
return this.success(pacs)
|
|
} catch (error) {
|
|
return this.error(error)
|
|
}
|
|
}
|
|
async findReading() {
|
|
try {
|
|
this.ctx.validate({
|
|
trialCode: 'string',
|
|
subjectCode: 'string',
|
|
TaskCode: 'string',
|
|
})
|
|
let config = await this.service.config.getConfig();
|
|
console.log(config, 'config')
|
|
let a = await this.ctx.service.pacs.findFromPacs(this.ctx.request.body, config)
|
|
console.log(a, 'a')
|
|
if (a) {
|
|
let $in = a.map(v => { return v.SeriesInstanceUID })
|
|
console.log($in)
|
|
let list = await this.ctx.model.Series.find({ SeriesInstanceUID: { $in: $in } })
|
|
// let list = await this.ctx.model.Pacs.find({})
|
|
console.log(list)
|
|
let newA = a.map(v => {
|
|
let i = list.find(o => {
|
|
return o.SeriesInstanceUID == v.SeriesInstanceUID
|
|
})
|
|
return {
|
|
...v,
|
|
...i._doc
|
|
}
|
|
})
|
|
return this.success(newA)
|
|
} else {
|
|
return this.error('找不到影像')
|
|
}
|
|
} catch (e) {
|
|
return this.error(e)
|
|
}
|
|
}
|
|
async pullImage() {
|
|
try {
|
|
this.ctx.validate({
|
|
pacs_id: 'string',
|
|
userName: 'string',
|
|
userId: 'string',
|
|
task_id: 'string',
|
|
trialCode: 'string',
|
|
trialId: 'string',
|
|
siteId: 'string',
|
|
subjectId: 'string',
|
|
subjectVisitId: 'string',
|
|
subjectCode: 'string',
|
|
TaskCode: 'string',
|
|
seriesList: 'array'
|
|
})
|
|
let { pacs_id, seriesList, trialId, siteId, subjectId, subjectVisitId, userName, userId, task_id, trialCode, subjectCode, TaskCode } = this.ctx.request.body
|
|
let config = await this.service.config.getConfig();
|
|
for (let i = 0; i < seriesList.length; i++) {
|
|
let series = seriesList[i]
|
|
await this.ctx.service.pacs.movescu(this.ctx.request.body, series, config)
|
|
await this.ctx.model.Series.findOneAndUpdate({
|
|
SeriesInstanceUID: series,
|
|
}, {
|
|
$set: {
|
|
// 拉取中
|
|
status: 1,
|
|
userName: userName,
|
|
userId: userId
|
|
}
|
|
}, { upsert: true, new: true })
|
|
}
|
|
let directoryPath = config.ossPacsPath + `/${trialCode}/${subjectCode}/${TaskCode}/${userId}/`
|
|
let pathList = fs.readdirSync(directoryPath)
|
|
let uploadQueues = []
|
|
for (let i = 0; i < pathList.length; i++) {
|
|
let item = path.join(directoryPath, pathList[i])
|
|
let file = fs.readFileSync(item)
|
|
await parseDicomFile(file, uploadQueues, item)
|
|
}
|
|
console.log(uploadQueues, 'uploadQueues')
|
|
const Authorization = this.ctx.get('Authorization');
|
|
let arr = []
|
|
for (let i = 0; i < uploadQueues.length; ++i) {
|
|
let info = {
|
|
trialId: trialId,
|
|
siteId: siteId,
|
|
subjectId: subjectId,
|
|
subjectVisitId: subjectVisitId,
|
|
fileSize: uploadQueues[i].dicomInfo.fileSize,
|
|
fileCount: uploadQueues[i].dicomInfo.fileCount,
|
|
IsDicomReUpload: uploadQueues[i].uploadState.AllowReUpload,
|
|
visitTaskId: task_id,
|
|
Authorization: Authorization,
|
|
}
|
|
arr.push(this.ctx.service.pacs.archiveStudy(uploadQueues, i, info, config))
|
|
}
|
|
let a = await Promise.all(arr)
|
|
// 获取
|
|
return this.success(a)
|
|
} catch (e) {
|
|
return this.error(e)
|
|
}
|
|
}
|
|
async pushImage() {
|
|
try {
|
|
this.ctx.validate({
|
|
pacs_id: 'string',
|
|
imagesList: 'array',
|
|
task_id: 'string',
|
|
trialCode: 'string',
|
|
subjectCode: 'string',
|
|
TaskCode: 'string',
|
|
})
|
|
let {
|
|
pacs_id, imagesList, task_id, trialCode, subjectCode, TaskCode
|
|
} = this.ctx.request.body
|
|
let pacs = await this.ctx.model.Pacs.findOne({ _id: pacs_id })
|
|
// let config = await this.service.config.getConfig();
|
|
let config = {}
|
|
let downloadErrorList = [], downloadsuccessList = []
|
|
let studyList = []
|
|
config.ossPacsPath = path.join(__dirname, "../public/DICOM")
|
|
for (let i = 0; i < imagesList.length; i++) {
|
|
let image = imagesList[i]
|
|
fs.mkdirSync(config.ossPacsPath + getObjectMkdir(`/${trialCode}/${subjectCode}/${TaskCode}/initial_data/1`), { recursive: true });
|
|
await this.service.oss.downloadImage(image.InstancePath, `${config.ossPacsPath}/${trialCode}/${subjectCode}/${TaskCode}/initial_data/${getObjectName(image.InstancePath)}`, config)
|
|
let status_result = await this.ctx.model.Status.findOneAndUpdate({
|
|
task_id: task_id
|
|
}, {
|
|
$set: {
|
|
downloadNum: i + 1,
|
|
status: '下载中',
|
|
downloadCount: imagesList.length
|
|
}
|
|
}, { upsert: true, new: true })
|
|
if (!~studyList.indexOf(`${config.ossPacsPath}/${trialCode}/${subjectCode}/${TaskCode}/initial_data`)) {
|
|
studyList.push(`${config.ossPacsPath}/${trialCode}/${subjectCode}/${TaskCode}/initial_data`)
|
|
}
|
|
}
|
|
console.log(studyList)
|
|
let status_result = await this.ctx.model.Status.findOneAndUpdate({
|
|
task_id: task_id
|
|
}, {
|
|
$set: {
|
|
status: '下载完成',
|
|
}
|
|
}, { upsert: true, new: true })
|
|
// 开始推送
|
|
for (let i = 0; i < studyList.length; i++) {
|
|
let study = studyList[i]
|
|
let a = await this.ctx.service.pacs.storescu(pacs, config, study)
|
|
let status_result = await this.ctx.model.Status.findOneAndUpdate({
|
|
task_id: task_id
|
|
}, {
|
|
$set: {
|
|
status: '推送中',
|
|
}
|
|
}, { upsert: true, new: true })
|
|
}
|
|
await this.ctx.model.Status.findOneAndUpdate({
|
|
task_id: task_id
|
|
}, {
|
|
$set: {
|
|
status: '推送完成',
|
|
}
|
|
}, { upsert: true, new: true })
|
|
return this.success({ downloadErrorList, downloadsuccessList })
|
|
} catch (error) {
|
|
return this.error(error)
|
|
}
|
|
|
|
}
|
|
//新增pacs位置
|
|
async addPACS() {
|
|
try {
|
|
this.ctx.validate({
|
|
trialId: 'string',
|
|
host: 'string',
|
|
port: 'string',
|
|
aet: 'string',
|
|
})
|
|
let {
|
|
host, port, aet, trialId
|
|
} = this.ctx.request.body
|
|
let para = this.ctx.request.body
|
|
let pacs = await this.ctx.model.Pacs.findOne({
|
|
host, port, aet, trialId
|
|
})
|
|
if (pacs) {
|
|
return this.error("PACS位置已存在", 60001)
|
|
} else {
|
|
pacs = this.ctx.model.Pacs(para)
|
|
let result = await pacs.save()
|
|
return this.success(result)
|
|
}
|
|
} catch (error) {
|
|
return this.error(error)
|
|
}
|
|
}
|
|
async editPacs() {
|
|
this.ctx.validate({
|
|
trialId: 'string',
|
|
host: 'string',
|
|
port: 'string',
|
|
aet: 'string',
|
|
})
|
|
let {
|
|
_id, host, port, aet, mode, trialId, description
|
|
} = this.ctx.request.body
|
|
let para = this.ctx.request.body
|
|
let result
|
|
await this.ctx.model.Pacs.findOneAndUpdate({
|
|
_id: _id
|
|
}, {
|
|
$set: {
|
|
// 拉取中
|
|
host: host,
|
|
port: port,
|
|
aet: aet,
|
|
mode: mode,
|
|
description: description
|
|
}
|
|
}, { upsert: true, new: true })
|
|
return this.success(result)
|
|
}
|
|
//删除pacs位置
|
|
async delPACS() {
|
|
try {
|
|
this.ctx.validate({
|
|
pacs_id: {
|
|
type: 'string',
|
|
format: this.config.regexp.objectId
|
|
}
|
|
})
|
|
let {
|
|
pacs_id
|
|
} = this.ctx.request.body
|
|
let pacs = await this.ctx.model.Pacs.findOne({
|
|
_id: pacs_id
|
|
});
|
|
if (!pacs) throw '未找到该PACS'
|
|
await this.ctx.model.Pacs.deleteOne({
|
|
_id: pacs_id
|
|
});
|
|
return this.success()
|
|
} catch (e) {
|
|
return this.error(e);
|
|
}
|
|
}
|
|
|
|
//测试连接
|
|
async testConnection() {
|
|
try {
|
|
this.ctx.validate({
|
|
pacs_ids: "array"
|
|
})
|
|
} catch (error) {
|
|
return this.error(error)
|
|
}
|
|
|
|
try {
|
|
let { pacs_ids } = this.ctx.request.body
|
|
if (!pacs_ids.length) return this.error("参数为空")
|
|
let result = await this.ctx.service.pacs.testConnection(pacs_ids)
|
|
return this.success(result)
|
|
} catch (error) {
|
|
let msg = error
|
|
if (error.message) {
|
|
msg = error.message.split('\n')
|
|
}
|
|
return this.error(msg)
|
|
}
|
|
}
|
|
async wxFindFromPacs() {
|
|
try {
|
|
let {
|
|
PatientID
|
|
} = this.ctx.request.body
|
|
if (!PatientID) {
|
|
throw 'PatientID必填'
|
|
}
|
|
let result = await this.ctx.service.pacs.findFromPacs({ PatientID: PatientID })
|
|
if (result.length > 0) {
|
|
let item = result[0]
|
|
let isHas = await this.ctx.model.Dcmtemp.findOne({ StudyInstanceUID: item.StudyInstanceUID })
|
|
if (isHas) {
|
|
this.ctx.model.Dcmtemp.findOneAndUpdate({
|
|
_id: isHas._id
|
|
}, {
|
|
$set: {
|
|
host: item.host,
|
|
port: item.port,
|
|
aet: item.aet,
|
|
done: 0,
|
|
NumberOfStudyRelatedInstances: item.NumberOfStudyRelatedInstances,
|
|
StudyInstanceUID: item.StudyInstanceUID,
|
|
status: 0
|
|
}
|
|
}).exec()
|
|
} else {
|
|
let params = {
|
|
host: item.host,
|
|
port: item.port,
|
|
aet: item.aet,
|
|
done: 0,
|
|
NumberOfStudyRelatedInstances: item.NumberOfStudyRelatedInstances,
|
|
StudyInstanceUID: item.StudyInstanceUID,
|
|
status: 0
|
|
}
|
|
let m = new this.ctx.model.Dcmtemp(params)
|
|
m.save()
|
|
}
|
|
}
|
|
return this.success(result)
|
|
} catch (error) {
|
|
let msg = error
|
|
if (error.message) {
|
|
msg = error.message.split('\n')
|
|
}
|
|
return this.error(msg)
|
|
}
|
|
}
|
|
|
|
//查询
|
|
async findFromPacs() {
|
|
try {
|
|
let result = await this.ctx.service.pacs.findFromPacs(this.ctx.request.body)
|
|
return this.success(result)
|
|
} catch (error) {
|
|
let msg = error
|
|
if (error.message) {
|
|
msg = error.message.split('\n')
|
|
}
|
|
return this.error(msg)
|
|
}
|
|
}
|
|
|
|
//获取下载进度
|
|
async percent() {
|
|
try {
|
|
this.ctx.validate({
|
|
task_ids: "array"
|
|
})
|
|
} catch (error) {
|
|
return this.error(error)
|
|
}
|
|
let { task_ids } = this.ctx.request.body
|
|
let result = await this.ctx.model.Status.find({ task_id: { $in: task_ids } })
|
|
return this.success(result)
|
|
}
|
|
//下载
|
|
async moveFromPacs() {
|
|
// 验证数据
|
|
try {
|
|
this.ctx.validate({
|
|
para: "array"
|
|
})
|
|
} catch (e) {
|
|
return this.error(e)
|
|
}
|
|
let { para } = this.ctx.request.body
|
|
try {
|
|
for (let i = 0; i < para.length; i++) {
|
|
let item = para[i]
|
|
let isHas = await this.ctx.model.Dcmtemp.findOne({ StudyInstanceUID: item.StudyInstanceUID })
|
|
if (isHas) {
|
|
this.ctx.model.Dcmtemp.findOneAndUpdate({
|
|
_id: isHas._id
|
|
}, {
|
|
$set: {
|
|
host: item.host,
|
|
port: item.port,
|
|
aet: item.aet,
|
|
done: 0,
|
|
NumberOfStudyRelatedInstances: item.NumberOfStudyRelatedInstances,
|
|
StudyInstanceUID: item.StudyInstanceUID,
|
|
status: 0
|
|
}
|
|
}).exec()
|
|
} else {
|
|
let params = {
|
|
host: item.host,
|
|
port: item.port,
|
|
aet: item.aet,
|
|
done: 0,
|
|
NumberOfStudyRelatedInstances: item.NumberOfStudyRelatedInstances,
|
|
StudyInstanceUID: item.StudyInstanceUID,
|
|
status: 0
|
|
}
|
|
let m = new this.ctx.model.Dcmtemp(params)
|
|
m.save()
|
|
}
|
|
}
|
|
return this.success()
|
|
} catch (error) {
|
|
return this.error(error)
|
|
}
|
|
}
|
|
|
|
//study抽取report
|
|
async getLocalPort() {
|
|
return this.success({ port: this.config.site.PACS_SCP_PORT, aet: this.config.site.PACS_SCP_AET })
|
|
}
|
|
}
|
|
module.exports = PacsController;
|