清除过滤器
文章
姚 鑫 · 六月 26, 2021
# 第十九章 使用%XML.TextReader
`%XML.TextReader`类提供了一种简单、容易的方法来读取可能直接映射到InterSystems IRIS对象,也可能不直接映射到InterSystems IRIS对象的任意XML文档。具体地说,该类提供了导航格式良好的XML文档并查看其中信息(元素、属性、注释、名称空间URI等)的方法。该类还基于`DTD`或`XML`架构提供完整的文档验证。但是,与`%XML.Reader`不同的是,`%XML.TextReader`不提供返回`DOM`的方法。如果需要DOM,请参阅前面的“将XML导入对象”一章。
**注意:使用的任何XML文档的XML声明都应该指明该文档的字符编码,并且文档应该按照声明的方式进行编码。如果未声明字符编码,InterSystems IRIS将使用前面的“输入和输出的字符编码”中描述的默认值。如果这些默认值不正确,请修改XML声明,使其指定实际使用的字符集。**
# 创建文本阅读器`Text Reader`方法
要读取不一定与 IRIS对象类有任何关系的任意XML文档,可以调用`%XML.TextReader`类的方法,该类将打开文档并将其作为文本阅读器对象加载到临时存储中。文本阅读器对象包含一个可导航的节点树,每个节点都包含有关源文档的信息。然后,方法可以导航该文档并查找有关该文档的信息。对象的属性提供有关文档的信息,这些信息取决于在文档中的当前位置。如果存在验证错误,这些错误也可以作为树中的节点使用。
## 整体结构
法应执行以下部分或全部操作:
1. 通过以下方法之一的第一个参数指定文档源:
`Method`| `First Argument`
---|---
`ParseFile()` |文件名,带有完整路径。请注意,文件名和路径只能包含ASCII字符。
`ParseStream()`|流
`ParseString()`| 字符串
`ParseURL()`| URL
**在任何情况下,源文档都必须是格式良好的XML文档;也就是说,它必须遵守XML语法的基本规则。这些方法中的每一个都返回一个状态(`$OK`或失败代码),以指示结果是否成功。可以使用常用机制测试状态;特别是可以使用`$System.Status.DisplayError(status)`查看错误消息的文本。**
对于这些方法中的每一个,如果该方法返回$OK,则它通过引用(其第二个参数)返回包含XML文档中的信息的文本阅读器对象。
其他参数允许控制实体解析、验证、找到哪些项等。这些内容将在本章后面的“解析方法的参数列表”中介绍。
2. 检查解析方法返回的状态,并在适当的情况下退出。
如果解析方法返回$OK,则有一个与源XML文档相对应的文本阅读器对象。可以导航此对象。
文档可能包含`“element”`、`“endelement”`、`“startprefixmapping”`等节点。
重要提示:在任何验证错误的情况下,文档包含“错误”或“警告”节点。
代码应该检查这些节点。
3. 使用以下实例方法之一开始读取文档。
- 使用`Read()`导航到文档的第一个节点。
- 使用`ReadStartElement()`导航到特定类型的第一个元素。
- 使用`MoveToContent()`导航到类型为`“chars”`的第一个节点。
4. 获取该节点感兴趣的属性的值(如果有的话)。可用的属性包括名称、值、深度等。
5. 根据需要继续在文档中导航并获取属性值。
如果当前节点是元素,则可以使用`MoveToAttributeIndex()`或`MoveToAttributeName()`方法将焦点移至该元素的属性。若要返回到元素(如果适用),请使用`MoveToElement()`。
6. 如果需要,可以使用`Rewind()`方法返回到文档的开头(第一个节点之前)。这是唯一可以在源代码中倒退的方法。
方法运行后,文本读取器对象将被销毁,所有相关的临时存储都将被清除。
## 示例1
下面是一个简单的方法,它可以读取任何XML文件,并显示每个节点的序列号、类型、名称和值:
```java
/// w ##class(PHA.TEST.Xml).WriteNodes("E:\temp\textReader.txt")
ClassMethod WriteNodes(myfile As %String)
{
set status = ##class(%XML.TextReader).ParseFile(myfile,.textreader)
//检查状态
if $$$ISERR(status) {do $System.Status.DisplayError(status) quit}
//逐个节点遍历文档
while textreader.Read()
{
Write !, "Node ", textreader.seq, " is a(n) "
Write textreader.NodeType," "
If textreader.Name'="" {
Write "named: ", textreader.Name
} Else {
Write "and has no name"
}
Write !, " path: ",textreader.Path
If textreader.Value'="" {
Write !, " value: ", textreader.Value
}
}
q ""
}
```
此示例执行以下操作:
1. 它调用`ParseFile()`类方法。这将读取源文件,创建一个文本阅读器对象,并通过引用在变量doc中返回该对象。
2. 如果`ParseFile()`成功,则该方法然后调用`read()`方法来查找文档中的每个后续节点。
3. 对于每个节点,该方法写入包含节点序列号、节点类型、节点名称(如果有)、节点路径和节点值(如果有)的输出行。输出将写入当前设备。
以下示例源文档:
```xml
yaoxin
1990-04-25
```
对于此源文档,前面的方法生成以下输出:
```java
DHC-APP>w ##class(PHA.TEST.Xml).WriteNodes("E:\temp\textReader.txt")
Node 1 is a(n) processinginstruction named: xml-stylesheet
path:
value: type="text/css" href="mystyles.css"
Node 2 is a(n) element named: Root
path: /Root
Node 3 is a(n) startprefixmapping named: s01
path: /Root
value: s01 http://www.root.org
Node 4 is a(n) element named: s01:Person
path: /Root/s01:Person
Node 5 is a(n) element named: Name
path: /Root/s01:Person/Name
Node 6 is a(n) chars and has no name
path: /Root/s01:Person/Name
value: yaoxin
Node 7 is a(n) endelement named: Name
path: /Root/s01:Person/Name
Node 8 is a(n) element named: DOB
path: /Root/s01:Person/DOB
Node 9 is a(n) chars and has no name
path: /Root/s01:Person/DOB
value: 1990-04-25
Node 10 is a(n) endelement named: DOB
path: /Root/s01:Person/DOB
Node 11 is a(n) endelement named: s01:Person
path: /Root/s01:Person
Node 12 is a(n) endprefixmapping named: s01
path: /Root
value: s01
Node 13 is a(n) endelement named: Root
path: /Root
```
请注意,注释已被忽略;默认情况下,`%XML.TextReader`忽略注释。
## Example 2
下面的示例读取一个XML文件并列出其中的每个元素
```java
/// w ##class(PHA.TEST.Xml).ShowElements("E:\temp\textReader.txt")
ClassMethod ShowElements(myfile As %String)
{
set status = ##class(%XML.TextReader).ParseFile(myfile,.textreader)
if $$$ISERR(status) {do $System.Status.DisplayError(status) quit}
while textreader.Read()
{
if (textreader.NodeType = "element")
{
write textreader.Name,!
}
}
q ""
}
```
此方法使用`NodeType`属性检查每个节点的类型。如果节点是元素,则该方法将其名称打印到当前设备。对于前面显示的XML源文档,此方法生成以下输出:
```java
DHC-APP>w ##class(PHA.TEST.Xml).ShowElements("E:\temp\textReader.txt")
Root
s01:Person
Name
DOB
```
# 节点类型
文档的每个节点都是以下类型之一:
文本阅读器文档中的节点类型
Type| Description
---|---
`"attribute"` |XML属性。
`"chars"` |一组字符(如元素的内容)。`%XML.TextReader`类识别其他节点类型(`“CDATA”`、`“EntityReference”`和`“EndEntity”`),但自动将它们转换为“字符”。
`"comment"`| XML注释。
`"element"`|XML元素的开始。
`"endelement"`|XML元素的结束。
`"endprefixmapping"`| 声明名称空间的上下文的结束。
`"entity"`|XML实体。
`"error"`| 解析器发现的验证错误。
`"ignorablewhitespace"`| 混合内容模型中标记之间的空白。
`"processinginstruction"`|XML处理指令。
`"startprefixmapping"`|XML命名空间声明,它可能包括也可能不包括命名空间。
`"warning"`| 解析器发现验证警告。
请注意,XML元素由多个节点组成。例如,以下XML片段:
```xml
Willeke,Clint B.
1925-10-01
```
SAX解析器将此XML视为以下节点集:
文档节点示例
Node Number |Type of Node| Name of Node, If Any| Value of Node, If Any
---|---|---|---
1 |element |Person
2| element |Name
3| chars || Willeke,Clint B.
4 |endelement| Name
5| element| DOB
6| chars || 1925-10-01
7| endelement| DOB
8| endelement| Person
例如,注意``元素被认为是三个节点:一个元素节点、一个字符节点和一个结束元素节点。还要注意,该元素的内容只能作为`chars`节点的值使用。
文章
姚 鑫 · 五月 21, 2021
# 第二章 设置和获取HTTP标头
# 设置和获取HTTP标头
可以设置和获取HTTP标头的值。
`%Net.HttpRequest`的以下每个属性都包含具有相应名称的HTTP标头的值。如果不设置这些属性,则会自动计算它们:
- `Authorization`
- `ContentEncoding`
- `ContentLength`(此属性为只读。)
- `ContentType` (指定`Content-Type`标头的Internet媒体类型(MIME类型)。)
- `ContentCharset` (指定`Content-Type`标题的字符集部分。如果设置此属性,则必须首先设置`ContentType`属性。)
- `Date`
- `From`
- `IfModifiedSince`
- `Pragma`
- `ProxyAuthorization`
- `Referer`
- `UserAgent`
`%Net.HttpRequest`类提供可用于设置和获取主HTTP标头的常规方法。这些方法忽略`Content-Type`和其他实体标头。
### ReturnHeaders()
返回包含此请求中的主`HTTP`标头的字符串。
### OutputHeaders()
将主`HTTP`标头写入当前设备。
### GetHeader()
返回此请求中设置的任何主HTTP标头的当前值。此方法接受一个参数,即头的名称(不区分大小写);这是一个字符串,如Host或Date
### SetHeader()
设置标题的值。通常,可以使用它来设置非标准标头;大多数常用标头都是通过Date等属性设置的。此方法有两个参数:
- 标头的名称(不区分大小写),不带冒号(`:`)分隔符;这是一个字符串,如Host或Date
- 标头值
不能使用此方法设置实体标头或只读标头(`Content-Length`和`Connection`)。
# 管理保活(Keep-alive)行为
如果重复使用`%Net.HttpRequest`的同一实例来发送多个HTTP请求,则默认情况下,InterSystems IRIS会使TCP/IP套接字保持打开状态,这样InterSystems IRIS就不需要关闭并重新打开它。
如果不想重复使用TCP/IP套接字,请执行以下任一操作:
- 设置`SocketTimeout`属性为0。
- 在你的HTTP请求中添加`'Connection: close'` HTTP头。
要做到这一点,在发送请求之前添加如下代码:
```java
Set sc=http.SetHeader("Connection","close")
```
注意,每个请求之后都会清除HTTP请求头,因此需要在每个请求之前包含此代码。
`%Net.HttpRequest`的`SocketTimeout`属性指定InterSystems IRIS将重用给定套接字的时间窗口(以秒为单位)。此超时旨在避免使用可能已被防火墙静默关闭的套接字。此属性的默认值为115。可以将其设置为不同的值。
# 处理HTTP请求参数
发送HTTP请求时(请参阅“发送HTTP请求”),可以在位置参数中包括参数;例如:`"/test.html?PARAM=%25VALUE"`将`PARAM`设置为等于`%value`。
还可以使用以下方法控制`%Net.HttpRequest`实例处理参数的方式:
### InsertParam()
将参数插入到请求中。此方法接受两个字符串参数:参数的名称和参数的值。例如:
```java
do req.InsertParam("arg1","1")
```
可以为给定参数插入多个值。如果这样做,这些值将接收从1开始的下标。在其他方法中,可以使用这些下标来引用目标值。
### DeleteParam()
从请求中删除参数。第一个参数是参数的名称。第二个参数是要删除的值的下标;仅当请求包含同一参数的多个值时才使用此参数。
### CountParam()
统计与给定参数关联的值数。
### GetParam()
获取请求中给定参数的值。第一个参数是参数的名称。如果请求没有同名的参数,则第二个参数是要返回的默认值;该默认值的初始值为空值。第三个参数是要获取的值的下标;仅当请求包含同一参数的多个值时才使用此参数。
### IsParamDefined()
检查是否定义了给定参数。如果参数有值,则此方法返回`TRUE`。参数与`DeleteParam()`相同。
### NextParam()
通过`$order()`对参数名称进行排序后,检索下一个参数的名称(如果有)。
### ReturnParams()
返回此请求中的参数列表。
# 包括请求正文
HTTP请求可以包括请求正文或表单数据。要包括请求正文,请执行以下操作:
1. 创建`%GlobalBinaryStream`的实例或子类。将此实例用于HTTP请求的`EntityBody`属性。
2. 使用标准流接口将数据写入此流。例如:
```java
Do oref.EntityBody.Write("Data into stream")
```
例如,可以读取一个文件并将其用作自定义HTTP请求的实体正文:
```java
set file=##class(%File).%New("G:\customer\catalog.xml")
set status=file.Open("RS")
if $$$ISERR(status) do $System.Status.DisplayError(status)
set hr=##class(%Net.HttpRequest).%New()
do hr.EntityBody.CopyFrom(file)
do file.Close()
```
# 发送分块请求
如果使用的是HTTP1.1,则可以分块发送HTTP请求。这涉及到设置`Transfer-Encoding`以指示消息已分块,并使用大小为零的块来指示完成。
当服务器返回大量数据并且在完全处理请求之前不知道响应的总大小时,分块编码非常有用。在这种情况下,通常需要缓冲整个消息,直到可以计算出内容长度(`%Net.HttpRequest`会自动计算)。
要发送分块请求,请执行以下操作:
1. 创建`%Net.ChunkedWriter`的子类,`%Net.ChunkedWriter`是定义以块形式写入数据的接口的抽象流类。在这个子类中,实现`OutputStream()`方法。
2. 在`%Net.HttpRequest`的实例中,创建`%Net.ChunkedWriter`子类的实例,并用要发送的请求数据填充它。
3. 将`%Net.HttpRequest`实例的`EntityBody`属性设置为等于此`%Net.ChunkedWriter实`例。
当发送HTTP请求时(请参见“发送HTTP请求”),它将调用`EntityBody`属性的`OutputStream()`方法。
在`%Net.ChunkedWriter`的子类中,`OutputStream()`方法应该检查流数据,决定是否分块以及如何分块,并调用类的继承方法来编写输出。
有以下方法可用:
### WriteSingleChunk()
接受字符串参数并将该字符串作为非分块输出写入。
### WriteFirstChunk()
接受字符串参数。写入适当的`Transfer-Encoding`标题以指示分块的消息,然后将字符串作为第一个分块写入。
### WriteChunk()
接受字符串参数并将字符串作为块写入。
### WriteLastChunk()
接受字符串参数,并将字符串作为块写入,后跟零长度块以标记结尾。
如果非NULL,则`TranslateTable`属性指定用于在写入时转换每个字符串的转换表。前面的所有方法都检查此属性。
# 发送表单数据
HTTP请求可以包括请求正文或表单数据。要包括表单数据,请使用以下方法:
### InsertFormData()
将表单数据插入到请求中。此方法接受两个字符串参数:表单项的名称和关联值。可以为给定表单项插入多个值。如果这样做,值将接收从1开始的下标。在其他方法中,可以使用这些下标来引用目标值
### DeleteFormData()
从请求中删除表单数据。第一个参数是表单项的名称。第二个参数是要删除的值的下标;仅当请求包含同一表单项的多个值时才使用此参数。
### CountFormData()
统计请求中与给定名称关联的值数。
### IsFormDataDefined()
检查是否定义了给定的名称
### NextFormData()
通过`$order()`对名称进行排序后,检索下一个表单项的名称(如果有)。
例1
插入表单数据后,通常调用`Post()`方法。例如:
```java
Do httprequest.InsertFormData("element","value")
Do httprequest.Post("/cgi-bin/script.CGI")
```
例2
```java
Set httprequest=##class(%Net.HttpRequest).%New()
set httprequest.SSLConfiguration="MySSLConfiguration"
set httprequest.Https=1
set httprequest.Server="myserver.com"
set httprequest.Port=443
Do httprequest.InsertFormData("portalid","2000000")
set tSc = httprequest.Post("/url-path/")
Quit httprequest.HttpResponse
```
# 插入、列出和删除Cookie
`%Net.HttpRequest`自动管理从服务器发送的`Cookie`;如果服务器发送`Cookie`,`%Net.HttpRequest`实例将在下一次请求时返回此`Cookie`。(要使此机制正常工作需要重用`%Net.HttpRequest`的同一实例。)
使用以下方法管理`%Net.HttpRequest`实例中的`Cookie`:
### InsertCookie()
将`Cookie`插入到请求中。指定以下参数:
- `Cookie`的名称。
- `Cookie`的值。
- 应存储`Cookie`的路径。
- 要从中下载`Cookie`的计算机的名称。
- `Cookie`过期的日期和时间。
### GetFullCookieList()
返回`Cookie`的数量,并(通过引用)返回`Cookie`数组。
### DeleteCookie()
请记住,`Cookie`是特定于HTTP服务器的。当插入`Cookie`时,使用的是到特定服务器的连接,而该`Cookie`在其他服务器上不可用。
文章
姚 鑫 · 九月 11, 2022
# 第二十九章 管理许可(二)
# 激活许可证密钥
`IRIS` 使用许可证密钥来确保其注册站点的正常运行、定义可用容量并控制对 `IRIS` 功能的访问。 许可证密钥以许可证密钥文件的形式提供,通常命名为 `iris.key`。
安装 `IRIS` 后,使用以下程序激活许可证密钥。始终可以使用相同的过程为任何已安装的实例激活新的许可证密钥(即升级密钥)。可以激活放置在管理门户可访问的任何位置的许可证密钥;作为激活的一部分,许可证密钥将作为 `iris.key` 复制到实例的 `install-dir/mgr` 目录(如果尚未命名)。
注意:也可以在 `Windows` 安装期间选择许可证密钥。执行此操作时,许可证会自动激活,并且许可证密钥会作为 `iris.key` 复制到实例的 `install-dir/mgr` 目录中;不需要此处描述的激活过程。
本节还讨论了许可证故障排除和在所有许可证单元都在使用时从操作系统命令行升级许可证。
要激活许可证密钥,请使用以下过程:
1. 导航到许可证密钥页面(系统管理 > 许可 > 许可证密钥)。将显示有关当前活动许可证密钥的信息。如果尚未激活任何许可证,则会显示这一点,例如通过标记客户名称:缺少许可证或不可读。此页面包含一个打印按钮,可让轻松打印显示的信息。
2. 单击激活许可证密钥并浏览到要激活的许可证密钥文件。当选择一个文件时,会显示有关它的信息,以便激活它之前验证是否拥有正确的许可证密钥;例如,它提供了所需的容量,并具有正确的到期日期。如果密钥无效,则会在错误消息中指出。如果许可证当前处于活动状态,则并排显示有关当前和选定许可证的信息。如果需要在激活后重新启动实例以使许可证密钥生效,则会记录这一点并提供原因。此对话框包括一个打印按钮,可让轻松打印有关当前活动许可证和选择的新许可证密钥的信息。
3. 单击激活以激活新的许可证密钥;它作为 `iris.key` 复制到实例的 `install-dir/mgr` 目录,覆盖之前的许可证密钥(如果有)。如果需要,确认对话框会提醒重新启动实例,并在新许可证启用的功能少于当前许可证时向发出警告。
通过使用 `Config.Startup`的 `LicenseID` 属性,可以将实例配置为从许可证服务器请求许可证密钥。在实例启动时,如果不存在 `iris.key` 文件并且已定义 `LicenseID`,则实例会从许可证服务器请求并激活许可证密钥。
**注意:相同的 `LicenseID` 必须在许可证密钥文件中,以及在需要下载许可证的实例上定义**
一般情况下无需重启实例,但升级许可证密钥时存在限制。如果将许可证类型从 `Power Unit` 更改为任何其他类型,则不会自动激活新密钥;这应该是一个罕见的事件。
另一个限制是许可证升级从通用内存堆 (`gmheap`) 空间中消耗的内存量。如果 `gmheap` 空间不可用,则无法扩展许可表条目的数量。如果没有足够的 `gmheap` 空间可用于许可证升级,则会将一条消息写入消息日志。可以从“高级内存设置”页面(系统管理 > 配置 > 高级内存设置)增加 `gmheap` 设置的大小。
如果新的许可证密钥比现有密钥消耗至少 `1000` 个 `64 KB` 页的 `gmheap` 空间,则必须重新启动 `IRIS` 实例才能完全激活新的许可证密钥。这种情况很少遇到,因为每个页面至少代表 `227` 个许可证。
## 更新许可证密钥
要更新许可证密钥,请替换 `KeyDirectory` 中的密钥文件并运行 `ReloadKeys^%SYS.LICENSE`。每个实例上的许可证监视器 (`^LMFMON`) 每 30 分钟检查一次,以查看配置的 `LicenseID` 是否有不同的密钥,如果有,则尝试执行升级。
**注意:虽然大多数升级在实时实例上成功,但某些情况可能需要重新启动实例。在这种情况下,许可证监视器会记录一个错误,并且直到第二天才尝试再次升级密钥(以避免记录重复的错误)。实例重启会在启动时加载新密钥。**
## 许可证故障排除
如果在输入许可证并重新启动 `IRIS` 后只有一位用户可以登录,请使用管理门户进行调查。当选择按进程时,许可证使用页面(系统操作 > 许可证使用)显示正在运行的进程数。还可以使用门户从许可证密钥页面(系统管理 > 许可 > 许可证密钥)显示许可证信息,如激活许可证密钥中所述。如果密钥无效,则 `CustomerName` 字段包含说明。
还可以在消息日志和系统监控日志中查看许可证错误消息,可以在 `Portal` 的消息日志页面(系统操作 > 系统日志 > 消息日志)和系统监控日志页面(系统操作 > 系统日志)中查看> 系统监控日志),分别。 `System Monitor` 将许可证到期警告和警报写入这些日志,而 `Health Monitor` 则写入许可证获取警报和警告。当超过许可限制时,许可模块会将警报写入消息日志。在 `Application Monitor` 中,可以配置基于许可证指标的警报以发送电子邮件通知或呼叫通知方法。
`$System.License.Help` 显示可用于解决许可证问题的方法列表:
```java
Do $System.License.Help()
```
### Administrator Terminal Session
有几个问题会阻止获得终端会话。当 `IRIS` 无法正常启动并进入单用户模式时,或者只是在没有可用许可证时,可能会发生这种情况。在这些情况下,可能需要创建管理员终端会话,该会话使用特殊许可证来解决问题。
### Administrator Session on Windows
使用命令提示符导航到 `install-dir\bin`。然后,以管理员身份执行以下命令:
```java
irisdb -s\mgr -B
```
这将从 `IRIS` 安装 `bin` 目录 (`install-dir\bin`) 运行 `IRIS` 可执行文件,指示 `install-dir\mgr` 的路径名(使用 `-s` 参数),并禁止所有登录,除了一个紧急登录(使用 `- B` 参数)。
例如,在默认目录中有一个名为 `MyIRIS` 的实例,该命令如下所示:
```java
c:\InterSystems\MyIRIS\bin>irisdb -sc:\InterSystems\MyIRIS\mgr -B
```
### Administrator Session on UNIX®, Linux, and macOS
使用命令提示符导航到 `install-dir/bin` 目录。然后,执行以下命令:
```java
iris terminal -B
```
例如,在默认目录中安装了一个名为 `MyIRIS` 的实例,该命令如下所示:
```java
User:/InterSystems/MyIRIS/bin$ iris terminal MyIRIS -B
```
## 从操作系统命令行升级许可证
`%SYSTEM.License.Upgrade()` 方法激活已复制到 `installdir\mgr` 目录的新许可证密钥。如果所有许可证单元都被用户使用,导致无法打开终端窗口,可以从命令行运行此方法以激活更大容量的新许可证密钥,如下所示:
```java
iris terminal -U %SYS '##Class(%SYSTEM.License).Upgrade()'
```
文章
Qiao Peng · 一月 14, 2021

