对检验系统来说,监听程序是什么?
是仪器接口?
是打印环境?
是虚拟盒子?
是导出环境?
这次结合打印解析监听程序消息链,为何打印、截图等需要开启着监听。
浏览器是BS的网页,打印截图等是CS程序,首先就不得不说到BS与CS交互的问题了。检验采用下图结构的多条通道进行尝试的模式交互。后期版本交互的关键就是WebSockt方式。
WebSockt的服务在哪里,当然寄宿在监听程序里面了。当然是为了客户端环境单一,其实可以寄宿在任意EXE里的,如下图,监听启动时候就把WebSockt服务起来了
以下才是本次分享的关键
常规写法下在消息服务里收到消息后写死消息处理的逻辑不就完事了。如下面模式:
socket.OnMessage = message =>
{
//按message内容写处理逻辑就完事,分支多就来几个if判断
}
一旦按以上那么写了的话,那么消息作为通道的想法就没用了,有新业务需要借助消息就只能不断加入代码和判断处理,也使得消息不在单纯、稳定。
为此:先抽离消息处理接口如下。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fleck;
namespace LIS.MsgCore
{
///<summary NoteObject="Class">
/// [功能描述:消息处理接口,不同消息处理实现该接口] <para/>
/// [创建者:zlz] <para/>
/// [创建时间:2017年03月29日] <para/>
///<说明>
/// [说明:监听程序主窗口]<para/>
///</说明>
///<修改记录>
/// [修改时间:本次修改时间]<para/>
/// [修改内容:本次修改内容]<para/>
///</修改记录>
///<修改记录>
/// [修改时间:本次修改时间]<para/>
/// [修改内容:本次修改内容]<para/>
///</修改记录>
///</summary>
public interface IMessageDeal
{
/// <summary>
/// 处理消息
/// </summary>
/// <param name="socket">套接字,可以获得id,发送消息给socket</param>
/// <param name="message">约定#分割的第一位描述消息类型,收到的消息内容</param>
/// <returns>是否继续往后传递消息,true是,false否</returns>
bool DealMessage(IWebSocketConnection socket,string message);
}
}
然后再定义一个消息链对象用来支持配置消息处理链
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LIS.MsgCore
{
///<summary NoteObject="Class">
/// [功能描述:消息处理链] <para/>
/// [创建者:zlz] <para/>
/// [创建时间:2017年03月28日] <para/>
///<说明>
/// [说明:路径工具类,提供路径操作]<para/>
///</说明>
///<修改记录>
/// [修改时间:本次修改时间]<para/>
/// [修改内容:本次修改内容]<para/>
///</修改记录>
///<修改记录>
/// [修改时间:本次修改时间]<para/>
/// [修改内容:本次修改内容]<para/>
///</修改记录>
///</summary>
public class MsgDealLink
{
/// <summary>
/// 处理连接集合
/// </summary>
public List<IMessageDeal> LinkList
{
get;
set;
}
}
}
然后消息服务收到消息后就只针对消息链的消息接口处理。调用消息链的每个对象来处理消息,直到返回false或者整个消息链都调完了,要点如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LIS.MsgCore;
using Fleck;
using System.Timers;
using System.Net.NetworkInformation;
using System.Net;
namespace LISMonitor
{
public class MessageServer
{
/// <summary>
/// 改变图标委托
/// </summary>
public ExcCodeDelegate ChangeIcon
{
get;
set;
}
/// <summary>
/// 消息服务
/// </summary>
private static WebSocketServer server = null;
/// <summary>
/// 存所有套接字
/// </summary>
private static List<IWebSocketConnection> allSockets = new List<IWebSocketConnection>();
/// <summary>
/// 消息处理链对象
/// </summary>
MsgDealLink dealLinks = null;
/// <summary>
/// 定时器
/// </summary>
Timer timer = new Timer();
/// <summary>
/// 消息端口
/// </summary>
public int port=8082;
/// <summary>
/// 初始化消息服务
/// </summary>
/// <returns></returns>
public string InitServer()
{
timer.Interval = 5000;
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
//取消息链对象
dealLinks = LIS.Core.Context.ObjectContainer.GetObject<MsgDealLink>();
//检查端口占用
bool isUse = PortInUse(port);
if (isUse == true)
{
port = 10210;
isUse=PortInUse(port);
}
if (isUse == true)
{
port = 19910;
isUse = PortInUse(port);
}
if (isUse == true)
{
port = 19902;
isUse = PortInUse(port);
}
if (isUse == true)
{
return "8082端口和10210,19910,19902端口都被占用!无法启动消息服务!";
}
try
{
server = new WebSocketServer("ws://127.0.0.1:" + port);
server.Start(socket =>
{
socket.OnOpen = () =>
{
//改变图标
if (ChangeIcon != null)
{
ChangeIcon("lm2");
timer.Start();
}
allSockets.Add(socket);
LIS.Core.Util.LogUtils.WriteDebugLog("#S消息服务打开连接:" + socket.ConnectionInfo.Id+socket.ConnectionInfo.Path);
};
socket.OnClose = () =>
{
//改变图标
if (ChangeIcon != null)
{
ChangeIcon("lm1");
timer.Start();
}
allSockets.Remove(socket);
LIS.Core.Util.LogUtils.WriteDebugLog("#S消息服务关闭连接:" + socket.ConnectionInfo.Id + socket.ConnectionInfo.Path);
};
socket.OnMessage = message =>
{
//改变图标
if (ChangeIcon != null)
{
ChangeIcon("msg");
timer.Start();
}
LIS.Core.Util.LogUtils.WriteDebugLog("#S消息服务收到消息:" + socket.ConnectionInfo.Id + socket.ConnectionInfo.Path + "内容:" + message);
LIS.Core.Util.LogUtils.WriteDebugLog("#S消息服务准备调用消息链...");
try
{
if (dealLinks != null && dealLinks.LinkList != null && dealLinks.LinkList.Count > 0)
{
foreach (IMessageDeal deal in dealLinks.LinkList)
{
LIS.Core.Util.LogUtils.WriteDebugLog("#S调用:" + deal.GetType().FullName + "...");
bool ret=deal.DealMessage(socket, message);
LIS.Core.Util.LogUtils.WriteDebugLog("#S调用:" + deal.GetType().FullName + "结束...");
//返回false不传递消息了
if (ret == false)
{
LIS.Core.Util.LogUtils.WriteDebugLog("#S消息链不继续传递消息...");
break;
}
}
}
LIS.Core.Util.LogUtils.WriteDebugLog("#S消息服务调用消息链结束...");
}
catch (Exception ex)
{
LIS.Core.Util.LogUtils.WriteExceptionLog("#S消息服务调用消息链异常", ex);
}
};
});
}
catch (Exception ex)
{
LIS.Core.Util.LogUtils.WriteExceptionLog("#S启动消息服务出错", ex);
return "#S启动消息服务出错:" + ex.Message+"调用堆栈:"+ex.StackTrace;
}
return "";
}
/// <summary>
/// 判断端口是否被占用
/// </summary>
/// <param name="port">端口</param>
/// <returns></returns>
public bool PortInUse(int port)
{
bool inUse = false;
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();
foreach (IPEndPoint endPoint in ipEndPoints)
{
if (endPoint.Port == port)
{
inUse = true;
break;
}
}
return inUse;
}
/// <summary>
/// 定时方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void timer_Elapsed(object sender, ElapsedEventArgs e)
{
//改变图标
if (ChangeIcon != null)
{
ChangeIcon("lm0");
timer.Stop();
}
}
}
}
然后各种业务就只需要实现消息处理接口就行了。以打印、截图、编辑图片代码为例:
打印
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Net;
using System.Management;
using System.Xml;
namespace LIS.MsgCore
{
/// <summary>
/// 打印消息处理实现
/// </summary>
public class MessagePrintDeal:IMessageDeal
{
/// <summary>
/// 处理消息
/// </summary>
/// <param name="socket">套接字,可以获得id,发送消息给socket</param>
/// <param name="message">约定#分割的第一位描述消息类型,收到的消息内容</param>
/// <returns>是否继续往后传递消息,true是,false否</returns>
public bool DealMessage(Fleck.IWebSocketConnection socket, string message)
{
LIS.Core.Util.LogUtils.WriteDebugLog("识别以print#开头的消息");
//识别打印消息
if (message.Split('#')[0] == "print")
{
LIS.Core.Util.LogUtils.WriteDebugLog("确定为打印消息,准备处理");
int index = message.IndexOf('#');
string msg = message.Substring(index+1);
string [] arrMsg=msg.Split('@');
//清除历史数据命令拦截
if (arrMsg.Length == 2 && arrMsg[0].Contains("ClearHistory"))
{
LIS.Core.Util.LogUtils.WriteDebugLog("拦截到清除缓存消息");
string chromePath = "";
bool IsChrome = SubKeyOperation.TryGetSoftwarePath("chrome", out chromePath);
if (IsChrome && chromePath.Length > 0)
{
DirectoryInfo di = new DirectoryInfo(chromePath);
string cachePath = di.Parent.Parent.FullName + "\\User Data\\";
DeleteFile(cachePath + "Default\\Cache", "");
DeleteFile(cachePath + "Profile 1\\Cache", "");
DeleteFile(cachePath + "Profile 2\\Cache", "");
DeleteFile(cachePath + "Profile 3\\Cache", "");
DeleteFile(cachePath + "Profile 4\\Cache", "");
DeleteFile(cachePath + "Profile 5\\Cache", "");
DeleteFile(cachePath + "Profile 6\\Cache", "");
DeleteFile(cachePath + "Profile 7\\Cache", "");
DeleteFile(cachePath + "Profile 8\\Cache", "");
}
//更新TRAK
if (arrMsg[1] != "")
{
if (System.IO.File.Exists(@"C:\TRAK\LISUpGrade.exe"))
{
Process.Start(@"C:\TRAK\LISUpGrade.exe", arrMsg[1]);
}
}
return false;
}
//报告打印消息直接处理,不驱动exe,提高速度
if (arrMsg.Length > 5 && (!arrMsg[4].Contains("PDF#")) && (arrMsg[0] == "iMedicalLIS://0" || arrMsg[0] == "iMedicalLIS://1") && (arrMsg[4] != "ReportView"))
{
string cmdLine = msg.Substring(14);
string[] tmpStrings = cmdLine.Split((char)64);
string printFlag = tmpStrings[0];
string connectString = tmpStrings[1].Replace("&", "&");
if (System.IO.File.Exists("C:\\TRAK\\ResultPrint.exe.config"))
{
XmlDocument doc = new XmlDocument();
doc.Load("C:\\TRAK\\ResultPrint.exe.config");
//找出名称为“add”的所有元素
XmlNodeList nodes = doc.GetElementsByTagName("add");
for (int i = 0; i < nodes.Count; i++)
{
//获得将当前ConfDataCode属性
XmlAttribute attConf = nodes[i].Attributes["key"];
//根据元素的第一个属性来判断当前的元素是不是目标元素
if (attConf != null && attConf.Value == "WebServiceAddressOne")
{
attConf = nodes[i].Attributes["value"];
if(attConf.Value!=null&&attConf.Value!="")
{
connectString = attConf.Value;
}
}
}
}
string rowids = tmpStrings[2];
string userCode = tmpStrings[3];
//PrintOut:打印 PrintPreview打印预览
string printType = tmpStrings[4];
//参数
string paramList = tmpStrings[5]; ///模块名称(LIS工作站,DOC医生,SELF自助,OTH其它)
string clsName = "";
string funName = "";
if (tmpStrings.Length >= 8)
{
clsName = tmpStrings[6];
funName = tmpStrings[7];
}
//没传报告主键退出
if (rowids == "" && printType != "ReportView")
{
LIS.Core.Util.LogUtils.WriteDebugLog("未传入报告主键");
return true;
};
string ip = "";
string hostName = Dns.GetHostName(); //本机名
System.Net.IPAddress[] addressList = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
for (int i = 0; i < addressList.Length; i++)
{
ip = addressList[i].ToString();
}
string mac = "";
//部分电脑有问题
//ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
//ManagementObjectCollection moc = mc.GetInstances();
//foreach (ManagementObject mo in moc)
//{
// if (mo["IPEnabled"].ToString() == "True")
// {
// mac = mo["MacAddress"].ToString();
// }
//}
paramList = paramList + "^HN" + hostName + "^IP" + ip + "^MAC" + mac;
//printFlag 0:打印所有报告 1:循环打印每一份报告
if (printFlag.Substring(0, 1) == "0")
{
DHCLabtrakReportPrint.ReportAccess reportPrint = new DHCLabtrakReportPrint.ReportAccess(rowids, userCode, paramList, connectString, printType, clsName, funName);
}
else
{
string[] tmpRowids = rowids.Split((char)94);
for (int i = 0; i < tmpRowids.Length; i++)
{
rowids = tmpRowids[i];
if (rowids != "")
{
DHCLabtrakReportPrint.ReportAccess reportPrint = new DHCLabtrakReportPrint.ReportAccess(rowids, userCode, paramList, connectString, printType, clsName, funName);
}
}
}
}
else
{
if (System.IO.File.Exists(@"C:\TRAK\ResultPrint.exe"))
{
LIS.Core.Util.LogUtils.WriteDebugLog("调用打印程序");
System.Diagnostics.Process.Start(@"C:\TRAK\ResultPrint.exe", msg.Replace("\"", "\\\"").Replace("+", "%2B"));
}
else
{
LIS.Core.Util.LogUtils.WriteDebugLog(@"C:\TRAK\ResultPrint.exe");
}
}
LIS.Core.Util.LogUtils.WriteDebugLog("处理完成,截断消息链");
return false;
}
LIS.Core.Util.LogUtils.WriteDebugLog("不是打印消息,传递消息链");
return true;
}
/// <summary>
/// 删除指定目录指定后缀名的文件
/// </summary>
/// <param name="path"></param>
/// <param name="extend"></param>
private void DeleteFile(string path, string extend)
{
if (Directory.Exists(path))
{
DirectoryInfo di = new DirectoryInfo(path);
FileInfo[] files = di.GetFiles();
if (files != null && files.Length > 0)
{
foreach (var v in files)
{
try
{
if (v.Extension == extend)
{
System.IO.File.Delete(v.FullName);
}
}
catch
{
}
}
}
}
}
}
}
截图
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Windows.Forms;
using LISScreenCapture;
namespace LIS.MsgCore
{
/// <summary>
/// 捕获图片消息处理实现
/// </summary>
public class MessageGetImageCommon : IMessageDeal
{
/// <summary>
/// 当前消息
/// </summary>
private string curMsg;
/// <summary>
/// 当前端口
/// </summary>
private Fleck.IWebSocketConnection curSocket;
/// <summary>
/// 处理消息
/// </summary>
/// <param name="socket">套接字,可以获得id,发送消息给socket</param>
/// <param name="message">约定#分割的第一位描述消息类型,收到的消息内容</param>
/// <returns>是否继续往后传递消息,true是,false否</returns>
public bool DealMessage(Fleck.IWebSocketConnection socket, string message)
{
LIS.Core.Util.LogUtils.WriteDebugLog("识别以getimage#开头的消息");
//识别打印消息
if (message.Split('#')[0] == "getimage")
{
LIS.Core.Util.LogUtils.WriteDebugLog("确定为捕获图片消息,准备处理");
int index = message.IndexOf('#');
string msg = message.Substring(index+1);
if (msg.Contains("#Capture"))
{
msg = msg.Replace("#Capture", "");
curMsg = msg;
curSocket = socket;
MethodInvoker mi = new MethodInvoker(this.ShowCaptureForm);
Application.OpenForms["FrmMian"].BeginInvoke(mi);
}
else
{
curMsg = msg;
if (curMsg.Length > 0)
{
if (curMsg.Substring(curMsg.Length - 1) == "#")
{
curMsg = curMsg.Substring(0, curMsg.Length - 1);
}
}
curSocket = socket;
MethodInvoker mi = new MethodInvoker(this.ShowForm);
Application.OpenForms["FrmMian"].BeginInvoke(mi);
}
LIS.Core.Util.LogUtils.WriteDebugLog("处理完成,截断消息链");
return false;
}
LIS.Core.Util.LogUtils.WriteDebugLog("不是捕获图片消息,传递消息链");
return true;
}
/// <summary>
/// 显示窗口
/// </summary>
private void ShowForm()
{
FrmGetImage frm = new FrmGetImage();
frm.Ftp = curMsg;
frm.Socket = curSocket;
frm.ShowDialog();
}
/// <summary>
/// 显示窗口
/// </summary>
private void ShowCaptureForm()
{
FrmCaptureReportImg frmC = new FrmCaptureReportImg();
frmC.Ftp = curMsg;
frmC.Socket = curSocket;
frmC.ShowDialog();
}
}
}
编辑图片
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using LISScreenCapture;
using System.Drawing;
using System.Windows.Forms;
namespace LIS.MsgCore
{
/// <summary>
/// 编辑图片消息处理
/// </summary>
public class MessageEditImageDeal : IMessageDeal
{
/// <summary>
/// 消息接口
/// </summary>
private Fleck.IWebSocketConnection cursocket = null;
/// <summary>
/// 窗口
/// </summary>
CutBord frm = null;
/// <summary>
/// 处理消息
/// </summary>
/// <param name="socket">套接字,可以获得id,发送消息给socket</param>
/// <param name="message">约定#分割的第一位描述消息类型,收到的消息内容</param>
/// <returns>是否继续往后传递消息,true是,false否</returns>
public bool DealMessage(Fleck.IWebSocketConnection socket, string message)
{
LIS.Core.Util.LogUtils.WriteDebugLog("识别以editimage#开头的消息");
//识别编辑图片消息
if (message.Split('#')[0] == "editimage")
{
LIS.Core.Util.LogUtils.WriteDebugLog("确定为编辑图片消息,准备处理");
int index = message.IndexOf('#');
cursocket = socket;
string msg = message.Substring(index + 1);
frm = new CutBord();
frm.Path = msg;
frm.SaveCallBack = new SaveCallback(SaveImg);
MethodInvoker mi = new MethodInvoker(this.ShowForm);
Application.OpenForms["FrmMian"].BeginInvoke(mi);
LIS.Core.Util.LogUtils.WriteDebugLog("处理完成,截断消息链");
return false;
}
LIS.Core.Util.LogUtils.WriteDebugLog("不是编辑图片消息,传递消息链");
return true;
}
/// <summary>
/// 显示窗口
/// </summary>
private void ShowForm()
{
frm.TopMost = true;
frm.ShowDialog();
}
/// <summary>
/// 保存图片
/// </summary>
/// <param name="img">图片</param>
/// <param name="path">路径</param>
public void SaveImg(Bitmap img, string path)
{
if (MessageBox.Show("是否要保存修改到服务器?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information) == DialogResult.OK)
{
Upload(img, path);
}
}
/// <summary>
/// 上传图片
/// </summary>
/// <param name="img">图片</param>
/// <param name="path">路径</param>
public void Upload(Bitmap img, string path)
{
Stream stream = new MemoryStream();
System.Drawing.Imaging.ImageFormat format = System.Drawing.Imaging.ImageFormat.Bmp;
//扩展名
string aLastName = path.Substring(path.LastIndexOf(".") + 1);
aLastName = aLastName.ToUpper();
if (aLastName == "JPG")
{
format = System.Drawing.Imaging.ImageFormat.Jpeg;
}
else if (aLastName == "EMF")
{
format = System.Drawing.Imaging.ImageFormat.Emf;
}
else if (aLastName == "EXIF")
{
format = System.Drawing.Imaging.ImageFormat.Exif;
}
else if (aLastName == "GIF")
{
format = System.Drawing.Imaging.ImageFormat.Gif;
}
else if (aLastName == "ICON")
{
format = System.Drawing.Imaging.ImageFormat.Icon;
}
else if (aLastName == "JPEG")
{
format = System.Drawing.Imaging.ImageFormat.Jpeg;
}
else if (aLastName == "PNG")
{
format = System.Drawing.Imaging.ImageFormat.Png;
}
else if (aLastName == "TIFF")
{
format = System.Drawing.Imaging.ImageFormat.Tiff;
}
else if (aLastName == "WMF")
{
format = System.Drawing.Imaging.ImageFormat.Wmf;
}
try
{
img.Save(stream, format);
LIS.File.Core.FileService fileService = new File.Core.FileService();
fileService.Upload(path.Substring(0, path.LastIndexOf("/") + 1), stream, path.Substring(path.LastIndexOf("/") + 1), "");
stream.Close();
cursocket.Send("1");
}
catch (Exception ex)
{
MessageBox.Show("保存失败:" + ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
然后把消息处理实现类配置到消息链对象即可,依赖容器配置,也能自己反射
至此一个有充分开放性的消息链通道就开发完成了。使得监听具有不光是检验打印的监听,也能是截图的监听,也能是编辑文件的、CA的等等,也能是非检验的BS-CS交互通道,只需要你使用消息处理接口配置到消息链中。这就是设计模式带来的魅力。