自己做的一个用于生成DICOM文件的服务器

框架: .ner core web api  .net8.0

Program.cs代码如下

using Microsoft.AspNetCore.HttpsPolicy;
using System.Diagnostics;

namespace PacsServer
{
    /* public class Program
     {
         public static void Main(string[] args)
         {
             //配置服务
             var builder = WebApplication.CreateBuilder(args);

             // 添加控制器服务,允许控制器处理HTTP请求
             builder.Services.AddControllers();


             //创建服务器
             var app = builder.Build();

             // 设置自定义端口,默认为 5000
             var port = "5001";
             if (args.Length > 0)
             {
                 port = args[0]; // 从命令行参数中获取端口号
             }

             //监听端口
             app.Urls.Add($"http://localhost:{port}");

             app.MapControllers();

             //允许服务器
             app.Run();
         }
     }*/
    public class Program
    {
        public static void Main(string[] args)
        {
            // 配置服务
            var builder = WebApplication.CreateBuilder(args);

            // 添加控制器服务,允许控制器处理 HTTP 请求
            builder.Services.AddControllers();

            //配置网页启动端
           /* builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();*/
          
            // 创建服务器
            var app = builder.Build();

            // 设置自定义端口,默认为 5000,暂关闭
            /*var port = "45101";
            if (args.Length > 0)
            {
                port = args[0]; // 从命令行参数中获取端口号
            }*/

            // 监听端口,部署iis上,暂关闭内部端口设置
            //app.Urls.Add($"http://localhost:{port}");

            //配置网页启动
           /* if (app.Environment.IsDevelopment())
            { }
            app.UseSwagger();
            app.UseSwaggerUI();           
            app.UseHttpsRedirection();
            app.UseAuthorization();*/

            app.MapControllers();

            // 自动打开浏览器访问应用程序
            //var url = $"http://localhost:{port}/swagger/index.html";
            //var url = $"{Environment.GetEnvironmentVariable("ASPNETCORE_URLS")}/swagger/index.html";
            //OpenBrowser(url);

            // 允许服务器
            app.Run();
     
        }
        private static void OpenBrowser(string url)
        {
            try
            {
                // 根据平台启动浏览器
                Process.Start(new ProcessStartInfo
                {
                    FileName = url,
                    UseShellExecute = true
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine($"无法启动浏览器: {ex.Message}");
            }
        }
    }

}
Model.Patient的代码如下
 
using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace PacsServer.Model
{
    public class Patient
    {
        public long CheckID { get; set; }
        public string PicturePath { get; set; }
        public string PatientName { get; set; }
        public int PatientAge { get; set; }

        public string PatientID { get; set; }
        public string CheckDate {  get; set; }
        public string DicomFilePath { get; set; }

        public bool AreRequiredPropertiesValid()
        {
            foreach (PropertyInfo prop in typeof(Patient).GetProperties())
            {
                if (prop.Name != nameof(PatientID) && prop.Name != nameof(DicomFilePath))
                {
                    var value = prop.GetValue(this);
                    if (value == null || (value is string strValue && string.IsNullOrWhiteSpace(strValue)))
                    {
                        return false;
                    }
                }
            }
            return true;
        }
    }
}
Func.DIcomFunc的代码如下
 
using FellowOakDicom;
using FellowOakDicom.Imaging;
using FellowOakDicom.IO.Buffer;
using FellowOakDicom.Network.Client;
using FellowOakDicom.Network;
using PacsServer.Controllers;
using PacsServer.Model;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using Xunit;
using Newtonsoft.Json;

namespace PacsServer.Func
{
    public class DicomFunc
    {
        public DicomFunc()
        {        

        }

