背景
由於我們檢驗對串口儀器已經有成熟的方案。而且是數據庫腳本語言通過串口轉網口連的。省去了連儀器要配連接電腦的開銷,同時不用在電腦部署環境和程序,用腳本調試也方便。對於儀器在電腦生成數據文件的、或者監聽對方數據庫、或者需要畫圖的等就不適合用串口轉網口走TCP的模式。爲此需要寫EXE運行在電腦上監聽處理數據。
碰到的問題有:
1.儀器數據格式各種各樣,需要監聽各種格式的數據(可能不同項目文件後綴都不同)。
2.儀器數據存放結構各種各樣,有都在一級目錄的,有在多級目錄的。
3.監聽非簡單文本數據(PDF、Excel、csv、直接數據生成mdb的等)
4.儀器廠商數據庫設計參差不齊(有的給LIS留了標記字段,有的連主鍵都難找出)
5.可能要按數據取指定地方的圖片,也可能要按數據繪圖,也肯能要選圖、截圖
6.監聽數據文件有的儀器的你不是監聽完刪掉(需要監聽變化)。監聽數據庫的要有重傳的問題。
7.怎麼確保監聽的兼容性,項目程序丟了用最新的肯定兼容老的,確保程序向着穩定發展,新加的公共功能之前所有的接口都能獲得,而不是越寫越散。
8.減少開發難度,避免陷入這些重複的繁瑣邏輯裏。
對於以上問題,如果採用碰到個儀器就從相似的儀器拷貝代碼寫一個EXE的做法。首先每個開發都要面臨處理上面這些繁瑣問題,基於拷貝程序帶過來的bug,新的修復了老的程序的bug還存在,接口質量得不到提高。新加的功能也無法惠及老的實現。雖然每個EXE的複雜度沒有統一模式的主程序大,不能實現產品化的把控。仔細觀察不難發現,對文件的監聽最後都可以提取爲一行行的數據讀取解析。對數據庫的監聽最後都可提取爲一行行數據行的讀取解析。對要上傳給儀器數據的最後都可提取爲查詢返回的一行行檢驗數據,按返回數據做不同操作(寫文件或者執行SQL等)。不同的儀器只是解析邏輯不同,對這些共性問題大家都是一致的。因此提取兩個接口:
儀器給檢驗數據處理接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace PreDeal.Interface
{
///<summary NoteObject="Class">
/// [功能描述:數據前處理接口,所有前處理改實現該接口,配置實現類實現相應的前處理] <para/>
/// [創建者:zlz] <para/>
/// [創建時間:2015年10月25日] <para/>
///<說明>
/// [說明:數據前處理接口,所有前處理改實現該接口,配置實現類實現相應的前處理]<para/>
///</說明>
///<修改記錄>
/// [修改時間:本次修改時間]<para/>
/// [修改內容:本次修改內容]<para/>
///</修改記錄>
///<修改記錄>
/// [修改時間:本次修改時間]<para/>
/// [修改內容:本次修改內容]<para/>
///</修改記錄>
///</summary>
public interface IPreDeal
{
/// <summary>
/// 前處理,實時處理用GetDataJSON方法調M及時響應,用PreDeal.Util.TxtUtil讀寫文本
/// </summary>
/// <param name="result">結果串</param>
/// <param name="machID">儀器ID</param>
/// <param name="dealProcess">處理程序</param>
/// <param name="index">當前文件的第幾行</param>
/// <param name="fileName">當前讀取的文件全名</param>
/// <returns>是否繼續後處理,true是,false否</returns>
bool PreDeal(string result,string machID, string dealProcess,int index,string fileName);
/// <summary>
/// 前處理數據庫,實時處理用GetDataJSON方法調M及時響應,用PreDeal.Util.TxtUtil讀寫文本
/// </summary>
/// <param name="row">當前行數據</param>
/// <param name="machID">儀器ID</param>
/// <param name="dealProcess">處理程序</param>
/// <param name="index">當前文件的第幾行</param>
/// <returns>是否繼續後處理,true是,false否</returns>
bool PreDealDataBase(DataRow row, string machID, string dealProcess, int index,string otherPara);
}
}
檢驗給儀器數據處理接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace PreDeal.Interface
{
///<summary NoteObject="Class">
/// [功能描述:上傳數據前處理接口,所有上傳前處理改實現該接口,配置實現類實現相應的前處理] <para/>
/// [創建者:zlz] <para/>
/// [創建時間:2015年10月25日] <para/>
///<說明>
/// [說明:數據前處理接口,所有前處理改實現該接口,配置實現類實現相應的前處理]<para/>
///</說明>
///<修改記錄>
/// [修改時間:本次修改時間]<para/>
/// [修改內容:本次修改內容]<para/>
///</修改記錄>
///<修改記錄>
/// [修改時間:本次修改時間]<para/>
/// [修改內容:本次修改內容]<para/>
///</修改記錄>
///</summary>
public interface IUPPreDeal
{
/// <summary>
/// 前處理
/// </summary>
/// <param name="labno">檢驗號</param>
/// <param name="labnoInfo">標本信息</param>
/// <param name="patInfo">患者信息</param>
/// <param name="upPara">配置的上傳前處理參數</param>
/// <param name="machID">儀器</param>
/// <param name="dealProcess">處理程序</param>
/// <returns>是否繼續後處理,true是,false否</returns>
bool UPPreDeal(string labno, string labnoInfo, string patInfo, string upPara, string machID, string dealProcess);
}
}
關係圖
通過以上結構實現的監聽將有以下特點:
1.主程序會隨發展趨於穩定化。
2.主程序加入的功能所有接口實現都會獲得。
3.主程序修復的bug所有接口實現都會修復。
4.只要接口實現提交代碼,監聽可以保證承諾的兼容性(新的程序可以替換老的)。
5.統一的日誌服務,統一的配置。
6.連儀器只要關注業務實現接口,不用關心別的繁瑣的處理。
要點
1.要面向接口編程
2.從各種差異裏抽取出大家的共性
3.結構比實現更重要,良好構思結構能朝預期方向良性發展,無結構只會越改越爛
實現事列
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PreDeal.Interface;
using LIS.Model.Bussiness;
using LIS.DAL.DataAccess;
using PreDeal.Ftp;
using PreDeal.Attributes;
namespace PreDeal
{
///<summary NoteObject="Class">
/// [功能描述:西森美康帶圖片的處理,處理數據格式] <para/>
/// [創建者:zlz] <para/>
/// [創建時間:2015年10月25日] <para/>
///<說明>
/// [說明:西森美康帶圖片的處理,處理數據格式
/// 0,2015-10-26,00:00:00,9999,XT1800I,41
/// 1,BASO#,2,0.01,,,,,
/// 1,BASO%,2,0.20,,,,,
/// 1,EO#,2,0.04,,,,,
/// 1,EO%,2,0.70,,,,,
/// 1,HCT,2,38.00,,,,,
/// 1,HFR,2,0,,,,,
/// 1,HGB,2,128.00,,,,,
/// 1,IRF,2,0,,,,,
/// 1,LFR,2,0,,,,,
/// 1,LYMPH#,2,1.52,,,,,
/// 1,LYMPH%,2,27.70,,,,,
/// 1,MCH,2,30.00,,,,,
/// 1,MCHC,2,337.00,,,,,
/// 1,MCV,2,89.20,,,,,
/// 1,MFR,2,0,,,,,
/// 1,MONO#,2,0.44,,,,,
/// 1,MONO%,2,8.00,,,,,
/// 1,MPV,2,9.80,,,,,
/// 1,NEUT#,2,3.48,,,,,
/// 1,NEUT%,2,63.40,,,,,
/// 1,NRBC#,2,0,,,,,
/// 1,NRBC%,2,0,,,,,
/// 1,P-LCR,2,23.50,,,,,
/// 1,PCT,2,23.00,,,,,
/// 1,PDW,2,11.40,,,,,
/// 1,PLT,2,234.00,,,,,
/// 1,RBC,2,4.26,,,,,
/// 1,RDW-CV,2,12.70,,,,,
/// 1,RDW-SD,2,40.80,,,,,
/// 1,RET#,2,0,,,,,
/// 1,RET%,2,0,,,,,
/// 1,WBC,2,5.49,,,,,
/// 3,,HPLT,D:\測試圖片\00000009999_151026145540_HPLT.gif,2
/// 3,,HRBC,D:\測試圖片\00000009999_151026145540_HRBC.gif,2
/// 3,,SBASO,D:\測試圖片\00000009999_151026145540_SBASO.gif,2
/// 3,,SDIFF,D:\測試圖片\00000009999_151026145540_SDIFF.gif,2
/// 3,,SNRBC,D:\測試圖片\00000009999_151026145540_SNRBC.gif,2
/// 3,,SPLT,D:\測試圖片\00000009999_151026145540_SPLT.gif,2
/// 3,,SPLT-O,D:\測試圖片\00000009999_151026145540_SPLT-O.gif,2
/// 3,,SRET,D:\測試圖片\00000009999_1510265540_SRET.gif,2
/// 3,,SRET-E,D:\測試圖片\00000009999_151026145540_SRET-E.gif,2
/// 9,41
/// ]<para/>
///</說明>
///<修改記錄>
/// [修改時間:本次修改時間]<para/>
/// [修改內容:本次修改內容]<para/>
///</修改記錄>
///<修改記錄>
/// [修改時間:本次修改時間]<para/>
/// [修改內容:本次修改內容]<para/>
///</修改記錄>
///</summary>
[Remark(Remark = "西施美康帶圖片的處理,處理數據格式。\n0,2015-10-26,00:00:00,9999,XT1800I,41\n1,BASO#,2,0.01,,,,,\n1,BASO%,2,0.20,,,,,\n3,,HPLT,D:\\測試圖片\00000009999_151026145540_HPLT.gif,2")]
public class DealImage : BaseDeal, IPreDeal
{
/// <summary>
/// 存流水號
/// </summary>
private static string episNo = "";
/// <summary>
/// 儀器代碼
/// </summary>
private static string machCode = "";
/// <summary>
/// 前處理
/// </summary>
/// <param name="result">結果</param>
/// <param name="machID">儀器ID</param>
/// <param name="dealProcess">處理程序</param>
/// <param name="index">當前文件的第幾行</param>
/// <param name="fileName">當前讀取的文件全名</param>
/// <returns></returns>
public bool PreDeal(string result, string machID, string dealProcess, int index, string fileName)
{
string[] strArr = result.Split(',');
if (strArr != null && strArr.Length > 0)
{
//第一行數據時清空流水號
if (index == 0)
{
episNo = "";
machCode = "";
}
if (index == -1)
{
DealData("", machID, episNo, dealProcess, index);
}
if (strArr[0] == "0")
{
//第一行數據提取流水號
episNo = result.Split(',')[3];
machCode = result.Split(',')[5];
DealData(DealNotSeeChar(result + "," + machCode), machID, episNo, dealProcess, index);
return false;
}
else if (strArr[0] == "1" || strArr[0] == "4" || strArr[0] == "9")
{
DealData(DealNotSeeChar(result + "," + machCode), machID, episNo, dealProcess, index);
return false;
}
else if (strArr[0] == "3")
{
//得到配置的處理程序處理數據
try
{
//圖片路徑
string imgPath = strArr[3];
if (System.IO.File.Exists(imgPath))
{
string ftpPath = "";
//得到ftp
FtpService ftp = GetFtpHelper(machID, dealProcess, out ftpPath);
//上傳圖片
ftp.Upload(imgPath);
System.IO.FileInfo fInfo = new System.IO.FileInfo(imgPath);
//保存圖片
SaveImg(machID, episNo, strArr[2], ftpPath.Split('^')[3] + fInfo.Name, dealProcess);
}
else
{
LIS.Core.Util.LogUtils.WriteDebugLog("文件不存在:" + imgPath);
}
}
catch (Exception ex)
{
LIS.Core.Util.LogUtils.WriteExceptionLog("獲得數據處理程序失敗", ex);
}
return false;
}
}
return true;
}
/// <summary>
/// 處理數據庫數據
/// </summary>
/// <param name="row">當前行數據</param>
/// <param name="machID">儀器ID</param>
/// <param name="dealProcess">處理程序</param>
/// <param name="index">當前數據的第幾行</param>
/// <param name="otherPara">其他參數</param>
/// <returns></returns>
public bool PreDealDataBase(System.Data.DataRow row, string machID, string dealProcess, int index, string otherPara)
{
throw new NotImplementedException();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PreDeal.Interface;
using PreDeal.Attributes;
using System.IO;
using PreDeal.Ftp;
namespace PreDeal.UPDeal
{
///<summary NoteObject="Class">
/// [功能描述:愛康唐篩上傳糖篩數據] <para/>
/// [創建者:zlz] <para/>
/// [創建時間:2015年10月25日] <para/>
///<說明>
/// [說明:默認的處理,配置改實現時實現讀一行傳一行的默認實現]<para/>
///</說明>
///<修改記錄>
/// [修改時間:本次修改時間]<para/>
/// [修改內容:本次修改內容]<para/>
///</修改記錄>
///<修改記錄>
/// [修改時間:本次修改時間]<para/>
/// [修改內容:本次修改內容]<para/>
///</修改記錄>
///</summary>
[Remark(Remark = "默認的處理,配置改實現時實現讀一行傳一行的默認實現")]
public class UpAiKangTangShai : BaseDeal, IUPPreDeal, IPreDeal
{
/// <summary>
/// 前處理
/// </summary>
/// <param name="labno">檢驗號</param>
/// <param name="labnoInfo">標本信息</param>
/// <param name="patInfo">患者信息</param>
/// <returns>是否繼續後處理,true是,false否</returns>
public bool UPPreDeal(string labno, string labnoInfo, string patInfo, string upPara, string machID, string dealProcess)
{
LIS.DAL.ORM.EntityManager.EntityManagerImpl manager = new LIS.DAL.ORM.EntityManager.EntityManagerImpl("OTAccessBaseDbFactory");
string[] infoArr = patInfo.Split('~');
string err;
//認爲m層組裝了sql語句
if (infoArr.Length > 1 && infoArr[1] != "")
{
LIS.Core.Util.LogUtils.WriteDebugLog("插入唐篩數據");
LIS.Core.Util.LogUtils.WriteDebugLog("執行SQL:" + infoArr[1]);
bool ret = manager.ExecUnSelectSQL(infoArr[1], out err);
if (err != "")
{
LIS.Core.Util.LogUtils.WriteDebugLog("插入唐篩數據出錯:" + err);
return false;
}
return true;
}
string sql = "insert into InputTable(Rep_No,Pat_Zip,Rep_Date,Pat_Name,Pat_Birthday,Pat_Addr,Pat_tel,Pat_Weight,Pat_Index,Rep_SampleDate,It_HCG,Pat_GestWeek,Pat_GestDay,Rep_Methor) values('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}','{13}')";
string[] patArr = patInfo.Split('^');
string[] labArr = labnoInfo.Split(',');
string LisPatientID = patArr[8];
if (LisPatientID == "")
{
LisPatientID = labno;
}
string LisPatientName = patArr[9];
string LisBirthday = patArr[13].Split(' ')[0];
string LisSampleID = labno+"^"+patArr[28];
string LisSamplingDate = patArr[0];
string LisTestDate = patArr[0].Split(' ')[0];
string LisDoctorName = patArr[6];
string LisLocation = patArr[5];
sql = String.Format(sql, LisSampleID, patArr[28], LisTestDate, LisPatientName, LisBirthday, patArr[29], patArr[30], patArr[31], patArr[32], LisTestDate, patArr[33], patArr[34], patArr[35], patArr[36]);
bool ret1 = manager.ExecUnSelectSQL(sql,out err);
if (err != "")
{
LIS.Core.Util.LogUtils.WriteDebugLog("插入唐篩數據出錯:"+err);
return false;
}
return true;
}
bool IPreDeal.PreDeal(string result, string machID, string dealProcess, int index, string fileName)
{
throw new NotImplementedException();
}
/// <summary>
/// 處理數據庫
/// </summary>
/// <param name="row"></param>
/// <param name="machID"></param>
/// <param name="dealProcess"></param>
/// <param name="index"></param>
/// <param name="otherPara"></param>
/// <returns></returns>
bool IPreDeal.PreDealDataBase(System.Data.DataRow row, string machID, string dealProcess, int index, string otherPara)
{
try
{
LIS.Core.Util.LogUtils.WriteDebugLog("繪製上傳唐篩圖片");
string episNo = row["ColA"].ToString();
string T21 = "1:" + row["ColL"].ToString();
string T18 = "1:" + row["ColM"].ToString();
string Name21 = DrawTangSaiImg("1:270", "T21", T21, "1:690", episNo);
LIS.Core.Util.LogUtils.WriteDebugLog("生成圖片文件:" + Name21);
string Name18 = DrawTangSaiImg("1:350", "T18", T18, "1:6200", episNo);
LIS.Core.Util.LogUtils.WriteDebugLog("生成圖片文件:" + Name18);
string ftpPath = "";
//得到ftp
FtpService ftp = GetFtpHelper(machID, dealProcess, out ftpPath);
LIS.Core.Util.LogUtils.WriteDebugLog("獲得FTP地址:" + ftpPath);
if (File.Exists(Name21))
{
System.IO.FileInfo fInfo21 = new System.IO.FileInfo(Name21);
//上傳圖片
ftp.Upload(Name21);
System.IO.File.Delete(Name21);
//保存圖片
SaveImg(machID, episNo, "T21", ftpPath.Split('^')[3] + fInfo21.Name, dealProcess);
}
if (File.Exists(Name18))
{
System.IO.FileInfo fInfo18 = new System.IO.FileInfo(Name18);
//上傳圖片
ftp.Upload(Name18);
System.IO.File.Delete(Name18);
//保存圖片
SaveImg(machID, episNo, "T18", ftpPath.Split('^')[3] + fInfo18.Name, dealProcess);
}
}
catch (Exception ex)
{
LIS.Core.Util.LogUtils.WriteExceptionLog("處理唐篩圖片異常", ex);
}
return true;
}
}
}