您好! 本文介绍另一种为基于 InterSystems Caché 的解决方案创建安装程序的简单方法。 主题将涵盖只需一项操作即可安装或从 Caché 中完全删除的应用程序。 如果您仍在编写需要执行多个步骤才能安装应用程序的安装说明,是时候将这个过程自动化了。
问题的提出
假设我们为 Caché 开发了一个小型实用程序,之后我们想要将其分发。 当然,最好不要让不必要的配置和安装细节打扰到安装它的用户。 此外,这些说明必须非常全面,而且要面向可能对 Caché 一无所知的用户。如果是 Web 实用程序,安装程序不仅会要求用户将其类导入 Caché,而且至少还要配置 Web 应用程序才能对其进行访问,这是相当大的工作量:



当然,所有这些操作都可以通过编程方式执行。 您只需要了解如何实现。 但即使是这样,我们也需要让用户执行操作,例如在终端中执行一个命令。
通过单次导入操作进行安装
Caché 允许我们在类导入期间执行安装。 这意味着用户只需要使用任一方便的方法导入包含类包的 XML 文件:
将 XML 文件拖放到 Studio 区域。
通过管理门户:系统资源管理器 -> 类 -> 导入。
通过终端:do $system.OBJ.Load("C:\FileToImport.xml","ck")。
我们为安装应用程序而预先准备的代码将在类导入和编译后立即执行。 如果用户要卸载我们的应用程序(删除软件包),我们还可以清理应用程序在安装过程中创建的所有内容。
创建投影
为了扩展 Caché 编译器的行为,或者,在我们的示例中,为了在类的编译或反编译期间执行代码,我们需要在软件包中创建一个投影类。 它是一个扩展了 %Projection.AbstractProjection 的类,并重载了它的两个方法:CreateProjection(在编译过程中执行)和 RemoveProjection(在重新编译或删除类时触发)。
通常,将这个类命名为 Installer 是个好方法。 我们来看一个名为“MyPackage”的软件包的简单安装程序示例:
Class MyPackage.Installer Extends %Projection.AbstractProjection [ CompileAfter = (Class1, Class2) ]
{Projection Reference As Installer;/// This method is invoked when a class is compiled.ClassMethod CreateProjection(cls As %String, ByRef params) As %Status{ write !, "Installing..."}/// This method is invoked when a class is 'uncompiled'.ClassMethod RemoveProjection(cls As %String, ByRef params, recompile As %Boolean) As %Status{ write !, "Uninstalling..."}}
这里的行为可以描述为:
第一次导入和编译软件包时,只触发 CreateProjection 方法。
以后再编译 MyApp.Installer 时,或者导入“新”的安装程序类覆盖“旧”类时,将触发旧类的 RemoveProjection 方法,且 %recompile 参数等于 1,之后调用新类的 CreateProjection 方法。
删除软件包(同时删除 MyApp.Installer)时,将只调用 RemoveProjection 方法,参数 recompile = 0。
还需要注意以下几点:
类关键字 CompileAfter 应该包括应用程序的类名列表,在执行投影类的方法之前,需要对它们进行编译。 始终建议在此列表中填入应用程序中的所有类,因为如果安装过程中出错,我们不需要执行投影类的代码;
两个方法都接受 cls 参数 - 它是顶级类名,在我们的示例中为 MyApp.Installer。 这个理念来自于创建投影类的本义 - 通过从派生自 %Projection.AbstractProjection 的类再派生,可以单独为我们的应用程序的任何类制作“安装程序”。 只有在这种情况下才会体现出意义,但对于我们的任务来说是多余的;
CreateProjection 和 RemoveProjection 方法都接受第二个参数 params - 它是一个关联数组,以“参数名称”-“值”对的形式处理有关当前编译设置和当前类的参数值的信息。 通过执行 zwrite params 可以非常容易地探索该参数的内容;
RemoveProjection 方法接受 recompile 参数,只有删除类时,该参数才等于 0,重新编译时不等于 0。
类 %Projection.AbstractProjection 还有其他方法,我们可以重新定义这些方法,但我们的任务并不需要这样做。
一个示例
让我们更深入地了解为我们的实用程序创建 Web 应用程序的任务,并创建一个简单示例。 假设我们的实用程序是一个 REST 应用程序,在浏览器中打开时,它只发送一个响应“I am installed!”。 要创建这样的应用程序,我们需要创建一个描述它的类:
Class MyPackage.REST Extends %CSP.REST{XData UrlMap{ }ClassMethod Index() As %Status{ write "I am installed!" return $$$OK}}
创建并编译该类后,我们需要将其注册为 Web 应用程序入口点。 我在本文的顶部图示了如何进行配置。 在执行所有这些步骤后,最好通过访问 http://localhost:57772/myWebApp/ 来检查我们的应用程序是否正常工作(注意以下几点:1. 末尾的斜线是必需的;2. 端口 57772 在您的系统中可能有所不同。 它将匹配您的管理门户端口)。
当然,所有这些步骤都可以通过 Web 应用程序创建方法 CreateProjection 以及删除方法 RemoveProjection 中的一些代码来自动执行。 在这种情况下,我们的投影类如下所示:
Class MyPackage.Installer Extends %Projection.AbstractProjection [ CompileAfter = MyPackage.REST ]{Projection Reference As Installer;Parameter WebAppName As %String = "/myWebApp";Parameter DispatchClass As %String = "MyPackage.REST";ClassMethod CreateProjection(cls As %String, ByRef params) As %Status{ set currentNamespace = $Namespace write !, "Changing namespace to %SYS..." znspace "%SYS" // we need to change the namespace to %SYS, as Security.Applications class exists only there write !, "Configuring WEB application..." set cspProperties("AutheEnabled") = $$$AutheUnauthenticated // public application set cspProperties("NameSpace") = currentNamespace // web-application for the namespace we import classes to set cspProperties("Description") = "A test WEB application." // web-application description set cspProperties("IsNameSpaceDefault") = $$$NO // this application is not the default application for the namespace set cspProperties("DispatchClass") = ..#DispatchClass // the class we created before that handles the requests return ##class(Security.Applications).Create(..#WebAppName, .cspProperties)}ClassMethod RemoveProjection(cls As %String, ByRef params, recompile As %Boolean) As %Status{ write !, "Changing namespace to %SYS..." znspace "%SYS" write !, "Deleting WEB application..." return ##class(Security.Applications).Delete(..#WebAppName)}}
在此示例中,每次编译 MyPackage.Installer 类都将创建一个 Web 应用程序,每次“反编译”都将其删除。 最好在创建或删除应用程序之前检查该应用程序是否存在(例如,使用 ##class(Security.Applications).Exists(“Name”) ),但是为了使本示例简单起见,这个作业就留给阅读本文的读者来完成了。
在创建 MyPackage.REST 和 MyPackage.Installer 类之后,我们可以将这些类导出为一个 XML 文件,并将该文件分享给所有有需要的人。 导入此 XML 的用户将自动设置 Web 应用程序,然后可以开始在浏览器中使用。
结果
与 InterSystems 社区上介绍的使用 %Installer 类部署应用程序的方法不同,这种方法有以下优点:
使用“纯”Caché ObjectScript。 至于 %Installer,需要用特定标签来填充 xData 块,大量文档对此进行了介绍。
安装我们的应用程序的方法是在类编译后立即执行的,我们不需要手动执行;
如果类(包)被删除,将自动执行删除我们的应用程序的方法,这不能通过使用 %Installer 来实现。
我的项目中已经使用这种应用程序安装方法 - Caché WEB Terminal、Caché Class Explorer 和 Caché Visual Editor。 您可以在此处找到 Installer 类的示例。
顺便提一下,开发者社区还有一个帖子介绍了投影的功能,作者是 John Murray。
另外值得一提的是 Package Manager 项目,该项目旨在让 InterSystems 数据平台的第三方应用程序只需通过一个命令或一次点击即可安装,就像类似 npm 的包管理器一样。
文章
Jingwei Wang · 三月 28, 2023
IRIS 配置和用户帐户包含需要跟踪的各种数据元素,许多人难以在 IRIS 实例之间复制或同步这些系统配置和用户帐户。那么如何简化这个过程呢?
在软件工程中,CI/CD 或 CICD 是持续集成 (CI) 和(更常见的)持续交付或(较少见的)持续部署 (CD) 的组合实践集。 CI/CD 能消除我们所有的挣扎吗?
我在一个开发和部署 IRIS 集群的团队工作。我们在 Red Hat OpenShift 容器平台上的容器中运行 IRIS。
如果您当前没有使用 Kubernetes,请不要停止阅读。即使您没有使用 Kubernetes 或在容器中运行 IRIS,您也可能会遇到与我和我的团队面临的挑战类似的挑战。
我们决定将代码与配置分开,并将它们放在不同的 GitHub 存储库中。每次在代码库中进行提交时,都会触发管道运行。结果,从代码库中的文件构建了一个新image。
我们通过将 YAML 文件和其他配置工件添加到部署 GitHub 存储库,将配置定义为以 GitOps 方式使用的代码。 GitOps 是一个软件开发框架,它使组织能够持续交付软件应用程序,同时使用 Git 作为单一事实来源有效地管理 IT 基础设施(以及更多)。 GitOps 的好处之一是能够轻松回滚。您所需要做的就是恢复到 Git 中的先前状态。
DevOps 是软件开发和 IT 行业的一种方法论。作为一套实践和工具使用,DevOps 将软件开发(Dev) 和IT 运营(Ops) 的工作集成并自动化,作为改进和缩短系统开发生命周期的一种手段。 [1]
我在维基百科上读到持续交付是“当团队在短周期内以高速和频率生产软件时,以便可以随时发布可靠的软件,并在决定部署时采用简单且可重复的部署过程。”
同时,维基百科将持续部署定义为“当新软件功能完全自动推出时”。
我们已决定将 YAML 文件存储在部署 GitHub 存储库中。
iris-cpf(上面的第 19 行)指的是一个 ConfigMap,其中包含用于 CPF Merge 的文件。
有多种可用的 CD 管道工具可以将配置作为代码推送部署,而不必手动应用文件。
例如,我的团队使用Argo CD 。它是一个 GitOps 工具,作为 Kubernetes 扩展部署在集群中。它很特别,因为它在集群中具有可见性。它的用户界面在浏览器中显示应用程序状态,因为 Argo CD 是 Kubernetes 扩展。
与仅启用基于推送的部署的外部 CD 工具不同,Argo CD 可以从 Git 存储库中拉取更新的(类似)代码并将其直接部署到 Kubernetes 资源。
像 Argo CD 这样的拉动部署工具将我们的 Kubernetes 集群的实际状态与我们的部署 repo 中描述的期望状态进行比较。
Argo CD 监视我们的部署 repo 和我们的 Kubernetes 集群。我们的部署repo 是唯一的真实来源。如果 GitHub 存储库中发生某些更改,Argo CD 将更新集群以匹配存储库中定义的所需状态。
Argo CD 代理同步 GitHub 存储库和 Kubernetes 集群。如果我们手动应用一个更改,当 Argo CD 将已部署的应用程序同步到 Git 中定义的所需状态时,它将被删除。
为了将不同的配置部署到不同的集群,我们使用Kustomize 。我们在部署仓库中定义了一个基本配置。我们还在部署 GitHub 存储库中定义了覆盖,以便为开发、SQA、阶段和生产等各种环境配置不同的系统默认设置和不同的images。
在第 417 行中,我们确定基于环境的系统默认设置存储在 SDS_ENV.xml 中。
那些没有使用 Kubernetes 的情况呢?我创建了许多可在 Open Exchange 中使用的应用程序。我学习了如何使用 Installer 类在 GitHub 存储库中定义 IRIS 配置,然后构建image并运行容器。
然而,如果在部署应用程序后需要对配置或用户帐户进行一些修改,情况会怎样呢?当涉及到持久卷时,事情就变得复杂了。发生这种情况是因为我们不想丢失存在于持久卷上的数据。
存储在持久卷上的所有这些东西是什么? IRIS 配置保存在数据目录的 mgr 目录中。 CPF Merge 功能应该使我们能够修改 iris.cpf 中的任何配置设置。
我的团队已将大量代码添加到 %ZSTART routines中,这些routines在 IRIS 计算或数据实例启动时执行。我们担心的一个问题是所谓的零大小 CPF 错误。我们经常遇到 IRIS 实例因大小为零的 CPF 文件而崩溃的情况。不幸的是,我们尚未发现该问题的根本原因。我们怀疑 CPF 合并操作以及在 %ZSTART routine中添加和删除的大量Routine、Global和包映射会导致零大小的 CPF 错误。
我们编写了代码来删除所有系统默认设置,并从作为卷安装在 IRIS 容器中的 Kubernetes ConfigMap 中导入它们。事实上,我们有两组系统默认设置:一组是在所有环境中导入的基本设置,另一组是因环境而异的环境特定设置。
我们决定从 XML 文件中导入用户。有时,当我们直接从 %ZSTART routine 执行这段代码时,我们会遇到问题。我们根据 InterSystems 的建议将此代码移至计划任务中。显然,我们发现了一个在某些情况下可能会破坏全局安全性的错误。无论如何,出于某种我现在不记得的原因,当用户帐户导入由计划从 %ZSTART routine 按需运行的任务执行时,此问题不再是问题。这一定是时间问题。计划任务晚于 %ZSTART routine运行。
我们创建了一个自定义密码验证routine来强制执行密码规则。
当我们需要一个新的 Web 应用程序时,我们应该怎么做?除了 CPF Merge,CSP Merge 怎么样?
我相信 Web 应用程序存储在 %SYS IRIS.dat 文件中。我考虑尝试在可以使用 ConfigMap 挂载的文件中定义 Web 应用程序。我们可以将代码添加到 %ZSTART 例程或添加另一个计划任务来查找文件并创建 IRIS 中尚不存在的任何 Web 应用程序。
使用 InterSystems Kubernetes Operator 部署的 Webgateway 容器具有一个持久数据卷,其中包含 CSP.conf 和 CSP.ini。但是,我们还没有实现在添加新的 Web 应用程序时根据需要自动更新这些文件的方法。
Lorenzo Scalese 创建了可在 Open Exchange 中使用的 config-api 和 config-copy 应用程序。他建议在您的应用程序安装程序模块中使用 IRS-Config-API 库。
IRIS-Config-API 可以在一个环境将 IRIS 配置导出到 JSON 文档,并在另一个环境中从 JSON 文档导入 IRIS 配置。
Lorenzo 创建了 iris-config-copy 工具,用于从一个 InterSystems IRIS 实例导出配置并将其导入另一个实例。如果我们在源实例和目标实例上都安装 iris-config-copy,则目标实例使用 REST 从源实例获取配置。
我们需要在源实例上创建一个 Web 应用程序,以使目标实例能够从源实例中检索 IRIS 配置。
Iris-config-copy 可以导出本地实例或远程实例的 IRIS 配置。
有一些方法可以导入特定的配置文件。我们可以导入Security、包含 SQL 连接的globals、CPF 配置数据或任务。
文章
姚 鑫 · 九月 30, 2021
# 第三十一章 SQL命令 DROP DATABASE
删除数据库(命名空间)。
# 大纲
```sql
DROP DATABASE dbname [RETAIN_FILES]
```
## 参数
- `dbname` - 要删除的数据库(命名空间)的名称。
- `RETAIN_FILES` - 可选-如果指定,则不会删除物理数据库文件(`IRIS.DAT`文件)。默认情况下,删除`.dat`文件以及命名空间和其他数据库实体。
# 描述
`DROP DATABASE`命令删除命名空间及其关联的数据库。
指定的`dbname`是包含相应数据库文件的命名空间和目录的名称。指定`dbname`作为标识符。命名空间名称不区分大小写。如果指定的`DBNAME`命名空间不存在, IRIS将发出`SQLCODE-340`错误。
`DROP DATABASE`命令是一个特权操作。
在使用`DROP DATABASE`之前,必须以`%Admin_Manage`资源的用户身份登录。
用户还必须拥有用于例程和全局数据库定义的资源的`READ`权限。
如果不这样做,将导致`SQLCODE -99`错误(权限冲突)。
使用`$SYSTEM.Security.Login()`方法为用户分配适当的权限:
```java
DO $SYSTEM.Security.Login("_SYSTEM","SYS")
&sql( )
```
必须具有`%Service_Login:Use`权限才能调用`$SYSTEM.Security.Login`方法。
不管权限如何,`DROP DATABASE`都不能用于删除系统命名空间。尝试这样做会导致`SQLCODE-342`错误。
`DROP DATABASE`不能用于删除当前正在使用或连接到的命名空间。尝试这样做会导致`SQLCODE-344`错误。
还可以使用管理门户删除命名空间。依次选择System Administration、Configuration、System Configuration、Namespaces以列出现有的`Namespace`。单击要删除的命名空间的删除按钮。
## RETAIN_FILES
如果指定此选项,则保留物理文件结构;删除数据库及其关联的命名空间。执行此操作后,后续尝试使用`DBNAME`将导致以下结果:
- `DROP DATABASE`不带`RETAIN_FILES`无法删除此物理文件结构。相反,它会导致`SQLCODE-340`错误(未找到数据库)。
- `DROP DATABASE WITH RETAIN_FILES`还会导致`SQLCODE-340`错误(找不到数据库)。
- `CREATE DATABASE`无法创建同名的新数据库。相反,它会导致`SQLCODE-341`错误(无法为数据库创建数据库文件)。
- 尝试使用此命名空间会导致``错误。
## 服务器初始化和断开代码
服务器初始化代码和服务器断开代码可以通过`$SYSTEM.SQL.Util.SetOption("ServerInitCode",value)`和`$SYSTEM.SQL.Util.SetOption("ServerDisconnectCode",value)`方法分配给命名空间。
可以使用相应的`$SYSTEM.SQL.Util.GetOption()`方法选项来确定当前值。
使用`DROP DATABASE`或其他接口删除命名空间,将删除这些`Server Init Code`和`Server Disconnect Code`值。
因此,删除并重新创建名称空间需要重新指定这些值。
# 示例
```sql
CREATE DATABASE DocTestDB ON DIRECTORY 'c:\InterSystems\IRIS142\mgr\DocTestDB'
```
```sql
DROP DATABASE DocTestDB RETAIN_FILES
```
文章
Michael Lei · 四月 13, 2022
这篇文章是对我的 iris-globals-graphDB 应用的介绍。在这篇文章中,我将演示如何在Python Flask Web 框架和PYVIS交互式网络可视化库的帮助下,将图形数据保存和抽取到InterSystems Globals中。
建议
阅读相关文档 使用 Globals
原生 SDK 介绍
PYVIS 互动式网络可视化库
第一步 : 通过使用Python 原生SDK建立与IRIS Globals的链接
#create and establish connection
if not self.iris_connection:
self.iris_connection = irisnative.createConnection("localhost", 1972, "USER", "superuser", "SYS")
# Create an iris object
self.iris_native = irisnative.createIris(self.iris_connection)
return self.iris_native
第二步 : 使用 iris_native.set( ) 功能把数据保存到Globals 里
#import nodes data from csv file
isdefined = self.iris_native.isDefined("^g1nodes")
if isdefined == 0:
with open("/opt/irisapp/misc/g1nodes.csv", newline='') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
self.iris_native.set(row["name"], "^g1nodes", row["id"])
#import edges data from csv file
isdefined = self.iris_native.isDefined("^g1edges")
if isdefined == 0:
with open("/opt/irisapp/misc/g1edges.csv", newline='') as csvfile:
reader = csv.DictReader(csvfile)
counter = 0
for row in reader:
counter = counter + 1
#Save data to globals
self.iris_native.set(row["source"]+'-'+row["target"], "^g1edges", counter)
第三步: 使用iris_native.get() 功能把节点和边缘数据从Globals传递给PYVIS
#Get nodes data for basic graph
def get_g1nodes(self):
iris = self.get_iris_native()
leverl1_subscript_iter = iris.iterator("^g1nodes")
result = []
# Iterate over all nodes forwards
for level1_subscript, level1_value in leverl1_subscript_iter:
#Get data from globals
val = iris.get("^g1nodes",level1_subscript)
element = {"id": level1_subscript, "label": val, "shape":"circle"}
result.append(element)
return result
#Get edges data for basic graph
def get_g1edges(self):
iris = self.get_iris_native()
leverl1_subscript_iter = iris.iterator("^g1edges")
result = []
# Iterate over all nodes forwards
for level1_subscript, level1_value in leverl1_subscript_iter:
#Get data from globals
val = iris.get("^g1edges",level1_subscript)
element = {"from": int(val.rpartition('-')[0]), "to": int(val.rpartition('-')[2])}
result.append(element)
return result
Step4: Use PYVIS Javascript to generate graph data
<script type="text/javascript">
// initialize global variables.
var edges;
var nodes;
var network;
var container;
var options, data;
// This method is responsible for drawing the graph, returns the drawn network
function drawGraph() {
var container = document.getElementById('mynetwork');
let node = JSON.parse('{{ nodes | tojson }}');
let edge = JSON.parse('{{ edges | tojson }}');
// parsing and collecting nodes and edges from the python
nodes = new vis.DataSet(node);
edges = new vis.DataSet(edge);
// adding nodes and edges to the graph
data = {nodes: nodes, edges: edges};
var options = {
"configure": {
"enabled": true,
"filter": [
"physics","nodes"
]
},
"nodes": {
"color": {
"border": "rgba(233,180,56,1)",
"background": "rgba(252,175,41,1)",
"highlight": {
"border": "rgba(38,137,233,1)",
"background": "rgba(40,138,255,1)"
},
"hover": {
"border": "rgba(42,127,233,1)",
"background": "rgba(42,126,255,1)"
}
},
"font": {
"color": "rgba(255,255,255,1)"
}
},
"edges": {
"color": {
"inherit": true
},
"smooth": {
"enabled": false,
"type": "continuous"
}
},
"interaction": {
"dragNodes": true,
"hideEdgesOnDrag": false,
"hideNodesOnDrag": false,
"navigationButtons": true,
"hover": true
},
"physics": {
"barnesHut": {
"avoidOverlap": 0,
"centralGravity": 0.3,
"damping": 0.09,
"gravitationalConstant": -80000,
"springConstant": 0.001,
"springLength": 250
},
"enabled": true,
"stabilization": {
"enabled": true,
"fit": true,
"iterations": 1000,
"onlyDynamicEdges": false,
"updateInterval": 50
}
}
}
// if this network requires displaying the configure window,
// put it in its div
options.configure["container"] = document.getElementById("config");
network = new vis.Network(container, data, options);
return network;
}
drawGraph();
</script>
第五步: 从app.py 主文件调用上面的代码
#Mian route. (index)
@app.route("/")
def index():
#Establish connection and import data to globals
irisglobal = IRISGLOBAL()
irisglobal.import_g1_nodes_edges()
irisglobal.import_g2_nodes_edges()
#getting nodes data from globals
nodes = irisglobal.get_g1nodes()
#getting edges data from globals
edges = irisglobal.get_g1edges()
#To display graph with configuration
pyvis = True
return render_template('index.html', nodes = nodes,edges=edges,pyvis=pyvis)
下面是关于此项目的 介绍视频:
欢迎大家来我们的 Bilibili主页观看更多视频!
谢谢!
文章
姚 鑫 · 九月 23, 2022
# 第四十一章 使用多个 IRIS 实例(一)
可以在单个主机系统上安装和运行多个 `IRIS®` 数据平台实例。每个实例都是一个独特的、独立的 `IRIS` 环境。
# 管理 IRIS 实例
有许多方法可以连接和管理 `IRIS` 实例,它可能是安装在给定系统上的几种方法之一。两种最常用的方法如下:
- 安装在 `Windows` 系统上的每个 `IRIS` 实例在系统托盘中都有自己的启动器,除其他选项外,还可以:
- 通过打开管理门户、 `Terminal`和 `Studio` 开发者客户端连接到实例。
- 启动、停止和重新启动实例。
- 打开用户和开发人员文档。
从启动器中,还可以管理多个远程 `IRIS` 实例,包括但不限于运行远程备份、编辑配置设置以及创建和编译远程对象和例程。
- `iris command` 在操作系统命令行上执行 iris 命令可让管理访问 `IRIS` 实例,其中包括其他选项,可以:
- 使用 `Terminal`连接到实例。
- 启动、停止和重新启动实例。
- 显示有关该实例以及系统上安装的其他实例的信息。
要在远程服务器上使用 `iris` 命令,请使用 `Telnet` 或 `SSH` 客户端;要将它与容器化实例一起使用,请在容器内使用它,或者使用 `docker exec` 命令从容器外部运行它。
# 连接到 `IRIS` 实例
`Terminal` 是一个命令行,可以在 `IRIS` 实例的任何命名空间中使用。使用命令 `iris terminal instname` 打开正在运行的实例的终端,其中 `instname` 是在安装时为实例指定的名称。容器化实例通常被命名为 `IRIS`。
使用在安装期间提供的密码或创建的帐户使用预定义的用户帐户之一登录。显示的提示指示登录命名空间,例如:
```java
# iris terminal IRISHealth
Node: intersystems2588, Instance: IRIS27
Username: admin
Password: ********
USER>
```
要退出终端并关闭窗口,请输入命令 `halt`。
当使用`docker exec` 命令打开容器化实例的终端时(如在部署和探索 `IRIS` 中使用 终端进行交互中所述),将自动以 `irisowner` 身份登录,无需进行身份验证。
在 `Windows` 系统上,必须从其位置( `IRIS` 实例的 `install-dir\bin` 目录)执行命令,或在命令中包含完整路径,例如 `c:\InterSystems\IRIS27\bin\iris terminal IRISHealth` .可以执行给定实例的二进制文件以连接到该实例或另一个;无论哪种方式,实例名称都是必需的。
文章
姚 鑫 · 二月 12, 2023
# 第七十四章 使用 irisstat 实用程序监控 IRIS - 查看 irisstat 输出
# 查看 `irisstat` 输出
可以立即查看 `irisstat` 数据(通过终端)或重定向到输出文件以供以后分析。查看数据的最常见方法是:
注意:当 `IRIS` 被强制关闭时,`irisstat` 会运行以捕获系统的当前状态。作为紧急关闭程序的一部分,输出被添加到消息日志中。
## irisstat 文本文件
`irisstat` 报告可以重定向到文件而不是终端,如果想收集一组 `IRIS` 工具(诊断报告任务、`IRISHung` 脚本、`^SystemPerformance` 实用程序)未提供的一组 `irisstat` 选项,这可能很有用或者如果在运行这些工具时遇到问题。
## 诊断报告任务
诊断报告任务会创建一个包含基本信息和高级信息的 HTML 日志文件,InterSystems 全球响应中心 (WRC) 可以使用该文件来解决系统问题。
注意:诊断报告任务不能在挂起的系统上运行;如果系统挂起,请参阅本附录中的 `IRISHung` 脚本。
## IRISHung 脚本
`IRISHung` 脚本是一个操作系统工具,用于在 `IRIS` 实例挂起时收集系统数据。位于 `install-dir\bin` 目录中的脚本名称是特定于平台的,如下表中指定:
Platform| Script name
---|---
`Microsoft Windows`| `IRISHung.cmd`
`UNIX®/Linux` |`IRISHung.sh`
`IRISHung` 脚本应以管理员权限运行。与诊断报告任务一样,`IRISHung` 脚本运行 `irisstat` 两次,间隔 `30` 秒,以防状态发生变化,并将报告与其他收集的数据一起打包到一个 `html` 文件中。从 `IRISHung` 获取的 `irisstat` 报告使用以下选项
```java
irisstat -e2 -f-1 -m-1 -n3 -j5 -g1 -L1 -u-1 -v1 -p-1 -c-1 -q1 -w2 -E-1 -N65535
```
`IRISHung` 还运行仅使用 `-S2` 选项的第三个 `irisstat`,它将其写入一个单独的输出部分,称为“自诊断”。 `-S2` 选项导致可疑进程留下小型转储;因此,运行 `IRISHung` 可能会收集有关负责挂起的特定进程的信息,而简单地强制实例关闭不会收集此信息。
此外,`IRISHung` 生成的 `irisstat` 输出文件通常非常大,在这种情况下,它们被保存到单独的 `.txt` 文件中。请记住在收集输出时检查这些文件。
## `^SystemPerformance Utility`
`^SystemPerformance` 实用程序收集有关 `IRIS` 实例及其运行平台的详细性能数据。在 `IRIS` 内运行一段可配置的时间,在该时间间隔内收集样本,并在完成时生成报告。
文章
Michael Lei · 七月 1, 2022
各位领导、老师大家好。非常荣幸有机会参加这次由中国数字医学杂志社组织的陕西省医院数字化转型研讨会。
IT这个行业很有意思,就是大家都很喜欢造词。这几年有一个词特别火,叫做数智化底座,很多厂商都先后推出了自己的数智化底座解决方案。结合最近对整个行业的一些观察,今天借这个机会,跟各位领导和老师探讨一下,医疗行业的数字化有什么特点,到底什么样的底座或者平台比较符合我们医疗行业,以及我们在建设数智化底座的时候需要考虑哪些问题。结合我们最近的一些观察和思考,有不当之处,欢迎各位老师批评、指正。
首先一点就是我们做任何工作,首先要解决“为什么”的问题?第一个核心思路,我想数字化转型是为智慧医院服务的,归根结底,还是要通过数字化的手段,来实现医院的高质量发展。针对这一目标,国家卫健委制定了智慧医院发展的三大目标,就是智慧医疗、智慧管理和智慧服务,我想说白了,无非就是让医院、医护人员以及我们的患者过的更好,提高我们治疗和护理水平、降本增效,同时能够让我们的患者得到更好的服务。所有的数字化建设,不管是平台还是应用,都应该围绕这一核心目标。
第二个核心思路,我们认为软件要为人服务。所谓的数字化转型,就是用软件来开展一切可以开展的业务,而软件是为人服务的,目的是提高我们的工作效率、认知水平和实现我们仅仅靠人力做不了的事情。比如智慧医来讲, 我们比较熟悉的是我们的HIS、电子病历、临床系统等等,这类系统主要的用户/使用者是我们的临床医护人员,主要的目的一方面是提高医护人员的工作效率,同时也是为了让医疗数据能够得以电子化的形式来永久保存,为支持临床数据的共享、交换、科研、监管等需求提供了数据来源。
而智慧管理的应用,更多是如何帮助医院,不管是院长还是科室主任等VIP客户了解自己的经营数据,以及根据这些数据来制定相关的管理决策,从而能够有效地控制成本和提高医院收入。
第三,目前我国医院数字化进程中亟需加强的,就是智慧服务。随着时代的发展,过去很长一段时间里支撑我们公立医院发展的几个核心要素,无论是大型医疗设备、医护人员数量、手术床位还是药品耗材物资,随着公立医院绩效考核、分级诊疗、医保政策、疫情等多种时代原因的综合作用,这些要素能够给医院带来的边际收入会逐渐递减,未来医院的收入增长,一定是通过数据要素驱动和数字化的手段,将医院服务从院内延展到院外,延展到患者的全生命周期的健康管理,以及随之带来的各种增值服务收入。这种服务一定是数字化的,更多是通过类似我们现在经常使用的手机APP来交付和实现的。
第三个核心思路,我们认为底座是服务应用的,应该根据我们的数字化应用的类型,来决定我们需要什么样的底座,而不是为了配合底座来取改变应用。现在行业内普遍有一种“唯技术化”、“赶时髦”的误区,不管做什么样的应用、实现何种业务,都说要云原生、微服务、分布式,怎么时髦怎么来。但是我们认为,底座或者是基础软件是服务于应用的,如果底座不能适应应用,或者说费了很大的力气改造底座而应用却没有改善,则是本末倒置,舍本逐末。举个不恰当的例子,就像我们做饭一样,如果家里只有3-4口人,有一口普通的锅、炉子就够用了,没有必要非要去整一口给100人做饭的锅和炉子,或者非要整100口小锅,小炉子,还是同样做3-4个人的饭,还是同样做那一碗羊肉泡馍。同样道理,如果是同一类型、同样功能的应用软件,如果只有500个人用,有个能支撑1000人用的底座就够了,非要搞个给10万人用的底座,不仅意义不大,而且浪费有限的投资。
那下面我们就来具体看看三类应用的特点,以及他们各自对底座有什么要求。
比如智慧医疗类的应用,主要面向医护人员,以数据的录入、增删改为主,这类系统的业务量,比如说每天的门诊量、检查量、手术量相对是比较稳定的,那么对于平台来讲,我们更多的需要是安全、稳定、高可用、性能等这些特性,SLA要求非常高、不能宕机或者随意的停机;
对智慧管理来讲,这一类主要是分析型的应用,这就不仅需要平台支持海量的数据存储与管理,也能支持医院或者厂商满足院长或者管理层的很多不管是常规还是临时性的数据分析类需求,比如说院长今天想看一个之前新的分析指标,或者监管部门临时增加了一些上报的指标,是否能够很快速的在智慧管理的应用里实现出来,这些都是平台或者底座需要具备的能力;
智慧服务类的应用,这类业务主要面向患者,这些都是比较典型的互联网化的业务,通过手机app、小程序等来交付和实现,业务量变化弹性很大,也要随时快速推出各种不同的创新业务,这时候对平台的要求就是要支持云原生、弹性扩展、容器化、快速集成、快速交付等等特性;
上面是从技术视角来看我们医院数字化转型需要什么样的平台支撑。下面从管理视角来看一下医院数字化的现状,以及针对这些现状我们在建设数智平台时建议采取的应对策略和思路。我们观察到整个医疗行业数字化有以下六个主要特点。
特点与思路一 医疗行业是个强监管的行业
医疗行业有着非常严谨和严格的行业标准和监管要求,以及大量的、数据共享交换的需求,这一点是任何其他行业所不具备的,因此,我们在建设数智底座时一定要考虑平台本身需要满足医疗的这些行业标准和要求,比如我们比较熟悉的互联互通、电子病历、HL7、IHE等等,这些都是最基本的要求。
特点与思路二 医疗行业是业务最复杂的行业,没有之一
我们说没有哪个行业的业务有医疗行业这样高的复杂程度。医院从来都不是一个单一业务,不管是大型综合医院,还是专科医院,基本都是一个科室一种业务,现在我们讲的单病种,或者DRGs,就变成了一个病种一种业务,还有很多复杂病、罕见病是一个病多个业务模式或者混合模式,比如说MDT等等;针对这种情况,我们说IT的技术其实很好掌握,不管是编程还是数据库,但是对医疗业务,尤其是电子病历的理解与认识,没有个10几20年的经验是没有办法沉淀下来的。因此,IT工程师好找,而医疗行业行业经验和业务专家不好找。因此医院在建设数智平台时,还是应该选择对医疗行业有长期积淀和丰富经验的合作伙伴,并且尽可能地向国内外顶级医院学习他们的成功经验,而不是仅仅从IT的能力来选择合作伙伴和底座;
电子病历数据模型示意图
特点与思路三 医疗数据利用水平亟待提高
尽管有不少医院都花了不少钱建设了数据中心、CDR、ODS等等系统,但是不管临床还是科研部门,对医疗数据利用需求与我们的系统或者平台支持的差距还非常大;比如说,医院最重要的数据资产之一,就是大量的患者的病历文档、检验检查、就诊记录等等,比如CDA,但是CDA文档比较大也比较重,很难进行深度利用,如何从整体样本中发现规律,如何进行科学分组,指导临床治疗方案等等,这些都是比较难实现的,我们最近就在尝试利用国际上已经比较成熟,但是国内还刚刚起步的HL7 FHIR 标准来做CDA文档的转化和解构,将CDA文档转化为更好利用的FHIR资源,来支持医院做更多的创新性数据应用。FHIR是英文快速医疗互操作资源的翻译,把CDA转换成FHIR之后,我们就可以把难以利用的文档数据转化为临床、科研、患者都可以轻松利用的结构化数据,从中发现疾病和患者规律,开展真实世界研究或者指导临床开展“精准医疗”或者类似网络购物体验的临床医疗方案推荐等等,帮助医院真正盘活数据资源,把资源变成资产,有效服务与临床、科研等业务需求。
特点与思路四 信息部门人员配置不足vs纷繁复杂的技术栈--少就是多
从整个医疗信息化行业来看,信息部门的人员配置是远远不足的,从医院的信息科到我们的服务厂商,从主任到项目经理,到厂商的研发、实施技术人员,整个医疗信息化行业的从业人员工作负载基本都处在一个饱和甚至过饱和的状态,针对这种情况,在医院数字化平台的技术选择上,我们应该采取“少就是多”的策略,选择尽可能少的技术和产品种类,或者说一体化的技术架构,用一套技术体系来支持多种业务应用的实现,从而降低管理和学习成本。
另外希望跟大家讨论的一个问题,就是在金融、零售、互联网行业十分普遍的基于开源架构的数据中台是否适合医疗行业?无可否认的是,开源技术的兴起为整个IT行业、包括传统行业带来了前所未有的繁荣和创新,但是同时开源软件也有发展过快、技术路线分散等特点,比如全球现在有超过100万个开源社区,每个社区都有自己的粉丝和市场,那么作为医院的信息部门和信息化行业的厂商应该如何选择?今天选择的技术会不会很快就过时了,技术支持如何延续?更不要说开源软件学习成本和人员成本都很高,以及这些技术如何能够在医院成功落地,实际的案例效果和投入产出如何,这些都是我们作为从业者需要考虑的显示问题。
互联网基于开源技术的数据中台真的适合医疗行业吗?
特点与思路五 有限的数字化资金预算vs高额投入的需求--集中优势兵力,逐个消灭敌人
伴随着人员投入的另外一个问题,是我们的资金投入问题,今天正好是建党101年的大喜的日子。1946年9月16日,毛主席在距离咱们西安300多公里的延安写下了《集中优势兵力、逐个消灭敌人》的重要文章,为我们全党全军最终迅速战胜敌人指明了方针和原则。如果把兵力比喻成我们的信息化投入,把敌人比喻成我们要解决的业务问题的话,我觉得这个方针同样适用。
过去由于历史原因,在每年或者总投入有限的情况下,很多医院采用撒胡椒面的方式,经过多年的积累,建设了很多单价不高的系统,少则几十个,多则上百个;面对几十个厂家,1百多个系统,如果算下总账,不仅建设成本没少花,而且集成、运维、沟通等管理成本也都非常高,效果也不尽如人意。因此,越是在整体预算有限的情况下,我们越是可能需要学习毛主席的策略,把有限的子弹和资金,相对集中地进行投资,来满足尽可能多的数字化需求,也许是能够让我们医院信息化迅速提高到一个新的台阶的更有效的策略。我们也欣喜地观察到,越来越多的医院意识到这一点,开始把有限的预算进行相对集中,通过一个规模大一点的项目来尽可能地解决更多的问题,很多厂商也开始推所谓的一体化平台,也是这种思路。
特点与思路六 医院数字化--无止境的旅程/没有终点的长跑
最后一点我想指出的是,医疗行业不管是医院本身,还是数字化转型,本质上都是一个长跑型的业务模式。横向来看,全世界范围内百年的企业屈指可数,但是百年甚至百年以上的医院却比比皆是,或者说是正当年。我们在刚开始的时候就提到,数字化转型是为智慧医院服务的,只要医院的临床、科研、管理和服务一天不停止发展,数字化的支撑就没有尽头的那一天。因此,对医院来说,数字化转型更像是一个没有终点的长跑,最重要的是跑了多远,而不是跑得多快。对底座来讲,更加需要一个相对稳健的技术路线,以及长跑型的陪跑者作为我们数字化转型和底座建设的合作伙伴。
InterSystems--40多年只做一件事,助力客户成功,做医院数字化转型的终身陪跑者
最后介绍一下我们公司,我们公司创立于1978年,是我们创始人当年和麻省总医院的几个医生一起,从亲自写第一行代码开始,一步一步走到今天,我们可能是全球医疗行业历史最悠久的平台软件公司。 经过40多年的发展,复旦百强榜中40%的医院、全美排名前20的医院、以及全世界、全国数百家不同等级的医院都在用我们的软件支撑他们的核心业务。之所以能够得到这么多客户的认可,我想主要原因还是我们长期深耕行业,专门为医疗行业打造了全球唯一一款医疗版数据平台,集中了医院数字化转型所需要的几乎所有的底层技术,并做了深度集成和优化,我们的逻辑就是通过一套软件,来满足医院在数字化转型过程针对数智底座的几乎所有需求,包括支持复杂的医疗业务与医疗行业的标准、多模型、互操作性、混合事务-分析处理、高级分析、API、混合云、容器化、分布式等等。最后一点我想说的,我们在国内从事医疗信息化20多年以来,最深的体会就是我们长期坚持做一件事,就是踏踏实实的做好我们的技术和产品、做好我们的服务、帮助客户解决问题,获得了最宝贵的东西,也就是客户多年的信任。未来我们希望能把这一件事坚持做下去,服务好我们的医院和合作伙伴,继续为医疗数字化服务下一个十年、二十年甚至更长的时间,以上就是我今天的分享,谢谢大家!
公告
Claire Zheng · 一月 16, 2023
亲爱的开发者社区成员们,
我们非常高兴地与您分享最新的开发者社区功能!
🔥 您的 2022 年回顾 🔥
现在,您可以生成个性化“2022 年活动概要”——体现了您在2022年在开发者社区的主要活动,包括发帖数量、评论、视图、热门和最喜欢的帖子/标签等等!
如何查看?有两种方式。
方式一:在您主页顶部会发现一个新横幅:
单击“深入了解”按钮查看您当年的个人亮点!比如:
方式二:点击右上角头像,进入个人主页后即可查看
快来回顾你的2022吧!欢迎在评论分享自己的“2022战报”!
文章
姚 鑫 · 三月 30, 2021
# 第十四章 使用SQL Shell界面(一)
# 执行SQL的其他方式
可以使用`$SYSTEM.SQL.Execute()` 方法从Terminal命令行执行一行SQL代码,而无需调用SQL Shell。以下示例显示如何在终端提示下使用此方法:
```sql
DHC-APP>SET result=$SYSTEM.SQL.Execute("SELECT TOP 5 name,dob,ssn FROM Sample.Person")
DHC-APP>DO result.%Display()
Name DOB SSN
yaoxin 54536 111-11-1117
xiaoli 111-11-1111
姚鑫 63189 111-11-1112
姚鑫 63189 111-11-1113
姚鑫 50066 111-11-1114
5 Rows(s) Affected
```
如果SQL语句包含错误,则`Execute()`方法成功完成;否则,该方法无效。 `%Display()`方法返回错误信息,例如:
```java
USER>DO result.%Display()
[SQLCODE: :]
[%msg: < Field 'GAME' not found in the applicable tables^ SELECT TOP ? game ,>]
0 Rows Affected
USER>
```
`Execute()`方法还提供了可选的`SelectMode`、`Dialect`和`ObjectSelectMode`参数。
InterSystems IRIS支持许多其他编写和执行SQL代码的方法
这些包括:
- 嵌入式SQL:嵌入ObjectScript代码中的SQL代码。
- 动态SQL:使用`%SQL`。
从ObjectScript代码中执行SQL语句的语句类方法。
- 管理门户SQL接口:使用Execute Query接口从InterSystems IRIS管理门户执行动态SQL。
# 调用SQL Shell
可以使用`$SYSTEM.SQL.Shell()`方法在终端提示符中调用SQL Shell,如下所示:
```java
DO $SYSTEM.SQL.Shell()
```
或者,可以使用%SQL作为一个实例调用SQL Shell。
Shell类,如下所示:
```java
DO ##class(%SQL.Shell).%Go("IRIS")
```
或
```java
SET sqlsh=##class(%SQL.Shell).%New()
DO sqlsh.%Go("IRIS")
```
无论如何调用,SQL Shell都会返回SQL Shell提示符,如下所示:
```java
[SQL]termprompt>>
```
其中[SQL]是指在SQL Shell中,termprompt是配置的终端提示符,`>>`是指SQL命令行。
默认情况下,SQL Shell提示符如下所示:`[SQL]nsp>>`,其中`“nsp”`是当前命名空间的名称。
```java
DHC-APP>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
The command prefix is currently set to: .
Enter q to quit, ? for help.
DHC-APP>> >
1>>
1>>SELECT TOP 5 name,dob,ssn FROM Sample.Person
2>>
2>>q
DHC-APP>>q
```
在这个提示符下,可以使用以下任一Shell模式:
- 单行模式:在提示符下键入一行SQL代码。
结束SQL语句,按`“Enter”`。
默认情况下,这将准备并执行SQL代码(这称为立即执行模式)。
对于查询,结果集显示在终端屏幕上。
对于其他SQL语句,将在终端屏幕上显示SQLCODE和行数值。
- 多行模式:在提示符下按Enter。这使进入多行模式。可以键入多行SQL代码,每个新行提示均指示行号。 (空行不会增加行号。)要结束多行SQL语句,请键入GO并按Enter。默认情况下,这既准备并执行SQL代码。对于查询,结果集显示在终端屏幕上。对于其他SQL语句,SQLCODE和行计数值显示在终端屏幕上。
多行模式提供以下命令,可以在多行提示符下键入以下命令,然后按Enter:`L`或`LIST`列出到目前为止输入的所有SQL代码。 `C`或`CLEAR`删除到目前为止输入的所有SQL代码。 `C n`或`CLEAR n`(其中`n`是行号整数)以删除特定的SQL代码行。 `G`或`GO`准备和执行SQL代码,然后返回单行模式。 `Q`或`QUIT`删除到目前为止输入的所有SQL代码并返回单行模式。这些命令不区分大小写。发出命令不会增加下一个多行提示的行号。打`?`在多行提示符处列出了这些多行命令。
为了准备一条SQL语句,SQL Shell首先验证该语句,包括确认指定的表存在于当前名称空间中,并且指定的字段存在于表中。如果不是,它将显示适当的SQLCODE。
如果该语句有效,并且具有适当的特权,则SQL Shell会回显SQL语句,并为其分配一个序号。无论您是否更改名称空间和/或退出并重新进入SQL Shell,这些数字在终端会话期间都将按顺序分配。这些分配的语句编号允许重新调用以前的SQL语句,如下所述。
也可以使用`DO Shell^%apiSQL`.在终端提示下调用SQL Shell。
要列出所有可用的SQL Shell命令,请输入`?`。在SQL提示下。
要终止SQL Shell会话并返回到`Terminal`提示符,请在SQL提示符下输入`Q`或`QUIT`命令或`E`命令。 SQL Shell命令不区分大小写。
以下是使用默认参数设置的示例SQL Shell会话:
```java
DHC-APP>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
The command prefix is currently set to: .
Enter q to quit, ? for help.
DHC-APP>>SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State
1. SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State
Name Home_State
xiaoli
姚鑫
姚鑫
姚鑫
姚鑫
5 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0523s/45502/270281/2ms
execute time(s)/globals/lines/disk: 0.0004s/225/2915/0ms
---------------------------------------------------------------------------
DHC-APP>>q
```
以下是使用默认参数设置的多行SQL Shell会话:
```java
DHC-APP>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
The command prefix is currently set to: .
Enter q to quit, ? for help.
DHC-APP>> >
1>>SELECT TOP 5
2>>Name,Home_State
3>>FROM Sample.Person
4>>ORDER BY Home_State
5>>GO
2. SELECT TOP 5
Name,Home_State
FROM Sample.Person
ORDER BY Home_State
Name Home_State
xiaoli
姚鑫
姚鑫
姚鑫
姚鑫
5 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0002s/18/1168/0ms
execute time(s)/globals/lines/disk: 0.0003s/225/2886/0ms
---------------------------------------------------------------------------
```
## GO命令
SQL Shell `GO`命令执行最新的SQL语句。在单行模式下,`GO`重新执行最近执行的SQL语句。在多行模式下,`GO`命令用于执行多行SQL语句并退出多行模式。单行模式下的后续`GO`将重新执行先前的多行SQL语句。
## 输入参数
SQL Shell支持使用`“?”`输入参数的使用SQL语句中的字符。每次执行SQL语句时,系统都会提示指定这些输入参数的值。必须以与`“?”`相同的顺序指定这些值字符出现在SQL语句中:第一个提示为第一个`“?”`提供一个值,第二个提示为第二个`“?”`提供一个值,依此类推。
输入参数的数量没有限制。可以使用输入参数将值提供给`TOP`子句,`WHERE`子句,并将表达式提供给`SELECT`列表。不能使用输入参数将列名提供给`SELECT`列表。
可以将主机变量指定为输入参数值。在输入参数提示下,指定一个以冒号(:)开头的值。该值可以是公共变量,ObjectScript特殊变量,数字文字或表达式。然后,SQL Shell会提示“这是文字(Y / N)吗?”。在此提示下指定N(否)(或仅按Enter)意味着将输入值解析为主机变量。例如,`:myval`将被解析为局部变量myval的值; `:^ myval`将被解析为全局变量`^myval`的值; `:$HOROLOG`将被解析为`$HOROLOG`特殊变量的值; `:3`将被解析为数字3; `:10-3`将被解析为数字`7`。在此提示符下指定Y(是)表示将输入值(包括冒号)作为文字提供给输入参数。
## 执行ObjectScript命令
在SQL Shell中,可能希望发出一个ObjectScript命令。例如,通过使用`SET $NAMESPACE`命令将InterSystems IRIS命名空间更改为包含要引用的SQL表或存储过程的命名空间。可以使用SQL Shell!命令或OBJ命令以发出由一个或多个ObjectScript命令组成的ObjectScript命令行。 (OBJ是OBJECTSCRIPT的缩写。)!,OBJ和OBJECTSCRIPT命令是同义词。以下示例显示了这些命令的用法:
```java
%SYS>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------
The command prefix is currently set to: .
Enter q to quit, ? for help.
[SQL]%SYS>>! SET oldns=$NAMESPACE SET $NAMESPACE="USER" WRITE "changed the namespace"
changed the namespace
[SQL]USER>>OBJ SET $NAMESPACE=oldns WRITE "reverted to old namespace"
reverted to old namespace
[SQL]%SYS>>
```
OBJ命令之后的其余命令行被视为ObjectScript代码。 !之间不需要空格。和ObjectScript命令行。可以在SQL Shell单行模式或SQL Shell多行模式下指定OBJ命令。以下示例在USER名称空间中定义的表上执行SELECT查询:
```java
%SYS>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------
The command prefix is currently set to: .
Enter q to quit, ? for help.
[SQL]%SYS>> >
1>>OBJ SET $NAMESPACE="USER"
1>>SELECT TOP 5 Name,Home_State
2>>FROM Sample.Person
3>>GO
/* SQL query results */
[SQL]USER>>
```
请注意,OBJ语句不会增加SQL行数。
在SQL Shell多行模式下,在返回行时将执行OBJ命令,但是直到指定`GO`才发出SQL语句。因此,以下示例在功能上与先前的示例相同:
```java
%SYS>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------
The command prefix is currently set to: .
Enter q to quit, ? for help.
[SQL]%SYS>> >
1>>SELECT TOP 5 Name,Home_State
2>>FROM Sample.Person
3>>OBJ SET $NAMESPACE="USER" WRITE "changed namespace"
changed namespace
3>>GO
/* SQL query results */
[SQL]USER>>
```
以下示例使用OBJ命令定义主机变量:
```java
USER>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------
The command prefix is currently set to: .
Enter q to quit, ? for help.
[SQL]USER>> >
1>>SELECT TOP :n Name,Home_State
2>>FROM Sample.Person
3>>OBJ SET n=5
3>>GO
```
## 浏览命名空间
SQL Shell支持BROWSE命令,该命令显示在当前名称空间中定义或可从其访问的架构,表和视图。该显示包括多个级别的提示。要返回上一个提示级别,请在提示时按`Return`键。名称区分大小写。
1. 在SQL Shell提示符下键入BROWSE,以列出当前名称空间中的架构。
2. 在“架构:”提示下,按名称或编号选择一个架构。这将列出架构中的表和视图。
3. 在“表/视图:”提示下,按名称或编号选择一个表(T)或视图(V)。这将显示表信息,然后显示选项列表。
4. 在“选项:”提示下,按编号选择一个选项。可以使用此选项列出为表定义的字段或映射。
指定选项1(按名称表示的字段)或选项2(按数字表示的字段)以显示“Field:”提示。指定选项3(地图)以显示“Map:”提示。
5. 在`“Field:”`提示下,按数字或名称选择一个字段,或指定*以列出所有字段。这列出了详细的字段信息。
在`“Map:”`提示下,按数字或名称选择地图,或指定`*`列出所有Map。这列出了详细的Map信息。
## CALL 命令
可以使用SQL Shell发出SQL `CALL`语句来调用SQL存储过程,如以下示例所示:
```java
DHC-APP>>CALL Sample.PersonSets('G','NY')
3. CALL Sample.PersonSets('G','NY')
Dumping result #1
Name DOB Spouse
Gallant,Thelma Q. 45767 94
Gibbs,Mark S. 37331 13
Goldman,Will H. 59069 10
Gomez,Mo Q. 55626 55
Gore,Alfred M. 42991 13
Gore,Fred X. 32391 6
Grabscheid,Jocelyn B. 59676 79
7 Rows(s) Affected
Dumping result #2
Name Age Home_City Home_State
Chadbourne,Danielle G. 34 Pueblo NY
Eastman,Clint G. 4 Miami NY
Pape,Linda M. 71 Vail NY
Peterson,Janice N. 49 Islip NY
Schaefer,Jocelyn V. 93 Zanesville NY
5 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0043s/2153/15795/0ms
execute time(s)/globals/lines/disk: 0.0015s/315/7829/0ms
---------------------------------------------------------------------------
```
如果指定的存储过程在当前名称空间中不存在,则SQL Shell会发出`SQLCODE -428`错误。
如果指定的输入参数多于存储过程中定义的参数,则SQL Shell会发出`SQLCODE -370`错误。可以使用文字(`“字符串”`),主机变量(`:var`)和输入参数(`?`)的任意组合为存储过程指定参数值。
可以在`CALL`语句中使用主机变量,如以下示例所示:
```java
[SQL]USER>>OBJ SET a="G",b="NY"
[SQL]USER>>CALL Sample.PersonSets(:a,:b)
```
- 可以在`CALL`语句中使用输入参数(`“?”`字符),如以下示例所示:
```java
[SQL]USER>>CALL Sample.PersonSets(?,?)
```
当执行`CALL`语句时,SQL Shell会提示为每个输入参数提供一个值。
## 执行SQL脚本文件
SQL Shell `RUN`命令执行一个SQL脚本文件。脚本文件的类型由`DIALECT`设置确定。 `DIALECT`的默认值为IRIS(InterSystems SQL)。
文章
Michael Lei · 九月 24, 2022
亚太地区的医疗数字化环境正在快速发展。在不断增长的消费者需求、快速发展的技术和更复杂的护理需求的驱动下,医疗机构正面临着提供数字优先、无缝、可持续的医疗服务的压力。
网络攻击不断增加、人们对安全问题的关注也越来越多、不断上升的医疗成本,以及数据的爆炸性增长,医疗机构正在转向数字医疗技术,以希望把复杂的事情变得简单。
在2022年,哪些趋势正在塑造亚太地区医疗服务的未来?哪些机会是成熟的创新?医疗行业领导者如何确保他们的组织能够做好准备来利用新出现的机会?
趋势一 不断增长的数据量带来了复杂性
在我们寻找新的方法来利用数据的同时,数据量继续上升,越来越多的数据被综合起来,以建立有意义的联系并提供可指导行动的洞察。
趋势二 人工智能在医疗领域的应用加速
随着医疗行业在人工智能成熟度方面赶上其他行业。数据既是一个关键的成功因素,也是人工智能成功应用的一个障碍。趋势三 互操作性解决方案释放数据的真正力量
随着部署互操作性解决方案的成本降低,在组织内部和外部,实现真正的互操作性的梦想也在逐步实现。
趋势四 远程医疗服务转向虚拟护理模式
远程医疗从一个在院外提供割裂的医疗服务转向一个更全面的、综合的虚拟护理模式。
趋势五 医疗物联网(Internet of Medical Things,IoMT)的崛起
随着IoMT市场价值的稳步增长,我们面临着的 "数据洪水",可能会影响我们释放这项技术的价值。
原文:https://www.intersystems.com/au/5-trends-shaping-the-future-of-digital-health-ebook-in-2022-anz/
文章
Michael Lei · 六月 23, 2021
InterSystems 数据平台包括了用于系统监视和警报的实用程序及工具,但对于不熟悉构建于 InterSystems 数据平台(又名 Caché)的解决方案的系统管理员来说,他们需要知道从何处下手以及需要配置什么。
本指南以在线文档和开发者社区帖子为参考,介绍了实现最低限度的监视和警报解决方案的途径,以及如何启用和配置以下组件:
1. **Caché Monitor:**扫描控制台日志并发送电子邮件警报。
2. **System Monitor:**监视系统状态和资源,根据固定参数生成通知(警报和警告),同时跟踪整体系统运行状况。
3. **Health Monitor:**对主要的系统和用户定义指标进行采样,并将它们与用户可配置的参数和既定的标准值进行比较,在样本超过适用或学习的阈值时生成通知。
4. **History Monitor:**维护性能指标和系统使用情况指标的历史数据库。
5. **pButtons:**每天按计划收集操作系统指标和 Caché 指标。
请记住,本指南是最低配置,所包含的工具灵活且可扩展,因此可在需要时提供更多功能。 本指南跳过了文档,让您直接上手。 您需要更深入地研究文档才能充分利用监视工具,同时,请考虑将本指南作为启动和运行的速查表。
----
# 1. Caché Monitor
控制台日志 `(install-directory/mgr/cconsole.log)` 必须被监视,可以通过第三方工具扫描日志文件,或者像我们这样使用附带的 Caché Monitor 实用工具将警报发送到一个电子邮件地址。
控制台日志是其他监视工具(包括 Caché System Monitor 和 Caché Health Monitor)用于编写警报和通知的中央存储库。
> 至少将 Caché Monitor 配置为向一个电子邮件发送警报。
使用 ^MONMGR 实用工具管理 Caché Monitor。
## Caché Monitor 基本设置
Caché Monitor 扫描控制台日志,并根据可配置的消息严重级别生成通知。 通知通过电子邮件发送到您配置的收件人列表。 默认扫描周期为每 10 秒一次,但可以更改。
> 提示:将 Caché Monitor 警报严重级别配置为 1(警告、严重和致命条目)。 如果您发现收到的警报太多,可以降回到警报级别 2(严重和致命条目)。
当 60 秒内有一系列条目来自一个给定进程时,只会针对第一个条目生成通知,然后暂停一小时。 因此,当出现问题时,必须进行调查。 因为没有新消息并不表示事件已过去。 此规则的例外是 [Caché Monitor 错误和陷阱](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_monitor#GCM_monitor_errors)中列出的控制台日志条目,会针对其中所有条目生成通知。
[有关完整的配置详细信息,请参见 ^MONMGR 的在线文档。](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_monitor#GCM_monitor_errors)
## Caché Monitor 速查表
启用 Caché Monitor 不需要很多操作。 确保 Monitor 已启动,然后设置电子邮件选项。
%SYS>d ^MONMGR
1) Start/Stop/Update MONITOR
2) Manage MONITOR Options
3) Exit
Option? **1**
1) Update MONITOR
2) Halt MONITOR
3) Start MONITOR
4) Reset Alerts
5) Exit
Option? **3**
Starting MONITOR... MONITOR started
1) Update MONITOR
2) Halt MONITOR
3) Start MONITOR
4) Reset Alerts
5) Exit
Option? ****
设置警报严重级别。
1) Start/Stop/Update MONITOR
2) Manage MONITOR Options
3) Exit
Option? **2**
1) Set Monitor Interval
2) Set Alert Level
3) Manage Email Options
4) Exit
Option? **2**
Alert on Severity (1=warning,2=severe,3=fatal)? 2 => **1**
设置电子邮件选项,您可能需要联系 IT 部门以获取电子邮件服务器的地址。 任何有效的电子邮件地址都应该可用。
1) Set Monitor Interval
2) Set Alert Level
3) Manage Email Options
4) Exit
Option? **3**
1) Enable/Disable Email
2) Set Sender
3) Set Server
4) Manage Recipients
5) Set Authentication
6) Test Email
7) Exit
Option?
确保在设置后测试电子邮件(选项 6)。
## Caché Monitor 示例
Caché System Monitor 针对高 CPU 利用率生成了一个严重级别为 2 的条目,该条目已发送到 cconsole.log:
03/07/18-11:44:50:578 (4410) 2 [SYSTEM MONITOR] CPUusage Alert: CPUusage = 92, 95, 98 (Max value is 85).
同时也向 Caché Monitor 电子邮件收件人发送了一封电子邮件,其内容与 console.log 相同,主题行为:
[CACHE SEVERE ERROR yourserver.name.com:instancename] [SYSTEM MONITOR] CPUusage Alert: CPUusage = 92, 95, 98 (Max value is 85).
## Caché Monitor 更多提示
在开发者社区的另一篇文章的评论中,Aric West 提示可以在发件人电子邮件中嵌入更多信息,例如,不要只设置一个有效的电子邮件,而是将发件人或收件人设置为:"某个名字" \
----
# Caché System Monitor 工具
Caché System Monitor 是一系列监视工具的汇总,通过 `^%SYSMONMGR` 实用工具进行配置。 如简介中所述,为实现最低限度的监视解决方案,我们将配置:
- System Monitor
- Health Monitor
- History Monitor
顺带说一下,是的,名称 _System Monitor_ 很令人烦恼地重载了其父名称 _Caché System Monitor_。
Caché System Monitor 和 Health Monitor 通知和警报会发送到控制台日志,使 Caché Monitor(在上一节中设置)可以在事件发生时生成电子邮件。
[有关 Caché System Monitor 的所有深入详细信息,请参见在线文档。](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_healthmon)
----
**警告说明:**您还将在 ^%SYSMONMGR 菜单中看到 **Application Monitor**。 本指南中不会配置 Application Monitor。 本指南中介绍的工具和实用程序对系统性能的影响可以忽略不计,然而 Application Monitor 有一些类是此规则的例外。 [有关详细信息,请参见 ^PERFMON 实用工具的文档](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_perfmon)。 如果您使用 Application Monitor,则必须先在非生产系统上测试,因为运行任何时长的 ^PERFMON 都可能对性能产生重大影响。
----
# 2. System Monitor
根据文档:“System Monitor 对重要的系统状态和资源使用指标(例如 ECP 连接的状态和使用中的锁表的百分比)进行采样,并根据固定状态和阈值生成通知(警报、警告和‘状态正常’消息)”。
[文档中有 System Monitor 状态和资源指标](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_healthmon_sysmon_alerts)的列表。 例如:日志空间(日志目录中的可用空间):
- 少于 250 MB = _警告_
- 少于 50 MB = _警报_
- 警告/警报后大于 250 MB = _正常_
## System Monitor 基本设置
System Monitor 警报和警告会写入控制台日志,因此请确保 Caché Monitor 设置为发送电子邮件警报(上一节)。
使用 ^SYSMONMGR 实用工具管理 System Monitor。 默认情况下,当实例运行时,System Monitor 也一直运行;可以使用 ^%SYSMONMGR 将其停止,但是当实例下次启动时,它会自动再次启动。
默认情况下,System Monitor 具有以下可以更改的设置:
- 每 30 秒获取一次传感器指标。
- 仅将警报、警告和消息写入 System Monitor 日志。
System Monitor 还维护单一整体系统运行状况状态,当运行 `ccontrol list` 等命令时可以查询或使用该状态:
- 绿色(正常)
- 黄色(警告)
- 红色(警报)
### System Monitor 速查表
对于最低限度的监视解决方案,实际上无需任何操作,因为当实例运行时,它也一直运行。
----
# 3. Health Monitor
根据文档:“Caché Health Monitor 通过在特定期间对一系列关键指标的值进行采样,并将这些值与配置的指标参数和这些期间的既定标准值进行比较,来监视正在运行的 Caché 实例;如果采样值过高,Health Monitor 将生成警报或警告。 例如,根据配置的 CPU 使用率最大值或者在星期一上午 9:00 至 11:30 期间采集的标准 CPU 使用率样本,如果 Health Monitor 在星期一上午 10:15 采样的 CPU 使用率过高,Health Monitor 将生成通知。”
[Health Monitor 对 41 个系统传感器进行采样,文档中给出了列表和默认值。](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_healthmon#GCM_healthmon_overview_api_sensors)
Health Monitor 警报(严重级别 2)和警告(严重级别 1)将写入控制台日志。 Health Monitor 生成:
- 警报,如果在一个期间内传感器的三次连续读数大于传感器最大值。
- 警告,如果在一个期间内传感器的五次连续读数大于传感器警告值。
对于为最大值或警告值设置了条目的传感器,即使 Health Monitor 本身未启用,也会立即生成警报。 例如,为 CPU 配置的最大值为 85,警告值为 75,所以当连续 5 次的 CPU 利用率测量值超过 75% 时,会将以下通知发送到控制台日志:
1 [SYSTEM MONITOR] CPUusage Warning: CPUusage = 83 ( Warnvalue is 75).
其他传感器需要收集足够长时间的指标才能创建_图表_。 需要图表来评估指标的平均值,从而得出标准偏差 (sigma),以便在值超出正常范围时发出警报。 在一个_期间_内收集指标。 有 63 个标准期间,一个期间示例是星期一上午 9:00 至 11:30。 期间可以更改。
## Health Monitor 基本设置
> Health Monitor 不会自动启动,需要使用 ^%SYSMONMGR 中的设置来启用它。
默认情况下,Health Monitor 在 Caché 启动后等待 10 分钟,以便系统达到正常运行状态,如果需要,可以更改此时间。
Caché Health Monitor 传感器对象带有默认值。 例如,如上文所述,_CPUPct_(系统 CPU 使用率百分比)的默认值为:基础值 50,最大值 90,警告值 80。
您可能更保守一些,并想要更改这些值,使用 ^%SYSMONMGR 可以更改这些值,例如,最大值 85,警告值 70。 现在,当 CPU 使用率达到 99% 时,我们会看到:
2 [SYSTEM MONITOR] CPUusage Alert: CPUusage = 99, 99, 99 (Max value is 85).
## Health Monitor 速查表
该速查表相当长,显示在总结后面。
----
# 4. History Monitor
[David Loveluck 在社区上有一个非常好的帖子。](https://community.intersystems.com/post/apm-using-cach%C3%A9-history-monitor)按照该帖中的说明启动 History Monitor,开始收集和评估指标。
----
# 5. pButtons
pButtons 实用工具可根据其创建的日志文件生成可读的 HTML 性能报告,其中包含操作系统和 Caché 指标。 pButtons 输出的性能指标可以被提取、绘图和评估。 例如,一天中 CPU 利用率或 Caché 数据库访问量的图表。
每天 24 小时运行 pButtons 收集是一种简单但至关重要的方法,用于收集指标以进行故障排除。 pButtons 对于趋势分析也非常有用。 以下社区文章详细介绍了 pButtons 以及如何将其计划为每天运行:[InterSystems 数据平台和性能 – 第 1 部分](https://community.intersystems.com/post/intersystems-data-platforms-and-performance-%E2%80%93-part-1)
> 如文章中所述,30 秒收集间隔对于趋势分析和 24 小时报告是合适的。
还有说明用于确保您即使没有运行最新版的 Caché,也能拥有最新版的 pButtons:[InterSystems 数据平台和性能 – 如何更新 pButtons](https://community.intersystems.com/post/intersystems-data-platforms-and-performance-%E2%80%93-how-update-pbuttons)。
虽然 pButtons 主要是一个支持工具,但您可以通过快速绘制所收集的指标来获得宝贵的系统使用情况洞察:[Yape - 另一个 pButtons 提取程序(自动创建图表)](https://community.intersystems.com/post/yape-yet-another-pbuttons-extractor-and-automatically-create-charts)
----
# 总结
本帖仅触及了监控选项的皮毛,例如 Health Monitor 将以默认设置工作,但随着时间的推移,您将希望探索可自定义的选项来满足您的应用程序配置文件。
## 接下来的方向?
如我们所见,我们将 System Monitor 和 Health Monitor 实用工具配置为将警报发送到作为中央报告位置的 cconsole.log。 我们使用了 Caché Monitor 将这些警报发送到电子邮件。 有一些第三方工具可以抓取日志并消耗非结构化的日志数据,您可能已经在组织中使用这些工具,而且也没有理由不使用它们。
如今,我看到许多客户在 VMware 上进行虚拟化。 如果您使用 vSphere,请考虑使用 Log Insight 来监视控制台日志。 在撰写本帖时(2018 年 3 月),对于您拥有的每个 vCenter Server 6.0 实例,您都有资格获取一个免费的 vRealize Log Insight for vCenter 25 OSI 许可证。 Log insight 是一个读取非结构化数据的工具,用于日志管理和分析 — 例如,您可以将其与 cconsole.log 配合使用 — 如果您对此感兴趣,请联系 VMware 以了解更多信息。 同时,我正计划在将来的帖子中展示如何将 Log Insight 与 cconsole.log 配合使用。
----
如果您在收集指标,您仍然需要研究它们,并了解他们的含义,我将继续发帖来展示如何解释所呈现的信息,特别是性能指标。
----
# 应用程序性能监视
David Loveluck 在社区中有一系列关于应用程序性能监视的帖子,请在社区中搜索 APM,或者[从这里开始](https://community.intersystems.com/post/what-apm)。
----
## 附录:Health Monitor 速查表
本速查表展示了我们在第 3 节看到的 Health Monitor 的启动过程,并完成了传感器阈值的编辑。
首先让我们启动 Health Monitor。
%SYS>**d ^%SYSMONMGR**
1) Start/Stop System Monitor
2) Set System Monitor Options
3) Configure System Monitor Classes
4) View System Monitor State
5) Manage Application Monitor
6) Manage Health Monitor
7) View System Data
8) Exit
Option? **6**
1) Enable/Disable Health Monitor
2) View Alerts Records
3) Configure Health Monitor Classes
4) Set Health Monitor Options
5) Exit
Option? **1**
Enable Health Monitor? No => **yes**
Health Monitor is Enabled. Stop and restart System Monitor to run Health Monitor
如我们从消息中所见,导航回到第一个菜单或再次启动 ^%SYSMONMGR 来停止 System Monitor 并再次启动它,以完成这一过程。
%SYS>**d ^%SYSMONMGR**
1) Start/Stop System Monitor
2) Set System Monitor Options
etc...
我们将在这里继续,以编辑 CPUPct 阈值为例。
%SYS>**d ^%SYSMONMGR**
1) Start/Stop System Monitor
2) Set System Monitor Options
3) Configure System Monitor Classes
4) View System Monitor State
5) Manage Application Monitor
6) Manage Health Monitor
7) View System Data
8) Exit
Option? **6**
1) Enable/Disable Health Monitor
2) View Alerts Records
3) Configure Health Monitor Classes
4) Set Health Monitor Options
5) Exit
Option? **3**
1) Activate/Deactivate Rules
2) Configure Periods
3) Configure Charts
4) Edit Sensor Objects
5) Reset Defaults
6) Exit
Option? **4**
先看一下所有传感器;
1) List Sensor Objects
2) Edit Sensor Object
3) Exit
Option? **1**
Sensor Base Max Max M Warn Warn M
-- ---- --- ----- ---- ------
CPUPct 50 80 0 70 0
CPUusage 50 85 0 75 0
CSPActivity 100 0 2 0 1.6
:
:
:
WDWIJTime 60 0 2 0 1.6
WDWriteSize 1024 0 2 0 1.6
1) List Sensor Objects
2) Edit Sensor Object
3) Exit
Option? **2**
Cannot configure while System Monitor is running.
1) List Sensor Objects
2) Edit Sensor Object
3) Exit
噢,我们需要先返回并禁用 Health Monitor 和 System Monitor。
1) Enable/Disable Health Monitor
2) View Alerts Records
3) Configure Health Monitor Classes
4) Set Health Monitor Options
5) Exit
Option? **1**
Disable Health Monitor? No => **yes**
Health Monitor is Disabled. Stop and restart System Monitor to halt Health Monitor
1) Enable/Disable Health Monitor
2) View Alerts Records
3) Configure Health Monitor Classes
4) Set Health Monitor Options
5) Exit
Option?****
1) Start/Stop System Monitor
2) Set System Monitor Options
3) Configure System Monitor Classes
4) View System Monitor State
5) Manage Application Monitor
6) Manage Health Monitor
7) View System Data
8) Exit
Option? **1**
1) Start System Monitor
2) Stop System Monitor
3) Exit
Option? **2**
Stopping System Monitor... System Monitor stopped
好了,Health Monitor 和 System Monitor 已停止。 现在导航回到 Health Monitor 并编辑一个传感器对象。
1) Start System Monitor
2) Stop System Monitor
3) Exit
Option?****
1) Start/Stop System Monitor
2) Set System Monitor Options
3) Configure System Monitor Classes
4) View System Monitor State
5) Manage Application Monitor
6) Manage Health Monitor
7) View System Data
8) Exit
Option? **6**
1) Enable/Disable Health Monitor
2) View Alerts Records
3) Configure Health Monitor Classes
4) Set Health Monitor Options
5) Exit
Option? **3**
1) Activate/Deactivate Rules
2) Configure Periods
3) Configure Charts
4) Edit Sensor Objects
5) Reset Defaults
6) Exit
Option? **4**
1) List Sensor Objects
2) Edit Sensor Object
3) Exit
Option? **2**
输入传感器名称(如果您知道的话),否则输入“?”以显示列表。 传感器? **?**
Num Sensor Threshold
1) CPUPct
2) CPUusage
:
:
:
46) WDWIJTime
47) WDWriteSize
Sensor? **1** CPUPct
Base? 50 =>****
Enter either an Alert Value or a Multiplier
Alert Value? 80 => **85**
Setting Max Multiplier and Warn Multiplier to 0. Enter a Warn Value
Warn Value? 70 => **75**
Sensor object CPUPct updated.
Base 50
MaxMult 0
AlertValue 85
WarnMult 0
WarnValue 75
1) List Sensor Objects
2) Edit Sensor Object
3) Exit
现在返回并启用 Health Monitor 和启动 System Monitor。
Option?****
1) Activate/Deactivate Rules
2) Configure Periods
3) Configure Charts
4) Edit Sensor Objects
5) Reset Defaults
6) Exit
Option?****
1) Enable/Disable Health Monitor
2) View Alerts Records
3) Configure Health Monitor Classes
4) Set Health Monitor Options
5) Exit
Option? **1**
Enable Health Monitor? No => **yes**
Health Monitor is Enabled. Stop and restart System Monitor to run Health Monitor
1) Enable/Disable Health Monitor
2) View Alerts Records
3) Configure Health Monitor Classes
4) Set Health Monitor Options
5) Exit
Option?****
1) Start/Stop System Monitor
2) Set System Monitor Options
3) Configure System Monitor Classes
4) View System Monitor State
5) Manage Application Monitor
6) Manage Health Monitor
7) View System Data
8) Exit
Option? **1**
1) Start System Monitor
2) Stop System Monitor
3) Exit
Option? **1**
Starting System Monitor... System Monitor started
好了,工作已完成!
----
文章
姚 鑫 · 七月 16, 2021
# 第五章 使用文件
# 使用文件
`%Library.File`类提供了几个类方法,允许对文件执行各种操作。
## 复制文件
若要复制文件,请使用`CopyFile()`方法,该方法返回一个布尔值来指示成功或失败。
此方法采用四个参数:
1. from 从—指定源文件的名称。
2. to至—指定目标文件的名称。
3. pDeleteBeforeCopy —指定在执行复制之前是否删除目标文件(如果存在)。默认值为0。
4. return 返回—输出参数。如果为负,则包含操作系统返回的错误代码,以防方法失败
下面的第一个示例将目录`e:\temp`中的文件`old.txt`复制到`new.txt`。第二个示例将相同的文件复制到默认目录中的`new.txt`。
```java
DHC-APP>write ##class(%File).CopyFile("e:\temp\old.txt", "e:\temp\new.txt", 0, .return)
1
DHC-APP>write ##class(%File).CopyFile("e:\temp\old.txt", "new.txt", 0, .return)
1
```
最后一个示例失败,Windows错误代码为2,或“找不到文件”
```java
DHC-APP>write ##class(%File).CopyFile("foo.txt", "new.txt", 0, .return)
0
DHC-APP>w return
-2
```
## 删除文件
要删除文件,请使用`delete()`方法,该方法成功时返回1,失败时返回0。这个方法需要两个参数。第一个参数是要删除的文件的名称。第二个参数是输出参数。如果为负,它包含操作系统返回的错误代码,以防方法失败。
在下面的第一个示例中,方法成功了。第二个示例失败,出现Windows错误代码2或“找不到文件”。
```java
DHC-APP>write ##class(%File).Delete("e:\temp\myfile.txt", .return)
1
DHC-APP>write ##class(%File).Delete("e:\temp\myfile.txt", .return)
0
DHC-APP>w return
-2
```
要在删除文件时匹配通配符,请使用`ComplexDelete()`方法。第一个参数指定要删除的文件的名称。第二个参数是输出参数。如果为负,它包含操作系统返回的错误代码,以防方法失败。
下面的示例删除所有带有。`e:\temp`目录中的out扩展名。
```java
DHC-APP>write ##class(%File).ComplexDelete("e:\temp\*.out", .return)
1
```
## 截断文件
要截断文件,请使用`truncate()`方法,该方法成功时返回1,失败时返回0。这个方法需要两个参数。第一个参数是要截断的文件的名称。第二个参数是输出参数。如果为负,它包含操作系统返回的错误代码,以防方法失败。
如果截断现有文件,方法会从文件中删除内容,但不会从文件系统中删除内容。如果截断不存在的文件,方法会创建一个新的空文件。
在下面的第一个示例中,方法成功了。第二个示例失败,Windows错误代码为5,或“访问被拒绝”
```java
USER>write ##class(%File).Truncate("e:\temp\myfile.txt", .return)
1
USER>write ##class(%File).Truncate("e:\no access.txt", .return)
0
USER>write return
-5
```
## 重命名文件
若要重命名文件,请使用`rename()`方法,该方法成功时返回1,失败时返回0。这个方法需要三个参数。第一个参数是要重命名的文件的名称,第二个参数是新名称。第三个参数是输出参数。如果为负,它包含操作系统返回的错误代码,以防方法失败。
在下面的第一个示例中,方法成功了。第二个示例失败,错误代码为183,或者“当文件已经存在时,无法创建该文件。”
```java
DHC-APP>write ##class(%File).Rename("e:\temp\oldname.txt", "e:\temp\newname.txt", .return)
1
DHC-APP>write ##class(%File).Rename("e:\temp\another.txt", "e:\temp\newname.txt", .return)
0
DHC-APP>write return
-2
```
使用此方法时,请小心指定路径,因为以下示例会将`e:\temp\oldname.txt`移动到默认目录,然后将其重命名为`newname.txt`。
```java
DHC-APP>write ##class(%File).Rename("e:\temp\oldname.txt", "newname.txt", .return)
1
```
## 比较文件
若要比较两个文件,请使用`Compare()`方法,如果两个文件相同,则返回布尔值1,否则返回0。该方法没有用于返回系统错误代码的输出参数。
在下面的第一个示例中,两个文件是相同的,方法返回1。在第二个示例中,两个文件不同,因此方法返回0。
```java
DHC-APP>write ##class(%File).Compare("e:\temp\old.txt", "e:\temp\new.txt")
1
DHC-APP>write ##class(%File).Compare("e:\temp\old.txt", "e:\temp\another.txt")
0
```
如果一个或两个文件都不存在,如下例所示,则该方法也返回0。
```java
DHC-APP>write ##class(%File).Compare("foo.txt", "bar.txt")
0
DHC-APP>write ##class(%File).Exists("foo.txt")
0
```
## 生成临时文件
要生成临时文件,请使用`TempFilename()`方法,该方法返回临时文件的名称。这个方法需要三个参数。第一个参数是临时文件所需的文件扩展名。第二个是生成临时文件的目录。如果未提供,该方法将在操作系统提供的临时目录中生成文件。第三个参数是输出参数。如果为负,它包含操作系统返回的错误代码,以防方法失败。
Windows示例:
```java
USER>write ##class(%File).TempFilename("txt")
C:\WINDOWS\TEMP\GATqk8a6.txt
USER>write ##class(%File).TempFilename("txt","C:\temp")
C:\temp\WpSwuLlA.txt
```
Unix示例:
```java
USER>write ##class(%File).TempFilename("", "", .return)
/tmp/filsfHGzc
USER>write ##class(%File).TempFilename("tmp", "/InterSystems/temp", .return)
/InterSystems/temp/file0tnuh.tmp
USER>write ##class(%File).TempFilename("", "/tmp1", .return)
USER>write return
-2
```
在上面的第三个示例中,目录不存在,该方法失败,系统错误代码为2,或“没有这样的文件或目录。”