外观
Haxall 文档(docHaxall)
原文: https://finproducts.atlassian.net/wiki/spaces/JIDD3/pages/34793553967/FIN+5.3.1+docHaxall 中文整理基于 FIN 5.3.1
manual
Haxall 文档(docHaxall)
Haxall 平台文档
chapters
| Getting Started | |
| Setup | 安装和运行 Haxall |
| Build | 从源头构建 |
| Architecture | |
| Architecture | 软件架构概览 |
| Runtime | 运行时间概览 |
| Folio | 标签数据库 Folio |
| Axon | Axon 脚本语言 |
| Libs | Library modules |
| Observables | 可观察订阅子系统 |
| Watches | 订阅以记录更改 |
| Cryptography | 帮助管理密钥和信任的证书 |
| Axon | |
| Axon Language | Axon 编程语言的特点 |
| Streams | 同步流 |
| Comps | Axon components |
| Axon Usage | Axon 代码的示例片段 |
| Units | 计量单位 |
| Connectors | |
| Connectors | 使用连接器的概览 |
| Connector Tuning | 调制连接器性能指南 |
| Custom Connectors | 创建自定义连接器 |
| 格式和协议 | |
| XML | 可扩展标记语言 |
| Appendix | |
| Axon Grammar | 正式 BNF 语法语法 |
| ChangeLog | 每个建筑的变动列表 |
建筑
概览
Haxall是一个开源软件框架,用于 Internet of Things它包括一个广泛的工具包干工具箱。 Project Haystack 数据。 把它从框中直接用作一个在边缘运行的灵活的IOT数据网关。 海瑟尔写在 Fantom 对Java VM 和 JavaScript 环境都有运行时间支持.
概念
Hexall由下列关键概念和子系统组成:
- Ontology:我们使用Haystack项目,使建筑环境的模型标准化
- Folio: 数据库因IOT数据的实时性质而优化
- Axon:用于登机和转换IOT数据的功能脚本语言
- HTTP API:所有功能都可以通过Haystack HTTP API访问.
- Connectors:与IoT设备和协议连接的框架
- Libs: 功能可插件模块
- Runtime: 用于项目工作的顶级 API
- Daemon: 包含电池的完整运行时间服务器
图书馆
功能被包成libs,这些模块是可重复使用的模块,您可以在运行时启用/失效. 最重要的图书馆包括:
axon: 海斯塔克数据值的核心职能hx: 核心运行时间功能math: 数学函数io: 以多种格式读写数据的功能conn: 与连接器一起工作task: 利用演员框架安排背景工作point: 支持应与可写分和历史收集
软件堆栈
下图说明了该系统如何结合在一起:
Sky缔造和平 đó
尤其在日本, SkyFoundry它是我们商业产品的基础 SkySpark 我们向社区开放了源头 因此,文件载有许多与不同工作或仅在SkySpark中提供的特性有关的注释。
为Haxall开发的图书馆将在SkySpark无缝运行. 例如,如果开发连接器或 Axon 函数库作为 Haxall lib,然后可以将同一pod投放到 SkySpark 运行时间中,它将作为普通扩展使用。
Hexall数据库也与SkySpark兼容二进制. 这样您就可以打开并运行一个在SkySpark和反之亦然的Haxall数据库. 注意并非所有扩展将运行在 Hexall - 只有核心开源 libs, 如 : listed above.
斧头
概览
Axon是Haxall和SkySpark内部用于脚本的编程语言. 大多数功能可以通过Axon访问:
- 查询 Folio 标记数据库
- 进行数据转换,如滚动和正常化
- 创建自己的函数库 应能满足以下条件: rule engine 以 Axon 书写
- 创建自定义报告
- 数据导入的脚 28.
- 数据同步和维护的背景任务
关键特征之一是,所有对数据库/分析引擎的查询都是完整的Axon表达式:
// find all the records with power tag
power
// same as above
readAll(power)
// find all recs with power and read history data for yesterday
readAll(power).hisRead(yesterday)
// find peak kW for yesterday for all power recs
readAll(power).hisRead(yesterday).hisRollup(max, 1day)这种设计使得从简单的查询到强大的数据转换管道——都使用单一的通用语法——具有极大的灵活性.
语言概览
Axon是一个完整的通用编程语言,特征如下:
- 简单:语言语法最低,设计在一个会话中充分学习 -向前推进功能:万物是函数(语言不是*object导向);关闭被语言和库大量用于简洁地表达数据转换.
- 动态打字:您不声明变量类型
- ** 面向目标**:语言设计是为了配合 Folio 数据库标签模式
- ** 面向组成部分**: Axon 提供了创建软件组件的专业语法
- ** 时间序列**:语言有专门的语法和丰富的函数库,用于处理时间、日期和日期范围等时间序列数据
- ** 面向单位**:Axon和Folio中的所有数字都可以明确标明。 unit 在计算过程中检查和进行的测量
有经验的程序员应该能够非常迅速地接上Axon,但是,在功能编程中扎根肯定会有帮助。
这个 Axon Language 我以为这个章子会挖进Axon语言的语法。
Func 建议
函数记录定义新的顶级函数,标签如下:
def: 函数作为符号“ func: {name}” 的程序名称src: 字符串标记函数的 Axon 源代码
这里有一个简单的添加函数:
def: ^func:myAdd
src:
(a, b) => a + b仅在SkySpark中,旧的函数定义样式可能使用 func 和 name 标签 :
name: "myAdd"
func
src:
(a, b) => a + b顶级命名空间
运行时项目的顶级命名空间定义如下:
- 核心职能
- 名称 func records
- 运行时间允许的空格
下列函数对于与顶级函数命名空间合作有用:
funcs(): 在顶级命名空间中定义的函数列表或匹配func(): 解决顶级命名空间中的函数
合格名称
函数调用中使用的名称可以是合格或不合格. 具备条件的函数指定一个使用双冒号的明确的库名空间:
equip::toPoints // qualified in "equip" ext namespace不合格函数名通过按此顺序搜索的隐含名称空间来解决:
- 函数的本地命名空间(参数/可变名称)
- func records
- 运行时间间隔
函数覆盖
如果 lib 函数被标记为 overridable,然后您可以通过宣布一个同名的函数记录来覆盖 * 。 作为项目建议解决的职能优先于扩展确定的功能。
试图覆盖不标记为 ext 函数 overridable 将会报告一个错误,而您的矩形功能将无法访问。 对于测试, 您可以通过添加标记标记来禁用每个工程的这些检查 。 disableOverridableChecks 页:1 projMeta 记录。
您可以使用其限定名称访问您覆盖的内置函数 :
geoTz(val) => do
// handle special case
if (val["geoCountry"] == "RU") return "Moscow"
// route back to built-in version
return geo::geoTz(val)
endAxon 语法
你真是个小人物
B溅射法的传说 :
:= is defined as
<x> non-terminal
"x" literal
'x' char literal
[x] optional
(x) grouping
s-e inclusive char range
x* zero or more times
x+ one or more times
x|x or产品
<topFunc> := <lambda-n> | <defcomp>
<defcomp> := "defcomp" <cells> [<do>] "end"
<cells> := [<cell> <nl>]*
<cell> := <id> <dict>
<expr> := <lambda> | <def> | <if> | <list> | <dict> | <do> |
<return> | <throw> | <tryCatch> | <defcomp> |
<assignExpr>
<do> := "do" <exprs> "end"
<exprs> := <expr> (<exprSep> <expr>)*
<exprSep> := ";" | <nl>
<var> := <qname>
<qname> := [<id> "::" ] <id>
<list> := "[" <listItems> "]"
<listItems> := [ <expr> ("," <expr>)* [","] ]
<dict> := "{" <dictItems> "}"
<dictItems> := [ <dictItem> ("," <dictItem>)* [","] ]
<dictItem> := <dictVal> | <dictMarker> | <dictRemove>
<dictVal> := <idOrKeyword> ":" <expr>
<dictMarker> := <idOrKeyword>
<dictRemove> := "-" <idOrKeyword>
<lambda> := <lambda-1> | <lambda-n>
<lambda-1> := <id> "=>" <expr>
<lambda-n> := "(" <params> ")" "=>" <expr>
<params> := [<param> ("," <param>)*]
<param> := <id> [":" <expr>]
<def> := <id> ":" <expr>
<if> := "if" "(" <expr> ")" <expr> ["else" <expr>]
<return> := "return" <expr>
<throw> := "throw" <expr>
<tryCatch> := "try" <expr> "catch" ["(" <id> ")"] <expr>
<defcomp> := "compdef [<compcell>] [<do>] "end"
<compcell> := <id> ":" <dict>
<assignExpr> := <condOrExpr> ("=" <assignExpr>)
<condOrExpr> := <condAndExpr> ("and" <condAndExpr>)*
<condAndExpr> := <compareExpr> ("or" <compareExpr>)*
<compareExpr> := <rangeExpr> (("==" | "!=" | "<" | "<=" |
">=" | ">" | "<=>") <rangeExpr>)*
<rangeExpr> := <addExpr> ".." <addExpr>
<addExpr> := <multExpr> (("+" | "-") <multExpr>)*
<multExpr> := <unaryExpr> (("*" | "/") <unaryExpr>)*
<unaryExpr> := ("-" | "not") <termExpr>
<termExpr> := <termBase> <termChain>*
<termBase> := <var> | <groupedExpr> | <literal> | <typeRef>
<groupedExpr> := "(" <expr> ")"
<termChain> := <call> | <dotCall> | <index> | <trapCall>
<call> := "(" [<callArg> ("," <callArg>)*] [<lambda>]
<callArg> := <expr> | "_"
<dotCall> := "." [<nl>] <qname> [<call> | <lamdba-1>]
<index> := "[" <expr> "]"
<trapCall> := "->" <id>
<typeRef> := [<typeLibName> "::"] <typename>
<typeLibName> := <id> ("." <id>)*
<literal> := <null> | <bool> | <number> | <str> | <date> |
<time> | <month> | <recId>
<idOrKeyword> := <id> | <keyword>
<keyword> := "and" | "catch" | "defcomp" | "deflinks" | "do" | "else" |
"end" | "false" | "if" | "not" | "null" | "or" | "return" |
"throw" | "true" | "try"键
<bool> := "true" | "false"
<null> := "null"
<id> := <idFirst> (<idRest>)*
<idFirst> := alphaLo
<idRest> := alphaLo | alphaHi | digit | '_'
<typename> := <alphaHi> (<idRest>)*
<alphaLo> := ('a' - 'z')
<alphaHi> := ('Z' - 'Z')
<digit> := ('0' - '9')
<digits> := <digit> (<digit> | "_")*
<number> := <hex> | <decimal>
<hex> := "0x" <hexDigit> (<hexDigit> | "_")*
<hexDigit> := ('a'-'f') | ('A'-'F') | digit
<decimal> := ["-"] <digits> ["." <digits>] [<exp>] [<unit>]
<exp> := "e"|"E" ["+"|"-"] <digits>
<unit> := <unitChar>*
<unitChar> := <alpha> | "%" | "_" | "/" | "$" | any char > 128 // see sys::Unit
<str> := see Fantom grammar (single line single quote, and multi-line triple quote)
<date> := yyyy-mm-dd
<time> := [h]h:mm[:ss[.fff]] // hour and secs optional
<month> := yyyy-mm
<ref> := "@" refChar (refChar)*
<symbol> := "^" refChar (refChar)*
<refChar> := <alpha> | <digit> | '_' | ':' | '.' | '~' | '-'
<nl> := newline "\n"Axon 语言
概览
本章完全涵盖了Axon编程语言的语法构造. 见 Axon 通过以下章节概述Axon的使用。
斯卡尔
支持以下语法:
- 诺尔:
null关键词 - 布尔:
trueorfalse关键词 - 号码:
4,-91,10_000,9.23kg,5.4e-45,74.2°F,5min - 斯特尔:
"hello"string literals - 乌里:
`io/sites.csv (英语).` - 日期:
YYYY-MM-DD,2010-01-27 - 时间:
[h]h:mm[:ss],3:45,08:12:05(总是24小时) - 范围:
0..100,2010-01-01..2010-01-31 - 日期范围:
YYYY-MM,2008-02是快捷键01-Feb-2008..29-Feb-2008
数字可选用单位附加注释 - 见 Units.
显然是没有文字上的DateTime、Coord或XStr的。 相反,使用 Axon 函数来构建 :
dateTime:dateTime(2012-10-19, 12:30)coord:coord(37.55, -77.55)xstr: xstr ("颜色","红色")
字符串字节
Axon中有三种字符串文字:
- 单引号字符串文字
- 三重引号字符串文字
- 原始字符串文字
单引号字符串使用标准的反斜线逃逸序列作为C类语言. 例如,对新线字符使用"\n"并使用""来编码引用字符本身:
"line 1 \n line 2"
"embedded \"quotes\"!"三重引号字符串使用反斜线逃逸,但并不需要逃避单个引号字符. 三重引用字符串也可能横跨多个行,但每个行的第一个非白空字符必须和开口引用的右侧对齐:
// triple quote multi-line example
"""This is a "multi-line" triple
quoted string"""
// above as a single line, single quoted string
"This is a \"multi-line\" triple\nquoted string"也可以通过前缀一个带有"r"字符的单引号字符串来创建原始字符串文字. 原始字符串字元不支持反斜线逃逸,使其对正则表达式有用 :
// raw string literal example
r"\foo\"注意"$"字符为未来的插值支持所保留,不能用于字符串文字,除非你反弹逃脱:
// backslash escape dolloar sign
"\$100"列表
Lists 存储一个数字索引对象零的线性序列。
[] // empty list
[6] // single item list
[1, 2, 3] // multiple items
[4, "four"] // mixed item types也见 Axon Usage.
命令
Dict是名称/值地图(关联阵列). 任何字符串密钥如果引用的话都可以使用,但是通过惯例,大多数地图都会使用有效的标识符作为其密钥.
{} // empty dictionary
{foo:6} // foo bound to 6
{foo:2+3} // foo found to 5 (value can be any expr)
{foo} // foo is bound to special "marker" value
{-foo} // foo is bound to special "remove" value
{n:"Bob",age:35} // mutiple value separated with comma
{"not tag"} // use string literal for non-identifier keys也见 Axon Usage.
运算符
排列顺序的运算符 :
围绕着这个主题: (x) x.y x.y() x->y x[y]
- 一号:
not x -x-与土著人民相比:* / - 补充:
+ - - 平等:
== != - 比较:
< <= >= > <=> - 有条件和:
and - 有条件或:
or - 任务:
=
数学运算符
操作的数学操作规则 :
=== Addition ===
num + num >> num
date + num >> date (num must be number of days)
dateTime + num >> dateTime (num must have duration unit)
time + num >> time (num must have duration unit)
dateSpan + num >> dateSpan (num must have duration unit)
uri + str >> uri concat
uri + uri >> uri concat
str + obj >> str concat
obj + str >> str concat
=== Subtraction ===
num - num >> num
date - num >> date (duration must be number of days)
date - date >> num (difference in number of days)
dateTime - num >> dateTime (num must have duration unit)
dateTime - dateTime >> num (difference in number of hours)
time - num >> time (num must have duration unit)
dateSpan - num >> dateSpan (num must have duration unit)
=== Multiplication ===
num * num >> num
=== Division ===
num / num >> num录像带中,
null + 2 >> null // null always carries into result任何数学操作 na() 成果 na()然而,无效仍然优先于 na():
na() + 2 >> na() // na() always carries into result
na() + 2 + null >> null // null trumps na()见 Units 以比较和算术操作方式处理单位数字。
两个
这个 == 和 != 操作者分别对等和不相等进行测试。 这些操作员检查标价是否平等或参考平等。 检查收藏是否均匀, equals() 函数。 平等运营商将与无效和不同类型合作。
3 == 4 >> false
4 == 4 >> true
4 != 4 >> false
4kW == 4m >> false (different units)
4kW != 4m >> true (different units)
5 == null >> false
5 == "str" >> false比较运算符
下列运算符用于对订单进行比较:
a < b a is less than b
a <= b a is less than or equal to b
a > b a is greater than b
a >= b a is greater than or equal to b
a <=> b evaluate to -1, 0, 1 if a is less than, equal, or greater than b比较运算符必须使用相同的类型. 然而,如果无效总是低于任何其他价值,则比较无效是安全的。 以下比较将引起例外:
- 将数字与不同的单位进行比较 - 看 Units
- 比较不同类型(例如比较字符串和数字)
- 比较收集类型(清单、数据或网格)
实例:
3 < 5 >> true
3 > 5 >> false
null < 5 >> true (null less than everything)
3 <=> 5 >> -1
3kW <=> 3kW >> 0
5kW <=> 3kW >> 1
nan() < 4 >> true (nan less than all other numbers)
4m < 5kW >> error (different unit)
4m < 5 >> true (non-null and null units ok)
5 < "str" >> error (different types)
5 < na() >> error (different types; may be relaxed in future)
({a}) < ({b}) >> error (collection types cannot be compared)布尔运算符
这个 not, and,以及 or 运算符按照布尔逻辑的标准真理表使用布尔值工作。
=== not ===
not true >> false
not false >> true
=== and ===
true and false >> false
true and true >> true
=== or ===
true or false >> true
false or false >> false这个 and 和 or 操作器为“短路”如下:
a and b // if a is false, then b is not evaluated
a or b // if a is true, then b is not evaluated实 计 数
索引运算符 target[key] 是一个快捷键 get() 函数。 它可用于索引字符串,列表,dicts,以及网格:
str[index] // get unicode char from string
str[start..end] // slice/substring
list[index] // get an item at zero based index
list[start..end] // perform list slice
dict[key] // lookup tag value by key
grid[num] // get row at index
grid[start..end] // slice of grid rows和Fantom一样,负指数可能用来从字符串的结尾进入.
陷阱操作员 -> 是一个快捷键 trap() 函数。 在 Dict 上使用时,它会按名称查找标签值. 您也可以使用这个 -> 用于自动解析 ID 的 Ref 上的操作员 readById():
id->foo >>> readById(id)->foo
id->siteRef->tag >>> readById(readById(id)->siteRef)->foo两者 [] 和 -> 用于查找名称中的标记值。 区别在于未定义密钥时会发生什么. 这个 [] 运算符(或 get() 函数)在未定义密钥时返回无效。 那个 -> 运算符(或 trap() 函数)提出未知的NameErr 例外。 如果您期望标记存在, 请使用 -> 运算符以获得有意义的例外,而不是通用 NullErr 。 如果您预计标记可能不存在, 请使用 [],但确保检查无效。
ẳng phòng/p sparseng/p sparseng/p sparseg cellsk sparsek sparsek cellsk cellsk cellsk cellsk cellsk cellsk cellsk cellsk cellsk tagk cellsk tagk doesk cellsk tagk tagk doesk tagk tagk tagk tagk tagk tagk tagk tagk tagk tagk tagk tagk tagk tagk tagk tagk tagk tagk tagk cellsl doesk tagk cellsk cellsk existk cellsl existk existl existk cellsk tagk cellsk cellsl existk cellsk cellsk cellsk cellsk cellsk cellsk cellsk cellsk cellsk cellsk existk cellsk existk cellsk existk tagk cellsk tagk cellsk tagk tagk tagk tagk tagk tagk tagk tagk tagk cellsk cellsk cellsn existk cellsk cellsk cellsk cellsn tagk cellsk cellsk cellsk tagk cellsk cellsk cellsk tagk cellsk existk existk existk cellsk existk existk cellsk tagk existk existk existk existk tagk cellsk existk existk tagk tagk tagk 例如, 此代码将提出一个例外 :
{foo:null}->foo备忘
冒号运算符用于定义一个变量为名称/值绑定:
a: 5
b: 3
a + b // yields 8您可以使用 = 将按照以下内容进行操作:
a: 4
a = a + 1变量必须已经通过 : 运算符在指定之前,否则将提出运行时例外。
范围
Axon使用有关闭支持的词典定义法. 每个函数调用都会产生一个范围. 注意,如果区块, 或尝试区块, * 不 创建新范围 。 例如,考虑此代码:
if (true) do
a: "foo"
end
return a这样的话,"foo"会由 a。一旦定义了一个变量,它就会在函数的任何地方可见。 这个模型更类似于JavaScript或Ruby,然后是Java或C#等静态语言.
顶级函数内的每个嵌套函数都具有其逻辑范围外函数变量的能见度. 这个设计被称为 closures (中文(简体) ). 简单的例子如下:
num: 0
f: ()=> num = num+10
3.times(f)
return num在这个 rồi的例子中,函数 f 转至 times 正在访问 num 变量。 结果将是30。 然而,一个嵌套函数可以定义一个隐藏外部范围的新变量:
x: "outer"
f: () => do
x: "inner"
return x
end
return [f(), x]在以上的例子中,结果将是"[内,外]". 变量 x 内部 f 隐藏未变的外部范围。
可变分辨率按以下顺序搜索:
- 当前函数的变量
- 逻辑范围外函数变量
- top-level namespace
兰布达斯
Lambdas是函数定义,使用零或更多参数并计算得出的值:
x => x * x // parens optional if single param
(x) => x * x // but you can use them
() => "some val" // no params
(x, y) => x + y // multiple params您会经常使用 def 运算符来创建命名函数 :
add: (x, y) => x + y
add(2, 3) >> 5您可以使用冒号定义参数的默认值 :
f: (a, b:2) => a + b电话
您调用函数 () 操作员。
函数预期的参数数称为其ality. 您必须给一个函数传递足够的参数, 以调整其相关性 。 如果函数参数有默认值,那么这些参数可能会被省略. 也允许将 * 更多 * 的参数传递给函数 - 其他参数被忽略:
f: (a, b:2, c:3) => a + b + c
f() >> error!
f(10) >> 15
f(10, 11) >> 24
f(10, 11, 13) >> 34
f(10, 11, 13, 14) >> 34如果你毅然离开 () 运算符,然后表达式对函数本身进行评价。 这对于传递函数来进行更高顺序的功能编程很有用.
点调用
通常将一个函数的结果吹向另一个函数,以建立类似Unix的管道。 | 操作员。 考虑以下表述:
c(b(a()))在上文的表述中,我们评价 a,然后将结果传递给 b,转而传递给 c。我们可以使用 . 点调用操作员 :
a().b().c()
a().b.c // may omit parens if no arguments还有一些例子:
year(today()) >> today().year
toStr(year(today())) >> today().year.toStr
hisRead(readAll(kw), 2009) >> readAll(kw).hisRead(2009)轨迹蓝巴达
许多功能被设计为将其他功能作为参数. 这些函数称为更高顺序函数. 举个简单的例子:
// default is to sort by alphabetic order
list: ["cape", "ape", "batch"]
list.sort >> ["ape", "batch", "cape"]
// we can pass a function to sort by string length
list.sort((x, y) => x.size <=> y.size) >> ["ape", "cape", batch"]这种模式是如此的常见,以至于代码可以开始与嵌入式括号(much like Lisp code)混淆. Axon借用了Fantom和Ruby的图案, 允许您在括号外拉出最后的争论,
list.sort() (x, y) => x.size <=> y.size >> ["ape", "cape", batch"]请注意,在使用点名时,您必须包含空 () 在指定 Lambda 参数前先解析 。 这个规则的例外是,如果lamdba采取一个单一的争论,没有parens:
list: [3, 4, 1]
list.map x => -x // ok
list.map() x => -x // ok
list.map() (x) => -x // ok
list.map (x) => -x // not ok更多实例:
eachDay(2010-07, day => echo(day)) // inside parens
eachDay(2010-07) day => echo(day) // trailing outside parens部分应用
当调用函数时, 您可以使用 _ 符号来执行 * 部分应用程序 *。 部分应用的函数对另一个函数进行评价,并附带一个或多个参数。 例如:
add: (a, b) => a + b
inc: add(_, 1)
inc(3) >> 4在以上的例子中, add(_, 1) 创建了一个新的函数, 其中有一个参数基本上是此的快捷键 :
inc: (x) => add(x, 1)块
显然是任何表达式,您都可以声明一个块,这是按顺序评估的一系列表达式。 块与 do 和 end 关键词 区块最常用于羊肉:
f: (a, b) => do
c: a + b
c*c
end
f(3, 4) >> 49 which is (3+4) * (3+4)整个块评价到最后一个表达式,或者您可以使用 return 短路关键字并立即返回一个值 :
f: (x) => do
if (x < 10) return "small"
if (x > 90) return "big"
"medium"
end
f(3) >> "small"
f(93) >> "big"
f(33) >> "medium"请注意: return 从最内侧函数返回,不一定是顶级函数。
若的各种问题
这个 if 表达式仅在给定条件属实的情况下用于评价块。 您也可以使用一个 else 如果条件不正确,则对块进行评价。
就像Axon的一切一样 if 它本身就是用来评价真实条款或虚假条款结果的一种表达方式。 如果条件不实,没有其他条款,则如果表达式评价为无效:
if (5.isOdd) "odd" >> "odd"
if (4.isOdd) "odd" >> null
if (4.isOdd) "odd" else "even" >> "even"你可以用一个 do block 如果您需要评价如果表达式中的多个表达式:
if (cond1) do
...
end else if (cond2) do
...
end else do
...
end作为方便,你可以省略 end 关键词,如果它立即超越 else 关键字 :
if (cond1) do
...
else if (cond2) do
...
else do
...
end抛出
这个 throw 表达式用于提出例外。 Axon的例外情况是: dicts 拥有 err 标记标记和 dis 显示信件标记。 为了提出一个例外 throw 关键字之后的表达式,该表达式可评为 dict :
throw {dis:"deep doo-doo!", ts:now()}请注意, 您的字典可以定义您在例外处理中可能想要使用的任何标记 。 作为方便,您也可以使用字符串:
throw {dis:"deep doo-doo!"} // as dict
throw "deep doo-doo!" // convenience for above这个 err 标记是自动为您添加的。
尝试/ 批量退出
这个 try/catch 表达式用于祗固例外。 举个简单祗举:
try
doSomethingDangerous()
catch
handleProblem()您可以声明一个变量 太短 。
try
doSomethingDangerous()
catch (ex)
handleProblem(ex)试题和捕捉条款的正文可以是任何表达或一种 do 块。
try do
...
end catch (ex) do
...
end作为方便,你可以省略 end 关键词,如果它立即超过 catch 关键字 :
try do
...
catch (ex) do
...
end与Axon中的一切一样,尝试/捕获是评价结果的一种表达方式. 如果没有例外,那么整个尝试/捕获物表达式会评价到尝试体条款,否则表达式评价到捕获块:
result: try getCurVal() catch "bad val"在上文的表述中,如果 day没有例外, getCurVal,然后其结果分配给变量 result如果丢弃例外,则指定字符串“bad val” result.
缺陷
这个 defcomp 关键词定义了组件 。 报告详细讨论了各组成部分。 Comps 章节。
Axon 用法
字符串示例
共性工作与字符串炽热: capitalize(), contains(), decapitalize(), endsWith(), get(), index(), indexr(), isEmpty(), isTagName(), lower(), replace(), size(), startsWith(), toTagName(), trim(), trimStart(), trimEnd(), upper().
123.toStr >> "123" // convert any object to string
"num=" + 3 >> "num=3" // use '+' for concat
"hi world".isEmpty >> false
"hi world".size >> 8
"hi world"[5] >> 114 // unicode char for 'r'
"hi world"[3..-2] >> "worl" // get with range is slice
"root toot".index("oo") >> 1
"root toot".indexr("oo") >> 6
"hi world".contains("hi") >> true
"Abc".upper >> "ABC"
"Abc".lower >> "abc"
"a,b,c".split(",") >> ["a", "b", "c"]
"fooBar".capitalize >> "FooBar"
"FooBar".decapitalize >> "fooBar"
" xyz ".trim >> "xyz"
"abcd".startsWith("ab") >> true
"abcd".endsWith("cd") >> true
"foo bar".isTagName >> false
"foo bar".toTagName >> "fooBar"
"root toot".replace("oo", "a") >> "rat tat"日期/时间示例
与日期、时间和日期时间合作的共同功能: date(), dateTime(), day(), dayOfYear(), dst(), firstOfMonth(), fromJavaMillis(), hour(), isLeapYear(), isWeekday(), isWeekend(), lastOfMonth(), minute(), month(), now(), nowTicks(), numDaysInMonth(), second(), startOfWeek(), time(), toJavaMillis(), toTimeZone(), today(), tz(), weekOfYear(), weekday(), year(), yesterday().
now() >> current DateTime in local timezone
today() >> today's Date in local timezone
yesterday() >> yesterday's Date in local timezone
today() + 1day >> tomorrow
today() + 7day >> next week
now() + 1hr >> one hour from now
dateTime(2023-03-14, 0:00, "New_York") >> create DateTime from Date, Time, and timezone
now().date >> Date portion of DateTime
now().time >> Time portion of DateTime
now().tz >> timezone string name of DateTime
today().year >> four digit year
today().month >> month as number between 1 and 12
today().day >> day of month as number between 1 and 31
now().hour >> hour as number between 0 and 23
now().minute >> minutes as number between 0 and 59
now().second >> seconds as number between 0 and 59
today().weekday >> day of week as number between 0 and 6
today().isWeekend >> true if Sunday/Saturday
today().isWeekday >> true if Monday - Friday
today().firstOfMonth >> first Date of the month
today().lastOfMonth >> last Date of the month
now().toTimeZone("UTC") >> convert DateTime to different timezone
today().numDaysInMonth >> number of days in Date's month
isLeapYear(2024) >> is a leap year
now().dst >> is DateTime in daylight saving time
today().dayOfYear >> number 1 to 366
today().weekOfYear >> number 1 to 53
startOfWeek() >> 0 for Sun or 1 for Mon based on locale
now().toJavaMillis >> milliseconds since Unix epoch of 1970
now().toJavaMillis.fromJavaMillis >> convert back to DateTime槐树日期
与 Spans 和 DateSpans 合作的共同功能 : eachDay(), eachMonth(), end(), lastMonth(), lastQuarter(), lastWeek(), lastYear(), numDays(), pastMonth(), pastWeek(), pastYear(), start(), thisMonth(), thisQuarter(), thisWeek(), thisYear(), toDateSpan(), toSpan(), today(), yesterday().
today() >> any Date value may be used span
thisWeek() >> DateSpan for current week (locale based start of week)
thisMonth() >> DateSpan for current month
thisQuarter() >> DateSpan for current quarter
thisYear() >> DateSpan for current year
pastWeek() >> DateSpan for previous 7 days
pastMonth() >> DateSpan for previous 30 days
pastYear() >> DateSpan for previous 365 days
lastWeek() >> DateSpan for last week (locale based start of week)
lastMonth() >> DateSpan for last month
lastQuarter() >> DateSpan for last quarter
lastYear() >> DateSpan for last year
toDateSpan(2023-01-01..2023-02-28) >> DateSpan with two explicit dates
toDateSpan(2023-02-14) >> DateSpan for a day
toDateSpan(2023-02) >> DateSpan for a month
toDateSpan(2023) >> DateSpan for a year
toDateSpan(2023-02).toSpan >> Convert DateSpan to Span
toDateSpan(2024-02).start >> First day of DateSpan
toDateSpan(2024-02).end >> Last day of DateSpan
toDateSpan(2024-02).numDays >> Number of days in DateSpan
eachDay(2024-02) (d) => echo(d) >> Iterate the Dates in a DateSpans
eachMonth(2024) (d) => echo(d) >> Iterate the months in a DateSpanUri 实例
与URI合作的共同功能: uriBasename(), uriDecode(), uriEncode(), uriExt(), uriHost(), uriIsDir(), uriName(), uriPath(), uriPathStr(), uriPort(), uriScheme()
`/a/b/file.txt`.uriName >> "file.txt"
`/a/b/file.txt`.uriBasename >> "file"
`/a/b/file.txt`.uriExt >> "txt"
`http://host:81/a/b/file.txt`.uriScheme >> "http"
`http://host:81/a/b/file.txt`.uriHost >> "host"
`http://host:81/a/b/file.txt`.uriPort >> 81
`http://host:81/a/b/file.txt`.uriPathStr >> "/a/b/file.txt"
`http://host:81/a/b/file.txt`.uriPath >> ["a", "b", "file.txt"]
`/a/b`.uriIsDir >> false
`/a/b/`.uriIsDir >> true
`file name.html`.uriEncode >> "file%20name.html"
"file%20name.html".uriDecode >> `file name.html`列表示例
处理清单的共同职能: add(), addAll(), all(), any(), concat(), contains(), get(), each(), find(), findAll(), first(), flatMap(), fold(), index(), indexr(), insert(), insertAll(), isEmpty(), last(), map(), moveTo(), remove(), set(), size(), sort(), sortr(), unique().
x: [10, 20, 30]
y: ["chat", "apple", "bee"]
x.isEmpty >> false
x.size >> 3
x[2] >> 30
x[1..-1] >> [20, 30] // get with range is slice
x.first >> 10
x.index(30) >> 2
x.index(40) >> null
x.contains(20) >> true
x.fold(sum) >> 60
x.any v => v < 20 >> true
x.all v => v < 20 >> false
x.concat(";") >> "10;20;30"修改列表 100. 函数总是返回新的列表(原始列表不可更改):
x.add(40) >> [10, 20, 30, 40]
x.addAll([40, 50]) >> [10, 20, 30, 40, 50]
x.set(2, 99) >> [10, 20, 99]
x.insert(0, 99) >> [99, 10, 20, 30]
x.insertAll(0, [88,99]) >> [88, 99, 10, 20, 30]
x.remove(1) >> [10, 30]
y.sort >> ["apple", "bee", "chart"]
y.sortr >> ["chart", "bee", "apple"]
y.sort((a,b)=>a.size<=>b.size) >> ["bee", "chat", "apple"]
y.each s => echo(s) >> iterator
y.map s => s.size >> [4, 5, 3]
y.flatMap s => [s, s.size] >> ["chat", 4, "apple", 5, "bee", 3]
y.find s => s.size == 3 >> "bee"
y.findAll s => s.size <= 4 >> ["chat", "bee"]
y.moveTo("chat", -1) >> ["apple", "bee", "chat"]
[1,1,1,2].unique >> [1,2]命令示例
处理法令的共同职能: all(), any(), get(), each(), find(), findAll(), has(), isEmpty(), map(), missing(), names(), remove(), set(), dis(), trap(), vals().
d: {dis:"Bob", bday:1980-06-01}
d.isEmpty >> false
d["bday"] >> 1980-06-01
d["foo"] >> null
d->dis >> "Bob"
d->foo >> UnknownNameErr exception
d.has("bday") >> true
d.missing("bday") >> false
d.names >> ["dis", "bday"]
d.vals >> ["Bob", 1980-06-01]
d.dis >> "Bob"
d.dis("bday") >> "1-Jun-1980"
d.any v => v.isDate >> true
d.all(isDate) >> false
// iterate keys, vals
d.each((v, k) => echo(k + ": " + v))关于使用 [] 对 -> 见 Axon Language.
修改命令的函数总是返回新的命令( 原件不可更改 ):
d.set("person", marker()) >> {dis:"Bob", bday:1980-06-01, person}
d.remove("bday") >> {dis:"Bob"}
d.map v => v + "!" >> {dis:"Bob!", bday:"1980-06-01!"}
d.find v => v.isDate >> 1980-06-01
d.findAll v => v.isDate >> {bday:1980-06-01}网格示例
工作网格的共同功能 : addCol(), addColMeta(), addMeta(), addRow(), addRows(), all(), any(), col(), cols(), colNames(), colToList(), each(), find(), findAll(), first(), flatMap(), foldCol(), foldCols(), get(), gridRowsToDict(), gridColsToDict(), has(), isEmpty(), join(), joinAll(), keepCols(), last(), map(), meta(), missing(), removeCol(), removeCols(), renameCol(), reorderCols(), rowToList(), size(), sort(), sortr(), toGrid(), unique().
// create grid from list of dicts
g: [{dis:"Site-A", area:2300ft²},
{dis:"Site-B", area:3100ft²},
{dis:"Site-C", area:1950ft²}].toGrid
g.isEmpty >> false
g.size >> 3
g.has("area") >> true
g.missing("foo") >> true
g.meta >> grid level meta data
g.cols >> [ Col("dis"), Col("area") }
g.colNames >> ["dis", "area"]
g.col("dis").name >> "dis"
g.col("dis").meta >> meta data for column "dis"
g.col("foo") >> throws UnknownNameErr
g.col("foo", false) >> null
g.colToList("area") >> [2300ft², 3100ft², 1950ft²]
g.first >> {dis:"Site-A", area:2300ft²}
g.last >> {dis:"Site-C", area:1950ft²}
g[1] >> {dis:"Site-B", area:3100²}
g[-2] >> {dis:"Site-B", area:3100²}
g[0..1] >> slice to new grid of Site-A, Site-B
g.each(row=>...) >> iterate each row as a dict
g.foldCol("area", sum) >> 7350ft²
g.any r => r->area > 2000ft² >> true
g.all r => r->area > 2000ft² >> false修改网格的函数总是返回一个新的网格(原始的不可改变):
g.sort("area") >> sort by area column
g.sortr("area") >> reverse sort by area column
g.sort((a,b)=>...) >> sort with function
g.map r => r.set("area", r->area.to(1m²)) >> area ft² -> m²
g.find r => r->dis == "Site-A" >> find row where dis == "Site-A"
g.findAll r => r->area < 2000 >> grid with rows where area < 2000
rowToName: (r) => r->dis[-1..-1].lower >> func to map "Site-A" -> "a"
g.gridRowsToDict(rowToName, r=>r->area) >> {a:2300ft², b:3100², c: 1950ft²}
g.gridColsToDict(c=>c.name,c=>c.name.size) >> {dis:3, area:4}
g.addMeta({title:"Sites"}) >> adds grid level meta data
g.addColMeta("area", {dis:"Sq Footage"}) >> add column level meta data
g.addCol("areaM2") r => r->area.to(1m²) >> add new column which is area in m²
g.renameCol("area", "sqFt") >> rename column area -> sqFt
g.reorderCols(["dis", "area"]) >> force specific column ordering
g.removeCol("area") >> remove a column
g.removeCols(["area"]) >> remove a list of columns
g.keepCols(["dis"]) >> remove all cols except given list
g.addRow({dis:"Site-D", area: 4000ft²}) >> add new row to end of grid
g.addRows([{dis:"Site-D"},{dis:"Site-E"}]) >> add list of new rows to grid
g.unique("dis") >> grid with rows with unique values in dis col两个网格之间的连接可使用列名 join() or joinAll():
// create another grid
h: [{dis:"Site-A", tz:"Chicago"},
{dis:"Site-B", tz:"Denver"},
{dis:"Site-C", tz:"New_York"}].toGrid
// join g and h by the dis column
g.join(h, "dis")
// resulting join grid
dis area tz
------ -------- --------
Site-A 2,300ft² Chicago
Site-B 3100ft² Denver
Site-C 1,950ft² New_YorkRegex 示例
正则表达式可以通过 reMatches(), reFind(), reFindAll(),以及 reGroups() 函数。 通常您会想要使用原始字符串文字 r"" 避免逃避反弹:
// check match for entire string
reMatches(r"AHU-(\d+)", "AHU") // false
reMatches(r"AHU-(\d+)", "AHU-10") // true
// find substring in a regex
reFind(r"AHU-(\d+)", "Store-2") // null
reFind(r"AHU-(\d+)", "Store-2 AHU-3") // "AHU-3"
// find all substring groups in a regex
reGroups(r"(Clg|Hgt)-(\d+)", "foo") // null
reGroups(r"(Clg|Hgt)-(\d+)", "Hgt-7") // ["Hgt-7", "Hgt", "7"]
reGroups(r"(Clg|Hgt)-(\d+)", "<Hgt-7>") // ["Hgt-7", "Hgt", "7"]请注意: reGroups() 返回每个组合的字符串列表 ()。列表中的第一个项目总是整个匹配。
A袂使用 Java 正则表达式引擎 - 见 java.util.regex.Pattern 完整规格。
读取示例
您可以进行简单的询问来切片和骰子您的数据 :
site // find everything with site tag
site and geoCity=="Richmond" // find all the sites in Richmond
equip and siteRef==xxxx // find all the equip within a site with rec id xxx
equip and siteRef->dis=="Foo" // find all the equip within a site with dis tag "Foo"
ahu and siteRef==xxxx // find all the AHUs within a site
point and equipRef==xxx // find all the points within a piece of equipment以上查询为 readAll(filter)纽带 。一般来讲,大多数查询会开始一些过滤,从数据库中选择一组建议。
他的例子
为了进行时间序列分析,你通常会将一个或多个记录输入到 hisRead() 函数( 只有 SkySpark ) :
// get history data for the points within a given piece of equipment
readAll(point and equipRef==xxx).hisRead(2009-10-03) // for a single day
readAll(point and equipRef==xxx).hisRead(2009-10) // for a month
readAll(point and equipRef==xxx).hisRead(2009) // for an entire year
readAll(point and equipRef==xxx).hisRead(pastWeek) // for last 7 days
readAll(point and equipRef==xxx).hisRead(today) // for today
readAll(point and equipRef==xxx).hisRead(2009-10-01..2009-10-07)任何时间范围函数都可以与 hisRead() 包括 today(), yesterday(), thisWeek(), pastWeek(), thisMonth(), pastMonth().
一般来说,一旦获得超过一日的数据,你将需要使用卷起来将数据压缩成一个更可管理的卷. 滚滚很容易:
// find daily max of electrical demand for month of March 2010
readAll(kw).hisRead(2010-03).hisRollup(max, 1day)当你开始进行时间序列分析时,你会开始使用函数来映射,过滤和折叠你的数据:
// find all the days where daily max exceeded 200 KW
readAll(kw).hisRead(2010-03).hisRollup(max, 1day).hisFindAll(v => v > 200)
// find all the periods of time where zone temp was above 75
readAll(zoneTemp).hisRead(pastMonth).hisFindPeriods(v => v > 75)当你开始为某些图案写下 矿藏数据的函数时, 你会想开始将它们组合起来。 一个常用的技巧是 hisFindPeriods 然后计算交叉点:
(ahu, dates) => do
// find periods in cooling mode for this AHU
coolPeriods: read(cool and equipRef==ahu->id)
.hisRead(dates).hisFindPeriods(v => v)
// find periods in heating mode for this AHU
heatPeriods: read(heat and equipRef==ahu->id)
.hisRead(dates).hisFindPeriods(v => v)
// compute when AHU running in both heating and cooling modes
hisPeriodIntersection([coolPeriods, heatPeriods])
endDef 示例
与单位合作的共同职能: conjuncts(), def(), defs(), tags(), terms():
def(^site) // lookup def for site tag
def("site") // convenience for site(^site)
defs() // list all definitions
tags() // list only tag definitions关键实例
这个 pivot() 和 xqPivot() 函数用于将网格或命令列表转换成枢轴表。 枢轴表用于根据单元格值的分组和滚动汇总数据。 这些函数只在SkySpark中可用.
我们用一个名为“形状*”的字典来定义如何生成枢轴表:
- 行:用于行组的选择
- cols:用于列组的选定
- 单元格:用于滚动的选择和折叠功能
选择是标记名称的点缀路径,带有标记/ref标记的特殊处理. 例如,"目标Ref.site"将选择任何目标类型的站点ID. 如果目标有站点标记标记,则其目标id本身,否则其站点Ref的价值.
所有形状标记(rows, cols, cells)都可能是单一的Dict或Dict的列表. 条子必须拥有 select 标记选择器。
使用行组和相组组合来按其独特的组合组合卷. 例如团体 [geoState, geoCity] 将为地理状态和地理城市标记的每一个独特的组合建立一个分组。 任何缺少组选择者之一的输入行都会被默认跳过结果 。
行分组用于定义生成的网格中的汇总行. 至少必须定义一个行组标签 。 列组用于为每个独特的列组在结果中创建一列.
单元格定义一个给定分组的滚动标记 。 根据所选标签的名称使用默认折叠功能. 与 fold 标记 。 支持以下折叠名称:
sum数字min数字max数字avg数字count任意periodUnion: Str 基期64, 也必须有日期栏
我们的例子都使用还对输入网格:
geoState, geoCity, dur, cost
"VA", "Richmond", 10h, 40$
"VA", "Richmond", 4h, 12$
"VA", "Richmond", 2h, 6$
"VA", "Norfolk", 8h, 8$
"VA", "Norfolk", 11h, 7$
"CA", "Richmond", 3h, 5$
"CA", "Richmond", 4h, 4$一个简单的轴线,按地理状态来计算
pivot(input, {
rows: {select:"geoState"},
cells: [{select:"cost"}, {select:"dur"}]
})
geoState, cost, dur
"CA", 9$, 7h
"VA", 73$, 35h折叠成本为 avg、 min 和 max 的枢轴 :
pivot(input, {
rows: {select:"geoState"},
cells: [{select:"cost", fold:"avg"}, {select:"cost", fold:"max"}, {select:"cost", fold:"min"}]
})
geoState, cost_avg, cost_max, cost_min
"CA", 4.5$, 5$, 4$
"VA", 14.6$, 40$, 6$使用两个标记进行行组:
pivot(input, {
rows: [{select:"geoState"}, {select:"geoCity"}],
cells: {select:"dur"}
})
geoState, geoCity, dur
"CA", "Richmond", 7h
"VA", "Norfolk", 19h
"VA", "Richmond", 16h使用列组的枢轴 :
pivot(input, {
rows: {select:"geoState"},
cols: {select:"geoCity"},
cells: {select:"dur"}
})
geoState, Norfolk dur, Richmond dur
"CA", null, 7hr
"VA", 19hr, 16hr用于行,col,和单元格的dicts被传递到生成网格的列元中.
能源实例
大部分能量查询使用读/读全部管道到他的读函数开始生命:
// daily consumption data for all sites in Mar 2011
readAll(energy and siteMeter).hisRead(2011-03).hisRollup(sum, 1day)
// monthly consumption data for all sites in 2010
readAll(energy and siteMeter).hisRead(2010).hisRollup(sum, 1mo)
// raw demand data for all sites on 1 Apr 2011
readAll(power and siteMeter).hisRead(2011-04-01)
// demand data normalized by area (square footage/meters)
readAll(power and siteMeter).hisRead(2011-04-01)
.energyNormByArea
// demand data normalized by degree-day
readAll(power and siteMeter).hisRead(2011-04-01)
.energyNormByDegreeDay
// demand data normalized by area and degree-day
readAll(power and siteMeter).hisRead(2011-04-01)
.energyNormByArea
.energyNormByDegreeDay
// table of site dis and total kWh consumption for March sorted by kWh
readAll(energy and siteMeter).hisRead(2011-03)
.hisRollup(sum, 1mo)
.hisFlatten((kw,ts,his) => { dis:his->siteRef->dis, kw:kw })
.sort("kw")
// average daily profile of hourly demand peak across last month
readAll(power and siteMeter).hisRead(lastMonth)
.hisRollup(max, 1hr)
.hisDailyProfile(avg)构建
概览
大多数用户应该只使用预建 distribution然而,如果你想弄脏你的手和从源头建造,那么就遵循这个向导。
从源头建造Hexall需要下列项目:
- Fantom 语言和运行时间
- Project Haystack 核心定义
- Xeto 核心 Xeto 库
- Haxall 重播本身
所有这些项目均可在GitHub上查阅。
方图
在大多数情况下,最简单的解决方案是只安装最新版本的 Fantom 而不是从零开始建设:
- Download 并解析最新版本
- 见 Fantom Setup
- 确保
fanorfan.bat位于您的可 即使执行路径
在有些情况下,目前版本的Haxall repo需要从源头建造Fantom. 我们称这为 *bootstrap * 建筑,因为 Fantom 用来编译 Fantom 本身. 见 Bootstrap 详细章节。
海斯塔克项目
Fantom安装后,可以克隆并重建工程海斯塔克舱:
- 拉动 Haystack-Defs GitHub 还原
- 在 repo 根目录中设置空的“ fan.props ”
- CD给您
{haystack-defs}根目录并运行fan -version; 验证您的信封路径如下:haystack-defs (work), fantom (home) - 运行
{haystack-defs}/src/build.fan; 应建为{haystack-defs}/lib/
谢托
您还需要克隆 Xeto repo :
- 拉动 Xeto GitHub 还原
你不需要做其他任何 与这个repo。
哈塞尔
从源头构建 Haxall :
从 Haxall GitHub 还原
将“fan.props”设置在Haxall的雷波根中,并附此线:
path=/path-to/haystack-defs;/path-to/xetoCD给您
{haxall}根目录并运行fan -version; 验证您的信封路径如下:haxall (work), xeto, haystack-defs,fantom (home)设定环境变量
FAN_BUILD_JDKHOME指向您的 JDK 安装运行
{haxall}/src/build.fan; 应建为{haxall}/lib/
请注意,从这里描述的源头构建时,批量文件将输入 {haxall}/bin 无法直接使用。 相反,你可以使用 fan <pod> 像这样的发射器:
fan hx version变更日志(ChangeLog)
本章保留英文原文,便于检索。
Build 3.1.12 (26 Jun 2025)
- FolioFile API
- hxIO support for folio files
- Fantom build 1.0.82
- Xeto CLI main moved from xetoTools to xeto
- ActorContext base type for all context types
- Fix his collection for imcompatible units
- Task metrics renamed and added evalLastDur
Build 3.1.11 (9 Dec 2024)
- Filter comparison of Numbers now requires same unit
- Xeto fits/fitsExplain defaults to tags only, new graph option
- Xeto validation for refs and target types
- Xeto validation for choices
- Xeto validation for value using spec meta
- Axon remove support for Xeto spec definition within language
- Axon remove support for Xeto slot spec references
- Axon AST dict for calls renamed target to func
- Axon remove dot call support when first arg is filter #8099
- Axon support for interface based dispatch
- Axon support for interface static methods
- Axon support for interface constructors
- Axon move xetoReload to hx lib
- New Axon funcs: gridReplace, evalToFunc, isDuration, isHisGrid, quantile
- Updated elec phase tags - PH #1109
- New synthetic ontology - PH #1125
- Redesign Java reflection code to use double checked locking
- Add HTTP setting for maxThreads
- Support for escaped tag names with leading two underbars
- Tweak %RH conversion ratio to match %
- Tighten up Folio tag value storage checks
- Fix for using SHA384 and SHA512 for hmac
- Fix to prevent keys from getting added with an empty alias
- Support for adding EC Keys through the CLI
- Fix readById, readByIds to not return records with trash tag
- Mqtt: allow configuration of sessionExpiryInterval
Build 3.1.10 (23 Apr 2024)
- Xeto new LibRepo and LibNamespae APIs replace XetoEnv
- Dict.spec removed, must use LibNamespace.specOf
- Dict.map added
- Xeto Lib.top/tops renamed to spec/specs
- Xeto global slots
- Xeto ph-gen redesign
- Xeto enum types
- Xeto sys::TimeZone enum
- Xeto sys::Unit enum
- Axon func xetoEnvReload renamed to xetoReload
- Axon new funcs: choiceOf
- New Xeto lib lazily loading design
- Number and unit quantity validation
- Add option to stripUncommittable to keep mod tag
- Change setColMeta, addColMeta to silently ignore when column not found
Build 3.1.9 (1 Dec 2023)
- New ECMA class-based JavaScript design
- Actor queue overflow limits
- Xeto support for instance data
- Redesign how Xeto specs are mapped to Haystack dicts
- Allow tag names to start with underbar
- Rename Xeto function library from data to xeto
- Fix Trio to handle T and F for true/false
- Fix to use TLS 1.3 when supported
- New noSort flag to ioWriteTrio
- New funcs: nowUtc, ioCreate, ioReadXeto, ioWriteXeto
Build 3.1.8 (9 Jun 2023)
- Axon shell
- Integrate data and xeto APIs
- New data function library
- New hx funcs: isWatched
- Connector hook to learn without open
- Haxall new platform APIs
- Haxall rename of hxSerial to hxPlatformSerial
- New strReplace point conversion
- Support for batch hisRead/hisWrite HTTP ops
- Flush out more HTTP API examples (in both Zinc and JSON)
Build 3.1.7 (17 Feb 2023)
- Add point conversion: boolToNumber
- Haystack 3.9.13
- New funcs: firstOfMonth, lastOfMonth
- More axon code examples in function docs
- Add session setting for maxSessionsPerUser
- Conn framework closed connector is onPing raises exception
- Conn support for multiple connectors per point with connPointsVia
- Date format support for quarter pattern
- Ephemeral tasks now linger for debugging
- Axon Usage more examples
- MQTT several bug fixes
- Ensure actor pools are always stopped on exit
- Dict each iteration is now always non-nullable
Build 3.1.6 (15 Nov 2022)
- Maintenance build
- Add opts to toMatrix()
Build 3.1.5 (5 Oct 2022)
- Support for Haxall developer program licensing
- Cloning support for cloneSyncHis
- New funcs: addCols
- More Axon examples in core functions
- Rework Number.toLocale pos;neg pattern behavior
Build 3.1.4 (4 May 2022)
- New design for choice defs
- New axon funcs: filter, parseSearch, refProjName, reFindAll, clamp, connPoints
- Axon string support: each, eachWhile, any, all
- Move toSites, toEquips, toPoints, equipToPoints to point ext
- Move matchPointVal, toOccupied to point ext
- Modify toPoints function to not implicitly call equipToPoints
- Fix toOccupied to not match other equipment's points
- Add toSpaces, toDevices functions
- Fix boundary conditions to maintain conn watch state
- New maxSessions setting
- Grid sort, reorderCols methods/functions ignore unknown cols
- Add user field to obsHisWrites observations for auditing
Build 3.1.3 (24-Feb-2022)
- New hxConn connector framework
- New MQTT connector
- New Nest connector
- New Ecobee connector
- Open source Haystack connector
- Open source SQL connector
- Open source Modbus connector
- Open source oBIX connector
- Open source Sedona connector
- Redesigned hxSerial library replaces serialMod
- New connector documentation in docHaxall
- Refactor dateSpan, span coercion in Axon
- New hx stub command line tool
- New hx crypto command line tool
- New funcs: streamCol, isNull, isNonNull
Build 3.1.2 (17-Dec-2021)
- New hxCrypto lib for arcbeam integration
- New hxClone lib to provide clone server
- New hxPy lib for python docker integration
- New docker API
- New mqtt API
Build 3.1.1 (8 Sep 2021)
- Initial release
comps (帮助)
概览
comps是专门执行软件组件的Axon功能. 组件与其他 Axon 函数一样被宣布,但通过 defcomp 关键词 与直线函数不同,组件 do ** not**有参数或返回值. 相反,数据是使用 * cells * 向组件输入和输出。
单元格以任意元数据命名变量。 单元格可能从外部获取/设置到组件,并用于输入/输出数据到组件的逻辑. 在组成部分之内 do 块单元格被访问并指定为普通变量。
语法
构成部分的定义与 defcomp 关键词,然后是零或更多单元格定义和a do 块。 下面是一个简单的组件,可以归纳两个输入数字:
defcomp
inA: {is:^number, defVal:0}
inB: {is:^number, defVal:0}
out: {is:^number, ro}
do
out = inA + inB
end
end这个 defcomp 关键词开始定义 。 然后我们定义三个命名的细胞 连同它们的元数据。 单元格元被定义为一个Dict字元,其中所有值必须是字元(不能是一个表达式). 这个 do 块执行我们的计算逻辑。 内部 do 屏蔽单元格作为普通变量访问。
方图
以下API用于在Fantom中与Axon组件合作:
Comp: 组件实例CompDef: 构成部分的定义CellDef之间的互动AbstractComp: Fantom 执行组件的基类
您可以在 Fantom 中执行自定义组件 HxLib您的扩展名中必须包含 Fantom 组件,包括与您的 pock 元数据相连接的下列线条刊物 。
meta = [...
"skyarc.fantomComps": "true",
]构成部分如下:
- 下级:
AbstractComp - 以
Axon函数元的方位a - 装有一台的申报建筑商
Obj参数 - 宣布囚室为公共场所
Cell侧 - 这只是一个问题:
onRecompute
以下是一个简单的例子:
@Axon { meta=Str<|dis:"Example Add"|> }
class ExampleAddComp : AbstractComp
{
new make(Obj init) : super(init) {}
@Cell { meta=Str<|dis:"A"|> }
Number a := Number.zero
@Cell { meta=Str<|dis:"B"|> }
Number b := Number.zero
@Cell { meta=Str<|ro|> }
Number? out
override Void onRecompute(AxonContext cx)
{
out = a + b
}
}注:组件的方应当是改变的,目前只有SkySpark提供。
通话
Comps可以称为正常功能. 与输入单元格一起传递命令 。 该函数将运行组件的 do块, 并返回所有单元格的 Dict 。 如果用上面的"foo"来命名我们的例子:
foo() // yields {inA:0, inB:0, out:0}
foo({inA:2, inB:3}) // yields {inA:2, inB:3, out:5}注意,如果你没有通过 Dict 或省略单元格,那么它们将默认为无效或值为 defVal 标记 。
注 大家的语法有变化
连接线程
概览
连接器调谐是您可以支配的最重要的管理系统和网络性能的机制. 本章涵盖调试和调试连接器的工具:
我认为 图书馆层面的调试 settings
- 连接器级别调谐 conn tags
- 通过点级调制 connTuning
- 调试通过 details
- 调试通过 tracing
- 分配点数 poll buckets
设置
所有连接器都通过设置支持以下库级调制选项(库ext本身的tags):
- connTuningRef: 点调谐后退
- maxThreads: 线程池大小
最大线程
连接器框架使用 Fantom 构建 actor APIs跋涉 默认情况下,每个连接器库分配最大线程池大小为100个. 这意味着最多100个一类的连接器可以同时进行处理. 如果你有100多个连接器,这可能使连接器无法进入所需的处理。 应用 maxThreads 标记到您的库的 exter reg , 从而可以调整线程池大小 。 如果您希望您的连接器都具有并行的线程, 请调用此值来匹配您的连接器数 。 然而,有时谨慎的做法是平衡线程池大小,以避免过多的CPU和内存使用,因为线程具有显著的间接费用.
粘结标签
所有连接器类型都etta 支持每个连接器上使用以下标记的的默认数调制选项:
- connTuningRef: 点调谐后退
- actorTimeout: 等待超时的时间
- connLinger: 停留状态的时间
- connOpenRetryFreq: 重试打开的频率
- connPingFreq: 强制键的频率
下文将更详细地讨论每个标签。
演员超时
这个 actorTimeout 标记配置消息超时的时间。 这决定了当连接器忙于屏蔽I/O或处理先前消息时,在同步消息上对向演员进行屏蔽的时间有多长. 其最典型用途为: Conn.sendSync 许多 libs 用于连接器特定功能的方法, 如 haystackReadAll().
此与联合国系统的连接器( 包括所有基于 HTTP 的连接器) 连接器的所有套接字超时使用 。 具体来说,此超时用于配置 connectTimeout 和 receiveTimeout 用于基本的 TCP 套接字。
如果未明确配置, 超时默认为 1min 。
康灵格
这个 connLinger 标签配置持续时间以打开连接。 对于许多连接器来说,开放过程可能很昂贵. 例如,许多基于HTTP的协议需要几个请求来认证和获取一个会话. 因此,根据进入模式,连接器有可能在开放状态和接近状态之间发生碰撞。 林格林通过在向近州过渡前保持一段时间的连接开放来缓解这种情况. 例如,如果将 connLinger 超时配置为 1min 并按下连接器,那么连接器会在返回到近状态前被打开一分钟. 如果没有配置, 默认为 30sec 。
打开的RetryFreq
这个 connOpenRetryFreq 标签配置介于打开的重试之间。 一些应用程序如手表或MQTT订阅需要连接器被绑定打开. 如果连接器未能打开,那么系统会自动重试,利用这个频率定期打开连接. 如果不配置默认值为 10sec 。
康平弗莱克
这个 connPingFreq 标签配置在强制 pings 之间的持续时间。 连接器的默认行为是保持关闭,除非一个应用程序明确强迫它们打开. 如果将这个标记添加到连接器中,则框架会自动强制一个周期性ping. 它应该在您想要监控连接状态时使用,但没有任何观察点来保持其开放性.
康恩图宁
调制点行为的主要机制是通过 connTuning 配置记录. 使用 connTuning 记录存储以下点级调制选项:
- pollTime: 向curVal投票的频率
- staleTime: 过渡期
- writeMinTime: 用来节奏写作
- writeMaxTime: 定期重写
- writeOnOpen我也是 : 打开连接器时发出一个写
- writeOnStart: 启动时发布写入
还有一些连接器特定选项,例如:
- <bacnetCov>:启用COV订阅
- <bacnetCovLifetime>:COV订阅的租赁寿命
每个连接点都通过 connTuningRef 标记 。 此标签按以下顺序搜索 :
- 点调整
- 点的连接器建议
- 连接器分机建议
- 回落到连接器类型的默认调制
示例
id: @fast-poll
dis: "Fast Polling"
connTuning
pollTime: 1sec
id: @slow-poll
dis: "Slow Polling"
connTuning
pollTime: 30sec
id: @conn
dis:"Modbus Connector"
conn
modbusConn
connTuningRef: @slow-poll
dis:"Fast Poll Point"
point
modbusPoint
modbusConnRef: @conn
modbusCur: "reg1"
connTuningRef: @fast-poll
dis:"Slow Poll Point"
point
modbusConnRef
modbusPointRef: @conn
modbusCur: "reg2"在以上的例子中,我们有两个不同的调音记录。 "快速民意调查点"对快速民意调查配置有明确的ConnTuning Ref. 慢波点继承了连接器的 connTuning Ref. 在自己的项目中,您可以调试指定的连接符 details.
以下各节提供这些点级调制标签的附加细节.
时间
这个 pollTime 标记指定一个持续时间 编号是用于为 curVal。此标签只用于使用桶投票模式的连接器。 使用COV订阅模型的连接器会忽略这个值. 如果未说明,则默认值为10sec。
暂停时间
这个 staleTime 标记指定一个持续时间 。 curStatus 从“ok”到“stale”。 它确保用户和应用程序意识到数据可能不是新鲜的而在使用。 向 stale 的过渡是在满足下列所有而在条件时发生的:
- 要点
curStatus目前为“ ok ” - 要点是不在实例表中
- 上次成功读取时间超过停滞时间
请注意,我们假定表中的点数目前是最新的,即使它们最后的读取时间超过了停滞的时间。 这是因为订阅价值的改变可能不叫 updateCurOk 如果没有收到任何更改,则持续进行。 如果未说明,默认为5min。
写入最小时间
这个 writeMinTime 标记指定一个持续时间 。 例如,如果配置到 5sec,那么会发布不快于 5sec. 成功写完之后, 如果在 5sec 窗口内有任何写作尝试, 则会被排队为待写 。 5sec 过后, 最后一个待发信件会发给连接器的 onWrite 调用。 注意, 写入 MinTime 只在成功写入后才执行 。 如果连接器报告写入失败, 那么在后续尝试中不会执行写入 MinTime 。
写入最大时间
这个 writeMaxTime 标记指定一个持续时间 。 例如,如果配置到 10min, 那么如果在 10min 之后没有成功写入, 那么会自动为连接器的写入排程 onWrite 调用。 写入 MaxTime 在工程到达后才生效 steady state.
打开时写入
这个 writeOnOpen 当连接器从闭合到打开的过渡时,标记标记用于发布写入。 此策略通常用于远程设备存储仅使用RAM写入,在重写后需要重新写入.
写入开始
这个 writeOnStart 该死的,当系统启动时,标记标记被应用来发布一个写入. 该死的,如果省略的话,系统会压制初始优先级数组评价.
细节
连接器和连接器点都提供纯文本报告,用于调试我们称为details的调试. 您可以使用 connDetails() 函数。 在SkySpark中,您可以使用上下文菜单或连接器视图查看细节.
连接器细节
连接器详细报告包含以下各节:
- 摘要和配置
- 投票方式和投票桶分配
- 框架管理的国家变量(内存、民意测验时间等)
- 框架管理的临时标记
- 连接器特定的额外调试
- 当前的演员信息和线程细节
点细节
详细情况报告包括以下各节:
- 摘要和配置
- 向fooCur、fooWrite、fooHis提供信息
- 框架管理的临时标记
- 连接器特定的额外调试
- 观察点
- 现值状态
- 写作状态
- 历史同步状态
- 历史收藏状态
- 可写入优先级阵列
仅包括适用的章节。
追踪
连接器提供调试跟踪,包括:
- 所有发送演员信息
- 开放、打、关闭的生命周期过渡
- 民意调查要求
- 用于联网信息的连接器特定调试
在Sky嚼 Park中,Connector QQ Trace视图提供了调试连接器跟踪日志的工具. 您也可以使用这些 Axon 函数管理追踪 :
connTrace(): 查询跟踪日志connTraceIsEnabled(): 如果启用连接器追踪, 返回connTraceEnable(): 能够在连接器上跟踪connTraceDisable(): 禁用连接器上的追踪connTraceClear(): 清除连接符的跟踪日志connTraceDisableAll(): 在所有连接器上禁用追踪
要注意,微量日志是使用循环缓冲器存储在内存中的. 对于追踪其信息有效载荷的连接器来说,这会导致大量的内存使用。 追踪总是默认在启动时被禁用,必须每个连接器手动启用. 使用 connTraceDisableAll() 函数在调试会话后。
纸桶
最简单的连接器通过投票支持curVal同步. 当连接器 pollMode 定义为: buckets 然后框架自动将所有点数分配给*poling buckers *。 投票桶可以用来调节投票的分组和频率. 点通过它们独特的组合成桶 connTuningRef 标记 。 输入频率通过 pollTime 标记 。绯闻 注意两个不同的ConTuning regs 和同一个PermTime 仍然被作为两个不同的桶进行模型化.
示例
id: @tuning-fast-1
dis: "Fast Bucket 1"
connTuning
pollTime: 1sec
id: @tuning-fast-2
dis: "Fast Bucket 2"
connTuning
pollTime: 1sec
id: @tuning-slow
dis: "Slow Bucket"
connTuning
pollTime: 15sec
dis: "Point F1-A", connTuningRef: @tuning-fast-1
dis: "Point F1-A", connTuningRef: @tuning-fast-1
dis: "Point F2-A", connTuningRef: @tuning-fast-2
dis: "Point F2-B", connTuningRef: @tuning-fast-2
dis: "Point S-A", connTuningRef: @tuning-slow
dis: "Point S-B", connTuningRef: @tuning-slow以上配置将产生以下三个民意调查桶
Fast Bucket 1 contains F1-A, F1-B
Fast Bucket 2 contains F2-A, F2-B
Slow Bucket contains S-A, S-BBuckets在启动时自动交错,这样组别不会在完全相同的间隔时间被调查. 上面的例子,我们有两个桶 配置了1秒的民意测验时间。 系统会自动在1sec窗口内随机错开投票间隔.
盖子下面的桶子 被调查 onPollBucket 调用。 然而,并非所有的协议都支持批次读取。 因此,水桶民意测验仍可能要求个别点级读取请求。
连接器
概览
连接器框架为整合到外部系统提供了标准化模式. 它基于以下主要特点:
- ** 连结器躬**:代表连接外部系统或设备的记录
- ** Connector Point **: 连接器的代理点,用于同步当前值笙歌、可写值或历史 证明了这一点 — ** Tuning **: 一套广泛的配置选项,用于细调连接器的行为和表现
- Learning:许多连接器提供了用于点击UI工具的外部系统"行走"的机制 -体现出诊断**:框架包括详细的调试和网络跟踪工具
- ** Axon API**:用于访问协议特定功能的Axon函数套件
- Fantom API:一个Fantom API的套件,方便创建自己的自定义连接器
包含连接器
Haxall 提供了丰富的可使用连接器的库:
- Haystack:通过Haystack HTTP API(用于尼亚加拉)提供客户端连接
- MQTT: 连接到经纪人以发布和订阅话题
- SQL: 使用 JDBC 连接到关系数据库
- Modbus: TCP 和 RTU Modbus 协议的客户端支持
- oBIX: oBIX XML 协议的客户端和服务器支持
- Sedona: Sedona Sox 协议的客户端支持
- Ecobee: 连接到 Ecobee 自动调温器
- Nest: 连接到包括 Nest 自动调温器在内的 Google 智能设备管理
SkySpark 包含以下额外的连接器:
- BACnet: BACnet IP 的客户端支持
- OPC: OPC UA的客户端支持
- EnergyStar: 与 Energy Star 组合管理器集成
- SNMP: SNMP 的客户支持,通常用于信息技术设备
许多第三方连接器也可用于 StackHub。或者你可以建立你自己的 custom connector 与连接器框架 API 。
命名公约
的家庭联系人框架严重依赖命名公约, 爸 你能促进一致性 一切的家庭都基于图书馆的名称. 让我们看看 haystack 作为示例的连接符 :
haystackConn: 连接器的标记标记haystackPoint: 相关点的标记标记haystackConnRef:每个点上提及其父连接器haystackCur: CurVal 订阅地址haystackWrite: 可写入点的地址haystackHis:历史再一次同步的地址
同样的模式由连接器框架在所有连接器类型中执行. Museveni慰蠀 蟿慰谓 蟿慰谓 蟿慰谓 蟿慰谓 蟿慰谓 蟿慰谓 蟿慰谓 蟿慰谓 蟿慰谓 蟿慰谓 蟿慰谓 蟿慰谓 蟿慰蟼.
连接器
每个连接符实例都是在 Folio 数据库,用于模拟与远程系统的逻辑连接。 一个连接器可以模拟一个外部设备,服务器,或者数据库. 每个连接器都有独特的地址和通信协议与外部实体融合.
以下标记通常用于配置连接符:
fooConn: 特定连接器类型的标记标记conn: 需要标记标记uri: 大多数能够获得的连接器都使用此标签来设置远程系统的地址username: 当远程系统需要认证时password: 当需要认证时, 密码应存储在 password db 已于disabled: 禁用连接符的标记标签
下列瞬态状态标记由框架本身管理:
connStatus: 好,下,过错或禁用connState: 关闭、打开、打开或关闭connErr: 如果 conn status 显示错误条件, 错误消息- 可通过下列途径获得额外的调试细节: details
除了上面的标记外,每个连接器都砂根定义了自己关于远程系统/设备的元数据的标记. 数据在 ping 通常包括:
- 远程设备或软件的制造和模型
- 远程系统的硬件/软件版本
连接状态
每个连接器都保持开放/关闭状态,可通过 connState 标签 :
closed: 已办理手续, 已办理手续, 已办理手续, 已办理手续, 已办理手续。closing:连接器目前位于其内部onClose回调open: 连接器打开opening:连接器目前位于其内部onOpen回调
框架管理状态,然而开放的语义会因连接器类型而异. 对于基于会话的HTTP连接器打开,意味着已经获得一个会话,以便在呼叫之间重复使用. 对于TCP连接器来说,这意味着一个插座已经对远程系统开放. 对于UDP协议来说,可能仅意味着一个打键请求已经成功.
- 每个连接器分别通过
connStatus标签 :
ok: 连接是正常的fault: 配置问题down: 通信或网络问题disabled: 手动禁用disabled标签unknown: 状态尚未计算( 启动状态)
地位由框架管理;通常在 onOpen 调用成功或失败。 注意,该状态是常见的 ok 和 closed - 连接器不开放,除非应用程序讷于当前需要打开。
连接点
大多数连接器支持同步 point 数据 :
- cur:当前实时价值的订阅/定价
- writes: 写入输出
- history sync:时间序列历史数据的同步
连接点的模型如下:
- 必须遵守标准 point ontology
fooPoint: 将点作为特定连接器类型进行注释fooConnRef: 将点与特定的连接符连接fooCur: 远程系统地址以同步实时当前值fooWrite: 用于写入输出的远程系统地址fooHis:是通过远程系统地址同步历史时间序列数据
并不是每个连接器都支持所有三个特性,在这种情况下只使用适用的标记. 下文将更详细地介绍这些要点的每个特点。
点曲线
支持的点 current value 以 cur 标记 。 连接器可用于使用 curVal 标记 。 当前值同步发生两种方式之一:
- 手动同步
connSyncCur()函数 - 当点被放入时自动 watch
当前值同步的机制是每个连接器类型和协议所特有的. 然而,大多数连接器使用以下战略之一:
- 定期对监视点进行投票 -- -- 可由 pollTime 配置选项
- 你说的换值协议
正在使用当前值订阅的连接点将通过 curStatus 标签 :
ok: 一切都没事stale: 点的curVal不是新鲜数据fault: 配置或硬件问题 - 见curErrdown: 通信或网络问题 - 见curErrdisabled: 手动禁用点或连接符unknown:我们什么都不知道(启动状态)remoteFault: 远程系统中的点是错误remoteDown: 远程系统中的点已下线remoteDisabled: 远程系统中的点已禁用remoteUnknown: 听着 :
如果父连接器处于错误/失效状态,那么所有子点自动继承连接器状态.
框架将在5min后自动将状态从"ok"过渡到"stale". 這一次的調音可以通過 staleTime 选项。
将能够写出点
海斯塔克模型 writable points 与 writable 标记标记。 可写入点保持内部16级优先阵列 point library。通过 writeVal 和 writeLevel 临时标记。 有效的关卡可以通过连接器写入远程系统. 默认情况下, 当本地数组的有效值被修改时, 就会发布信件 。 但是有许多调制选项来控制写入时间:
- writeMinTime:油门写着
- writeMaxTime: 定期重发
- writeOnStart: 启动时强制写入
- writeOnOpen: 连接器转换为打开时强制写入
配置用于写入的连接点会通过 writeStatus 标签 :
ok: 上次写成功down:连接/联网问题-见writeErrfault: 配置或硬件错误 - 见writeErrdisabled: 手动禁用点或连接符unknown:我们什么都不知道(启动状态)
如果父连接器处于错误/失效状态,那么所有子点自动继承连接器状态.
下次 点历史同步
一些连接器支持历史数据同步,当基础协议提供了相应的特征时. 历史同步必须和 task library 页:1 connSyncHis() 函数。 初始历史同步后,此函数将使用 null 在上次同步后同步历史项目。
以下是一个将所有干草历史点每1小时同步 cá性工作的例子:
dis:"Sync Haystack Histories"
obsSchedule
obsScheduleFreq:1h
task
taskExpr: "readAll(haystackHis).connSyncHis(null)"为同步历史而配置的连接点通过 hisStatus 标签 :
ok: 上次同步尝试成功fault: 配置问题 - 见hisErrdown: 通信或网络问题 - 见hisErrdisabled: 手动禁用点或父连接器unknown:我们什么都不知道(启动状态)pending: 同步已排好并正在等待运行syncing: 连接器框架未使用
如果父连接器处于错误/失效状态,那么所有子点自动继承连接器状态.
对于不提供历史同步的连接器,您可以设置当前值订阅并使用本地 history collection
点转换
以下标记用于配置Folio存储的正态数据与连接器远程设备之间的转换:
curConvert: 从原始读值转换为 curValcurCalibration: 在更新 curVal 之前调整读值writeConvert: 将本地写入Val 转换为原始值以写入远程系统hisConvert(a) 与《公约》和《京都议定书》有关的其他事项
转换被指定为使用 point convert syntax.
例如,如果一个连接器以摄氏值提供温度数据,我们希望将其转换成华氏,那么我们可以将这个标记添加到我们的点上:
curConvert: "°C => °F"如果传感器需要校准,我们可能会进一步决定调整增减几度. 例如,如果我们希望在传感器读取的数值中加上2°F,那么:
curConvert: "°C => °F"
curCalibration: 2°F如果我们也为同一点写一个值,那么我们需要反转写入方向的转换:
curConvert: "°C => °F"
curCalibration: 2°F
writeConvert: "°F => °C"使用 details 以调试原始值和转换值。
多个连接器
标准做法是将一个给定点与一个连接点联系起来. 然而,为不同的协议指定一个点是可能的。 例如,一个点可以同时具有 bacnetConnRef (单位:千美元) sqlConnRef。当您以这种方式配置一个点时,默认行为是登录一个警告:
Duplicate conn refs: bacnet + sql [@xyz]如果您真想使用多个连接器,则必须应用 connDupPref 标记应优先使用连接符名称。 这是默认用于自动订阅手表和历史同步等操作的协议 :
dis: "Dual Conn Point"
point
bacnetPoint
sqlPoint
bacnetConnRef: @bacnet-conn
sqlConnRef: @sql-conn
connDupPref: "bacnet"您可以明确确定用于 connSyncCur() 和 connSyncHis() 通过使用 connPointsVia() 函数。
注:系统无法在添加/更改时更新其苍蝇上的查询表。 connDupPref 标记 。 您需要重启才能生效 。
密码学
概览
本章涵盖了TLS密钥和证书的加密和管理.
加密工具
使用 crypto 命令 hx 用于管理您的密钥和信任存储的工具。 以下各节说明如何将这一工具用于各种共同任务。 要运行 crypto 命令执行 :
# List all available crypto actions
hx crypto所有的东西 crypto 动作允许您指定安装目录。 此目录包含 crypto/ 您的安装目录 。
hx crypto <action> -d ./path/to/dir/HTTPS 密码
要配置 Haxall 以服务 HTTPS 连接, 您必须首先将您的服务器的私钥和证书链添加到加密密钥中, 您 ** must ** 使用别名添加到密钥中 https。您应该使用 crypto 用于此工具的工具:
// Get help on adding entries to the skyarc key store
hx crypto add -help
// Option 1) Import the private key and certificate from an existing key store
// (p12, pfx, jks)
hx crypto add -alias https -import keystore.p12
// If the store is password protected, use the -pass option to specify the password
// NOTE: the terminal/command prompt may interpret certain special characters in your
// password in a way that the import will fail indicating an incorrect password
// was entered. If this happens, change the password on the p12 file to something
// containing only alpha-numeric characters and try again.
hx crypto add -alias https -import keystore.p12 -pass "password"
// Option 2) Import the private key and certificate chain explicitly
hx crypto add -alias https -priv private_key.pem -certs publicCertChain.pem你是不是需要更新您的 https 证书, 您可以再次运行命令, 它会用别名覆盖条目 https 在键盘。
在将 https 条目添加到 keystore 之后, 您需要编辑 https for Haxall http ext scop 以打开 https 指定 https 端口 :
diff(read(ext == "http"), {httpsEnabled:true, httpsPort:8443}).commit如果您正在使用 SkySpark , 您可以为主机启用和配置 HTTP 设置视图中的 HTTPS 。
要启用 https 需要重新启动 。 如果每件事物配置正确, 您将在控制台中看到类似此的日志信件
[2016-06-09 09:03:12] <web> {} [info] http started on port 8080
[2016-06-09 09:03:12] <web> {} [info] https started on port 8443如果有问题装入 https 从密钥tore 中输入,您将看到类似此的错误消息 。 确定您用别名装入私钥和证书链 https.
[2016-06-09 09:05:20] <http> {} [err] Failed to obtain entry with alias 'https' from the keystore. Disabling HTTPS
[2016-06-09 09:05:20] <web> {} [info] http started on port 8080注:当运行 https 时,所有 http 流量都会重定向到 https 。
在 SkySpark 中, 您可以使用此程序通过 UI 上传您的私钥和证书 被用作 :
- 前往
Host应用 - 选择
Crypto视图 - 点击
Add Cert按钮 - 在对话框中指定密钥/证书捆绑的别名(例如:
https) - 然后粘贴在您的私人密钥和证书链的 PEM 编码中
信任的证书
Haxall为信任的证书维持自己的信托商店. 默认情况下,信托商店包括Java环境中的每一个信任证书. 如果您试图通过 SSL/TLS 连接到远程主机时正在获得此例外 :
sys::IOErr: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target然后,这意味着证书链中的一个或多个证书没有被信任的当局签署. 要信任任何SSL/TLS端点的所有证书( 如 HTTP, SMTP等) 使用 crypto 工具 :
// Get help on adding entries to the trust store
hx crypto trust -help
// Usage 1: Trust all certificates in the certificate chain for the given URI.
hx crypto trust -uri https://example.com
// Usage 2: Add all certificates in the given PEM file to the trust store
hx crypto trust -cert certToTrust.pem说明:在使用Hexall之前必须停止使用 crypto 工具。
在SkySpark中,您还可以使用以下程序:
- 前往
Host应用 - 选择
Crypto视图 - 点击
Trust Uri动作按钮 - 在对话框中输入要信任所有证书的SSl/TLS端点的uri。 异名可选.
- 点击Ok。
如果程序失败, 您可以尝试从命令行中信任 URI 。 crypto 工具,如上文所述。 使用这个工具可以让你更深入地了解它为什么会失败.
自定义连接符
概览
连接器框架的定义是 Fantom API 在 hxConn 舱。 框架处理所有复杂的细节, 用于线程、定时器和状态管理 —— 它将您的执行归结为一组回调 。 这个设计可以使您快速创建自己的自定义连接茹,并强制实施所有连接茹类的一致性.
API中的关键课程:
ConnLib:您的 HxLib 子类Conn: 一个连接器作为演员的模型ConnPoint: 连接器下的一个点的模型ConnDispatch: 回调处理的基础类ConnTrace: 用于在连接器中添加跟踪
步骤
要创建自定义连接符,需要以下步骤:
- 推出新的方图 pod
- 创建您的标签 definitions
- 创建您 ConnLib 子类
- 创建您的 ConnDispatch 处理回调的子类
提示: 使用 hx stub tool 以阻断自定义连接符所需的代码 :
hx stub -type conn acmeFoo取决于您想要支持的特性, 有几个额外的可选步骤:
- 执行 learn 树
- 执行点 curVal 有人支持我们
- 执行点 writes
- 执行点 history sync
- 提供更多的斧头 functions 您的连接器
所有标记和类名都将来自您的库名 。 您不能偏离命名惯例 - 框架期望您的标记和类名遵循标准命名模式 。
方顿波德
所有连接器必须被定义为 Fantom pod。您通常会有以下源级目录结构:
hxFoo/
build.fan
lib/
lib.trio
conn.trio
point.trio
fan/
FooLib.fan
FooDispatch.fan
FooFuncs.fan
test/
FooConnTest.fan保证你 build file 将连接符登记为 Haystack lib, 并使用下列行:
index = ["ph.lib": "foo"]缺陷
所有连接器都属于 HxLib 在芬托姆执行一个海斯塔克图书馆 您磐石连接器必须正式定义其连接器和点记录的全部标记 。
库定义应遵循标准 HxLib lib def:
// lib.trio
def: ^lib:foo
depends: [^lib:ph, ^lib:axon, ^lib:hx, ^lib:conn]
typeName:"hxFoo::FooLib"
doc: "My custom foo connector"定义您的连接器建议 :
// conn.trio
def: ^fooConn
is: ^conn
connFeatures: {learn, pollMode:"buckets"}
doc: "My custom foo connector"
---
defx: ^uri
tagOn: ^fooConn
---
defx: ^username
tagOn: ^fooConn
---
defx: ^password
tagOn: ^fooConn您在连接器上定义的标签将取决于连接端点所需的数据。 大多数需要认证的连接器将通过常规使用标记:uri,用户名和密码(如上所示目的).
这个 connFeatures 标记声明您所支持的特性 - 当您的连接器靴子时, 它会被框架内置 。 值必须是使用下列标记的嵌入式命令 :
如果您的连接器会支持点, 那么您还需要该点和相关地址标记的定义 :
// point.trio
def: ^fooPoint
is: ^connPoint
doc: "Point which synchronizes data via a foo connector."
---
def: ^fooConnRef
is: ^ref
of: ^fooConn
tagOn: ^fooPoint
doc: "Associate a point to its parent foo connector"
---
def: ^fooCur
is: ^str
tagOn: ^fooPoint
doc: "Current value address for foo connector points"
---
def: ^fooWrite
is: ^str
tagOn: ^fooPoint
doc: "Write address for foo connector points"
---
def: ^fooHis
is: ^str
tagOn: ^fooPoint
doc: "History sync address for foo connector points"地址标签及其值类型取决于您的具体协议 。 大多数连接器使用 str or uri 用于地址类型。
康恩利布
所有连接器必须创建一个子类: ConnLib。这里举一个例子:
using hx
using hxConn
const class FooLib : ConnLib
{
}在大多数情况下,这只是空的根码。 但有一些功能需要超载. 例如,如果想要将额外的调试添加到细节中,那么您将会覆盖 onConnDetails or onPointDetails 方法。 方法。 。
没有
所有连接器必须创建一个子类: ConnDispatch 处理回调。 任何可变状态 您的连接器管理应存储在本类中 。 此类的一个实例是: Conn 演员。
所有实施都必须处理下列回调:
onOpenonPingonClose
以下是一个简单的例子:
class FooDispatch : ConnDispatch
{
new make(Obj arg) : super(arg)
{
// must call super with opaque arg
// parent runtime and conn is available within your constructor
}
override Void onOpen()
{
// open your connector here
// raise exception if open fails
}
override Void onClose()
{
// close and cleanup goes here
}
override Dict onPing()
{
// ping the device and return a dict with meta data for conn rec
}
}学习
学习特征用于"行走"外部系统的本土数据模型,以发现哪些点是可用的. 要在连接器中添加学习:
- 界定
learn标记在您的终端connFeatures - 推翻
onLearn回调
学习参数是一个连接器特定标识符,用于保持远程系统数据模型树或图内位置的跟踪. 无效的论证表明有人呼吁学习树根. 每一个要学习的呼叫都取了参数, 返回树的这个级别上的项目的网格 。 如果某一项可以作为“文件夹”导航,那么它应该在 learn 栏。
如果学习项目支持映射到某个点, 那么您得出的网格应该包括标准点数据, 如 point, fooPoint, fooCur, fooWrite, fooHis, kind, unit等 类.
点曲线
当前值通过手动同步 connSyncCur() 函数 onSyncCur 调用。 这种回调可使用一批点数. 如果您的协议支持批次读取, 那么通常会最高效地同步整个批次 。
当点被放入时, 对当前值的连续同步进行管理 watch。监视状态由调用管理 onWatch 和 onUnwatch。两个调用都与一批分数配合。 通常有两个监视策略:
- 如果协议支持更改订阅值,则地图表/unwatch召回订阅/不订阅
- 如果协议不支持订阅,那么您的连接器应该使用民意测验调度器来定期对观察中的点进行投票. 投票点详细介绍于 polling 第骏节。
您的点最终会同步其当前值的方法很多:
- 有一次 - 一次 - One time
onSyncCur读 - 从
onWatch - 订阅价值变动活动发出的同步信息
- 定期 polling
在所有情况下,如果当前值被成功读取,那么连接器应该调用 updateCurOk Haystack 以当前值表示。 如果发现错误, 如地址错误, 请打电话 updateCurErr。这些方法管理 curVal, curStatus,以及 curErr 自动为您设置标签。
作为一般原则,如果有例外,读出一个要点,就打电话 updateCurErr 但除此以外, 特殊情况应采用下列例外类型:
FaultErr: 连接器正在正确通信, 但有一个配置错误 。RemoteStatusErr:远程点可以正确读取,但远程系统状态不是"ok". 例如,如果远程点是“残废的”,那么使用这个例外将本地点设置为“残废的”。
将能够写出点
可写分的标准行为由 point library 用于管理16级优先级阵列。 当它计算出新的有效水平时,框架会发出 onWrite 调用。 您的回调应该将新值写入远程系统, 然后调用 updateWriteOk or updatWriteErr.
下次 点历史同步
佑联协议如果支持历史时间序列同步,则执行 onSyncHis 调用。 ủار العدي العدي الكري العري البري العري العري العري العري الري العري الري الري الري العيري الري الديري ال . 那你必须回电 updateHisOk 包含最新数据或其他调用 updateHisErr 如果有错误。 框架会自动处理写给历史学家和管理您 hisStatus 和 hisErr 标记。
投票
投票(Polling)是连接器对正在观察的一组点的当前值同步的过程. 框架提供了两种不同的投票模式:1)人工和2)桶. 为了实现其中一项投票功能,增加了 pollMode 标签在 connFeatures 你的定义。
手动投票
当联系人希望自己处理所有投票细节时,使用人工投票。 例如,它被Haystack和oBIX连接器用来实施"为改变而投球"的设计模式. 框架援引 onPollManual 基于配置频率的调用。 要使用人工投票,您的库必须定义一个命名的标记 fooPollFreq:
def: ^fooPollFreq
is: ^duration
tagOn: ^fooConn
val: 5sec这个 val 标记确定未明确配置的默认频率。
Buckets 投票
巴基特投票是标准,内置战略,以实现大量点数的可金枪鱼,可伸缩的投票. Bucket民意测验详细描述于 Tuning 章节。 从执行的角度来看,你必须做的就是推翻 onPollBuckets 调用。 如果你不推翻这个方法,那么它会自动路径到 onSyncCur.
自定义消息
每个康恩案例都是一个子类 Actor 接受以 HxMsg。内建信件通过各种调用方式被传送到您的 ConnDispatch 子类。 但您也可以添加将发送到 onReceive 调用。 此调用只接收框架未处理的信件 。 因此,在使用此特性时,请确保使用与内置消息类型永不冲突的名称;一个好的技术是用您的库名前缀您的所有消息ID. 第 14 条 onReceive 而不是被记录下来 而是被未来提升到召唤线
斧头真菌
如果您希望提供您连接器特有的 Axon 函数, 请使用 a 的标准模式 FooFuncs 课。 典型的模式是查看您的 ConnLib , 然后用它来解决 Conn 和 ConnPoint 的发送实例 。 以下是一些示例代码:
class FooFuncs
{
** Example connector function
@Axon { admin = true }
static Future fooConnSomething(Obj conn)
{
cx := HxContext.curHx
lib := (FooLib)cx.rt.lib("foo")
c := lib.conn(Etc.toId(conn))
return c.send(HxMsg("connSomething"))
}
** Example point function
@Axon { admin = true }
static Future fooPointSomething(Obj pt)
{
cx := HxContext.curHx
lib := (FooLib)cx.rt.lib("foo")
p := lib.point(Etc.toId(pt))
return p.conn.send(HxMsg("pointSometing", p))
}
}组合
概览
Folio 是用于存储和组织您数据的标记数据库 。 标记模型可以方便地设计自由形式,动态的数据模型. 但它也提供头等舱支持 Haysatck ontology 这样您就可以以标准化和可重复使用的方式模拟您的数据。
Folio将数据组织为三级层次:
- Projects:或"projs"是组织中用来将记录组合在一起的顶级单位(典型的对应现实项目). 项目封装一个平面记录列表,Folio中没有预定义的树结构或表格.
- Records:或"recط"是数据建模的基本单位. 记录基本上是由平面标记图定义的关联数组
- Tags:标记是模型的叶片水平. 标签是一个名称/ 值对
项目
在洽谈时, SkySpark 服务器可能托管多个项目. 项目用来将记录组合成一个单一的"数据库". 以下特征在项目一级运作:
- 数据库安全(虽然用户数据库在各项目之间共享)
- 备份
- 查询和过滤路径
项目必须使用法律名称。 programmatic name 并且必须是四个字符或更长。 保留了三个或三个以下字母的项目以及一些特殊名称。 为了利用云/集群支持,强烈建议对所有项目使用独特的项目名称。
项目实际储存在文件系统中,结构如下:
// haxall hosts one database under the "db" directory
{haxall-dir}/
db/ // database files
io/ // Axon I/O scratch directory
backup/ // backup zip files
// skyspark hosts multiple databases under the "proj" directory
{skyspark-home}/
var/
proj/
projA/
db/ // database files
io/ // Axon I/O scratch directory
backup/ // backup zip files
projB/
...在SkySpark中,项目名称由"var/proj/"目录下的目录名称严格定义. 当系统关闭时, 此目录可以重新命名或复制为新名称 。
记录
记录或rec是Folio数据库中建模的基本单位. 所有记录都是海斯塔克的 dict - 名称/值对图。 分配给记录的标签是自由格式;您可以随时添加、更新或删除标签。 对于IoT数据,您应该按照标准来模拟您的命令 ontology.
有几个标签有特殊的含义:
id:所有记录都有一个需要的ID标记,并带有唯一识别记录的Ref值mod: a DateTime timetamp 表示上次修改记录的时间戳;该值用于 concurrency controldis: 显示名称是一个可选的标签, 它应该用于最终用户所看到的任何模型数据记录; 这是作为记录的“ 标题” 和与记录链接的默认标签; 另请参见Etc.dictToDis
标记
标记是存储在记录中的名称/值对. 标签名称必须遵循标准规则 Naming。标记值是海斯塔克的任意一个 kinds: 下列斜拉线类型之一:
Marker:表示标记仅用于标记记录;标记通常用于将记录指定为"类型".Ref: Folio 记录的标识符Bool: 虚假的真实性Number: 64 位可选浮点数 UnitsStr: Unicode 字符串Uri: 每架RFC 3986通用资源标识符Date: 标准 Fantom 日期类Time: 标准日班时数DateTime: 标准 Fantom 时间戳加时区Bin: 文件系统存储的二进制流数据Coord: 纬度/经度地理坐标XStr: 扩展类型字符串List: 0 或多个项目的线性序列Dict: 嵌入式名称/值对Grid: 筑巢瓮二维网格
请注意,虽然Boul是得到支持的,但惯例是使用标记标记的存在。
储存
Folio坚持数据到磁盘,但作为一个基于内存的数据库运作. 记录在启动时从磁盘中读取,并存储在RAM中,以便快速访问. 这种设计支持传感器数据的实时性质.证明了这一点,但是它也对Folio施加了限制,因为大多数硬件的RAM比磁盘空间要少.
命名
程序名称被Folio用于以下特征:
- 作为标签名称
- 作为网格列名称
- 作为项目名称
- 作为
name标签
Programmatic names 使用情况 camelCaseNaming 也可使用下列方法:
- 第一个字符必须是ASCII小写臀部字母:
a-z - 其他字符必须是ASCII字母或数字:
a-z,A-Z,0-9,或_
查询
有助于促进对数据库的查询。 filters。过滤器允许您使用基本的布尔逻辑和比较操作符构造上游。 过滤器支持路径: 任何带有Ref值的标记都可用 -> 查询操作期间的操作员。
支持下列类型的查询:
readAll(): 查询所有匹配过滤器的记录read(): 查询一个匹配过滤器的单一记录readById(): 由 id 优化搜索
索引
对Folio项目的所有查询都采取上游形式 Filters 用于匹配一组记录。 在最简单的情况下,对项目中的每个记录进行扫描,并与过滤器核对,以便匹配。 因为记录"存储在RAM中"这个操作非常快;一个运行中的服务器,可以大致扫描每毫秒10K记录. 这次会用你的数据库线性地放大。
ظار المل المل العير الحير العدي العبر العي المري العري الحري العري الري الري الري العري الدي الري الديي الديري ال .ظار العب العب ال الحير البر العب العب العر العب العر الحر الع ال العر الع العي .
查询优化器
为了随着记录数量的增加而优化性能,Folio会为"热"标记建立并维持基于内存的索引. 查询优化器使用这些索引来避免扫描每个记录. 让我们举个例子查询:
site and equipRef==xxxxx50 这个查询有两个标签被使用: site 和 equipRef。如果 site 我们只关心如果一个dream有标签(我们不在乎它的值). 在涉及 equipRef 我们关心的是,如果一个 reg 有标记 * 和 * 它有特定的Ref值。 Folio索引处理两种情况:它索引了哪些记录有标记,但也分索引了标记值。
查询优化器会选择用于扫描的最佳索引. 如果我们有三百块的标签 site (无论值多少)我们有15个带标签/值对的矩形 equipRef==xxxx,然后查询优化器将选择最小的索引。 在这种情况下,它将选择 equipRef==xxxx 在实施一项结果之前 我们只需要扫描15件
自动索引
您不需要配置使用索引的特殊内容 。 Folio总是跟踪查询所使用的标记. 一旦检测到"热 咱们"标记,它就会自动为这个标记建立索引. 当前的算法在100个查询中使用了标记后会索引一个标记 。
查询图宁
对一个有上百万个矩体的大型数据库进行全面扫描可能需要10毫秒。 这可能足够快,可以充斥用户界面屏幕,但分配到多个功能时,可以快速加起来. 因此,如果与大型数据库合作,必须确保热查询使用该索引。
分析 结果查询的最佳方式是Debug QQ Folio视图,该视图提供了大量关于标签索引和查询运行的统计数据.
“按标签阅读”一节列出了所有已分析的用于优化的标签的统计数据:
- tag:查询过滤器使用的标签名称
- numReads:在标签符合指数优化条件的查询中使用此标签的次数
- ** 已索引**:如果已索引,则带有此标签的矩的数量
“按计划阅读”列出了查询优化器选择的所有查询计划的统计数据:
- 计划:计划和标签名称类型
- numReads:反对使用计划的次数
- ** 总时间**:使用平板电脑运行总时间
- avgTime:运行这个查询计划平均需要时间
注意当前查询优化器无法使用NOT过滤器和OR过滤器的索引. 实例:
ahu and not rooftop // can use ahu index, but not rooftop index
ahu or chiller // will be unoptimized
equip and (ahu or chiller) // will use equip index抖动
Folio数据库的修改被封装为diffs. Diffs是一组适用于记录的更改. Diffs的工作就像一个版本控制系统中的补丁文件一样. diffs包括能力:
- 添加或删除记录
- 更新/在现有记录上添加一组标签
- 删除现有记录上的一组标记(使用特殊标记)
remove值)
在Fantom diffs中,与 Folio.commit 方法 :
db.commit(Diff(rec, Etc.makeDict1("change", "new-val")))在Axon diffs中,使用 diff() 和 commit() 函数 :
commit(diff(rec, {change:"new-val"}))瞬变 diffs
一般来说,当一个diff被执行时,它会被写入文件系统以进行耐久性. 但是,如果你的应用迅速改变实时数据,这会造成严重的性能问题。 为了支持实时数据,Folio支持transition diffs的概念. 仅对 in-memory 表示记录,但不序列化为文件系统。 临时 diffs do ** not ** 更新 mod 标记记录。
废物
记录通过添加 trash 标记标记。的决议和任何其它记录一样在数据库中继续运行,除非明确无误,否则不包含在任何已读操作中 {trash} 选项。
货币控制
所有记录都钳制所需 mod 标记表示其最后一次持续修改的时间戳。 这个时间戳被用来实施乐观的货币控制. 这个模型允许查询和diff操作而无需明确锁定. 在构建diffs时,它们被传递到被读取的矩形版本. 如果在 diff 承诺 期间, 数据库检测到记录自上次读取以来已被修改, 那么承诺失败了 。 ConcurrentChangeErr.
diffs支持能力 强制 * 承诺通过货币控制。 在特定应用程序完全控制下的状态标记更新时,通常使用此功能. 使 diffs 不更新 mod 但是,除非使用武力旗帜,否则仍要检查它们是否同时更改。
密码
在一个项目中存储的密码有两种:
- 单向:用户账户存储一个带盐味的单向散列密码,通常使用SCRAM密码散列 -双向:很多 connectors 需要密码来建立连接。 这些密码必须是双向的, 因为必须写, 但也以简单文字形式读回
在这两种情况下,散列或密码都是需要保护的秘密. 散列比较安全,因为如果损坏,很难计算原始密码。 这些秘密被存储在核心folio数据库之外的一个名为"passwords.props"的文件中. 把它们从folio数据库里拉出来 让他们更容易安全
用于保护密码文件的基本哲学是,只有写入访问权被授予VM进程外部(通过Axon和REST API). 密码的读取必须在过程内完成. 外部应用程序可能使用密码存储 passwordSet() 函数。 内部代码只能使用 Folio.passwords 方法。
备份
Folio支持在运行期间对项目采取备份的能力. 备份是一个zip文件,其中包括记录、采取必要行动和历史数据的原子拷贝。
- 方顿 API:
Folio.backup - 斧头API:
folioBackup()
Proj 梅塔语Name
每个腻胶项目都应该有一个 projMeta 标记 。 此记录用于存储工程宽度设置. 在 projMeta 建议上可以配置以下标记 :
dis: 如果您想要使用项目程序名称以外的字符串, 请显示项目名称doc: 项目摘要字符串- steadyState: 国家稳定过渡的延迟
除了上面的标记外,系统还自动维护一个 version 标记在 projMeta 记录上(不修改此标记)。
图书馆
概览
图书馆或libs是哈塞尔建筑中模块化的基本单元. 所有自来水都是合适的 Haystack libs 包装为 Fantom pods.
Libs将以下一个或多个特性包成一个命名的,版本化的模块:
- 海斯塔克的标签
- SkySpark 用于应用程序、视图
- Fantom HxLib课程,以进行背景处理
- Axon 函数
仅发布诸如de淇s和函数等声明性数据的libs称为resource libs. 资源libs不能包含任何Fantom代码,但能够在运行时被热重装. 包含一个或多个 Fantom 课程的波德被称为 * fantom libs *. 所有 Fantom 自由必须定义 HxLib 子类。
邮箱
所有自由号必须装入方图 pod. Pods只是包含一个“ meta.props”文件,用于基本元数据。 任何zip文件都可以通过添加以下的"meta.props"文件来制作一个Fantom sock:
pod.name=acmeFoo
pod.version=1.0.9
pod.summary=Summary of pod
pod.depends=
pod.fcode=false"meta.props"使用的标准化字段有几十个,但上面的都是需要的字段.
大部分时间你都会用Fantom来建造你的舱 build 对不起拥护 您 对不起拥护者 :
acmeFoo/
build.fan
pod.fandoc
fan/
AcmeFooLib.fan
AcmeFooFuncs.fan
lib/
lib.trio
other-defs.trio
test/
AcmeFooLibTest.fanResource pocks将只包含带有定义的"lib/"目录.
建议使用 stub 生成新库源目录的工具。
构建文件
这个 build file 是一个负责任的 Fantom 脚本,用于将您的源代码编译为 pock 文件 。 以下是一个负责任的样本文件:
#! /usr/bin/env fan
using build
class Build : BuildPod
{
new make()
{
podName = "acmeFoo"
summary = "Foo is something awesome"
meta = ["org.name": "Acme",
"org.uri": "https://acme.com/",
"proj.name": "Cool Libs",
"proj.uri": "https://acme.com/cool-libs",
"license.name": "MIT",
"vcs.name": "Git",
"vcs.uri": "https://github.com/acme/cool-libs",
]
depends = ["sys 1.0",
"concurrent 1.0",
"haystack 3.1",
"axon 3.1",
"hx 3.1"
]
srcDirs = [`fan/`, `test/`]
resDirs = [`lib/`]
index = ["ph.lib": "acmeFoo"]
}
}资源闲置应省略 depends 和 srcDirs 字段。
其重要性在于所有libs用索引道具"ph.lib"来注册Haystack库名. 通过会议,一个名为“acmeFoo”的图书馆被包装成一个名为“acmeFoo”的pod。ظارم الك الك الك الك الك الك الك الك الك الك الك الك الك الك الك الك الك الك , المري , الك , الك , الك , الك , الحرار , , الورارار , , , , ال , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
缺陷
所有libs必须定义“lib/lib.trio”文件,将库注册为Haystack 4 lib:
// lib.trio
def: ^lib:acmeFoo
depends: [^lib:ph, ^lib:axon, ^lib:hx]
typeName:"acmeFoo::AcmeFooLib"
doc: "Foo is something awesome"Fantom libs 必须使用类 Qname 注册 typeName;资源 libs 应省略此行。
您可以另加包装 defs 在“ lib/” 目录下使用您喜欢的任何命名惯例。 特殊文件"lib/skyarc.trio"用于注册SkySpark仅defs - 它在非SkySpark运行时被忽略.
缩写
使用 Fantom 的 Libs 必须创建一个子类 HxLib。此类的一个实例是由运行时间创建的 。 lifecycle 调用以进行背景处理。
以下是一个简单的例子:
using hx
const class AcmeFooLib : HxLib
{
override Void onStart() { log.info("$typeof started!") }
override Void onStop() { log.info("$typeof stopped!") }
}生命周期
液化物的生命周期与总的 runtime lifecycle:
- 证据(构建者)
HxLib.onStartHxLib.onReadyHxLib.onSteadyState(见 Runtime)
停止自由生命周期是:
HxLib.onUnreadyHxLib.onStop
在即兴阶段,只有运行时间的基本信息. 您必须等待到在Start上访问服务并寻找其他的libs。
当你的解放正在运行, 你可以安排定期的房屋维护。 要使用此特性来覆盖 HxLib.houseKeepingFreq 导致定期召回到 HxLib.onHouseKeeping.
斧头真菌
Axon 搁浅函数可能作为三进制文件捆绑在“ lib/” 目录下 :
// funcs.trio
def: ^func:acmeFooAxon
src:
() => "hello world in Axon!"在Haxall, 您的功能必须使用 def=^func:name 以上所示语法. 那个 name 加号 func 不支持 SkySpark 使用的标记标记模式 。
Fantom Funcs (英语).
您也可以通过创建名为"<libName>Funcs"的类,在 Fantom 中执行您的 Axon 函数. Axon 函数作为公开的静态方法执行,附加说明 Axon 侧面 :
using haystack
using axon
class AcmeFooFuncs
{
** Fandoc is used for reference docs
@Axon
static Str fooAcmeFantom()
{
"hello world in Fantom!"
}
}在范托姆执行各项职能时必须考虑到安全问题。 Fantom funcs在Axon安全沙盒外运行, 所以上面的代码执行任何安全限制。 永远不要创建允许不受限制地访问基础OS的功能.
设置
提示在运行时间数据库中启用,并通过 ext 标记 。 此记录可以存储可通过下列方式访问的设置数据: HxLib.rec 方法。 您可以为您的设置创建静态键入类, 该类也被UI工具使用 :
- 创建一个子类:
TypedDict - 推翻
HxLib.rec自动返回您的类型
以下是一个例子:
const class TaskLib : HxLib
{
** Settings record
override TaskSettings rec() { super.rec }
}
const class TaskSettings : TypedDict
{
** Constructor
new make(Dict d, |This| f) : super(d) { f(this) }
** Max threads for the task actor pool
@TypedTag { restart=true }
const Int maxThreads:= 50
}这个 HxLib.onRecUpdate 当设置被修改时会引用回调 。
网页
要将 HTTP 处理添加到您的 lib 中 :
- 创建一个子类:
HxLibWeb - 推翻
HxLib.web与你的子类实例
这里有一个非常简单的例子:
const class FooLib : HxLib
{
override const FooWeb web := FooWeb(this)
}
const class FooWeb : HxLibWeb
{
new make(FooLib lib) : super(lib) {}
override Void onGet()
{
res.headers["Content-Type"] = "text/plain; charset=utf-8"
res.out.printLine("Foo hello world: $req.modRel")
}
}插入 URI 命名空间的 Libs 如下 :
- 海瑟尔:
/{libName} - 天空公园:
/api/{projName}/ext/{libName}
管理层
Libs可以在运行时启用/失效. 启用的 lib 保存在运行时数据库中 ext 标记 。 您不能直接添加或删除extئار ,而是必须使用专用的 API 。
Fantom 的 Lib 管理 :
HxRuntime.libs: 访问HxRuntimeLibsHxRuntimeLibs.list: 列出已启用的 libsHxRuntimeLibs.get: 按名称查找 libHxRuntimeLibs.add: 启用一个业已命名的 libHxRuntimeLibs.remove: 禁用一个 lib
Axon 的 Lib 管理 :
libs()名称空间中的已启用的 libslibAdd(): 启用名称的 liblibRemove(): 按名称禁用 lib
请注意,增加/取消自由必须正确,以确保满足自由依赖。
固执
这个 hx stub 工具将为一个新的库生成所有锅炉板码和结构. 在命令行上运行以下以查看选项 :
hx help stub可观察器
概览
观测到的子系统提供了一个处理同步数据流的框架。 可眷顾是一个命名的数据源,它产生一个无约束的Dict数据项,称为可依望. 观测者是订阅可观测数据流的数据消费者。 * 签署 * 是在观察者和观察者之间具有约束力的。 所有观察者在背景线索上使用一个 Actor.
所有观察者均以标记def命名. 以下为内置 observables:
- obsSchedule: 观察预定活动
- obsCommits:观察对Folio数据库的承诺
- obsWatches:观察记录的观察状态
- obsCurVals: 遵守瞬间承诺 curVal 和 curStatus
- obsHisWrites观察者写给历史学家
- obsPointWrites: 观察可写入点的更新
以下是仅可观测到的 SkySpark :
设立观察员有两个机制:
意 见
观测数据项称为烁视。 所有内在的观察者都把他们的观察模拟成一个诏令. 观测中使用的标记是可观测者专用的. 然而,所有观测都有下列标准标记:
type哔哩哔哩ts: 生成观测结果时在主机时区中的日期时间
任务观察
由 task ext 可以使用标记订阅可观察到的。 任务通过应用appropiate标记标记及其相关的配置 我们在操作时订阅了特定的可观察标记.
Fantom 观察
用 Fantom 写的 HxLibs 可使用 HxLib.observe 用于订阅可观测数据的方法。 订阅应在 onStart 调用。 如果 lib 未安装, 系统会自动处理未订阅 。
示例代码 :
const class AcmeLib : HxLib
{
override Void onStart()
{
config := Etc.makeDict1("obsScheduleFreq", Number(5, Unit("sec")))
observe("obsSchedule", config, #onSchedule)
}
Void onSchedule(Dict msg)
{
log.info("onSchedule: $msg")
}
}可观察器
以下各节详细列出内在可观测数据. 每个可观测对象定义了它观测命令中使用的一套自定义标记. 每个可观测到的还定义了一组用于调制订阅的配置标记.
时间表
这个 obsSchedule 根据可预知的时间表定期发生可观测火灾。
附表观测(事件)仅包括标准标记:
type:"ob表".ts: 生成调度事件的日期时间
这些是面向时间的配置标签( 只使用一个) :
obsScheduleFreq: 持续时间数以生成固定频率的事件。 此配置事件生成之间的频率, 该频率独立于观察者的执行时间 。 例如,10秒的频率需要3秒处理每个事件,每10秒运行,而不是每13秒运行。obsScheduleTimes: 列出一个或多个时间值,以根据主机默认时区在一天的某个时间生成事件.
你觉得这些是面向日期的配置标签:
obsScheduleSpan: 定义包含日期范围的 Span XStrobsScheduleDaysOfWeek: a Str 格式化为逗号分隔的周日列表:太阳, mon, tue, wed, thu, fri, satobsScheduleDaysOfMonth: Str 格式化为月份整数天的逗号分隔列表。 负日用于从月的最后一天开始索引. 例如 -1 表示月份的最后一日, -2 表示第二日到最后一日等.
您必须精确定义一个时间导向标记 : obsScheduleFreq or obsScheduleTimes;它们不能一起使用。 面向日期的配置标记可以一起用作逻辑的AND.
示例配置 :
// run every 5min every day
obsScheduleFreq: 5min
// run at midnight and at 2pm every day
obsScheduleTimes: [00:00, 14:00]
// run at 2am on weekdays only
obsScheduleTimes: [02:00]
obsScheduleDaysOfWeekdays: "mon,tue,wed,thu,fri"
// run at 2am on 1st and 15th of the month
obsScheduleTimes: [02:00]
obsScheduleDaysOfMonth: "1, 15"
// run at 2am on last day of the month
obsScheduleTimes: [02:00]
obsScheduleDaysOfMonth: "-1"
// run at 2am on first Monday of the month
obsScheduleTimes: [02:00]
obsScheduleDaysOfWeekdays: "mon"
obsScheduleDaysOfMonth: "1,2,3,4,5,6,7"如果观察者在队列中已经有消息,则会压制时间表观察. 如果观察者处理其消息的速度不够快,这就阻止了预定的事件排队.
提交
这个 obsCommits 使用可观测数据来监测对表格中一组记录的更改。 Folio 数据库。
提交观测结果包括下列标记:
type:提供资料ts: 承诺完成时的日期时间(与记录mod标签不同)subType:"添加,"更新","删除"id: 记录的标识符oldRec: 旧版本的记录命令或空命令如果添加的话newRec: 删除后记录命令或空命令的新版本user: 用户命令执行上下文或如果在上下文之外无效
这些是配置标记 :
obsFilter: 用于鼠标集的过滤字符串来观察或省略所有鼠标obsAdds: 标记以监听添加的事件obsUpdates:只是个新事件obsRemoves: 要为删除事件监听的标记obsAddOnInit: 启动时为所观察到的记录发射一个加号; 必须与 obsFilter 一起使用
您必须至少指定其中之一 obsAdds, obsUpdates,或 obsRemoves (通常你会使用所有三个)。 观测基于输入和退出所配置的过滤器集的记录而开火. 添加 trash 标记与删除整个记录一样。
以下是一个实例配置,该配置可观察到对以下列标记的记录的更改: conn 标签 :
obsAdds
obsUpdates
obsRemoves
obsFilter: "conn"在以上观察员中,如果加上 conn 标记到现有记录中,它将收到“添加”事件(与用该标记添加新记录一样)。 同样删除 conn 标记或添加 trash 标记将启动“ 取消” 事件 。 任何非短暂的 diff 记录 conn 标记将启动“ 更新” 事件 。
监视
这个 obsWatches 当记录进入和流出时,可以监听 watch。“监视”事件提供了一个正在进入监视状态(指明从0到至少一个监视状态)的矩形列表。 "unwatch"事件提供了一个正在退出被监视状态(向零监视器过渡)的 regs 列表.
观察观察包括以下标记:
type:"观察"ts: 过渡日期subType:"表或"无表".recs: 出入监视状态的密码记录列表
这些是配置标记 :
obsFilter: 用于鼠标集的过滤字符串来观察或省略所有鼠标
如果出现 obsFilter 指定,然后在订阅时将当前正在观看的匹配建议的初步清单开火。
曲线值
这个 obsCurVals 可观测到的瞬间监视器对 curVal or curStatus 标记 。 它能够创建基于事件的逻辑 cur points.
观测事件包括以下标记(遵循与obsCommittee事件完全相同的模式): 1.
type:"阴阳经".ts: 临时执行的日期subType审查工作: "更新"id: 记录的标识符oldRec: 旧版本的记录 dictnewRec: 记录文件的新版本
这些是配置标记 :
obsFilter: 用于鼠标集的过滤字符串来观察或省略所有鼠标
注意:对可观测数据谨慎行事,因为容易订阅过多的数据。 一般来说,你不应该观察到可能以高频变化的模拟点。 但是,这种可观察到的只应用于布林/埃纳姆的状态过渡。
他的笔迹
这个 obsHisWrites 观察历史数据的收听器会写到 historized point.
观测活动包括下列标记:
type:"丑闻"ts: 写历史的时间id: 记录的标识符rec: 历史count的最新资料span:实情Spanuser: 用户命令执行上下文或如果在上下文之外无效
这些是配置标记 :
obsFilter: 用于鼠标集的过滤字符串来观察或省略所有鼠标
注意: 只有标准历史书写有限定的跨度才会触发此事件 。 专门更新,例如: hisClear() 不一定会产生观察结果。
将能够写出点
这个 obsPointWrites 可观察到监视器更新到优先级数组 writable points。只有当 * 有效的 * 写值被修改时( 在中标级别) , 才启用事件 。
观测活动包括下列标记:
type的两项:ts身分: 写点的时间id: 记录的标识符rec: 点val: 有效写值level: 有效书写级别who: 用于发布写入的用户或应用程序的字符串first: 如果这是初始稳定状态写入, 标记标记
这些是配置标记 :
obsFilter: 用于鼠标集的过滤字符串来观察或省略所有鼠标
弧形
这个 obsArcs 可观测到的收听文档更改 arc lib.
火花观测包括以下标记:
type:"丑闻"ts: 活动时间arc: 与 有一个文档的网格 - 见arcRead()用于格式
这些是配置标记 :
obsFilter: 用于鼠标集的过滤字符串来观察或省略所有鼠标
闪光点
这个 obsSparks 观察者聆听了今天由 rule engine用于发现新火花后立即发出通知。
火花观测包括以下标记:
type:"烟雾"ts: 检测到火花时的日期时间spark: 火花的装饰
这些是配置标记 :
obsRuleFilter: 规则记录的过滤字符串obsTargetFilter: 目标记录的过滤字符串
观测到的只有检测到的今天的火花(基于主机的时区). 特定目标和规则的火花每天只观测一次。 然而,如果系统重启,那么当规则引擎再次运行时,火花将会被重新观测. 您可以通过 ruleReobserve() 函数。
活动
这个 obsEvents 监听新事件 event engine.
事件观察包括以下标记:
type:"丑闻"ts: 事件创建时的日期时间( 可能与事件 ts 不同)event命令事件
这些是配置标记 :
obsFilter: 为所有事件观察或省略的事件过滤字符串
运行时间
概览
所有 pearl子系统组件都被包成一个称为runtime的单顶级对象. 运行时间通过 HxRuntime API. (英语). 可以在单个JVM内运行多个运行时间.
守护进程
H有助于加强守护进程 是运行时间的默认执行. 守护进程通过 hx init 命令,并通过 hx run中包含这些命令。 Setup 章节。
您可以使用 Fantom力图定制 Deamon 的靴子 hxd::HxdBoot。这里有一个实例来说明使用自定义配置和需要的 lib :
boot := HxdBoot
{
it.dir = myDatabaseDir
it.config["serialSpi"] = "myPod::SerialSpiImpl"
it.requiredLibs.add("myCustomLib")
}
boot.run守护进程默认行为 :
等问题: 为数据库创建 HxFolio 实例
- 在运行时间数据库中存储用户的简单用户管理器
- HTTP API 与所有标准操作
- 简单的Axon贝壳作为UI
- 利用循环缓冲器提供基本的记忆史学家
Sky缔造和平 đó
SkySpark是多租户,支持多个项目运行时间. SkySpark 对主机执行一次运行 Sys.rt。每个项目也是全程运行。 Proj.rt.
SkySpark在主机级别上运行一些低级的libs作为SysMods. 例如,只有一个例子是: http, api,以及 user libs(由所有项目作为 SysMods 共享). 然后每个运行的项目都会运行自己的实例 * Exts * , 包括libs, 如 point, task, conn等 类. 由于这个原因,Haxall文档经常使用library/lib和扩展/ext互换的术语.
生命周期
运行时间生命周期定义了启动和关闭的多阶段系列. 启动阶段为:
- 证明: " 自由 " 被创建,但只有基本信息 坛网:libs可以查询服务和其他libs,启动背景处理
- 准备: 所有 libs 已完成在Start调用, HTTP 端口打开
- 稳态:在准备给定应用程序和连接器回旋时间后延迟
进行监督:
- 未就绪:关闭外部港口
- 停止:停止所有背景处理
当运行时进程被关闭时自动关闭 Env.addShutdownHook.
见 lib chapter 这些阶段如何映射到 HxLib 召回电话。
稳定状态
稳定状态是用来给系统时间 以达到平衡的靴子 之后的可配置延迟。 这使内部服务在与外部系统互动之前有时间旋转。 默认稳定状态延迟为10秒。 可通过应用 steadyState 标记到 projMeta 记录。
配置的延迟在数据库装入后开始, 所有库都已完成 。 onStart 和 onReady 召回电话。 一旦延迟结束, steady state 旗向真向转换,库接收 onSteadyState 调用。
- 在*稳定状态之前发生的活动:
- 需要当前点值的应用程序应初始化手表
- 连接者应开始投票/为观察点签字,以确保一旦达到稳定状态,数据就更新
- 在* 稳定状态之后进行的活动:
- 任务在稳定状态(包括预定任务)之前不订阅其可观测值
- 地方历史收藏被压制到稳定状态
- Connector 书写被压制到稳定状态,然后将所有可写分的当前书写水平广播给连接者,如果有的话,它们可能会发出远程书写。
writeOnStart调整策略已配置 所发挥的作用 — 规则引擎( 只有SkySpark) 在稳定状态后才能开始计算规则
设置
概览
Hexall作为简单的ZIP文件分发. 要安装它:
- Preinstall Java 在您的机器上
- Download 并解析最新版本
- Verify 通过运行安装
hx version - Initialize 使用新数据库
hx init - Run 您使用的新数据库
hx run - Login 你可以试试Axon
- Import 您可以播放一些数据
也见 Fantom Setup 其他细节。
预安装 Java
你必须把Java 8 或以后安装在您的系统上作为先决条件. 我们建议使用最新的LTS版本,即目前Java 17.
您应该能够测试 Java 从命令行安装如下 :
prompt> java -version
java version "1.8.0_301"
Java(TM) SE Runtime Environment (build 1.8.0_301-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.301-b09, mixed mode)确保使用64位版本的Java.
在Windows和Unix上, 70."bin/fanlaunch"脚本会选择Java的版本,通过以下规则从最高优先级到最低优先级: 1.
- FANQJAVA环境变量
- JAVAQHOME 环境变量
- 无论Java在你的默认路径
下载
新的Hexall蛊惑一般每隔六至十周发布一次。 发布时间表总是遵循SkySpark架构. 构建被包装成蛊惑文件并张贴在 GitHub.
下载最后的发布到您的本地机器并解析它 。 根目录将根据目前的构建版本命名为"haxall-3.1.xx". 这称为家目录,其结构如下:
{home}/ // home directory where you unzip
bin/ // batch and bash commands
lib/ // libraries
fan/ // fantom pods
java/ // sys.jar used to bootstrap Fantom
etc/ // configuration files你已经检查
解析分发后,您将在“ bin” 目录中找到可执行文件 。 在Unix上,你可能需要在贝壳脚本上调用"chmod +x". 考虑在你的道路上设置"宾"如下:
// Windows
set PATH=%PATH%;c:\haxall\bin
// Unix
PATH=$PATH:/opt/haxall/bin
chmod +x /opt/haxall/bin/*如果你不想把垃圾放进你的道路,那就确保你 cd 到主目录或本目录运行命令。 如果所有设备都安装正确的话,你应该可以运行 hx version:
haxall-3.1.3> bin/hx version
Haxall CLI
Copyright (c) 2009-2022, SkyFoundry LLC
Licensed under the Academic Free License version 3.0
hx.version: 3.1.3
java.version: 1.8.0_301
java.vm.name: Java HotSpot(TM) 64-Bit Server VM
java.vm.vendor: Oracle Corporation
java.vm.version: 25.301-b09
java.home: /Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre
fan.version: 1.0.77.3102
fan.platform: macosx-x86_64
fan.env.path: /stuff/haxall-3.1.3Hexall安装是标准Fantom安装的超级集. 如果你有什么麻烦,你看 Fantom Setup 来排除你的环境
输入
在您运行 Haxall 守护进程之前, 您必须初始化一个数据库 。 你用这个 hx init 命令。 数据库存储在目录中,然后成为运行时间的名称。 要创建一个名为“ demo”的新项目数据库:
haxall-3.1.3> bin/hx init demo
hx init [/stuff/haxall-3.1.3/demo]
su username> brian
su password> ######
su password (confirm)> ######
http port [8080]>
[06:26:25 16-Feb-22] [info] [init] Create projMeta
[06:26:25 16-Feb-22] [info] [init] Create lib [ph]
...
[06:26:26 16-Feb-22] [info] [init] Update httpPort [8080]
[06:26:26 16-Feb-22] [info] [init] Create su ["brian"]
Success!你可以重新运行 hx init 在同一目录上再次命令修改您的超级用户账户或 HTTP 端口。
运行
您可以使用 hx run 命令 :
haxall-3.1.3> bin/hx run demo
[06:29:06 16-Feb-22] [info] [web] http started on port 8080此时您已启动切口并接受 HTTP 向指定端口提出的请求 。 如果无法打开端口,则确保不使用同一端口的其他软件。 在unix上确保您使用大于1024的端口. 重运行 hx init 如果您需要改变您的端口。
登录
软件运行后, 您可以使用您的浏览器点击 http://localhost:8080/ (或任何你使用的端绅). 使用用户名和密码绅士配置的登录 hx init。默认情况下,您会被重定向到 Axon 壳的“ / shell ” 下。 您可以尝试一些基本的表达式来测试系统:
now()
libs()
pods()导入
你可以找到一些真实的世界 海斯塔克的数据 project-haystack.org。从 Axon shell 中,您可以使用以下表达式导入一个示例工程:
ioReadZinc(`https://project-haystack.org/example/download/alpha.zinc`).map(r=>diff(null, r, {add}).commit)这将在本地数据库中添加您可以查询的示例数据 。 在外壳中尝试以下表达式:
site
equip
ahu
read(ahu)->id
point and equipRef==read(ahu)->id
vav and airRef==read(ahu)->id流线
概览
流是Axon数据处理管道. 流不提供列表 好了 或 Grid 收藏类型 。 相反,流程定义了一系列处理步骤,这些步骤可以懒惰地处理数据。
让我们研究一个简单的例子来说明:
// without streams
readAll(site)
.map(s => s.set("area", s->area.to(1m²)))
.findAll(s => s->area > 1000m²)
// with streams
readAllStream(site)
.map(s => s.set("area", s->area.to(1m²)))
.findAll(s => s->area > 1000m²)
.collect(toGrid)两个代码片段运行以下管道:
- 阅读Folio的所有网站
- 转换
area标记到 m2 - 仅过滤面积大于1000平方米的地点
- 返回网格结果
我们可以看到,转换步骤使用 map() 和 findAll() 都一样。 但第一个例子是 readAll() 将所有网站作为网格同时读入内存。 然后我们打电话 map() 它在记忆中构建另一个不可改变的网格。 然后我们叫 findAll() 用于构建另一个网格的记忆。 所有这些中间网格造价相对昂贵.
或者,采用流化方法的效率要高得多。 我们从头开始 readAllStream() 与 readAll() 不构造内嵌网格,而是每次找到新的匹配记录时都会对流进行迭代。 要求 map() 和 findAll() 不要创建任何中间网格,而只是处理从Folio流出的物品. 最后一步明确收集了我们所绘制/发现的所有记录成网格——这是创造内在网格的唯一一步.
术语
我们在坛流 API 中使用了以下术语:
- ** 来源**:这是数据流的来源。 通常来源为内存收藏或呼叫
readAllStream()。所有源函数返回一个流实例。 - 转换:数据转换的中间流阶段。 所有变换功能各种不同都以一个流作为第一个参数并返回一个新的流实例.
- ** 最终**:收集或减少数据流的流的最后阶段。 终端函数以流为第一个参数,总是返回非流值.
- 上游:在特定阶段之前的所有阶段
- ** 下游**:特定阶段之后的所有阶段
- ** 绑定**:有明确终点的溪流;例如,来自内存收藏的任何溪流都以收藏的大小为界
- ** 无限制**:数据项可能无限流动的流。
- 背压:当源生成的数据比终端步骤所消耗的速度快时,自动停止处理的流
函数索引
流创建函数 :
- stream: 从内存收藏创建流
- streamCol消除歧视
- readAllStream: 带有过滤器的 Folio 流记录
- readByIdsStream: 来自 Folio 的流记录 by id
- ioStreamLines: 读取 Str 线条的流
- ioStreamCsv: 读取来自 CSV 的指令流
流变函数 :
- limit: 数据项数后的短流
- skip: 跳过流中数据项的数目
- map:将每个项目映射到新项目
- flatMap: 将每个项目映射为零或更多新项目
- filter笙歌
- findAll: 具有上游功能的过滤项
- addMeta: 添加网格级别元a
- setMeta: 设置网格级别元a
- addColMeta: 添加列级元a
- setColMeta: 设置列级元a
- reorderCols: 重排网格列
- removeCol: 删除网格列
- removeCols: 删除网格列
- keepCols: 保持网格列
连续终止功能:
- collect: 收集到内存列表
- each: 将所有项目重命名函数
- eachWhile: 以断开符标出项目召回函数
- first: 返回第一流数据项
- last: 返回最后一流数据项
- find: 找到不可或缺项目
- all: 如果所有项目都符合上游函数, 则返回
- any: 如果任何项目符合上游函数, 则返回
- reduce: 将流减少为单个累积值
- fold: 数字的折叠流
- commit: 向 Folio 数据库输入 diffs 流
- feed: 创建实时数据流以覆盖视图
函数细节
每个流线函数的细节.
添加 ColMeta 数据
这个 addColMeta() 函数在收集到网格时添加列级元。 标记与上游网格元合并. 此函数推断 collect(toGrid).
readAllStream(site).addColMeta("geoAddr", {hidden}).collect添加金属
这个 addMeta() 函数在收集到网格时添加网格级别元。 标记与上游网格元合并. 此函数推断 collect(toGrid).
readAllStream(site).addMeta({title:"Sites"}).collect全部
这个 all() 函数终止流并返回 Bool 表示所有项目是否与上游函数匹配。 溪流在第一个假匹配上是短路的.
[1, 2, 3].stream.all(v => v.isOdd)任何
这个 any() 函数终止流,返回 Bool ,说明是否有任何项目符合上游函数。 溪流在第一对真格上是短路的.
[1, 2, 3].stream.any(v => v.isOdd)收集
这个 collect() ′======================================================================================================================================================================================================= 收集将返回列表或网格,取决于 to 参数。 使用 toList() or toGrid() 函数如下:
(0..10).stream.collect(toList)
(0..10).stream.collect(toGrid)如果 to 函数被省略,然后使用下列规则自动选择默认值:
- 如果来源是
stream()网格脸谱,返回网格 - 如果任何溪流阶段操纵网格元,例如:
setMeta网格 - 如果不符合上述条件,则退回名单
你可以用这个 debugType() 以检查回收的收款类型。
承诺
这个 commit() 函数终止 diffs 流,将其写入 Folio 数据库。 前一步骤必须产生 diff() 对象。 数值被默认跳过 。
此函数会返回所承诺的diff数量,但这并不能保证行为,将来可能会改变.
此函数会通过定期屏蔽来自动应用后压, 以确保 Folio 写入队列在程序开始前得到充分处理
(1..5).stream.map(n => diff(null, {dis:"C-"+n}, {add})).commit每个
这个 each() 函数终止流,将每个项目延到回调函数:
(0..10).stream.each(v => echo(v))都一样
这个 eachWhile() 函数终止一个流,并将每个项目重命名为调用函数。 如果函数返回非无效,则该流立即关闭并返回所产生的对象。 如果函数返回每个项的无效,则整个流会重叠并返回无效。
(0..10).stream.eachWhile v => do
echo(v)
if (v > 5) "break" else null
end种子
这个 feed() 函数终止流并创建用于视图消耗的活数据种子(仅SkySpark):
readAllStream(point).limit(5).feed过滤器
这个 filter() 函数通过使用一个 filter 表达式 :
readAllStream(equip).filter(siteMeter and elec and meter).collect寻找
这个 find() 函数终止与第一个项匹配的上游函数的流:
(0..10).stream.find(v => v.isOdd)查找全部
这个 findAll() 函数通过使用上游函数过滤项目来改变流:
(0..10).stream.findAll(v => v.isOdd).collect第一个
这个 first() 函数终止流并返回第一个数据项流:
(0..10).stream.first折叠
这个 fold() 函数通过使用折叠函数折叠所有上游项目而终止流:
(0..10).stream.fold(sum)平面图
这个 flatMap() 函数使用1到N变换函数转换流。 变换功能必须返回项目列表或网格,以发送下游. 您可以返回无效以表示无项目( 与空列表相同) 。
readAllStream(site)
.flatMap(s => s.toEquips)
.collectiOStreamCsv (英语)
这个 ioStreamCsv() 通过读取逗号分隔值 I/O 手柄的行来创建 Dicts 流.
ioStreamCsv(`io/import-points.csv`).limit(3).collectioStream 线条
这个 ioStreamLines() 创建从 I/O 手柄读取行的流 。
ioStreamLines(`io/import-points.csv`).limit(3).collect保留科尔斯
这个 keepCols() 执行者: 执行者: 由此推论 collect(toGrid).
readAllStream(site).keepCols(["dis", "area"]).collect最后一个
这个 last() 函数终止流并返回最后一个数据项流:
(0..10).stream.last有经验的
这个 limit() 函数在达到指定限制阈值后切换流 :
readAllStream(point).limit(5).collect地图
这个 map() 函数使用1到1的转换函数转换流:
readAllStream(site)
.map(s => s.set("area", s->area.to("m²")))
.collect读取全部结构
这个 readAllStream() 协商一致意见使用过滤表达式创建 Dict 记录的新流 :
readAllStream(equip).collect读取 ByIds Stream
这个 readByIdsStream() 函数使用 ID 列表创建 Dict 记录的新流 :
readByIdsStream([id1, id2]).collect删除 Col
这个 removeCol() 函数在收集到网格时按名称删除一个列。 此函数推断 collect(toGrid).
readAllStream(site).removeCol("geoAddr").collect删除字符
这个 removeCols() 函数在收集到网格时删除列名列表。 此函数推断 collect(toGrid).
readAllStream(site).removeCols(["geoAddr", "geoStreet"]).collect重排颜色
这个 reorderCols() 函数在收集到网格时指定一个明确的列顺序。 它需要一份列名列表. 列表中未指定的任何列将被删除。 此函数推断 collect(toGrid).
readAllStream(site).reorderCols(["dis", "area"]).collect减少
这个 reduce() 函数通过使用自定义还原函数减少所有上游项目而终止流:
(1..5).stream.reduce(1, (acc,val)=>acc*val)设置 ColMeta
这个 setColMeta() 函数在收集到网格时替换列级元。 从上游步骤推断出的任何列元a均已清除。 此函数推断 collect(toGrid).
readAllStream(site).setColMeta("geoAddr", {hidden}).collect设置Meta
这个 setMeta() 函数取代收集到网格时的网格级别元。 从上游步骤推断出的任何网格元数据均予清理。 此函数推断 collect(toGrid).
readAllStream(site).setMeta({title:"Sites"}).collect跳过
这个 skip() 函数在流开始时丢弃给定数项:
(1..10).stream.skip(3).collect流线
这个 stream() 函数从内存集合创建新流 :
- 列表:项目流
- 网格:流线
- 范围:将包含范围流为数字
实例:
["a", "b", "c"].stream.collect
(3..7).stream.collect所以我们想,
这个 streamCol() 函数从网格和列中创建新流树立。 它把柱状细胞变成细胞的序列.
实例:
readAll(site).streamCol("area").fold(sum)单位
概览
F沅和Axon中的所有数字都可以附加一个可选的单位. 各单位将检查并进行所有数字操作。
单元系统
作为一项一般原则,与某一特定网站有关的所有数据都应完全使用SI度量系统或美国习惯系统。 一个站点内的嚷声单元系统会给分析器造成严重头痛,这需要温度/压力常数或不同单元变量的相关性。 所有标准库都假设,特定网站(及其相关气象站)内的数据是一贯的计量数据或美国习惯数据。
如果您需要在设计用于全球的函数库中定义一个默认的常数,那么常规就是创建一个需要站点记录的函数. 一个好的例子是 degreeDaysBase() 定义默认度日平衡点:
(site) => do
if (isMetric(site)) 18°C else 65°F
end亦见 isMetric() 函数。
数据库
SkySpark 使用的单位数据库由 Unit Fantom API. (原始内容存档于2017-09-29). 它作为文本文件保存在"的提议/sys/units.txt"下. 任何单位标识符可在数字字节后立即用于关联单位:
23celsius // full name allowed
102.5square_meter
23°C // symbol preferred
102.5m²根据公约,该符号是首选标记。 所有编码到 Zinc 使用符号,而不是全名.
比较
Axon平等操作员检查标尺和单位。 但是,如果一个数字有一个空单位,那么,比较只根据平面:
123ft == 123ft >> true
123ft == 123 >> true
123ft == 123m >> false当执行的操作大于或少于操作时,这些单位必须大相径庭或至少一个是无效的:
7ft > 3ft >> true
7ft > 3 >> true
7ft > 3m >> raises UnitErr: ft <=> m算术
有单元的算术处理如下:
// addition (associative)
a + a >> a
a + null >> a
°F + Δ°F >> °F
°C + Δ°C >> °C
a + b >> throws UnitErr
// subtraction
a - a >> a
a - null >> a
null - a >> a
°F - °F >> Δ°F
°C - °C >> Δ°C
°F - Δ°F >> °F
°C - Δ°C >> °C
a - b >> throws UnitErr
// multiplication (associative)
a * b >> a * b
a * null >> a
// division
a / b >> a / b
a / null >> a
null / b >> throws UnitErr一般说来,如果其中一个数字有一个空单位,那么另一个歌剧的单位就通过. 添加或子跟踪两个带有不同单位的编号将引起例外——自动单位转换不执行。 我们还为°F和°C提供特别的允许,因为这些温度系统不是零基.
注意乘法和分法会试图根据操作的单位来得出单位. 衍生单元必须与单位数据库预先定义的单元匹配,否则将丢出UnitErr。
实例:
12kg + 5 >> 17 kg
12kg + 5kg >> 17 kg
12kg + 5lb >> UnitErr: kg + lb
75°F - 50°F >> 25 Δ°F
400kW * 2h >> 800 kWh
800kW / 200m² >> 4 kW/m²改划
你可以用这个 to() 函数在单位间转换:
65°F.to(1°C) >> 18.333 °C
2000m².to(1ft²) >> 21,527.83 ft²
100kWh.to(1BTU) >> 341,280.104 BTU
100kWh.to(1L) >> Inconvertable units: kWh and L对单位的分级值被忽略了,但是通过惯例我们使用"1". 或者可以使用下列 122. 单位字符串:
65°F.to("°C")使用 as() 不进行转换而更改单位的函数:
65°F.as(1°C) >> 65°C
65°F.as("°C") >> 65°C监视
概览
监视器是一种用于订阅对其中一组记录的实时更改的机制。 Folio 数据库。 观察以OB的观点将根据OBIX/Haystack的手表为模式,这些手表是围绕着投票换变化设计的。 投票为建立简单但强有力的网络提供了基础。 仅仅通过投票 计算已经改变的数值, 我们可以保持非常小的有效载荷。 这反过来又使我们能够一秒钟有效地进行几次投票。
将记录添加到手表中也是用于用户订阅外部数据的一种机制 connector framework.
HTTP API 软件
如果在网络上使用手表,则使用以下 HTTP API 操作:
我们需要的是,
以下Axon功能用于手表工作:
watchOpen()watchPoll()watchAdd()watchRemove()watchClose()
示例代码 :
// read all points under specific connector and put into watch,
// the second parameter to watchOpen is debug string to
// indicate who is opening the watch
grid: readAll(point and fooConnRef==xxxx).watchOpen("My Custom App")
// grid is now the result of my readAll operation, plus it
// contains my watch id to use for polling
watchId: grid.meta->watchId
// now enter a loop to poll for any changes to my original points
changes: watchPoll(watchId)
// when done we should gracefully close the watch, or it will
// close automatically if we fail to poll and its lease expires
watchClose(watchId)方图 APIs
- 以下内容:
HxWatchService和HxWatchAPI用于在Fantom与手表合作. 与手表合作的Fantom API比Axon提供了更多的弹性和性能,因为它们在抽象度较低的情况下运作.
以下是使用手表的 Fantom 代码的简单片段 :
// open new, empty watch
watch := rt.watch.open("My Custom App")
// add some points to the watch
points := rt.db.readAll("point and fooConnRef==xxxx")
watch.addGrid(points)
// poll for changes
while (isAlive)
{
dicts := watch.poll
}
// re-poll to get current state of all recs in watch
refresh := watch.poll(Duration.defVal)
// close the watch
watch.closeXML 函数
概览
XML或扩展标记语言是企业用于数据交换的一种通用格式. Hexall支持标准化地翻译 Grid 输入 XML。 注意这种格式不是由Haystack项目标准化的,而是针对Haxall的。
使用量
方图 API :
XParser解码为杏仁XDoc模拟 DOM 模型和写作OutStream拥有许多用于 XML 逃逸序列的方法
斧头 API :
ioWriteXml()编码网格
(原始内容存档于2017-09-21). REST APIs:
- content negotiation 用于“文本/xml” MIME 类型
网格格式
SkySpark 定义标准映射 Grid 输入用于 REST API 的 XML 中 ioWriteXml() 函数。 XML代表与海斯塔克数据模型保持完全的忠诚——所有元数据和类型信息都得到维护. 然而,缺点是XML的动词比锌要多得多.
网格到XML映射如下:
- 顶级
<grid ver="3.0">包含:- 一个可选
<meta>有网格元的元素a - 一个
<cols>有栏定义的元素 - 零点或更多
<row>有行定义的元素
- 一个可选
- 列定义是
<{name}>可选dis属性和可选<meta>子元素( disa 总是跳过) - 行是编码在
<row>含有数值元素的元素 - 元数据和行值ẽ
<{name}>要素 - 价值单元格总是定义
kind如果值无效, 则带有类型名称或“ null” 属性 - 如果值是斜线,那么
val属性映射到方图toStr表示数值。 如果值为无,则标记,或删除,则val标记被省略 - 参考值可包括可选值
dis属性 B. 建 议
示例
// Zinc
ver:"3.0" projName:"test"
dis dis: "Equip Name" primaryCol:M,equip,siteRef,installed
"RTU-1",M,@153c600e-699a1886 "HQ",2005-06-01
"RTU-2",M,@153c600e-699a1886 "HQ",1999-07-12
// XML
<grid ver='3.0'>
<meta>
<projName kind='Str' val='test'/>
</meta>
<cols>
<dis dis='Equip Name'>
<meta>
<primaryCol kind='Marker'/>
</meta>
</dis>
<equip/>
<siteRef/>
<installed/>
</cols>
<row>
<dis kind='Str' val='RTU-1'/>
<equip kind='Marker'/>
<siteRef kind='Ref' dis='HQ' val='153c600e-699a1886'/>
<installed kind='Date' val='2005-06-01'/>
</row>
<row>
<dis kind='Str' val='RTU-2'/>
<equip kind='Marker'/>
<siteRef kind='Ref' dis='HQ' val='153c600e-699a1886'/>
<installed kind='Date' val='1999-07-12'/>
</row>
</grid>给你的列表值 :
// Zinc
ver:"3.0"
list
[1,2,3]
// XML
<grid ver='3.0'>
<cols>
<list/>
</cols>
<row>
<list kind='List'>
<item kind='Number' val='1'/>
<item kind='Number' val='2'/>
<item kind='Number' val='3'/>
</list>
</row>
</grid>嵌入值 :
// Zinc
ver:"3.0"
dict
{dis:"hello!", foo}
// XML
<grid ver='3.0'>
<cols>
<dict/>
</cols>
<row>
<dict kind='Dict'>
<dis kind='Str' val='hello!'/>
<foo kind='Marker'/>
</dict>
</row>
</grid>网格值 :
// Zinc
ver:"3.0"
grid
<<
ver:"3.0"
dis
"Grid"
>>
// XML
<grid ver='3.0'>
<cols>
<grid/>
</cols>
<row>
<grid kind='Grid'>
<cols>
<dis/>
</cols>
<row>
<dis kind='Str' val='Grid'/>
</row>
</grid>
</row>
</grid>您也可以通过 oBIX 是交替的 XML 格式。