        public static void DicomCreat(Patient patientMsg)
        {
            //判断数据是否存在异常
            if (!patientMsg.AreRequiredPropertiesValid())
            {
                return;
            }

            //检测图片格式是否正常
            string[] validExtensions = { ".jpg", ".jpeg", ".bmp", ".png", ".gif", ".tiff" };
            bool isTruePath = validExtensions.Any(ext => patientMsg.PicturePath.EndsWith(ext, StringComparison.OrdinalIgnoreCase));
            if (!isTruePath)
            {
                return;
            }

            //日期格式纠正
            DateTime birthDate = DateTime.ParseExact(patientMsg.CheckDate, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture).Date;
            patientMsg.CheckDate = birthDate.ToString("yyyyMMdd");
            //年龄纠正
            string patientAgeString = patientMsg.PatientAge.ToString("D3") + "Y";

            patientMsg.PatientID = patientMsg.PatientName + patientMsg.PatientAge;              //暂时定义为名字+年龄

            byte[] pixels;
            using (Bitmap bitmap = new Bitmap(patientMsg.PicturePath))
            {
                pixels = GetPixels(bitmap); // 读取像素数据
                                            // 在 using 语句块结束时,Bitmap 资源会被自动释放

                MemoryByteBuffer buffer = new MemoryByteBuffer(pixels);

                //设置数据集
                var dataset = new DicomDataset
            {
                { DicomTag.SpecificCharacterSet,"GB18030" },
                { DicomTag.PatientName, patientMsg.PatientName },
                { DicomTag.PatientID, patientMsg.PatientID },
                { DicomTag.PatientAge,  patientAgeString},
                { DicomTag.StudyDate, patientMsg.CheckDate},
                { DicomTag.SeriesDate,  patientMsg.CheckDate },

                { DicomTag.PhotometricInterpretation, PhotometricInterpretation.Rgb.Value },
                { DicomTag.Rows, (ushort)bitmap.Height },
                { DicomTag.Columns, (ushort)bitmap.Width },
                { DicomTag.BitsAllocated, (ushort)8 },
                { DicomTag.BitsStored, (ushort)8 },
                { DicomTag.HighBit, (ushort)7 },
                { DicomTag.SamplesPerPixel, (ushort)3 },
                { DicomTag.SOPClassUID, DicomUID.VideoEndoscopicImageStorage },    //内窥镜成像
                { DicomTag.SOPInstanceUID, DicomUID.Generate() }
            };

                //同次检查判断
                PatientService _patientService = new PatientService();
                var uidClass = _patientService.GetOrCreateUIDs(patientMsg.CheckID);

                dataset.AddOrUpdate(DicomTag.StudyInstanceUID, uidClass.GetStudyInstanceUID());
                dataset.AddOrUpdate(DicomTag.SeriesInstanceUID, uidClass.GetSeriesInstanceUID());

                //设置像素数据
                DicomPixelData pixelData = DicomPixelData.Create(dataset, true);
                pixelData.BitsStored = 8;
                pixelData.SamplesPerPixel = 3;
                pixelData.HighBit = 7;
                pixelData.PixelRepresentation = PixelRepresentation.Unsigned;
                pixelData.PlanarConfiguration = PlanarConfiguration.Interleaved;
                pixelData.AddFrame(buffer);

                DicomFile dicomFile = new DicomFile(dataset);
                int index = patientMsg.PicturePath.LastIndexOf('\\');
                string filePath = patientMsg.PicturePath.Substring(0, index) + "\\dcmfile";
                string imagePath = patientMsg.PicturePath.Substring(index + 1);
                imagePath = System.IO.Path.GetFileNameWithoutExtension(imagePath);
                if (!Directory.Exists(filePath))
                {
                    Directory.CreateDirectory(filePath);
                }
                patientMsg.DicomFilePath = filePath + "\\" + imagePath + ".dcm";
                dicomFile.Save(patientMsg.DicomFilePath);
                Console.WriteLine("createSuccess");


                //能否ping通
                if (PacsConfigController.IsServerReachable)
                {
                    DicomUpdataAsync(patientMsg.DicomFilePath);
                    Console.WriteLine("uploadSuccess");
                }
                else
                {
                    Console.WriteLine("pacs服务器不可达!");
                }
            }
        }

        /// <summary>
        /// 上传dcm文件
        /// </summary>
        /// <param name="dicomFilePath"></param>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="TimeoutException"></exception>
        private static async void DicomUpdataAsync(string dicomFilePath)
        {
            var client = DicomClientFactory.Create(ServerConfig.ServerAddress, int.Parse(ServerConfig.Port), false, "Colposcope", ServerConfig.AET);
            client.ClientOptions.AssociationRequestTimeoutInMs = (int)TimeSpan.FromMinutes(5).TotalMilliseconds;

            DicomCStoreResponse response = null;
            DicomRequest.OnTimeoutEventArgs timeout = null;
            try
            {
                var request = new DicomCStoreRequest(dicomFilePath)
                {
                    OnResponseReceived = (req, res) => response = res,
                    OnTimeout = (sender, args) => timeout = args
                };
                await client.AddRequestAsync(request);
                await client.SendAsync();

                // 验证响应
                if (response == null) { throw new InvalidOperationException("Response is null."); }
                if (response.Status != DicomStatus.Success) { throw new InvalidOperationException("Response status is not successful."); }
                if (timeout != null) { throw new TimeoutException("Operation timed out."); }
            }
            catch (Exception ex)
            {
                throw;
            }

        }

