1、LIS 系统通讯程序原理与实现一、BSLIS 仪器数据采集方法BSLIS 对检验仪器的数据采集主要通过串行口通讯、USB 端口通讯、TCP/IP 通讯、定时监控数据库和手工录入等几种方法。串行口通讯最为普遍,采用 RS-232C 标准,一般的仪器都支持此标准。定时监控数据库对仪器管理机上已有的检验信息数据定时直接进行读取,而后转发到 BSLIS 系统,一般在国产仪器中较常见。另外,检验科还有很多手工进行测试的项目,其信息的采集主要依靠手工的录入。下面对各种方式进行简要的介绍:(一)RS-232 通讯方式RS-232 是美国电子工业协会 EIA(Electronic Industry Assoc
2、iation)制定的一个接口标准,其全名为 RS-232C,其中RS 是推荐标准的意思,C 代表标准的版本号。该标准是用于连接数据终端设备 DTE 和数据通讯设备 DCE 的接口规范。它被广泛应用于检验自动化设备同 PC 之间的通讯。RS-232C有很多种型号,一般常见的有 9 脚和 25 脚两种。该标准支持的速率为 0-20000bps,限制电缆长度为 50 英尺,电缆长度如果大于 50 英尺时,也可以使用,但为了保证信号的质量,必须仔细测试。RS-232C 标准规定:正电压为 3-15V,负电压为-3-15V。但在实现 RS-232C 标准时各厂家生产的产品并没有完全统一,因此在实际应用中
3、有许多特别情况。RS-232 通讯很受传输距离的限制,但将 RS-232 接口转换成双端平衡传送和差分接收方法,并对信号进行光电隔离,无需外接电源,可以实现延长 RS-232 通讯距离和抗干扰保护接口之目的,通讯距离可达 2 公里。从通讯方式上来看,目前,根据仪器的不同,主要有两种方式:单向通讯、双向通讯。 单向通讯:仪器只向 LIS 工作站发送检验数据,不接收LIS 工作站发出的任何指令。 双向通讯:仪器不仅向 LIS 工作站发送检验数据,还能接受从 LIS 工作站发出的指令。RS-232 因价格便宜,应用方便,所以在现代自动化实验室中,约有 90%的仪器采用该通讯方式同外部进行数据交换。而
4、且,一些仪器还支持双向通讯。(二)USB 端口通讯由于信息技术的进步,串口通讯也朝高速化方向发展,近年来在个人计算机的快速发展下,使用串口通讯发展出了USB(Universal Serial Bus,通用串行总线) ,其信号传输方式也是串行通讯(一次只传送一位) 。其通讯速率达12Mbps,在 1999 年所发表的 USB2.0 的版本已经将其速度提升到 480Mbps。USB 在实验室数据通讯中的应用主要有两种情况。一种是仪器提供 USB 接口,则可以直接利用 USB 口进行大批量数据传输(可以传输图片) 。另一种,仪器本身没有 USB 接口,只提供 RS-232 接口,我们可以将工作站中的
5、 USB 口转换为 RS-232,通过 RS-232 同仪器进行通讯,这种方式可以利用 USB 传输速率快,不占用系统资源的优点,将一个 USB口转换成多个 RS-232 口,实现一个工作站连接多台仪器。一些 BSLIS 用的计算机主板上仅有 1 个串口,要实现一台计算机连接多台仪器,可使用 USB 转串口的方法实现。补充说明:在使用 USB 转串口通讯时候,LIS 通讯程序端“校验位”不为“None” ,而此时仪器上设置“检验位”为“None” 。具体可以在“Even” 、 “Odd”等中选择一个试验。(三)TCP/IP 通讯方式目前许多智能仪器均提供了局域网接口,允许用户通过TCP/IP
6、协议与仪器通讯。 TCP/IP 通讯在实现上经常借助于Socket 技术。在不同的平台上有不同的 Socket API,开发的模式也不尽相同。在常见的 Microsoft 平台下,一般采用 WinSock API。Winsock API 一般采用异步方式通讯,此时,不能用类似于 for 语句的循环来实现对多组数据的发送,更不能用循环语句来接收数据。比如,你可以用 for 语句来实现若干文件的复制,这很普遍也很正常,但在 Socket 编程以及大多数网络应用编程中都是行不通的,因网络通讯的基本方式是请求和应答。在 Unix 平台下大多采用阻塞方式开发,此时可以利用一些传统的方法。这两种方式,各有
7、优缺点,一般认为对于复杂逻辑采用后者较好,如果采用异步的方式,则要不断更新状态。另外,和所有的通讯编程一样,Socket 编程也遵循数据分包传送这一基本规则。也就是说,在 Socket 编程中,每次发送和接收一个包,以保证数据传输的安全性和稳定性,同时也不至于过多地占用系统资源。因为采用网络接口,其通讯速度非常快,功能也很强大,因此,这种通讯方式往往被用于一些需要进行图片传输的仪器,典型的有:拜尔 120 血液分析仪、B-D 公司的流式细胞仪等。但采用此种方法也有一定的缺陷,其主要是接口程序开发相对复杂一些。另外,由于网络病毒的影响,对仪器的正常运行也构成了一定的威胁。(四)定时监控数据库采用
8、此种方式的仪器,一般都是一些小规模的产品或国产仪器。该类型仪器在设计同外部进行通讯时没有采用国际标准,甚至有的仪器就没有同外部进行通讯的接口。对于这种仪器,一般采用的方法是在仪器管理机上植入“DataSpy For BSLIS”(定时数据库监控器) ,按照一个时间间隔定时扫描相关数据库,再通过串口通讯转发到 BSLIS 工作站。这种方式通讯的最大优点是,避免了仪器管理机与LIS、HIS 的网络连接,减少了网络病毒等不利因素的干扰。典型的仪器如:北京普利生的血流变、一些酶标仪、骨髓分析工作站、精子分析仪、尿液分析工作站等等。为了提高灵活性和降低医院信息化成本,DataSpy For BSLIS
9、提供了支持 BSLIS 系统植入到仪器管理机的工作模式。这样可减少 1 台 BSLIS 工作站。(五)手工输入虽然目前实验室已基本实现了自动化,大量引进自动化分析仪器,但仍有许多项目必须采用手工操作进行分析。在处理此类数据时,必须采用人工录入将项目及其结果引入到系统。对于此类模式,BSLIS 提供了灵活多变的“项目组套 ”,允许成批输入项目,大大提高了工作效率,降低了人工成本。BSLIS 支持的手工输入模式有:单个样本内项目组套输入、单个样本内新增单个项目输入、批量样本内新增项目组套输入、批量样本内新增单个样本输入。还支持批量删除、批量修改结果等。二、Windows 下 RS-232 串口通讯
10、实现基本原理和方法与以往 DOS 下串口通讯程序不同的是,Windows 不提倡应用程序直接控制硬件,而是通过 Windows 操作系统提供的设备驱动程序来进行数据传递。串行口在 Win 32 中是作为文件来进行处理的,而不是直接对端口进行操作,对于串口通讯,Win 32 提供了相应的文件 I/O 函数与通讯函数,通过了解这些函数的使用,可以编制出符合不同需要的通讯程序。与通讯设备相关的结构有 COMMCONFIG ,COMMPROP,COMMTIMEOUTS,COMSTAT,DCB,MODEMDEVCAPS,MODEMSETTINGS 共 7 个,与通讯有关的 Windows API 函数共
11、有 26 个,详细说明可参考 MSDN 帮助文件。下面介绍实现串口通讯的三种方法: 方法一:使用微软串口通讯控件 MSComm此方法适用于支持 ActiveX/COM 技术的任何语言,典型的支持语言有 VC+、Delphi、PB、VB 等。控件可以在VC+安装盘上找到,文件名为 MsComm32.ocx。下面以 VC+为例说明程序实现:首先,在对话框中创建通讯控件,若 Control 工具栏中缺少该控件,可通过菜单 Project Add to Project Components and Control 插入即可,再将该控件从工具箱中拉到对话框中。此时,你只需要关心控件提供的对 Window
12、s 通讯驱动程序的 API 函数的接口。换句话说,只需要设置和监视 MSComm 控件的属性和事件。在 ClassWizard 中为新创建的控件定义成员对象(CMSComm m_Serial) ,通过该对象便可以对串口属性进行设置,MSComm 控件共有 27 个属性,这里只介绍几个常用属性: 属性名称 属性作用CommPort 设置并返回通讯端口号,缺省为 COM1。Settings 以字符串的形式设置并返回波特率、奇偶校验、数据位、停止位。PortOpen 设置并返回通讯端口的状态,也可以打开和关闭端口。Input 从接收缓冲区返回和删除字符。Output 向发送缓冲区写一个字符串。Inp
13、utLen 设置每次 Input 读入的字符个数,缺省值为0,表明读取接收缓冲 区中的全部内容。InBufferCount 返回接收缓冲区中已接收到的字符数,将其置 0 可以清除接收缓冲区。InputMode 定义 Input 属性获取数据的方式(为 0:文本方式;为 1:二进制方式) 。Rthreshold和 SThreshold表示在 OnComm 事件发生之前,接收缓冲区或发送缓冲区中可以接收的字符数。以下是通过设置控件属性对串口进行初始化的实例: BOOL CSampleDlg: PortOpen()BOOL m_Opened;.m_Serial.SetCommPort(2); / 指
14、定串口号m_Serial.SetSettings(“4800,N,8,1“); / 通讯参数设置m_Serial.SetInBufferSize(1024); / 指定接收缓冲区大小m_Serial.SetInBufferCount(0); / 清空接收缓冲区m_Serial.InputMode(1); / 设置数据获取方式m_Serial.SetInputLen(0); / 设置读取方式m_Opened=m_Serail.SetPortOpen(1); / 打开指定的串口return m_Opened;- 打开所需串口后,需要考虑串口通讯的时机。在接收或发送数据过程中,可能需要监视并响应一些
15、事件和错误,所以事件驱动是处理串行端口交互作用的一种非常有效的方法。使用 OnComm 事件和 CommEvent 属性捕捉并检查通讯事件和错误的值。发生通讯事件或错误时,将触发 OnComm 事件,CommEvent 属性的值将被改变,应用程序检查 CommEvent 属性值并作出相应的反应。在程序中用ClassWizard 为 CMSComm 控件添加 OnComm 消息处理函数:void CSampleDlg:OnComm().switch(m_Serial.GetCommEvent()case 2:/ 串行口数据接收,处理; 方法二:在单线程中实现自定义的串口通讯类 控件简单易用,但由
16、于必须拿到对话框中使用,在一些需要在线程中实现通讯的应用场合,控件的使用显得捉襟见肘。此时,若能够按不同需要定制灵活的串口通讯类将弥补控件的不足,以下将介绍如何在单线程中建立自定义的通讯类。 该通讯类 CSimpleComm 需手动加入头文件与源文件,其基类为 CObject,大致建立步骤如下: (1)打开串口,获取串口资源句柄 通讯程序从 CreateFile 处指定串口设备及相关的操作属性。再返回一个句柄,该句柄将被用于后续的通讯操作,并贯穿整个通讯过程。CreateFile()函数中有几个值得注意的参数设置:串口共享方式应设为 0,串口为不可共享设备;创建方式必须为 OPEN_EXIST
17、ING,即打开已有的串口。对于dwFlagAndAttribute 参数,对串口有意义的值是FILE_FLAG_OVERLAPPED,该标志表明串口采用异步通讯模式,可进行重叠操作;若值为 NULL,则为同步通讯方式,在同步方式下,应用程序将始终控制程序流,直到程序结束,若遭遇通讯故障等因素,将导致应用程序的永久等待,所以一般多采用异步通讯。 (2)串口设置 串口打开后,其属性被设置为默认值,根据具体需要,通过调用 GetCommState(hComm,m_hIDComDev=CreateFile( “COM2“, GENERIC_READ | GENERIC_WRITE,0,NULL,OPE
18、N_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL ); / 打开串口,异步操作if( m_hIDComDev = NULL ) return( FALSE );dcb.DCBlength = sizeof( DCB );GetCommState( m_hIDComDev, / 获得端口默认设置dcb.BaudRate=CBR_4800;dcb.ByteSize=8;dcb.Parity= NOPARITY;dcb.StopBits=(BYTE) ONESTOPBIT;.(3)串口读写操作 主要运用 ReadFile()与 W
19、riteFile()API 函数,若为异步通讯方式,两函数中最后一个参数为指向OVERLAPPED 结构的非空指针,在读写函数返回值为FALSE 的情况下,调用 GetLastError()函数,返回值为ERROR_IO_PENDING,表明 I/O 操作悬挂,即操作转入后台继续执行。此时,可以用 WaitForSingleObject()来等待结束信号并设置最长等待时间,举例如下: BOOL bReadStatus;bReadStatus = ReadFile( m_hIDComDev, buffer, dwBytesRead, if(!bReadStatus)if(GetLastError
20、()=ERROR_IO_PENDING)WaitForSingleObject(m_OverlappedRead.hEvent,1000);return (int)dwBytesRead);return(0);return (int)dwBytesRead);定义全局变量 m_Serial 作为新建通讯类 CSimpleComm的对象,通过调用类的成员函数即可实现所需串行通讯功能。与方法一相比,方法二赋予串行通讯程序设计较大的灵活性,端口的读写可选择较简单的查询式,或通过设置与外设数据发送时间间隔 TimeCycle 相同的定时器:SetTimer(1,TimeCycle,NULL),进行定时
21、读取或发送。 CSampleView: OnTimer(UINT nIDEvent)char InputData30;m_Serial.ReadData(InputData,30);/ 数据处理 若对端口数据的响应时间要求较严格,可采用事件驱动I/O 读写, Windows 定义了 9 种串口通讯事件,较常用的有: EV_RXCHAR: 接收到一个字节,并放入输入缓冲区。 EV_TXEMPTY: 输出缓冲区中的最后一个字符发送出去。 EV_RXFLAG: 接收到事件字符 (DCB 结构中 EvtChar成员) ,放入输入缓冲区。 在用 SetCommMask()指定了有用的事件后,应用程序可调
22、用 WaitCommEvent()来等待事件的发生。SetCommMask(hComm,0)可使 WaitCommEvent()中止。 方法三:多线程下实现串行通讯 方法一、二适用于单线程通讯。在很多工业控制系统中,常通过扩展串口连接多个外设,各外设发送数据的重复频率不同,要求后台实时无差错捕捉,采集,处理,记录各端口数据,这就需要在自定义的串行通讯类中创建端口监视线程,以便在指定的事件发生时向相关的窗口发送通知消息。线程的基本概念可详见 VC+参考书目,Windows 内部的抢先调度程序在活动的线程之间分配 CPU 时间,Win 32 区分两种不同类型的线程,一种是用户界面线程 UI(Use
23、r Interface Thread),它包含消息循环或消息泵,用于处理接收到的消息;另一种是工作线程(Work Thread) ,它没有消息循环,用于执行后台任务。用于监视串口事件的线程即为工作线程。 多线程通讯类的编写在端口的配置,连接部分与单线程通讯类相同,在端口配置完毕后,最重要的是根据实际情况,建立多线程之间的同步对象,如信号灯,临界区,事件等,相关细节可参考 VC+ 中的同步类。 一切就绪后即可启动工作线程: CWinThrea *CommThread = AfxBeginThread(CommWatchThread,/ 线程函数名(LPVOID) m_pTTYInfo, / 传递
24、的参数THREAD_PRIORITY_ABOVE_NORMAL, / 设置线程优先级(UINT) 0, / 最大堆栈大小(DWORD) CREATE_SUSPENDED , / 创建标志(LPSECURITY_ATTRIBUTES) NULL); / 安全性标志同时,在串口事件监视线程中: if(WaitCommEvent(pTTYInfo-idComDev,ResetEvent(pTTYInfo-hPostEvent); / 置同步事件对象为非信号态:PostMessage(CSampleView,ID_COM1_DATA,0,0); / 发送通知消息用 PostMessage()向指定窗口
25、的消息队列发送通知消息,相应地,需要在该窗口建立消息与成员函数间的映射,用ON_MESSAGE 将消息与成员函数名关联。 BEGIN_MESSAGE_MAP(CSampleView, CView)/AFX_MSG_MAP(CSampleView)ON_MESSAGE(ID_COM1_DATA, OnProcessCom1Data) ON_MESSAGE(ID_COM2_DATA, OnProcessCom2Data) ./AFX_MSG_MAPEND_MESSAGE_MAP()然后在各成员函数中完成对各串口数据的接收处理,但必须保证在下一次监测到有数据到来之前,能够完成所有的中间处理工作。否则
26、将造成数据的捕捉错误。多线程的实现可以使得各端口独立,准确地实现串行通讯,使串口通讯具有更广泛的灵活性与严格性,且充分利用了 CPU 时间。但在具体的实时监控系统中如何协调多个线程,线程之间以何种方式实现同步也是在多线程串行通讯程序实现的难点。 以 VC+ 6.0 为工具,实现串行通讯的三种方法各有利弊:实现方法 MsComm 控件 单线程 多线程优点 简单易用。 较灵活,利用程序编制简单。可在非对话框的 DLL 等中使用。灵活、准确,CPU 利用率高,适用于要求较高的实时监控系统。缺点 只在对话框中使用。 一般只使用一些简单应用,性能不好。线程的同步较难。根据不同需要,选择合适的方法,将达到
27、事半功倍的效果。BSLIS 系统需要监控实时性较强的检验仪器设备的数据,运用了第三种原理实现,采用了目前在 VC+开发环境下使用较为普遍的 CserialPort 类( 12-11-1997 版) ,本人对其做了修改,使支持快速接收大批量的检验结果数据。CserialPort 和对话框通过 WM_COMM_RXCHAR 等Windows 自定义事件传递信息,使用 CserialPort 类编制程序时,需要在主对话框中处理这些事件的窗口成员函数。下面是 CserialPort 类中主要的成员函数功能说明:成员函数 功能说明InitPort 带串口参数打开串口,函数参数是波特率、数据位等。调用此函数自动启动串口数据监控。StartMonitoring 开始数据监控。RestartMonitoring 在串口参数不修改的情况下,重新启动串口监控。StopMonitoring 关闭串口数据监控。SetReadBuffSize 设置每次读取串口数据最大字符个数。Add By Trueway LeeWriteToPort 向串口写数据。关于 CserialPort 类的具体实现请见程序和代码注释。三、BSLIS 通讯程序介绍