实时数据库系统作为现代信息系统的核心,是企业级应用的实时数据中枢,通过该系统,上层应用系统(如 ERP 系统),可以获得良好的实时统计数据和实时统计信息的支撑。随着SUPCON实时数据库在现代企业信息化建设当中得到了越来越广泛的应用,不但对WINDOWS平台上主流编程语言提供了良好的支持,而且在ESP-iSYS4.0版本中为Java语言的开发提供了JRtdbc接口库,该接口在Jdk1.5平台上开发,其良好的面向对象设计与实现带来了每秒访问磁盘数据50000点以上的高性能,最大化的满足了工业实时应用性能需求,在符合Java纯面向对象的编程风格的同时,又打破了Java语言低效的束缚,以JRtdbc为桥梁,应用Java语言可以方便快速的开出各种优秀的实时应用。通过阅读本文,读者不仅能用JRtdbc进行java实时应用的开发,而且能够充分体会到JRtdbc提供的简单快速开发的优越性。
系统结构
整个JRtdbc的对象层次结构:
系统总体结构图-1
如上UML图,实时数据库当中的对象体系被明确的映射到了JRtdbc中,隐藏了实时数据库的一些艰涩的概念,其中IConnectionPool为连接池,类似于jdbc的数据库连接池,也是用JRtdbc进行开发时第一个要获得的对象,可以通过其获取实时数据库连接IConnection,实时数据库连接聚合了系统的四个核心工厂
名称 | 功能 |
ITagFactory | 获取位号/位号组实例 |
IUserFactory | 添加删除枚举用户/用户组 |
ITypeFactory | 添加删除枚举对象类型 |
IRegion getRegionRoot() | 获取对象树根节点,遍历实时数据库对象树 |
通过工厂方法,可以得到实时数据库的四大功能模块,位号与位号组;用户与用户组;对象类型与权限体系;整个实时数据库对象树的访问与操作;设计一致而且清晰,让开发人员更容易理解与使用,下面逐一进行介绍并讲解使用方法。
使用方法
实时数据库连接的建立
JRtdbc提供了连接池工厂PoolFactory,包含二个工厂方法:
IConnectionPool getPool() ; Œ
IConnectionPool getPool(int poolLength,int timeout) ;
IConnectionPool getPool(int poolLength,int timeout) ;
其中工厂方法Œ用于构建默认参数配置的连接池实例,工厂方法允许用户自定义连接池配置,参数poolLength定义为连接池连接上限,如果池中未达到上限,有新连接请求,直接新建连接放入池中返回,如已达上限,则偿试重登录其他用户空闲连接,无空闲连接存在则进入阻塞状态直到有连接释放,从而保证了系统不会无限制地增加并发连接并且可以充分利用空闲连接。参数timeout定义为连接池中的连接空闲达到一个给定的毫秒数后就自动释放连接,从而进一步节省实时数据库连接资源。IConnectionPool 连接池接口,通过getConnection(String conStr)方法获取一个与实时数据库之间的连接IConnection。JRtdbc连接池经过专门设计,用户只须获得连接,像普通对象一样调用,无须考虑底层连接状态,完全由连接池托管,摆脱了像Jdbc那种获得连接后还要时时不忘close()的那种烦恼,降低了使用复杂度。如何获取实时数据库连接?
//首先获得连接池实例,能过连接字符串得到连接IConnection实例
IConnectionPool pool = l();
IConnection conn = nection( String conStr ) ;
IConnectionPool pool = l();
IConnection conn = nection( String conStr ) ;
连接字符串格式:
host:;user=xxxxxx;password=xxxxxx;
其中各字段具体含义如下:
字段 | 含义 |
host | 为实时数据库所在计算机的IP和端口,其中,前指一个4段式IP地址,“:”后为实时数据库的端口地址 |
user | 用户名,有效内容为大小写英文字母、下划线 |
password | 密码,有效内容为大小写英文字母、数字、下划线、连字符 |
如果建立连接成功,则返回一个有效的IConnection对象实例,如果在连接建立过程中存在异常情况,则抛出IOException类型的异常。
位号与位号组
位号是存储在实时数据库当中的数据点,对应底层控制系统的数据源,通过JRtdbc曝露给用户的只是一个简单的ITag对象,调用其方法即可对位号/位号组进行操作,工业现场的控制设备,在提供数值的同时,也提供了对数值质量的判断,所以,实时数据库中的数据是有质量码的,通过质量码,可以确定读回的值是否可信、可用。在通常的应用中,验证质量码是缺省的,当出现错误质量码的时候,位号读取方法会抛出BadValueException异常,除了质量码跟数值,位号值还包括了采集数据值的时间戳,因此把数值,质量码,时间戳封装进了一个TagValue,作为一个位号值。位号组则是以一组位号为单位的TagGroup实例,联合读取各位号的实时值或历史值,以二维表的形式返回,以位号组中各位号名为列,支持以列名或者列索引获取组内某位号的每行数据。通过位号工厂即可获取位号与位号组对象,ITagFactory提供如下工厂方法:
名称 | 功能 |
getTag(String name) | 获取位号实例 |
getTagGroup(String[] names) | 获取位号组实例 |
getTagGroup(String regex) | 获取位号组实例 |
(一) 读位号实时值
通过位号工厂ITagFactory的getTag(tagNameStr)方法来获得实际的位号实例。该方法将根据位号的具体属性,返回虚位号或实位号的实体,而JRtdbc使用者并不需要关系其细节。具体的方法如下:
ITag t = Factory().getTag( tagName ) ;
通过ITag实例,可以完成读取实时值的操作。虽然每个位号均有其原生的数据类型,但ESP-iSYS在读取时做了安全的类型转换,也就是说,对于一个int类型的位号,仍然可以采用readFloat或readString方法,来获得浮点值或字符串值。ITag接口提供了针对不同数据类型的方法,如下:
方法 | 功能 |
readBoolean | 读取boolean类型的值 |
readDouble | 读取double类型的值 |
readFloat | 读取float类型的值 |
readInt | 读取int类型的值 |
readLong | 读取long类型的值 |
readShort | 读取short类型的值 |
readString | 读取String类型的值 |
这些方法均会返回实时数据库中位号的最新值,通过TagValue位号值封装类的getTimeStamp方法,可以获取到该位号值对应的时间戳。典型的使用代码如下:
ITag t = Factory().getTag( tagName ) ;
TagValue v = lue() ;
Date timeStamp = eStamp() ;
SimpleDateFormat sft = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss " );
String timeString = ( timeStamp);
TagValue v = lue() ;
Date timeStamp = eStamp() ;
SimpleDateFormat sft = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss " );
String timeString = ( timeStamp);
如果两种数据类型间不能正确转换时,readXXXX方法将会抛出TypeCastFailedException异常。
(二) 写位号实时值
写实时值的方法和读取实时值是对应的,写位号同样可以采用不同的类型写入,其列表如下:
方法 | 功能 |
writeBoolean | 写boolean类型的值 |
writeDouble | 写double类型的值 |
writeFloat | 写float类型的值 |
writeInt | 写int类型的值 |
writeLong | 写long类型的值 |
writeShort | 写short类型的值 |
readString | 写String类型的值 |
而JRtdbc会自动为该写操作分配时间戳和打上正确的质量码。
(三) 读位号历史数据
通过ITag实例,可以完成读取实时数据库内存与磁盘历史数据 ITag接口提供了如下方法:
方法 | 功能 |
readAllMemHisValue() | 读取全部内存历史值 |
readMemHisPointValue(Date p) | 读取某时刻磁盘历史值 |
readMemHisValue(Date b,Date e) | 读取某个时间段的内存历史值 |
readMemHisValue(Date b,Date e,long i) | 采样读取某个时间段的内存历史值 |
readDiskHisPointValue(Date p) | 读取某时刻磁盘历史值 |
readDiskHisValue(Date b,Date e) | 读取某个时间段的磁盘历史值 |
readDiskHisValue(Date b,Date e,long i) | 采样读取某个时间段的磁盘历史值 |
readHisValue(Date b,Date e,long i) | 自动区分内存或磁盘历史 |
采样,即为以一定的时间间隔去读位号值,如果某时刻无值存在,以NULL填充,因此采样读历史数据,读出来的是一个以相等时间间隔的值序列。不采样纯粹读出时间条件范围内的所有位号值,典型的读位号历史值的代码如下:
ITag t = (“TT3502”) ;
//读取全部内存历史
ArrayList vs = lMemHisValue() ;
//读取时间段的磁盘历史,begin,end 起始与结束时间,inter采样间隔,无值时以NULL填充进容器。
ArrayList vs = skHisValue( begin,end,inter)
//读取全部内存历史
ArrayList
//读取时间段的磁盘历史,begin,end 起始与结束时间,inter采样间隔,无值时以NULL填充进容器。
ArrayList
(四) 读取位号组值
读位号组的接口方法与单个位号是一样的,统一接口,易于使用,首先以一组位号名为参数,通过位号工厂得到位号组对象,调用读取方法,与单个位号不同的只是位号组返回的是以位号名为列的二维表TagDataTable形式的结构,与Jdbc的Record类似,可以遍历表中的所有行,通过位号名或者列索引获得组内某个位号的值,因此Java开发人员可以像处理Jdbc的Record一样处理位号组返回的数据,典型的使用代码如下:
String[] names = {“TT3501”,”TT3502”,”TT3503”}
ITagFactory tf = conn.getTagFactory() ;
ITagGroup tgrp = tf.getTagGroup(names) ;
//读实时值
TagDataRow r = tgrp.readValue() ;
TagValue v = r.getValue(“TT3501”) ;
//读历史值
TagDataTable t = tgrp.readDiskHisValue(begin,end,interval);
For(TagDataRow row : t.getRows()){
TagValue v = row.getValue(0);//以索引获得
System.out.println( v.getTimeStamp() ) ;//打印获取时间。
}
通过JRtdbc操作实时数据库位号与位号组是如此的方便与直观,用户完全感觉不到正在操作的是深奥的实时数据库。
ITagFactory tf = conn.getTagFactory() ;
ITagGroup tgrp = tf.getTagGroup(names) ;
//读实时值
TagDataRow r = tgrp.readValue() ;
TagValue v = r.getValue(“TT3501”) ;
//读历史值
TagDataTable t = tgrp.readDiskHisValue(begin,end,interval);
For(TagDataRow row : t.getRows()){
TagValue v = row.getValue(0);//以索引获得
System.out.println( v.getTimeStamp() ) ;//打印获取时间。
}
通过JRtdbc操作实时数据库位号与位号组是如此的方便与直观,用户完全感觉不到正在操作的是深奥的实时数据库。
用户与用户组
登录实时数据库的用户,以一个用户对象IUser存储在实时数据内部,当然还有其隶属的用户组IUserGroup,用户也可以在权限范围内增加删除用户,为用户分配权限,分配用户组,所有操作类似于平常操作系统的用户用户组结构,从系统UML图可以看出,IUser与IUserGroup派生于IUserBase,IUserBase抽取出了公共的对权限访问列表操作的方法。通过IConnection获得IUserFacotry用户工厂,该工厂提供了如下工厂方法:
方法 | 功能 |
addUser(UserAttribute attr) | 添加用户 |
addUserGroup(UserGroupAttribute attr) | 添加用户组 |
delUser( String name ) |
删除用户 |
delUserGroup(String name) | 删除用户组 |
getUsers() | 枚举用户 |
getUserGroups() | 枚举用户组 |
(一) 添加删除枚举用户/用户组
添加用户/用户组一般操作相当简单,取得连接后,得到用户工厂,建立用户或用户组属性对象,调用工厂方法addUser与addUserGroup即可,删除用户/用户组只须以名称为参数调用delUser/delUserGroup方法,典型代码如下:
IUserFacotry uf = rFacotry() ;
//添加用户
IUser u = r( new UserAttribute(“TESTUSER”)) ;
Boolean b = r(“TESTUSER”) ; //删除用户
//添加用户组
IUserGroup grp = rGroup( new UserGroup(“admin”)) ;
Boolean b = rGroup(“admin”);//删除用户组
//枚举用户用户组
ArrayList users = rs() ;
ArrayList grps = ups() ;
//添加用户
IUser u = r( new UserAttribute(“TESTUSER”)) ;
Boolean b = r(“TESTUSER”) ; //删除用户
//添加用户组
IUserGroup grp = rGroup( new UserGroup(“admin”)) ;
Boolean b = rGroup(“admin”);//删除用户组
//枚举用户用户组
ArrayList
ArrayList
(二) 验证用户权限
实时数据库有完整的安全机制,每个对象类型都有一系列的权限项存在,只有在权限允许的情况下,用户才能对实时数据库中的对象进行操作,IUser接口提供了简单的接口来验证用户是否具有操作实时数据库对象的权限,比如用户是否有权删除一个位号,删除位号这个权限项被定义在位号对象类型内,典型代码如下:
IUser u = new User(conn,”admin”) ;
String path = “.\水化车间\TT3502”;//某车间下的一个位号
String right = “删除位号” ;
Boolean b = u. authenticate(path,right) ;
If( b ) { ……} else { n(“用户无权限删除该位号”);}
String path = “.\水化车间\TT3502”;//某车间下的一个位号
String right = “删除位号” ;
Boolean b = u. authenticate(path,right) ;
If( b ) { ……} else { n(“用户无权限删除该位号”);}
(三) 用户/用户组权限访问列表
用户/用户组拥有一个允许访问列表和一个拒绝访问列表,这二个列表描述的是在对象类型当中定义的权限项是否具有,JRtdbc使用者可以通过IUser与IUserGroup提供的接口获得访问列表ArrayList
IUser u = new User(conn,”admin”) ;
String path = “.\水化车间\TT3502”;//某车间下的一个位号
String right = “删除位号” ;
Boolean b = u. removeACE(path,right) ;//删除了用户对位号的删除权限
IUser u2 = new User(conn,”test”) ;
Boolean b = u2.addACE(path,right) ;//用户具有了删除权限
//枚举
ArrayList aces = u.getACList() ;
ArrayList dcls = u.getDCList() ;
String path = “.\水化车间\TT3502”;//某车间下的一个位号
String right = “删除位号” ;
Boolean b = u. removeACE(path,right) ;//删除了用户对位号的删除权限
IUser u2 = new User(conn,”test”) ;
Boolean b = u2.addACE(path,right) ;//用户具有了删除权限
//枚举
ArrayList
ArrayList
对象类型与权限体系
实时数据据库当中的对象都有其对应的对象类型,例如,位号就是一个对象树上的特殊类型。对象类型都派生于IElementType,该接口提供了如添加自定义属性描述、添加权限项等功能。类型工厂ITypeFactory提供了下列工厂方法:
名称 | 功能 |
addType(ElementTypeAttribute attr) | 添加对象类型 |
delType(String typename) | 删除对象类型 |
getTypes() | 枚举对象类型 |
获取连接,得到类型工厂后,即可添加删除对象类型
(一) 添加删除对象类型
在实时数据中添加对象类型非常的简单,比如在实时数据库当中添加一种报表这样的一种类型,可先获得类型工厂,然后建立新类型的属性对象TypeAttribute其中只有类型名必须指定,调用addType添加,删除类型只须调用delType,并以对象类型名称为参数,典型代码如下:
ITypeFactory typef = eFacotry() ;
TypeAttribute attr = new TypeAttribute(“报表”) ;
IElementType type = e( attr ) ;//添加对象类型。
Boolean b = e(“报表”) ;//删除对象类型
ArrayList types = es() ;//枚举
TypeAttribute attr = new TypeAttribute(“报表”) ;
IElementType type = e( attr ) ;//添加对象类型。
Boolean b = e(“报表”) ;//删除对象类型
ArrayList
(二) 添加自定义属性描述
在实时数据库当中,可以为实时数据库对象添加自定义属性,存储在对象属性ElementAttribute的Hashtable中,实时数据库为验证自定义添加的属性正确性,用户必须提供自定义属性的描述信息,为简化操作,JRtdbc把描述信息抽象成TypeBase,具体描述类派生于此基类,提供了自定义属性的相关约束条,如最大值,最小值,值类型等,目前支持三种自定义类型的添加,Int32Type整型,DoubleType浮点型和StringType字符串型。比如现有report1这么一个报表对象,用户要为其添加一个名为path的路径属性,值为C:\report,则首先应该获取报表对象类型对象,在类型对象当中添加这个自定义属性的描述信息,如自定义属性名应该为path,类型为String,最大长度为100等信息,这样才能正确的为具体报表对象添加进这个自定义属性,典型代码如下:
StringType t = new StringType( path,true,”c:\report”) ;
Length(100);
Boolean b = ribute( t ) ;
Length(100);
Boolean b = ribute( t ) ;
(三) 添加权限项
实时数据库中每种对象类型都有其权限项列表,比如报表这个对象类型,它有完全控制、读报表,写报表、添加报表、删除报表等权限项,用户可以通过接口对权限项进行添加或删除,首先新建一个权限属性对象,设置权限项名称等信息,获得对象类型对象,调用addRight添加即可,删除操作直接以权限名为参数,调用delRight即可,典型代码如下:
RightAttribute attr = new RightAttribute(“报表完全控制”) ;
Boolean b = type.addRight( attr ) ;//添加权限项
Boolean b = type.delRight( rightName ) ;//删除权限项
Boolean b = type.addRight( attr ) ;//添加权限项
Boolean b = type.delRight( rightName ) ;//删除权限项
对象树的访问与操作
为简化在ESP-iSYS上进行Java开发,ESP-iSYS内部以企业资源树的形式来组织对象,位号、报表、区域、流程图等都是树上的对象,其中区域里面可以有对象也可以包含子区域,就像企业里面的组织结构,比如一个部门里有许多对象,也有许多车间,车间里又有位号之类的对象可能还有二级车间,这里部门和车间或二级车间在实时数据库里就表示为区域对象。对象树上的所有对象都派生于IElement,使得开发人员可以统一对待所有对象,对象属性有预定义属性和自定义属性,预定义属性以成员变量的形式存于对象实例中,自定义属性存放于Hashtable中,须先以TypeBase在对象类型中添加自定义属性描述后,才能为对象正确添加自定义属性,目前支持浮点型、整型、字符串型三种类型,如图:
可以通过IConnection方法getRegionRoot()获取对象树的根区域节点IRegion,从而完全遍历与访问各类对象。
(一) 添加对象
连接建立后,可通过IConnection的getRegionRoot方法获得对象树根结点,遍历到欲添加对象的区域节点,然后以对象属性ElementAttribute为参数,对象属性封装类已提供了大部份默认值,用户可只对必要字段进行赋值即可构建属性对象,避免了对大串属性一一赋值的烦琐,简化开发,再调用addElement(ElementAttribute attr)方法添加对象,相同方法添加位号或其它对象,典型代码如下:
IRegion root = ionRoot() ;
RealTagAttribute realTagAttr = new RealTagAttribute(“TT”);
ITag t = ( realTagAttr ) ;添加位号
//IElement e = ment(ElementAttribute attr) ;
RealTagAttribute realTagAttr = new RealTagAttribute(“TT”);
ITag t = ( realTagAttr ) ;添加位号
//IElement e = ment(ElementAttribute attr) ;
(二) 添加自定义属性
遍历对象树获取对象实例,调用getElementAttribute方法获取对象属性,调用addCustAttrbute(name,value)添加自定义属性,如在对象类型属性内未添加自定义属性描述,或者与描述信息不符,则抛出InValidAtrributeException,典型代码如下:
ITagFactory tf = conn.getTagFactory() ;
ITag t = tf.getTag( “TT3502” ) ;
ElementAttribute ta = t.getElementAttribute() ;
Ta.addCustAttr(“CustAttr”,”CustAttrValue”) ;
ITag t = tf.getTag( “TT3502” ) ;
ElementAttribute ta = t.getElementAttribute() ;
Ta.addCustAttr(“CustAttr”,”CustAttrValue”) ;
异常处理模块
JRtdbc有一套完整的异常体系,所有JRtdbc自有的异常全部派生于RtdbcException,含义明确,对抛出原因一目了然,并且大部份定义为未检异常,因为这些异常抛出后,用户无力恢复,未检异常不用让用户必须Try,Catch,这样减少代码量,降低开发复杂度。具体如下:
名称 | 含义 |
RtdbException | 实时数据库异常基类 |
BadValueException | 位号值质量码异常,读到坏值时抛出 |
InValidPackageException | 包解析错误时抛出 |
InValidAttributeException | 添加自定义属性时,属性无效时抛出 |
实例展示
下面是一个使用JRtdbc开发的一个读取位号实时值与历史值的完整示例,紧凑简短的几句Java代码即可读取到工业自动化领域当中的实时信息。
总结
以上详细介绍了ESP-iSYS平台Java接口的架构与使用步骤,“一个连接,四个工厂”通过短短的数行Java代码即能揭开实时数据库的神秘面纱,让开发人员畅游整个实时数据库,Java爱好者们可以尽情享受用Java快速开发实时应用所带来的乐趣。浙江中控软件技术有限公司在今后会不断的对其进行维护和升级,并对开发者提供良好的技术支持,共同努力在J2EE平台上开发出更多更优秀的实时应用。
(转载)