清除过滤器
文章
Jingwei Wang · 十月 9, 2024
Foreign Table 的概念
Foreign Table 是 IRIS SQL 中一种特殊类型的表。它不代表 IRIS 本地全局管理的数据,而是投射出来的,由 “外部服务器 ”管理的外部数据。从 SQL 的角度来看,外部表与普通表一样,可以在查询中使用,没有任何特定限制。
在数据编织中可以帮助整合不同的来源的数据,以支持各种应用、分析并提供智能洞察力。
Foreign Table 和 Linked Table 的区别
Linked Table
Lined Table 不能读取外部文件
对于 JDBC 和 ODBC 的数据源是分开的
Foreign Table
可以读取外部文件
不局限于同一类型的数据源
Foreign Table 的用例
当某个数据集时由外部应用程序在外部数据库中管理,而你的 IRIS 查询需要在该外部数据集中查找某些内容时,您可以使用某种 ETL 流程将数据加载到 IRIS 中,但如果外部数据经常更新,而你的查询需要访问当前的版本,那么这个过程可能会变得很棘手。可以通过 Foreign Table 将数据投射到 IRIS 并在查询时从外部源检索当前数据来解决这个问题。
当您要使用某些基于文件的大型数据集,您只需要偶尔查询一下,用来建立报告或训练模型。由于文件过大,加载文件可能会很麻烦,而且大型数据会占用昂贵的存储空间,而且一旦文件更新,就必须清除并重新加载。Foreign Table可以保持 SQL 查询对数据的可访问性,而不会增加 IRIS 的存储空间占用,也可以会保持最新数据。
Foreign Table 操作步骤
创建外部服务器
CREATE FOREIGN SERVER Sample.PostgresDB FOREIGN DATA WRAPPER JDBC CONNECTION 'postgresConnection'
创建 Foreign Tables
CREATE FOREIGN TABLE Sample.AccountTeam ( TeamID BIGINT, Name VARCHAR(50), CountryCode VARCHAR(10) ) SERVER Sample.PostgresDB TABLE 'Sample.Teams'
查询
SELECT t.Name, COUNT(m.*) FROM Sample.AccountManager m JOIN Sample.AccountTeam t ON m.TeamID = t.TeamID WHERE t.CountryCode = 'UK' AND m.Salary > 100000 GROUP BY t.Name
删除 Foreign Tables
DROP FOREIGN TABLE Example.MyForeignTable
删除外部服务器
DROP FOREIGN SERVER Example.PostgresDB CASCADE
( CASCADE 选项用来删除外部服务器和该外部服务器上定义的所有外部表 )
Foreign Table 演示
如果您想获取更详细的对于Foreign Table的演示,您可以参考 InterSystems 2023 峰会上面的演示示例,该示例使用docker,示例代码请参考Github ,安装后访问http://localhost:8888/
文章
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,或“没有这样的文件或目录。”
文章
姚 鑫 · 五月 27, 2021
# 第八章 处理收到的电子邮件
# 处理收到的电子邮件
本节介绍如何处理通过`%Net.POP3`检索到的电子邮件(`%Net.MailMessage`)。
## Message Basics
检索电子邮件(`%Net.MailMessage`)后,通常首先确定它是哪种类型的邮件以及如何阅读它;也就是说,它是否是多部分邮件以及各部分是否是二进制的。在此步骤中,您可以使用`ContentType`属性。或者,可以使用`IsBinary`、`IsHTML`和`IsMultiPart`属性,它们间接提供与`contentType`相同的信息。
如果消息是多部分消息,则每个部分都是`%Net.MailMessagePart`的一个实例。
## Message Headers
消息本身和消息的每个部分都有一组标头。
`%Net.MailMessage`和`%Net.MailMessagePart`类提供的属性使可以轻松访问最常用的标头。例如,`%Net.MailMessage`提供收件人、发件人、主题和日期等属性。`Headers`数组属性允许访问任何自定义标题.
此外,如果已通过`%Net.POP3`检索到消息,则可以使用`GetAttribute()`方法。在给定标头名称和属性的情况下,此方法返回该属性的值。
## Message Contents
了解常规消息结构后,请使用以下技术检索内容:
- 对于多部分消息,请使用`Parts`属性,该属性是部分的数组。`Parts.Count()`给出部件的数量。每个部件的键都是一个整数,从1开始。使用`GetAt()`方法检索给定的部件。消息部分是`%Net.MailMessagePart`的实例。
- 对于二进制消息(或消息部分),请使用`BinaryData`属性。
- 对于文本消息(或消息部分),请使用`TextData`属性。
- 如果`IsHTML`为0,则`TextData`属性为普通文本字符串。
- 如果`IsHTML`为1,则`TextData`属性为HTML文本字符串。
请注意,发送邮件的电子邮件客户端确定邮件中的任何包装。邮件服务器无法控制这一点,
## 其他消息信息
`MessageSize`属性表示邮件的总长度(不包括任何附加的电子邮件)。
以下方法提供有关消息的其他信息:
### GetLocalDateTime()
返回检索消息的日期和时间,并转换为`$HOROLOG`格式的本地时间。
### GetUTCDateTime()
返回检索消息的日期和时间,并以`$HOROLOG`格式转换为UTC。
### GetUTCSeconds()
返回自1840年12月31日以来检索消息的日期和时间(秒)。
以下类方法也可用于时间/日期转换:
### HToSeconds()
将`$HOROLOG`格式的日期/时间转换为自1840年12月31日以来的秒的类方法。
### SecondsToH()
将自1840年12月31日以来的秒数转换为`$HOROLOG`格式的日期/时间的类方法。
示例1:`ShowMsgInfo()`
```java
ClassMethod ShowMsgInfo(msg as %Net.MailMessage)
{
Write "Message details *****",!
Write "To (count): ", msg.To.Count(),!
Write "From: ", msg.From,!
Write "Cc (count): ", msg.Cc.Count(),!
Write "Bcc (count): ", msg.Bcc.Count(),!
Write "Date: ", msg.Date,!
Write "Subject: ", msg.Subject,!
Write "Sender: ", msg.Sender,!
Write "IsMultipart: ", msg.IsMultiPart,!
Write "Number of parts: ", msg.Parts.Count(),!
Write "Number of headers: ", msg.Headers.Count(),!
Write "IsBinary: ", msg.IsBinary,!
Write "IsHTML: ", msg.IsHTML,!
Write "TextData: ", msg.TextData.Read(),!
Write "BinaryData: ", msg.BinaryData.Read(),!
}
```
此方法产生类似于以下内容的输出:
```java
Message details *****
To (count): 1
From: "XXX XXX"
Cc (count): 0
Bcc (count): 0
Date: Fri, 16 Nov 2007 11:57:46 -0500
Subject: test 5
Sender:
IsMultipart: 0
Number of parts: 0
Number of headers: 16
IsBinary: 0
IsHTML: 0
TextData: This is test number 5, which is plain text.
BinaryData:
```
示例2:`ShowMsgPartInfo()`
以下方法写入有关消息一部分的信息:
```java
ClassMethod ShowMsgPartInfo(msg as %Net.MailMessage, partno as %Integer)
{
Set part=msg.Parts.GetAt(partno)
Write "Message part details *****",!
Write "Message part: ", partno,!
Write "IsMultipart: ", part.IsMultiPart,!
Write "Number of parts: ", part.Parts.Count(),!
Write "Number of headers: ", part.Headers.Count(),!
Write "IsBinary: ", part.IsBinary,!
Write "IsHTML: ", part.IsHTML,!
Write "TextData: ", part.TextData.Read(),!
Write "BinaryData: ", part.BinaryData.Read(),!
}
```
这将产生与以下内容类似的输出(给定的消息与前面显示的不同):
```java
Message part details *****
Message part: 1
IsMultipart: 0
Number of parts: 0
Number of headers: 2
IsBinary: 0
IsHTML: 0
TextData: 1 test string
BinaryData:
```
示例3:`ShowMsgHeaders()`
下面的方法写入有关消息头的信息;可以编写一个类似的方法,对消息部分执行相同的操作。
```java
ClassMethod ShowMsgHeaders(msg as %Net.MailMessage)
{
Set headers=msg.Headers
Write "Number of headers: ", headers.Count(),!
//iterate through the headers
Set key=""
For {
Set value=headers.GetNext(.key)
Quit:key=""
Write "Header:",key,!
Write "Value: ",value,!!
}
}
```
这将产生类似于以下内容的输出:
```java
Number of headers: 16
Header: content-class
Value: urn:content-classes:message
Header: content-type
Value: multipart/alternative; boundary="----_=_NextPart_001_01C8286D.D9A7F3B1"
Header: date
Value: Fri, 16 Nov 2007 11:29:24 -0500
Header: from
Value: "XXX XXX"
Header: message-id
Value:
Header: mime-version
Value: 1.0
...
```
# 自动编码和字符翻译
电子邮件部分包含有关使用的字符集和使用的内容传输编码(如果有的话)的信息。作为参考,本节介绍如何使用此信息。
## 外发电子邮件
`%Net.SMTP`检查每个部分的字符集属性,然后应用适当的转换表。
如果未指定给定部件的字符集属性,InterSystems IRIS将使用UTF-8。
`%Net.SMTP`还检查`ContentTransferEncoding`属性。如果此属性为 `"base64"`或`"quoted-printable"`,则在创建消息时,`%Net.SMTP`会根据需要对正文进行编码。(如果内容传输编码为 `"7bit"` 或 `"7bit"`,则不需要编码。)
重要提示:请注意,如果内容为`“Base64”`编码,则不能包含任何`Unicode`字符。如果要发送的内容包括`Unicode`字符,请确保使用`$ZCONVERT`将内容转换为UTF-8。
## 传入电子邮件
`%Net.POP3`检查每个邮件部分的`Content-Transfer-Encoding`标头,并根据需要对正文进行解码。
然后`%Net.POP3`检查每个邮件部分的`Content-Type`标头。这会影响消息部分的字符集属性,还会控制在InterSystems IRIS中创建消息部分时使用的转换表。
文章
Hao Ma · 一月 15, 2021
在这个由三个部分组成的系列文章中,介绍了如何在OAuth 2.0标准下使用IAM简单地为IRIS中的未经验证的服务添加安全性。
第一部分介绍了一些OAuth 2.0背景知识,以及IRIS和IAM的一些初始定义和配置,以帮助读者理解确保服务安全的整个过程。
第二部分详细讨论和演示了配置IAM所需的步骤——验证传入请求中的访问令牌,并在验证成功时将请求转发到后端。
本系列的最后一部分将讨论和演示IAM生成访问令牌(充当授权服务器)并对其进行验证时所需的配置,以及一些重要的最终考虑事项。
如果您想试用IAM,请联系InterSystems销售代表。
场景2:IAM作为授权服务器和访问令牌验证器
与上个场景不同的是,该场景中将使用一个名为“OAuth 2.0 Authentication”的插件。
如果要在资源所有者密码凭证流中将IAM作为授权服务器使用,客户端应用程序必须对用户名和密码进行身份验证。只有在身份验证成功时,才能发出获取IAM访问令牌的请求。
首先,将其添加到“SampleIRISService”中。正如下面截屏所示,需要填充一些不同的字段来配置此插件。
首先,将“SampleIRISService”的ID粘贴到“service_id”字段中,这样就可以在服务中启用该插件。
在“config.auth_header_name”字段中,需要指定携带授权令牌的头名称。本例中,我保留默认值“authorization”。
“OAuth 2.0 Authentication”插件支持的OAuth 2.0流包括授权码授权(Authorization Code Grant)、客户端凭证(Client Credentials)、隐式授予(Implicit Grant)或资源所有者密码凭证授权(Resource Owner Password Credentials Grant)。我们在本文中使用的是“资源所有者密码凭证”流,故选中“config.enable_password_grant”。
在“config.provision_key”字段中输入要用作配置密钥的字符串。此值用来向IAM请求访问令牌。
本例中,我保留了所有其他字段的默认值。可以在此处查看插件文档中每个字段的完整引用。
下面是插件配置的最终效果:
创建插件后,需要为“ClientApp”客户端创建凭证。
为此,打开左侧菜单上的“Consumers”,然后单击“ClientApp”。接下来,点击“Credentials”标签,然后点击“New OAuth 2.0 Application”按钮。
在下个页面的“name”字段输入名称,以标识应用程序,在“client_id”和“client_secret”字段分别定义客户端ID和客户端密钥,最后在应用程序中输入URL,在“redirect_uri”字段上授权后,用户将被发送到该URL。然后,单击“Create”。
现在,可以发送请求了。
需要发出的第一个请求是获取IAM访问令牌。“OAuth 2.0 Authentication”插件自动创建一个端点,并将“/oauth2/token”路径附加到已经创建的路由上。
注意:必须使用HTTPS协议和IAM的代理端口(默认端口为8443)监听TLS/SSL请求。这是OAuth 2.0规范要求。
因此在本例中,需要向URL发出一个POST请求:
https://iamhost:8443/event/oauth2/token
请求主体中应包括以下JSON:
{
"client_id": "clientid",
"client_secret": "clientsecret",
"grant_type": "password",
"provision_key": "provisionkey",
"authenticated_userid": "1"
}
如上所示,该JSON包含了在创建“OAuth 2.0 Authentication”插件时定义的值(如“grant_type”和“provision_key”),以及在创建客户端凭证时定义的值(如“client_id”和“client_secret”)。
当提供的用户名和密码成功通过身份验证后,客户端应用程序还应该添加“authenticated_userid”参数值作为已通过身份验证的用户的唯一标志。
该请求及其响应如下:
现在可以请求从上面的响应中获取事件数据(包括“access_token”值),并作为对URL的GET请求中的“bearner token”
https://iamhost:8443/event/1
如果访问令牌过期,可以使用收到的刷新令牌和过期的访问令牌一起生成一个新的访问令牌,方法是向用于获取访问令牌的相同端点发出POST请求,但主体略有不同:
{
"client_id": "clientid",
"client_secret": "clientsecret",
"grant_type": "refresh_token",
"refresh_token": "E50m6Yd9xWy6lybgo3DOvu5ktZTjzkwF"
}
该请求及其响应如下:
“OAuth 2.0 Authentication”插件的一个有趣很好的特性是能够查看和禁用访问令牌。
若要查看令牌列表,向以下IAM的Admin API终端发送GET请求即可:
https://iamhost:8444/{workspace_name}/oauth2_tokens
其中{workspace_name}是IAM工作区的名称。如果启用了RBAC,则需要输入必要的凭证才能调用IAM Admin API。
注意,“credential_id”是在ClientApp客户端内部创建的OAuth应用程序(本例中名为SampleApp)的id,“service_id”是应用此插件的“SampleIRISService”的id。
要想禁用令牌,可以向以下端点发送删除请求
https://iamhost:8444/Sample/oauth2_tokens/{token_id}
其中{token_id}是要禁用的令牌id。
如果尝试使用无效令牌,将包含该无效令牌的GET请求作为Bearer Token 发送到URL,则会收到一条消息,提示令牌无效或已过期:
https://iamhost:8443/event/1
最后需要考虑的因素
本文演示了如何将IAM中的OAuth 2.0身份验证添加到IRIS中未经身份验证的服务中。需要牢记的是,IRIS中的服务本身仍是未经身份验证的。因此,如果有人绕过IAM层直接调用IRIS服务端点,则可以在不经过任何身份验证的情况下查看信息。出于该原因,在网络级别设置安全规则以防止不必要的请求绕过IAM层是很重要的。
可以在此处了解更多有关IAM的信息。
如果您想试用IAM,请联系InterSystems销售代表。
文章
Hao Ma · 一月 30, 2021
IIS在Windows Server里是默认安装,在Windows7和Windows10里面需要用户到"控制面板>程序"里面找到"Turn Windows features on or off"的设置来启动。 本文假设IIS已经在用户的服务器上启动,并且正常工作。
通常情况下,当IRIS安装在Windows系统时,用户会在同一台机器上使用IIS,很少有单独安装一台IIS服务器给远程IRIS提供Web服务器的,当然这样也绝无问题。
有两个软件包可以安装CSP Gateway。一个是IRIS的安装包。在IRIS的安装过程中, 如果有IIS正在工作, 那么安装程序会自动的安装CSP Gateway。 2018年以前的Ensemble或者Cache'的版本的安装过程中会跳出一个询问框,让用户选择是否"安装CSP网关。。。”。而后来的版本大多不做询问而自动为用户做了选择。 如果需要强制安装或者不安装CSP Gateway,那么需要在安装中选"Customer Component"设置。
另一个软件包是单独的CSP Gateway安装包, 可以在InterSystems的WRC或者工程师处得到。这是一个只有10多兆的很小的安装包,它只安装CSP Gateway, 并不安装IRIS实例,适合在单独的IIS硬件服务器上安装CSP Gateway; 或者, 当一个服务器上已有了IRIS, 但后来想添加IIS服务和CSP Gateway,用这个单独的安装包也很方便。
单独的CSP Gateway的安装会在IIS的工作目录“C:\Inetpub"下添加子目录CSPGateway,或者WebGateway, 然后在IIS的default Website上添加CSP Gateway的配置。整个配置相当简单。下面的步骤是在Windows10上单独安装Web Gateway安装包2020.1连接同一台服务器上的IRIS的的过程。
其中后面配置CSP Gateway访问IRIS应用的部分可以参考Apache上配置CSP Gateway的文档。
## CSP Gateway安装
1. 使用浏览器访问 http://localhost, 显示IIS的欢迎主页, 确认IIS已工作。
2. 打开WebGateway-20201.1.xxxx-win_x64应用程序开始安装。安装时会提示用户输入连接的IRIS Server的IP地址和端口。默认的Application Name用CSP,IP地址端口使用localhost, 51773。安装过程会重启IIS服务,安装结束后用户不用手工重启。
3. 安装后的检查IIS
- 检查Web Gateway安装后的文件。 确认在IIS的安装目录(默认为C:\inetput)下安装了CSPGateway子目录, 其中包含若干dll文件。它们是IIS中用到的CSP Gateway的模块,在不同的CSP Gateway版本中这些Dll文件的数量和名字稍有不同。
- 创建了c:/intersystems/WebGateway的文件夹,早些的版本中并不创建这个目录。
- 打开IIS配置界面, 确认在Default Site下安装了CSP application.
在某些版本的Web Gateway安装后, /csp被安装为Virtual Directory, 关于IIS中application和virtual diectory的区别请自行查询, 无论安装成那种类型, 对csp的使用和配置没有区别。使用HealthConnect 2018以前版本的安装包安装的CSP Gateway生成CSP和ensemble两个Application。
4. 查看CSP Gateway登录页面。
登录http://localhost/csp/bin/Systems/Module.cxw。 在主页上会显示Web Gateway的版本, Web Server Type是"Microseof-IIS"。配置文件和日志文件在c:\Inetpub\CSPGateway目录下。 (如果是打开时出现HTTP错误500.19, 你需要重启IIS)
5. 查看连接的IRIS Server。
从左边菜单栏进入Server Access配置界面。其中csp是默认安装的IRIS连接。2018以前的版本可能用的是LOCAL。不管怎么说,使用"Edit Server"打开,可以看到连接的IRIS的端口和设置。
配置UserName"CSPSystem"和Password "SYS",并保存。
在左边菜单栏,使用"Test Server Connection"测试到“csp"的连接,测试成功会显示IRIS的版本。
Test Server Connection
Server connection test was successful: csp (localhost:51773)
$ZVersion: IRIS for Windows (x86-64) 2020.2 (Build 199U) Tue Apr 28 2020 17:17:56 EDT
6. 访问IRIS维护主页 (可选)
从链接 http://WebServer/csp/sys/Utilhome.csp 访问IRIS维护主页System Management Portal应该可以成功了,但您会发现有部分网页内容(element)无法加载。这是因为在默认的安装中,只将"csp cls zen cxw"这4种类型的请求发送给CSP Gateway, 而被称为Static file的文件,比如.js, .css, .png等等类型的文件并没有被发送给CSP Gateway. 这是另外的一个安全机制,强制客户人工的配置是否需要从Web服务器访问IRIS维护主页。如果答案是NO, 那么访问IRIS维护页面就只能通过PWS,用IRIS服务器的52773的接口。 如果用户认为从Web服务器访问IRIS维护页面是必要的, 需要修改CSPFileTypes配置,把任意类型的请求发送给IRIS。
7. 访问IRIS上的其他Web Application
IRIS上其他的Web Application, 比如”/api", ”/test"等等,通常情况下当Web Application被添加后,配置会自动同步到Web Gateway, 用户不用去Web Gateway的页面配置路由。但IIS上必须填写响应的配置,才能把请求从IIS发送到CSP Gateway. 这样操作:
- 添加applications。如下图所示, 在IIS的default site下添加了两个新application, test和api.
- 为每个applcation配置"Handler Mappings"。**使用右边动作栏中的”Add Module Mapping",而不是另3种动作。**
还要注意不要勾选"Request Restrictions"的"invoke handler only if request is mapped to"选择框,这样在"Handler Mappings"页面看到的Path Type一栏中显示的是"Unspecified", 否则会显示"File"或者其他内容。
- 测试访问一些应用, 比如
http://172.16.58.200/api/mgmnt/v2/
http://172.16.58.200/test/test.webservice1.cls
一般情况下,到目前为止IIS已经能够正常工作,将需要的请求发送给IRIS。如果出现故障或者需要调整CSP gateway的配置,请参考[WebGateway系列_配置Apache连接IRIS]
请求如何在nginx配置呢 @Hao.Ma 马老师给看看? 目前只有在线文档, 回头我们写一个贴上来
文章
Michael Lei · 七月 3, 2023
你好社区在本文中,我将介绍我的应用程序irisChatGPT ,它是基于LangChain Framework构建的。首先,让我们对框架进行一个简单的概述。
全世界都在谈论ChatGPT以及大型语言模型 (LLM) 如何变得如此强大,并且表现超出预期,提供类似人类的对话。这只是将其应用于每个企业和每个领域的开始!
剩下的最重要的问题是如何将这种能力应用于适合企业需求的特定领域数据和特定场景响应行为。
LangChain为这个问题提供了结构化且有效的答案! LangChain 技术可以帮助实现法学硕士的巨大潜力,通过围绕法学硕士提供抽象层并使法学硕士的使用变得简单有效,从而构建令人惊叹的应用程序。 LangChain 是一个框架,可以快速轻松地开发使用大型语言模型(例如 GPT-3)的应用程序。
然而,该框架引入了额外的可能性,例如,轻松使用外部数据源(例如维基百科)来放大模型提供的功能。我相信你们都可能尝试过使用 Chat-GPT,并发现它无法回答特定日期之后发生的事件。在这种情况下,在维基百科上搜索可以帮助 GPT 回答更多问题。
LangChain结构
该框架分为六个模块,每个模块允许您管理与法学硕士互动的不同方面。让我们看看这些模块是什么。
模型:允许您实例化和使用三种不同类型的语言模型,它们是:
大型语言模型 (LLM):这些能够理解自然语言的基础机器学习模型。它们接受输入中的字符串并在输出中生成字符串。
聊天模型:由 LLM 支持的模型,但专门用于与用户聊天。您可以在这里阅读更多内容。
文本嵌入模型:这些模型用于将文本数据投影到几何空间中。这些模型将文本作为输入并返回数字列表,即文本的嵌入。
提示:提示是我们如何与模型交互以尝试从中获取输出。现在知道如何编写有效的提示至关重要。这个框架模块可以让我们更好的管理提示。例如,通过创建我们可以重用的模板。
索引:最好的模型通常是与一些文本数据相结合的模型,以便为模型添加上下文或解释某些内容。这个模块可以帮助我们做到这一点。
链:很多时候,要解决任务,对 LLM 的单个 API 调用是不够的。该模块允许集成其他工具。例如,一个调用可以是一个组合链,其目的是从维基百科获取信息,然后将此信息作为模型的输入。该模块允许连接多个工具以解决复杂的任务。
内存:该模块允许我们在模型调用之间创建持久状态。能够使用记住过去说过的话的模型肯定会改善我们的应用程序。
代理:代理是一个法学硕士,它做出决定,采取行动,观察其所做的事情,并以这种方式继续,直到完成其任务。该模块提供了一组可以使用的代理。
现在让我们更详细地了解一下如何利用不同的模块来实现代码。
Langchain工作原理
步骤1 :用户向LangChain发送问题第2步 :LangChain将此问题发送至Embedding Model步骤3:嵌入模型将文本转换为向量,文本以向量形式存储在数据库中并返回给LangChain步骤4 :LangChain将这些向量发送到向量数据库(有多个向量数据库,我们在我们的应用程序中使用chroma)步骤5:向量数据库返回前 K 个近似最近邻 ( KNN ) 向量第6步:LangChain 将问题与KNN向量一起发送到大型语言模型 (LLM) (我们在应用程序中使用 OpenAI)步骤7:LLM向Langchain返回答案步骤8:Langchain将答案返回给用户
关于申请
irisChatGPT应用程序利用围绕大型语言模型 (LLM) 构建的最热门 Python 框架LangChain的功能。 LangChain 是一个框架,可以快速轻松地开发使用大型语言模型的应用程序。应用程序是在系统间嵌入式 Python功能的帮助下使用 objectscript 构建的。它还包含Streamlit Web 应用程序,这是一个开源 Python 应用程序框架,用于为数据科学和机器学习创建漂亮的 Web 应用程序。
特征
以下是应用程序功能列表以及相关屏幕截图
内置Intersystems ObjectScript 参考ChatGPT
内置InterSystems 大奖赛 2023 ChatGPT
使用 SQLDatabaseChain 回答有关缓存数据库的问题
创建您自己的 chatGPT 模型并与其聊天
OpenAI 聊天GPT
维基百科搜索
使用DuckDuckGo(DDG)通用搜索引擎在互联网上搜索
使用Python REPL LangChain功能生成Python代码
Streamlit Web 应用程序在线演示
谢谢
文章
Jeff Liu · 二月 5, 2021
VSCode 是目前很流行的一款免费开发工具,IRIS也支持使用其进行连接和开发,相比Studio 只能在windows 环境使用,Vscode 可以跨平台使用。
我们传统的工具Studio 是连接代码服务器的形式,不能便捷的使用目前流行的有本地代码的版本控制工具(如git),但VSCode可以存在本地代码,并且能方便的使用各类存在本地代码的版本控制工具。
以下的内容会帮助大家来配置使用VSCode连接IRIS 进行开发。
VSCode 可以在微软的官网免费下载https://code.visualstudio.com/
如果安装为英文要切换为中文则可以通过Command Palette 中运行Configure Display Language,install another language, 选择中文,再根据提示安装相应的语言插件。
接下来在扩展商店里搜索intersystems 安装如图所见的3个扩展包
VSCode目前有两种方式可以链接IRIS:
直接连接代码服务器进行编辑
使用客户端方式连接,将代码下载到本地,便于使用有本地代码的版本控制工具
注:两种连接方式的连接端口都为服务器web port
下面具体讲解两种连接方式。
直接连接代码服务器进行编辑
其总体的使用方式和studio相似,直接连接服务器进行代码编辑。在vscode中可以使用全局只读的方式来浏览代码,从而避免误操作修改了代码。
如果会经常使用这个服务器,可以考虑先建立一个工作区,将设置保存在工作区,当然直连服务器的模式下,建立服务器不是必选项。
如果我们正确的安装了之前提到的三个扩展,其中下面扩展会帮助我们连接IRIS代码服务器,并保存在VSCode的全局 settings.json配置文件中:
进入VSCode 设置中找到“扩展”下的Intersystems Server Manager ,进入设置可见安装自带的服务器连接字符串可以依据用户需要进行添加和修改
配置完成可以回到工作区界面进行连接
选择Choose Server and Namespaces, 从右边的服务器列表中选择要连接的服务器,也可以使用“+”来根据提示信息来添加新的服务器。
选择服务器之后会提示输入密码(如果没有在配置中写明登录用户,也会提示输入登录用户)验证成功后可以选择想查看的命名空间,最后按照提示选择是以编辑或者只读方式来查看代码。
在服务器直连的模式下,可以将多个服务器的多个命名空间的代码添加至当前工作区。
目前的连接方式进入之后会频繁的要求登录验证,输入密码,这里实际可以将密码存在VSCode的钥匙链中:
1.打开命令面板
2.输入store 使用下图中的命令则可以为选定的服务器存储密码,但是每个服务器只能保存一个密码,相应的在命令面板中输入clear 会提示清除密码的选项用来删除已存储的密码
2. 使用客户端方式连接
如果使用客户端的连接方式,则必须要在工作区内添加一个文件夹,以及保存此工作区。当添加文件夹到工作区之后侧边栏会多出图示的按钮:
点击按钮选择Choose server and namespaces
此时如果在vscode中已经添加过想要连接的IRIS 代码服务器,这里会直接显示已存储的服务器,设置法与之前提到的直连方式相同。
连接完成后如果想添加多个命名空间到工作区,可以使用右上角的+号进行添加
接下来可以在客户端模式连接导出ObjectScript代码到本地。从而进行源代码的版本控制管理。
在objectscript explorer中选择任意的包,或者类右键选择export,则可以将其导出到目前工作区的文件夹下
代码会被导出到工作区目录的src文件夹下。
客户端模式中使用 GIT
首先打开一个GIT Repo文件夹,如果本来然后按照客户端连接方式连接用来开发的服务器。在任何的包或文件上点击import and compile 可以将版本控制的代码导入开发服务器。 注意这时候只要在本地新建文件并且保存,新的修改就会被同步到开发服务器实例上。
以客户端方式连接时连接多个服务器或多个命名空间的方式:
首先将一个文件夹添加到工作区,并保存工作区,在保存之后的工作区中可以看到如下的工作区配置文件(
此处我使用的默认工作区配置文件名,实际可以自行定义),其中定义了工作区的目录结构:
此时我们在这个工作区内添加一个子目录,在工作区的列表右键单击,选择“将文件夹添加到工作区”:
添加后可以看到,在工作区配置文件中增加了新的目录,为了这个显示层级更加清楚,可以在settings 中使用files.exclude 来过滤是否显示子目录
此时子再点击侧边栏中的 object script explorer,则会让用户选择使用哪一个子目录进行连接:
要使用另一个目录连接一个服务器的命名空间时,将第一个已连接的文件夹下.vscode 拷贝到第二个文件夹:
直接对这个文件进行修改,将其配置成为想连接的服务器以及命名空间
连接配置文件同样支持如下的写法:
此时再回到object script exploer,可见到添加了的服务器。
可见这里服务器的连接和本地代码编译的目标对象只由当前文件夹下.vscode/settings.json控制。
所有的之前提到的配置文件都可以手工编辑,不必一定在vscode中进行添加文件夹和配置连接。
在VSCode中调试
以客户端方式连接时可以进行调试:
在侧边栏选择调试按钮,首次调试应在工作区创建launch.json,并选择ObjectScript Debug
对于Class Method,可以直接在类中点击“Debug this Method”
下边是一个Class Method 实例的实例调试界面
附加到进程调试:
对于需要附加到的进程,在lauch.json中添加”program” 属性,并填入进程号,保存。
program 中也可以填入要debug的routine 比如“##class(Test.test).test()”, “name”可以修改成自定义的名字,之后会在debug运行按钮的下拉菜单显示。
调试的配置可以添加多个,都会显示在debug下拉菜单中
进入调试界面,点击xDebug 旁边的绿色启动按钮开始调试,其他同Class Method 调试方式
关于VSCode几个编辑器小技巧:
在底部栏点击当前连接的服务器会出现提示菜单,从这里可以便捷进入实例管理界面和class reference
在任意的类或者方法上点击右键可以转到定义。如果使用了#dim预定义变量,则可以在变量上使用转到变量声明,而且可以使用转到类型定义类型的定义。
尾声
当前版本不支持xml 代码文件导入!VSCode community中已经有人提到过这个问题,开发者回复之后的版本很有可能会加入
本项目的Github的issue地址,可以在这里提出问题:https://github.com/intersystems-community/vscode-objectscript/issues
支持的产品 :Caché/Ensemble 2016.2 以及更新, 以及所有的基于IRIS 的产品版本.
调试入参类型为%Library.DynamicAbstractObjec的参数如何输入入参呢 对象入参的话,的确直接调试没法从键盘输入,可能还是需要附加到进程或者写一个临时方法来生成这个入参对象,再调用想调试的方法检查断点。 知乎文章:使用VS Code进行Caché数据库开发 好的,谢谢
文章
Hao Ma · 十二月 23, 2024
技术支持团队在不同的项目中发现了类似中间人攻击的情况, 和各位分享一下。
我们的系统一般是安装在内网里,没有恶意的中间人攻击的风险。但是在有些医院发现了这样的情况:IT在网络中安装了某种网络监控或者嗅探的设备, 它会在通信通道中模拟其中一方,或者双方的通信节点, 以截获通信双方的网络流量。通常它不影响双方的通信,但偶尔,它会中断双方的连接, 造成业务的中断。实质上这也是一种中间人攻击的情况,只不过这是用户允许的行为,偶然出现了故障。
我们看看以下的例子:
以下的wireshark抓包截图中, 172.18.1.131和172.18.1.145在正常的通信过程中, 忽然收到了RST消息,造成了TCP连接上的复位。
其中172.18.1.131是intersystems的health connect系统, 它在序号50134的包里面首先发送了RST,因此客户怀疑是不是Health Connect出错,中断了连接,也就把问题提交了InterSystems的技术支持。
我们的技术支持检查了各种内部日志,没有发现任何错误,咨询了InterSystems的网络专家,才发现这是个网络层的错误,也就是说: 这个RST不是Health Connect发送的, 同样, 序号50135的RST消息, 也不一定是172.18.1.145发送的,这是中间网络层的行为。 经过客户的证实,发现客户在网络层安装的监控工具, 不知道什么原因,触动了某种安全机制,它模拟两个系统,分别向对方各发送了一个RST消息。
我们的技术人员之所以发现了疑似中间人攻击的情况, 是基于仔细研究了RST消息中的TTL值。
IP 数据包中的 TTL(Time To Live)是什么:
当 IP 数据包在网络中传输时,TTL 用于防止数据包在网络中无限循环转发。 发送方在创建 IP 数据包时设置一个初始 TTL 值,通常为 64、128 或 255 等。每经过一个路由器,TTL 值就会减 1。当 TTL 减为 0 时,路由器会丢弃该数据包,并向源地址发送一个 ICMP(Internet Control Message Protocol)超时消息。TTL确保网络中的数据包不会无限制地循环,避免网络拥塞。同时,TTL 也可以帮助网络管理员诊断网络问题,例如通过观察 TTL 值的变化来确定数据包经过的路由路径。可以通过 IP 数据包中的 TTL 值来大致判断路由信息,但这种方法并不十分精确,只能提供一些有限的线索。
为了理解上面一段话的意思,我们向不同的目的地发ping包观察一下TTL值:
hma@CNMBP23HMA ~ % ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.132 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.162 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.151 ms
^C
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.132/0.148/0.162/0.012 ms
hma@CNMBP23HMA ~ % ping bjsvmse01
PING bjsvmse01.iscinternal.com (172.19.85.68): 56 data bytes
64 bytes from 172.19.85.68: icmp_seq=0 ttl=64 time=10.388 ms
64 bytes from 172.19.85.68: icmp_seq=1 ttl=64 time=6.013 ms
64 bytes from 172.19.85.68: icmp_seq=2 ttl=64 time=4.614 ms
^C
--- bjsvmse01.iscinternal.com ping statistics ---
7 packets transmitted, 7 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 3.941/5.778/10.388/2.050 ms
hma@CNMBP23HMA ~ % ping www.baidu.com
PING www.a.shifen.com (110.242.68.4): 56 data bytes
64 bytes from 110.242.68.4: icmp_seq=0 ttl=52 time=14.167 ms
64 bytes from 110.242.68.4: icmp_seq=1 ttl=52 time=12.411 ms
64 bytes from 110.242.68.4: icmp_seq=2 ttl=52 time=12.560 ms
^C
--- www.a.shifen.com ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 12.411/13.046/14.167/0.795 ms
hma@CNMBP23HMA ~ % ping www.intersystems.com
PING e9933.b.akamaiedge.net (104.124.163.63): 56 data bytes
64 bytes from 104.124.163.63: icmp_seq=0 ttl=42 time=173.183 ms
64 bytes from 104.124.163.63: icmp_seq=1 ttl=42 time=170.947 ms
64 bytes from 104.124.163.63: icmp_seq=2 ttl=42 time=169.031 ms
64 bytes from 104.124.163.63: icmp_seq=3 ttl=42 time=171.195 ms
64 bytes from 104.124.163.63: icmp_seq=4 ttl=42 time=168.908 ms
64 bytes from 104.124.163.63: icmp_seq=5 ttl=42 time=169.736 ms
上面我分别发送ICMP到4个地址:127.0.0.1,bjsvmse01, www.baidu.com, www.intersystems.com, 从对端响应消息中显示的TTL值分别是64, 64, 52, 42。
TTL=64是Linux系统的TTL初始值。 在linux系统ping 127.0.0.1时返回的当然就是64。 第二个服务器bjsvmse01返回TTL也是64, 说明它和我的本机在同一个网络,他们中间没有路由器的转接。
第3个第4个ping返回的消息中的TTL分别是52和42, 理解为:从Baidu.com的服务器到我的本机,经过了64-52=12个路由,而从intersystems.com到我的本机经过了64-42=22个路由。因为intersystems.com的服务器在国外,经过更多路由。从ping的结果看, 响应时间也更长,平均响应要100多ms。
有了TTL的知识,我们来看看前面医院里的RST是怎么回事。
首先是RST的包, 可以看到TTL是128。 这就不对了。 128是Windows系统的TTL初始值, 而我们技术人员已经知道了, Health Connect是安装在一个Linux系统上,如果是Health Connect发出的RST,TTL应该是低于64的.
我们去和一个正常工作的从这个服务器发出的TCP消息来比较,比如上面的HTTP消息,下面的截中可以看到TTL是64, 这是对的。本来这两个服务器就在一个网络里。
因此, 结论就是: 抓包中显示为从Health Connect 172.1.18.131发出的RST消息,因为TTL不对, 应该不是真的从Health Connect服务器发出的。 类似的情况出现在不同的客户的不同医院环境, 所以我们认为维护或者支持任何可能应该了解一下这个情况。 如果遇到,请及时和医院的IT工程师了解情况。
有关TCP消息中的TTL值, 再补充几点:
如果源和目的服务器不在一个网络,TTL值不是64和128,也可以用来判断是否是真实的服务器发送的消息。我们有个支持的工单里, 发现了正常工作的TTL是62,而发送了一个错误的TCP消息,TTL是57, 这也是可疑且需要网络工程师确认的。
上面的例子中,从Health Connect对端,也就是172.18.1.145也发出了一个无法理解的RST消息。一个TCP端,收到一个RST消息后,不需要也不应该再回复,所以这个RST和上面Health Connect发出的RST没有关系。检查这个RST, 它的TTL值是128, 而正常通信时从这个服务器发出的消息也是128, 显然这是个Windows系统,所以这样的情况下, 只通过TTL是无法判断消息来源的身份真假的。除非在不同的地方,比如在中间的交换机上和该服务器上分别抓包比较,才可以得到线索。
网络路径中的某些设备(如防火墙、代理服务器等)对数据包进行了特殊处理。这些设备可能会修改数据包的 TTL 值,以确保数据包能够在网络中存活足够长的时间到达目的地. 典型的是当ping 8.8.8.8,也就 是 Google 的公共 DNS 服务器的时候,收到的TTL可能是不同的值,下面的截图中TTL是111, 如果您也测试一下, 您可能发现这个值是150, 170等等, 这时不只是源服务器的操作系统,LINUX/UNIX/Windows的初始值起作用,而是在中间网络设备将这个值做过特殊处理。
hma@CNMBP23HMA ~ % ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=111 time=50.109 ms
64 bytes from 8.8.8.8: icmp_seq=1 ttl=111 time=45.896 ms
^C
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 45.896/48.002/50.109/2.107 ms
hma@CNMBP23HMA ~ %
因此,关于TTL值的局限性, 有这样的说法:
准确性有限:
由于网络中的路由可能会动态变化,TTL 值推断出的路由信息可能并不完全准确。
一些网络设备可能会对 TTL 值进行修改或干扰,导致推断结果不准确。
不完整性:
对于一些复杂的网络环境,如使用 VPN(Virtual Private Network)、代理服务器或网络地址转换(NAT)的情况,TTL 值可能无法准确反映真实的路由路径。
文章
姚 鑫 · 二月 15, 2021
# 第三十五章 Caché 变量大全 $ZNSPACE 变量
包含当前命名空间名称。
# 大纲
```java
$ZNSPACE
```
# 描述
`$ZNSPACE`包含当前命名空间的名称。通过设置`$ZNSPACE`,可以更改当前名称空间。
要获取当前命名空间名称,请执行以下操作:
```java
DHC-APP>SET ns=$ZNSPACE
DHC-APP>WRITE ns
DHC-APP
```
还可以通过调用`%SYSTEM.SYS`类的`Namespace()`方法来获取当前命名空间的名称,如下所示:
```java
DHC-APP>SET ns=$SYSTEM.SYS.NameSpace()
DHC-APP>WRITE ns
DHC-APP
```
可以使用`%SYS.Namespace`类的`Existes()`方法测试命名空间是否已定义,如下所示:
```java
DHC-APP>WRITE ##class(%SYS.Namespace).Exists("USER")
1
DHC-APP>WRITE ##class(%SYS.Namespace).Exists("LOSER")
0
```
对于UNIX®系统,默认命名空间建立为系统配置选项。对于Windows系统,它是使用命令行启动选项设置的。
命名空间名称不区分大小写。InterSystems IRIS始终以全大写字母显示显式名称空间名称,以全小写字母显示隐含的名称空间名称。
要获取指定进程的命名空间名称,请使用`%SYS.ProcessQuery`类的方法,如下例所示:
```java
DHC-APP>WRITE ##CLASS(%SYS.ProcessQuery).%OpenId($JOB).NameSpaceGet()
DHC-APP
```
# 设置当前命名空间
可以使用`ZNSPACE`命令、`SET $NAMESPACE`、`SET $ZNSPACE`或`%cd`实用程序更改当前名称空间。
- 在终端命令提示符下,`ZNSPACE`命令是更改名称空间的首选方式。`SET $ZNSPACE`在功能上与`ZNSPACE`命令相同。
- 在代码例程中,新建`$NAMESPACE`,然后设置`$NAMESPACE = NAMESPACE`是更改当前名称空间的首选方式。通过使用`new $NAMESPACE`和`SET $NAMESPACE`,可以建立一个名称空间上下文,该上下文在方法结束或发生意外错误时自动恢复到前一个名称空间。
可以使用`SET $ZNSPACE`更改进程的当前命名空间。将新命名空间指定为字符串文字或计算结果为带引号的字符串的变量或表达式。可以指定显式名称空间(`“NAMESPACE”`)或隐式名称空间(`“^SYSTEM^DIR”或“^^DIR”`)。
如果指定当前命名空间,则`SET $ZNSPACE`不执行任何操作,也不返回任何错误。如果指定了一个未定义的名称空间,则`SET $ZNSPACE`会生成一个``错误。
不能`new $ZNSPACE`特殊变量。
# 示例
在以下示例中,如果当前命名空间不是`USER`,则`SET $ZNSPACE`命令会将当前命名空间更改为`USER`。请注意,由于`if`测试,命名空间必须全部用大写字母指定。
```java
/// d ##class(PHA.TEST.SpecialVariables).ZNSPACE()
ClassMethod ZNSPACE()
{
SET ns="USER"
IF $ZNSPACE=ns {
WRITE !,"命名空间已经 ",$ZNSPACE
} ELSEIF 1=##class(%SYS.Namespace).Exists(ns) {
WRITE !,"命名空间是 ",$ZNSPACE
SET $ZNSPACE=ns
WRITE !,"将命名空间设置为 ",$ZNSPACE
} ELSE {
WRITE !,ns," 不是定义的命名空间"
}
QUIT
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZNSPACE()
命名空间是 DHC-APP
将命名空间设置为 USER
```
此示例要求`UnnownUser`已分配`%DB_IRISSYS`和`%DB_USER`角色。
文章
姚 鑫 · 二月 25, 2022
# 第六十五章 SQL函数 %INTERNAL
返回逻辑格式表达式的格式转换函数。
# 大纲
```
%INTERNAL(expression)
%INTERNAL expression
```
# 参数
- `expression` - 要转换的表达式。
字段名、包含字段名的表达式,或返回可转换数据类型(如`DATE`或`%List`)中的值的函数。
# 描述
`%INTERNAL`将表达式转换为逻辑格式,与当前选择模式(显示模式)无关。逻辑格式是数据的内存格式(对其执行操作的格式)。`%INTERNAL`通常用于选择列表`SELECT-ITEM`。
**可以在`WHERE`子句中使用`%INTERNAL`,但强烈建议不要使用`%INTERNAL`,因为使用`%INTERNAL`会阻止在指定字段上使用索引,并且`%INTERNAL`会强制所有比较区分大小写,即使该字段有默认排序规则也是如此。**
应用`%INTERNAL`会将列标题名称更改为诸如“`Expression_1`”之类的值;因此,通常需要指定列名别名,如下面的示例所示。
`%INTERNAL`将数据类型%DATE的值转换为整数数据类型值。`%INTERNAL`将数据类型`%TIME`的值转换为数字`(15,9)`数据类型值。之所以提供此转换,是因为ODBC或JDBC客户端不识别逻辑`%DATE`和`%TIME`值。
`%INTERNAL`是否转换日期取决于日期字段或函数返回的数据类型。`%INTERNAL`转换`CURDATE`、`CURRENT_DATE`、`CURTIME`和`CURRENT_TIME`值。它不转换`CURRENT_TIMESTAMP`、`GETDATE`、`GETUTCDATE`、`NOW`和`$HOROLOG`值。
不能将流字段指定为ObjectScript一元函数(包括所有格式转换函数,`%Internal`除外)的参数。`%INTERNAL`函数允许将流字段作为表达式值,但不对该流字段执行任何操作。
`%INTERNAL`是InterSystems SQL扩展。
要将表达式转换为显示格式,而不考虑当前的选择模式,请使用`%EXTERNAL`函数。要将表达式转换为`ODBC`格式,而不考虑当前的`SELECT`模式,请使用`%ODBCOUT`函数。
# 示例
下面的动态SQL示例以当前选择模式格式返回出生日期(道布)数据值,并使用`%INTERNAL`函数返回相同的数据。出于演示目的,在此程序中,为每次调用随机确定`%SelectMode`值:
```java
ClassMethod Internal()
{
s tStatement = ##class(%SQL.Statement).%New()
s tStatement.%SelectMode=$RANDOM(3)
if tStatement.%SelectMode=0 {WRITE "Select mode LOGICAL",! }
elseif tStatement.%SelectMode=1 {WRITE "Select mode ODBC",! }
elseif tStatement.%SelectMode=2 {WRITE "Select mode DISPLAY",! }
s myquery = 2
s myquery(1) = "SELECT TOP 5 DOB,%INTERNAL(DOB) AS IntDOB "
s myquery(2) = "FROM Sample.Person"
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
w !,"End of data"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).Internal()
Select mode DISPLAY
DOB IntDOB
04/25/1990 54536
01/02/2014 63189
01/02/2014 63189
01/28/1978 50066
5 Rows(s) Affected
End of data
```
下面的示例显示了此函数的两种语法形式;在其他方面它们是相同的。它们指定`%LIST`字段的`%EXTERNAL`(显示格式)、`%INTERNAL`(逻辑格式)和`%ODBCOUT`(ODBC格式):
```sql
SELECT TOP 10 %EXTERNAL(FavoriteColors) AS ExtColors,
%INTERNAL(FavoriteColors) AS IntColors,
%ODBCOUT(FavoriteColors) AS ODBCColors
FROM Sample.Person
```
```sql
SELECT TOP 10 %EXTERNAL FavoriteColors AS ExtColors,
%INTERNAL FavoriteColors AS IntColors,
%ODBCOUT FavoriteColors AS ODBCColors
FROM Sample.Person
```
文章
shaosheng shengshao · 九月 14, 2022
研究Healthshare2018在已经安装完成使用的情况下,部署IIS,并代理平台。之前看到可以通过单独的CSP Gateway安装包进行处理这种问题,该文主要是获取不到该安装包的时候可以如何实现IIS的处理。
第一步:首先按照网上教程部署IIS服务。安装完成之后会在C盘创建一个名叫intepub的包。
第二步:在inetpub包下面直接插入CSPGateway 。这个包是在其他先安装IIS下,后安装Healthshare的CSP网关服务的时候在inetpub下面自动插入的包,直接找一个复制过来。
第三步:打开IIS,创建一个叫csp的网站,按照下图配置。
第四步:在点击模块进行CSPms模块的添加。
接下来是配置好的CSPms的模块。
注意:我们从图中看到的CSPms模块是本机继承的模块,我们在右边添加本机模块的时候并不能找到C:\inetpub\CSPGateway\CSPms.dll这个模块,导致加不进去。那我们就直接在配置文件上面改。找到C:\Windows\System32\inetsrv\config该文件路径下的applicationHost.config文件,为了稳妥起见,我们先备份一下该文件。找到<globalModules>节点,在下面加入<add name="CSPms" image="C:\Inetpub\CSPGateway\CSPms.dll" />找到<modules>节点,在下面加入<add name="CSPms" />找到hiddenSegments节点,找到下面的bin参数,删除<add segment="bin " />,如下图所示。
保存文件,如果保存过程中出现不允许修改的问题,可以把文件贴到桌面,在这个文件上面改造,在copy回去直接替换。再回到iis页面,网站csp的模块里面就会出现对应的CSPms的本机模块。后面进行服务映射,双击点击处理程序映射。 第六步:接下来测试我们打开平台管理页面能不能打开。
如果出现了这个页面,那我们就快成功了,这个错误可能我们大家都有见过。我们打开C:\inetpub\CSPGateway下面的CSP.ini的文件。里面有个
有这个Password字段,因为我们的CSPGateway这个包是从其他服务器上copy过来的,这个密码只能用于那台服务,不能用于这个服务器,那我们可以拿57772端口的apache配置下的这个密码拿过来用。在安装路径下D:\InterSystems\HealthShare\CSP\bin的CSP.ini,也有一个Password字段,把这个密码copy过来放到C:\inetpub\CSPGateway下面的CSP.ini上。重启IIS服务,如果出现权限问题,也把CSP.ini拷贝到桌面,修改好后再贴回去进行替换。再次进行访问,就可以进去桌面了。第七步:点入到CSP网关管理,如果提示出现warning字眼,那是C:\inetpub\CSPGateway下面的用户权限问题。
对CSPGateway右键按照下图示例进行IIS_IUSRS用户权限的添加。对文件夹下的CSP.ini和CSP.log做一样的操作。
好文,欢迎参赛!
文章
Michael Lei · 一月 17, 2023
ZPM 设计用于与 InterSystems IRIS 数据平台的应用程序和模块一起使用。 它由两个组件组成:ZPN 客户端(用于管理模块的 CLI)和注册表(模块和元信息的数据库)。 我们可以使用 ZPM 来搜索、安装、升级、移除和发布模块。 使用 ZPM,可以安装 ObjectScript 类、前端应用程序、互操作性生产环境、IRIS BI 解决方案、IRIS 数据集或任何文件,例如嵌入式 Python wheel。
今天的这份实战宝典将分为 3 个部分:
1. 安装 ZPM
2. 生成模块
3. 在注册表中查找、安装、发布模块
1. 安装 ZPM
* 下载最新版本的 ZPM(它应该是一个 XML 文件)[下载链接](https://pm.community.intersystems.com/packages/zpm/latest/installer)
* 将下载的 XML 导入到 IRIS(它只能部署到已打开 IRIS 的 IRIS 终端)并按 Enter 键
_write $SYSTEM.OBJ.Load("C:\zpm.xml", "c")_
请注意,“C:\zpm.xml”是下载的 XML 文件的路径,这一步可能需要一些时间。
* 安装完成后,只需输入 _zpm_,按 Enter 键,您会看到您在 zpm shell 中

2. 生成模块
在开始生成模块之前,我们需要准备一个文件夹,里面有一个或多个可以加载的文件,因此我在 C 盘下创建了一个名为 zpm 的文件夹。
执行命令 _generate C:/zpm_
在指定所有必要的内容后,您的第一个模块已成功生成,您还会看到

注意:
1. 模块版本正在使用语义化版本控制
2. 模块源文件夹是包含所有类文件的文件夹
3. zpm 还提供了一个选项,可以添加 web 应用程序和依赖项,本例中我将其留空
现在,打开文件资源管理器,您会看到一个名为“module.xml”的文件,如下面的屏幕截图所示

输入命令 _load C:\ZPM\ _,您会看到您的模块已重新加载、验证、编译和激活

3. 在注册表中查找、安装、发布模块
在当前注册表中查找可用的软件包:_zpm:USER>search_
举例来说,从当前注册表安装软件包,让我们在公共注册表中安装一个名为 zpmshow 的模块:_zpm:USER>install zpmshow _(命令为 install "moduleName")
加载后发布模块:_zpm:USER>publish myFirstZPMDemo_
可以使用 _zpm:USER>search_ 验证发布,本例中,您可以看到“myfirstzpmdemo 0.1.0”位于当前注册表中。

注意:如果发布模块时遇到以下错误:_“ERROR! Publishing module, something went wrong”(错误!发布模块时出错了)_,确保当前注册表的状态为“已启用”且可用。
可以使用_ zpm:USER>repo -list, _验证当前注册表的状态。

可用视频:[点击此处](https://www.loom.com/share/0ca097f0dea4476ea294841295f972b2%C2%A0%C2%A0)
文章
姚 鑫 · 一月 25
# 第七章 C - D 开头的术语
#### 当前设备 (current device)
**系统**
处理 `I/O` 命令的设备。当你登录时,当前设备是你的主设备,通常是你登录时使用的终端或个人计算机。
#### 当前目录 (current directory)
**通用**
你当前工作的目录。
#### 基于游标的 SQL (cursor-based SQL)
**SQL**
一种嵌入式 `SQL` 查询类型,打开一个游标来处理查询。当你的应用程序需要访问多行数据时,必须使用游标。游标像一个指针——它专注于访问和处理一行数据,然后移动到序列中的下一行。
#### 游标 (cursor)
**SQL**
在多行数据中向前移动的迭代器。
#### 自定义存储 (custom storage)
**对象(Objects)**
自定义存储允许你通过编写自己的存储接口方法实现来决定对象的存储结构。通常,使用自定义存储的类不会被投射到 `SQL`。
# 以 D 开头的术语
#### 数据库 (database)
**通用**
`IRIS.DAT` 文件。它可以包含代码和数据。通过全局、包和例程映射,任何给定的数据库都可以被多个命名空间使用。
#### 数据库缓存 (database cache)
**系统**
用于缓存从数据库中检索的数据的系统内存(`RAM`),这样相同查询的重复实例可以从内存中而不是存储中检索结果,从而提供显著的性能提升。为了获得最佳性能,数据库缓存应至少与应用程序工作负载的工作集一样大。
#### 数据库加密 (database encryption)
**系统**
将 `IRIS` 数据库以加密状态存储在磁盘上的过程。当 `IRIS` 从磁盘读取数据时,数据会在运行时自动解密,以便其合法用户可以访问数据。当数据写入磁盘时,会进行加密。磁盘上的数据也称为静态数据(`at rest`)。
#### 数据库加密密钥 (database-encryption key)
**系统**
用于加密 `IRIS` 数据库的 `AES` 密钥。
#### 数据库完整性 (database integrity)
**通用**
数据库在其内容或结构上保持未损坏的状态。数据库中的数据有时由于物理完整性退化而变得不可读。内部指针可能由于内部完整性退化而损坏。
#### 数据定义语言 (Data Definition Language)
**InterSystems SQL**
数据定义语言(`DDL`)是一种基于命令的语言,用于创建、定义表的结构和删除表。
#### 数据位置 (data location)
**系统**
全局所在的数据集。复制全局的全局集合和删除操作的来源是映射到全局数据位置的目录(或目录和系统)。
文章
YuHao Wan · 十一月 5, 2022
### 0. 算法概述
SM4算法是一种分组密码算法。其分组长度为128bit,密钥长度也为128bit。加密算法与密钥扩展算法均采用32轮非线性迭代结构,以字(32位)为单位进行加密运算,每一次迭代运算均为一轮变换函数F。SM4算法加/解密算法的结构相同,只是使用轮密钥相反,其中解密轮密钥是加密轮密钥的逆序。
### 1. 密钥及轮密钥
密钥长度为128比特,表示为MK=(MK(0),MK(1),MK(2),MK(3)),其中MKi(i=0,1,2,3)为字。
轮密钥表示为(rk(0),rk(1),...,rk(31)),其中rk(i)(i=0,...,31)为32比特字。轮密钥由秘钥生成。

### 2. 消息填充分组
首先,将明文转化为字节,由于SM4加密算法按照128个位进行分组,所以很大几率会出现最后一个分组不够128位的情况,需要进行**填充**,填充方式有很多,比如ZeroPadding、PKCS7Padding、PKCS5Padding,不管使用哪种方式,最后每个分组都是128位。每个分组按照**32位**一个字分成四个字。
#### ECB模式与CBC模式
- ECB模式
电子密码本模式,最古老,最简单的模式,将加密的数据分成若干组,每组的大小跟加密密钥相同。不足的部分进行填充。
按照顺序将计算所得的数据连在一起即可,各段数据之间互不影响。
优点:简单,有利于并行计算,误差不会被传递。
缺点:不能隐藏明文的模式,可能对明文进行主动攻击。
- CBC模式
密文分组链接模式,也需要进行分组,不足的部分按照**指定的数据**进行填充。
需要一个**初始化向量**,每个分组数据与上一个分组数据加密的结果进行**异或运算**,最后再进行加密。将所有分组加密的结果连接起来就形成了最终的结果。
优点:不容易进行主动攻击,安全性好于ECB。
缺点:不利于并行计算,误差传递,需要初始化向量。
#### 三种填充方式的比较
某些加密算法要求明文需要按一定长度对齐,叫做**块大小**(BlockSize),比如16字节,那么对于一段任意的数据,加密前需要对最后一个块填充到16 字节,解密后需要删除掉填充的数据。
- **ZeroPadding**,数据长度不对齐时使用**0**填充,否则不填充。
- **PKCS7Padding**,假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是**n**;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小。
- **PKCS5Padding**,PKCS7Padding的子集,块大小固定为**8**字节。
由于使用PKCS7Padding/PKCS5Padding填充时,最后一个字节肯定为填充数据的长度,所以在解密后可以准确删除填充的数据,而使用ZeroPadding填充时,没办法区分真实数据与填充数据,所以只适合以\0结尾的字符串加解密。
### 3. 迭代运算
本加解密算法由32次迭代运算和1次反序变换R组成。

#### 3.1 轮函数F和合成置换T

### 4. Caché实现
```
/// SM4算法是一种分组密码算法。其分组长度为128bit,密钥长度也为128bit。
/// 加密算法与密钥扩展算法均采用32轮非线性迭代结构,以字(32位)为单位进行加密运算,每一次迭代运算均为一轮变换函数F。
/// SM4算法加/解密算法的结构相同,只是使用轮密钥相反,其中解密轮密钥是加密轮密钥的逆序。
/// 本方法适用于 ECB模式,ZeroPadding填充模式
Class Utility.SM4 Extends %RegisteredObject
{
/// Creator: wyh
/// CreatDate: 2022-11-03
/// Description:SM4加密
/// Input: msg:原文 mk:128位密钥
/// Output: 密文
/// Debug: w ##class(Utility.SM4).Encrypt("342622199009262982", "F2D8D966CD3D47788449C19D5EF2081B")
ClassMethod Encrypt(msg, mk)
{
#; 1. 密钥及轮密钥
#; a) 密钥长度为128比特,表示为MK=(MK(0),MK(1),MK(2),MK(3)),其中MKi(i=0,1,2,3)为字
#; b) 轮密钥表示为(rk(0),rk(1),...,rk(31)),其中rk(i)(i=0,...,31)为32比特字。轮密钥由密钥生成。
#; 密钥扩展算法:
#; (K(0),K(1),K(2),K(3))=(MK(0)^FK(0),MK(1)^FK(1),MK(2)^FK(2),MK(3)^FK(3))
#; rk(i)=K(i+4)=K(i)^T'(K(i+1)^K(i+2)^K(i+3)^CK(i)),i=0,1,...,31
#; 系统参数FK(0)=(A3B1BAC6),FK(1)=(56AA3350),FK(2)=(677D9197),FK(3)=(B27022DC)
#; 固定参数CK(i)(i=0,1,...,31)为:
#; 00070E15, 1C232A31, 383F464D, 545B6269,
#; 70777E85, 8C939AA1, A8AfB6BD, C4CBD2D9,
#; E0E7EEF5, FC030A11, 181F262D, 343B4249,
#; 50575E65, 6C737A81, 888F969D, A4ABB2B9,
#; C0C7CED5, DCE3EAF1, F8FF060D, 141B2229,
#; 30373E45, 4C535A61, 686F767D, 848B9299,
#; A0A7AEb5, BCC3CAD1, D8DFE6ED, F4FB0209,
#; 10171E25, 2C333A41, 484F565D, 646B7279.
s mk = $zcvt(mk, "L")
f i = 0 : 1 : 3 d
.s MK(i) = $e(mk, 8 * i + 1, 8 * (i + 1))
s FK = "a3b1bac656aa3350677d9197b27022dc"
f i = 0 : 1 : 3 d
.s FK(i) = $e(FK, 8 * i + 1, 8 * (i + 1))
s CK = "00070e151c232a31383f464d545b626970777e858c939aa1a8afb6bdc4cbd2d9e0e7eef5fc030a11181f262d343b424950575e656c737a81888f969da4abb2b9c0c7ced5dce3eaf1f8ff060d141b222930373e454c535a61686f767d848b9299a0a7aeb5bcc3cad1d8dfe6edf4fb020910171e252c333a41484f565d646b7279"
f i = 0 : 1 : 31 d
.s CK(i) = $e(CK, 8 * i + 1, 8 * (i + 1))
f i = 0 : 1 : 3 d
.s K(i) = ..HexXOR(MK(i), FK(i))
f i = 4 : 1 : 35 d
.s K(i) = ..HexXOR(K(i - 4), ..T2(..HexXOR(..HexXOR(..HexXOR(K(i + 1 - 4), K(i + 2 - 4)),K(i + 3 - 4)), CK(i - 4))))
f i = 0 : 1 : 31 d
.s rk(i) = K(i + 4)
#; 2. 消息填充分组
#; 每组128位,每组再分X(0),X(1),X(2),X(3)作为明文输入.
s hex = ..s2hex(msg)
s len = $l(hex)/32
s rtn = ""
f i = 0 : 1 : len-1 d
.k X
.s M(i) = $e(hex, 32 * i + 1, 32 * (i + 1))
.f j = 0 : 1 : 3 d
..s X(j) = $e(M(i), 8 * j + 1, 8 * (j + 1))
#; 3. 迭代运算,密文输出(Y(0),Y(1),Y(2),Y(3))
#; a) 32次迭代运算
#; X(i+4)=F(X(i),X(i+1),X(i+2),X(i+3),rk(i)),i=0,1,...,31
#; F(X(i),X(i+1),X(i+2),X(i+3),rk(i))=X(i)^T(X(i+1)^X(i+2)^X(i+3)^rk(i)),i=0,1,...,31
#; b)反序变换
#; (Y(0),Y(1),Y(2),Y(3))=R(X(32),X(33),X(34),X(35))=(X(35),X(34),X(33),X(32))
.f k = 0 : 1 : 31 d
..s X(k + 4) = ..HexXOR(X(k), ..T(..HexXOR(..HexXOR(..HexXOR(X(k + 1), X(k + 2)), X(k + 3)), rk(k))))
.s rtn = rtn_X(35)_X(34)_X(33)_X(32)
q rtn
}
/// Creator: wyh
/// CreatDate: 2022-11-03
/// Description:SM4解密
/// 解密变换与加密变换结构相同,不同的仅是轮密钥的使用顺序,解密时使用轮密钥序(rk(31),rk(32),...,rk(0)).
/// Input: hex:密文 mk:128位密钥
/// Output: 明文
/// Debug: w ##class(Utility.SM4).Decrypt("5efcbbfdb7a326b340295acb1c0e20fe2622730932bdb5302b5a4ee308944ecc", "F2D8D966CD3D47788449C19D5EF2081B")
ClassMethod Decrypt(hex, mk)
{
s mk = $zcvt(mk, "L")
f i = 0 : 1 : 3 d
.s MK(i) = $e(mk, 8 * i + 1, 8 * (i + 1))
s FK = "a3b1bac656aa3350677d9197b27022dc"
f i = 0 : 1 : 3 d
.s FK(i) = $e(FK, 8 * i + 1, 8 * (i + 1))
s CK = "00070e151c232a31383f464d545b626970777e858c939aa1a8afb6bdc4cbd2d9e0e7eef5fc030a11181f262d343b424950575e656c737a81888f969da4abb2b9c0c7ced5dce3eaf1f8ff060d141b222930373e454c535a61686f767d848b9299a0a7aeb5bcc3cad1d8dfe6edf4fb020910171e252c333a41484f565d646b7279"
f i = 0 : 1 : 31 d
.s CK(i) = $e(CK, 8 * i + 1, 8 * (i + 1))
f i = 0 : 1 : 3 d
.s K(i) = ..HexXOR(MK(i), FK(i))
f i = 4 : 1 : 35 d
.s K(i) = ..HexXOR(K(i - 4), ..T2(..HexXOR(..HexXOR(..HexXOR(K(i + 1 - 4), K(i + 2 - 4)),K(i + 3 - 4)), CK(i - 4))))
f i = 0 : 1 : 31 d
.s rk(i) = K(35 - i)
s len = $l(hex)/32
s rtn = ""
f i = 0 : 1 : len-1 d
.k X
.s M(i) = $e(hex, 32 * i + 1, 32 * (i + 1))
.f j = 0 : 1 : 3 d
..s X(j) = $e(M(i), 8 * j + 1, 8 * (j + 1))
.f k = 0 : 1 : 31 d
..s X(k + 4) = ..HexXOR(X(k), ..T(..HexXOR(..HexXOR(..HexXOR(X(k + 1), X(k + 2)), X(k + 3)), rk(k))))
.s rtn = rtn_X(35)_X(34)_X(33)_X(32)
q ..hex2str(rtn)
}
/// 非线性变换τ构成
/// τ由4个并行的S盒,设输入A=(a0,a1,a2,a3),输出为B=(b0,b1,b2,b3)
/// (b0,b1,b2,b3)=τ(A)=(Sbox(a0),Sbox(a1),Sbox(a2),Sbox(a3))
/// w ##class(Utility.SM4).tau("942600f0")
ClassMethod tau(a)
{
f i = 0 : 1 : 7 d
.s a(i) = $e(a, i + 1)
s s(0) = "d690e9fecce13db716b614c228fb2c05"
s s(1) = "2b679a762abe04c3aa44132649860699"
s s(2) = "9c4250f491ef987a33540b43edcfac62"
s s(3) = "e4b31ca9c908e89580df94fa758f3fa6"
s s(4) = "4707a7fcf37317ba83593c19e6854fa8"
s s(5) = "686b81b27164da8bf8eb0f4b70569d35"
s s(6) = "1e240e5e6358d1a225227c3b01217887"
s s(7) = "d40046579fd327524c3602e7a0c4c89e"
s s(8) = "eabf8ad240c738b5a3f7f2cef96115a1"
s s(9) = "e0ae5da49b341a55ad933230f58cb1e3"
s s(10) = "1df6e22e8266ca60c02923ab0d534e6f"
s s(11) = "d5db3745defd8e2f03ff6a726d6c5b51"
s s(12) = "8d1baf92bbddbc7f11d95c411f105ad8"
s s(13) = "0ac13188a5cd7bbd2d74d012b8e5b4b0"
s s(14) = "8969974a0c96777e65b9f109c56ec684"
s s(15) = "18f07dec3adc4d2079ee5f3ed7cb3948"
f i = 0 : 1 : 15 d
.f j = 0 : 1 : 15 d
..s s(i, j) = $e(s(i), 2 * j + 1, 2 * (j + 1))
s rtn = ""
f i = 0 : 1 : 3 d
.s r = ..hex2int(a(2 * i))
.s c = ..hex2int(a(2 * i + 1))
.s rtn = rtn _ s(r, c)
return rtn
}
/// 线性变换L
/// 非线性变换τ的输出是线性变换L的输入.设输入为B,输出为C.
/// C=L(B)=B^(B