        public async static Task<bool> DicomPing(ServerConfigDTO server)
        {
            var client = DicomClientFactory.Create(server.ServerAddress, int.Parse(server.Port), false, "Colposcope", server.AET);

            client.ClientOptions.AssociationRequestTimeoutInMs = (int)TimeSpan.FromMinutes(5).TotalMilliseconds; // 设置关联请求超时时间

            DicomCEchoResponse response = null;
            DicomRequest.OnTimeoutEventArgs timeout = null;

            var request = new DicomCEchoRequest
            {
                OnResponseReceived = (req, res) => response = res, // 响应接收事件
                OnTimeout = (sender, args) => timeout = args // 超时事件
            };

            await client.AddRequestAsync(request); // 添加C-Echo请求

            try
            {
                await client.SendAsync(); // 发送请求

                // 验证响应
                if (response == null) { return false; }
                if (response.Status != DicomStatus.Success){ return false;}
                if (timeout != null){ return false;}
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }


        public static byte[] GetPixels(Bitmap bitmap)
        {
            byte[] bytes = new byte[bitmap.Width * bitmap.Height * 3];
            int width = bitmap.Width;
            int height = bitmap.Height;
            int i = 0;

            for (int y = 0; y < height; y++)    //上到下
            {
                for (int x = 0; x < width; x++) //左到右
                {
                    var srcColor = bitmap.GetPixel(x, y);
                    //bytes[i] = (byte)(srcColor.R * .299 + srcColor.G * .587 + srcColor.B * .114);    // 取消注释生成灰度图
                    bytes[i] = srcColor.R;
                    i++;
                    bytes[i] = srcColor.G;
                    i++;
                    bytes[i] = srcColor.B;
                    i++;
                }
            }
            return bytes;
        }       

    }

    public class UIDClass
    {
        public string StudyInstanceUID { get; private set; }
        public string SeriesInstanceUID { get; private set; }

        public UIDClass(string studyInstanceUID, string seriesInstanceUID)
        {
            StudyInstanceUID = studyInstanceUID;
            SeriesInstanceUID = seriesInstanceUID;
        }

        public string GetStudyInstanceUID()
        {
            return StudyInstanceUID;
        }

        public string GetSeriesInstanceUID()
        {
            return SeriesInstanceUID;
        }
    }

    public class PatientService
    {
        private readonly string filePath;
        public PatientService()
        {
            filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Patient.json");
        }

        // 加载表中的数据
        public Dictionary<long, UIDClass> LoadPatients()
        {
            if (!File.Exists(filePath))
            {
                return new Dictionary<long, UIDClass>();
            }

            var json = File.ReadAllText(filePath);
            return JsonConvert.DeserializeObject<Dictionary<long, UIDClass>>(json);
        }

        // 保存数据到表
        public void SavePatients(Dictionary<long, UIDClass> patients)
        {
            var json = JsonConvert.SerializeObject(patients, Formatting.Indented);
            File.WriteAllText(filePath, json);
        }

        // 获取或创建UIDClass对象
        public UIDClass GetOrCreateUIDs(long patientID)
        {
            var patients = LoadPatients();

            if (!patients.TryGetValue(patientID, out var uidClass))
            {
                uidClass = new UIDClass(
                    DicomUID.Generate().UID,
                    DicomUID.Generate().UID
                );
                patients[patientID] = uidClass;
                SavePatients(patients);
            }

            return uidClass;
        }
    }
}
Controllers.DicomController的代码如下
 
using Microsoft.AspNetCore.Mvc;
using PacsServer.Func;
using PacsServer.Model;
namespace PacsServer.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class DicomController : ControllerBase
    {

        [HttpPost("execute")]
        public IActionResult Execute([FromBody] Patient patientMsg)
        {

            if (patientMsg == null || !ModelState.IsValid)
            {
                foreach (var error in ModelState.Values.SelectMany(v => v.Errors))
                {
                    Console.WriteLine(error.ErrorMessage);
                }
                return BadRequest(ModelState);
            }

            DicomFunc.DicomCreat(patientMsg);

            return Ok("Request processed successfully");
        }
    }
}
Controllers.PacsConfigController1的代码如下
using Microsoft.AspNetCore.Mvc;
using PacsServer.Func;

namespace PacsServer.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class PacsConfigController : ControllerBase
    {
        public static bool IsServerReachable { get; set; } = false; //pacs服务器是否可达
        [HttpPut("config")]
        public async Task<IActionResult> Config([FromBody] ServerConfigDTO serverConfig)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            //赋值逻辑
            ServerConfig.ServerAddress = serverConfig.ServerAddress;
            ServerConfig.Port = serverConfig.Port;
            ServerConfig.AET = serverConfig.AET;

            IsServerReachable = await DicomFunc.DicomPing(serverConfig);      //测试服务器能否ping通

            if (IsServerReachable)
            {
                Console.WriteLine("pingSuccess");
                return Ok();
            }
            else
            {
                return StatusCode(StatusCodes.Status500InternalServerError, "Failed to ping DICOM server");
            }
        }

    }


    public  class  ServerConfig
    {
        public static string ServerAddress { get; set; }
        public static string Port { get; set; }
        public static string AET { get; set; }
    }

    public class ServerConfigDTO
    {
        public string ServerAddress { get; set; } = string.Empty;
        public string Port { get; set; } = string.Empty;
        public string AET { get; set; } = string.Empty;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值