上一篇证实Linux能用DotNetCore打印,能正常驱动虚拟打印机打印PDF。这里就适配实际的打印到多平台支持上。打印动态库先改为DotNetCore版本,并且把UI交互部分抽取接口(称为Shell)。控制台、GTK、Asp.NetCore各自实现接口来做打印程序,这样把UI交互部分剥离成一个壳。所以叫Shell的意思。
执行入口
using System;
namespace LISConsoleClient
{
class Program
{
/// <summary>
/// 执行入口
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
OperPrintShell shell = new OperPrintShell();
shell.ShowMsgInfo("欢迎使用iMedicalLIS控制台客户端");
shell.ShowMsgInfo("准备初始化消息服务");
//开启消息侦听
MessageServer server = new MessageServer();
string ret = server.InitServer();
shell.ShowMsgInfo("初始化消息服务返回:"+ret);
string input = "";
while(input.ToLower()!="y")
{
shell.ShowMsgInfo("退出程序请输入:y");
input = Console.ReadLine();
}
}
}
}
消息服务
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LIS.BLL.MsgCore;
using Fleck;
using System.Timers;
using System.Net.NetworkInformation;
using System.Net;
namespace LISConsoleClient
{
/// <summary>
/// 消息服务类
/// </summary>
public class MessageServer
{
/// <summary>
/// 消息历史队列
/// </summary>
public static Queue<HosDto> HosQueue = new Queue<HosDto>();
/// <summary>
/// 消息服务
/// </summary>
private static WebSocketServer server = null;
/// <summary>
/// 存所有套接字
/// </summary>
private static List<IWebSocketConnection> allSockets = new List<IWebSocketConnection>();
/// <summary>
/// 消息处理链对象
/// </summary>
MsgDealLink dealLinks = null;
/// <summary>
/// 消息端口
/// </summary>
public int port = 8082;
/// <summary>
/// 壳
/// </summary>
OperPrintShell shell = new OperPrintShell();
/// <summary>
/// 初始化消息服务
/// </summary>
/// <returns></returns>
public string InitServer()
{
//取消息链对象
dealLinks = new MsgDealLink();
dealLinks.LinkList = new List<IMessageDeal>();
MessagePrintDeal printDeal = new MessagePrintDeal();
dealLinks.LinkList.Add(printDeal);
//检查端口占用
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)
{
shell.ShowMsgInfo("8082端口和10210,19910,19902端口都被占用!无法启动消息服务!", "");
return "8082端口和10210,19910,19902端口都被占用!无法启动消息服务!";
}
try
{
server = new WebSocketServer("ws://127.0.0.1:" + port);
server.Start(socket =>
{
socket.OnOpen = () =>
{
shell.ShowMsgInfo(socket.ConnectionInfo.Id + socket.ConnectionInfo.Path, "S消息服务打开连接:");
allSockets.Add(socket);
};
socket.OnClose = () =>
{
shell.ShowMsgInfo(socket.ConnectionInfo.Id + socket.ConnectionInfo.Path, "S消息服务关闭连接:");
allSockets.Remove(socket);
};
socket.OnMessage = message =>
{
shell.ShowMsgInfo(socket.ConnectionInfo.Id + socket.ConnectionInfo.Path + "内容:" + message, "S消息服务收到消息:");
shell.ShowMsgInfo("S消息服务准备调用消息链...", "");
try
{
HosDto hosDto = new HosDto();
hosDto.Msg = message;
hosDto.Socket = socket;
HosQueue.Enqueue(hosDto);
if (HosQueue.Count > 30)
{
HosQueue.Dequeue();
}
if (dealLinks != null && dealLinks.LinkList != null && dealLinks.LinkList.Count > 0)
{
foreach (IMessageDeal deal in dealLinks.LinkList)
{
shell.ShowMsgInfo("#S调用:" + deal.GetType().FullName + "...", "");
bool ret = deal.DealMessage(socket, message);
shell.ShowMsgInfo("#S调用:" + deal.GetType().FullName + "结束...", "");
//返回false不传递消息了
if (ret == false)
{
shell.ShowMsgInfo("#S消息链不继续传递消息...", "");
break;
}
}
}
shell.ShowMsgInfo("#S消息服务调用消息链结束...", "");
}
catch (Exception ex)
{
shell.ShowMsgInfo("消息服务调用消息链异常:" + ex.Message, "");
}
};
});
}
catch (Exception ex)
{
shell.ShowMsgInfo("启动消息服务出错:" + ex.Message, "");
return "#S启动消息服务出错:" + ex.Message + "调用堆栈:" + ex.StackTrace;
}
return "";
}
/// <summary>
/// 历史实体
/// </summary>
public class HosDto
{
/// <summary>
/// 消息
/// </summary>
public string Msg
{
get;
set;
}
/// <summary>
/// 套接字
/// </summary>
public IWebSocketConnection Socket
{
get;
set;
}
}
/// <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;
}
}
}
打印处理类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Net;
using System.Xml;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
namespace LISConsoleClient
{
/// <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)
{
/// <summary>
/// 壳
/// </summary>
OperPrintShell shell = new OperPrintShell();
//传入壳
DHCLabtrakReportPrint.ReportAccess.Shell = shell;
shell.ShowMsgInfo("识别以print#开头的消息", "");
//识别打印消息
if (message.Split('#')[0] == "print")
{
shell.ShowMsgInfo("确定为打印消息,准备处理", "");
int index = message.IndexOf('#');
string msg = message.Substring(index+1);
//包含版本参数分隔符
string TRANVetionInfo = "";
if(msg.Contains("$TRAKVTSP$"))
{
msg = msg.Replace("$TRAKVTSP$", ""+(char)0);
string [] mvArr = msg.Split((char)0);
TRANVetionInfo = mvArr[1];
msg = mvArr[0];
string LoacalVetion = "0";
if(TRANVetionInfo!="")
{
if(System.IO.File.Exists(@"C:\TRAK\TRAKVetionInfo.json"))
{
string VetionInfoStr = ReadTxt(@"C:\TRAK\TRAKVetionInfo.json");
VetionInfoStr = VetionInfoStr.Replace("\r\n", "" + (char)0);
string[] VArr = VetionInfoStr.Split((char)0);
LoacalVetion = VArr[0];
}
string[] TRANVetionInfoArr = TRANVetionInfo.Split('^');
int LoacalVetionInt = Convert.ToInt32(LoacalVetion);
int ServerVetionInt= Convert.ToInt32(TRANVetionInfoArr[0]);
}
}
string [] arrMsg=msg.Split('@');
//报告打印消息直接处理,不驱动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("&", "&");
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")
{
shell.ShowMsgInfo("未传入报告主键", "");
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++)
{
if (addressList[i].AddressFamily.Equals(System.Net.Sockets.AddressFamily.InterNetwork))
{
ip = addressList[i].ToString();
}
}
string mac = "";
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"))
{
shell.ShowMsgInfo("调用打印程序", "");
System.Diagnostics.Process.Start(@"C:\TRAK\ResultPrint.exe", msg.Replace("\"", "\\\"").Replace("+", "%2B"));
}
else
{
shell.ShowMsgInfo(@"C:\TRAK\ResultPrint.exe", "");
}
}
if (System.IO.File.Exists(@"C:\TRAK\ResultPrint.exe"))
{
shell.ShowMsgInfo("调用打印程序", "");
System.Diagnostics.Process.Start(@"C:\TRAK\ResultPrint.exe", msg.Replace("\"", "\\\"").Replace("+", "%2B"));
}
else
{
shell.ShowMsgInfo(@"C:\TRAK\ResultPrint.exe", "");
}
shell.ShowMsgInfo("处理完成,截断消息链", "");
return false;
}
shell.ShowMsgInfo("不是打印消息,传递消息链", "");
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
{
}
}
}
}
}
/// <summary>
/// 读取文件数据
/// </summary>
/// <param name="path">文件全路径</param>
/// <returns></returns>
private string ReadTxt(string path)
{
FileStream fs = null;
try
{
fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader sr = new StreamReader(fs, GetFileEncodeType(fs));
string str = sr.ReadToEnd();
return str;
}
finally
{
fs.Close();
}
}
/// <summary>
/// 获得编码格式
/// </summary>
/// <param name="fs"></param>
/// <returns></returns>
private Encoding GetFileEncodeType(FileStream fs)
{
byte[] Unicode = new byte[] { 0xFF, 0xFE, 0x41 };
byte[] UnicodeBIG = new byte[] { 0xFE, 0xFF, 0x00 };
byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF }; //带BOM
Encoding reVal = Encoding.Default;
BinaryReader r = new BinaryReader(fs, System.Text.Encoding.Default);
int i;
int.TryParse(fs.Length.ToString(), out i);
byte[] ss = r.ReadBytes(i);
if (IsUTF8Bytes(ss) || (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF))
{
reVal = Encoding.UTF8;
}
else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00)
{
reVal = Encoding.BigEndianUnicode;
}
else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41)
{
reVal = Encoding.Unicode;
}
fs.Position = 0;
return reVal;
}
/// <summary>
/// 判断是否是不带 BOM 的 UTF8 格式
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static bool IsUTF8Bytes(byte[] data)
{
int charByteCounter = 1; //计算当前正分析的字符应还有的字节数
byte curByte; //当前分析的字节.
for (int i = 0; i < data.Length; i++)
{
curByte = data[i];
if (charByteCounter == 1)
{
if (curByte >= 0x80)
{
//判断当前
while (((curByte <<= 1) & 0x80) != 0)
{
charByteCounter++;
}
//标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X
if (charByteCounter == 1 || charByteCounter > 6)
{
return false;
}
}
}
else
{
//若是UTF-8 此时第一位必须为1
if ((curByte & 0xC0) != 0x80)
{
return false;
}
charByteCounter--;
}
}
if (charByteCounter > 1)
{
throw new Exception("非预期的byte格式!");
}
return true;
}
}
}
消息处理链
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LISConsoleClient
{
///<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;
}
}
}
初版操作外壳,不同交互体系各自实现接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LISConsoleClient
{
/// <summary>
/// 操作打印的壳
/// </summary>
public class OperPrintShell : DHCLabtrakReportPrint.IOperPrintShell
{
/// <summary>
/// 保存文件对话框
/// </summary>
/// <param name="Filter"></param>
/// <returns></returns>
public string SaveFileDialog(string Filter)
{
Console.WriteLine("请输入要保存的路径:");
string path = Console.ReadLine();
return path;
}
/// <summary>
/// 显示App信息
/// </summary>
/// <param name="info"></param>
/// <param name="coderInfo"></param>
public void ShowAppInfo(string info, string coderInfo)
{
string head = DateTime.Now.ToString("hh:mm:ss") + "#";
//head = head.PadRight(30);
Console.WriteLine(head + info);
Console.WriteLine(head + coderInfo);
}
/// <summary>
/// 显示消息
/// </summary>
/// <param name="info"></param>
/// <param name="title"></param>
public void ShowMsgInfo(string info, string title = "")
{
string head = DateTime.Now.ToString("hh:mm:ss") + "#" + title;
//head = head.PadRight(30);
Console.WriteLine(head + info);
}
}
}
控制台运行
浏览器驱动打印
打印报告
引入壳的概念后剥离了交互部分,也使其他UI对接变的容易,如果微软不支持Linux的桌面就可以做个GTK界面来当Linux客户端界面。或者直接用控制台支持打印。