4x/device/0docs/设备云管家协议脚本配置指南/VE值解析表达式.md

331 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Value-Expression 值表达式
| 作者 | 时间 | 版本 | 描述 |
| ------ | ------------- | ---- | --------------- |
| 黄杰添 | 2022年6月20日 | V1.0 | 初始版本 |
| 黄杰添 | 2022年10月8日 | V1.1 | 增加Enums表达式 |
值表达式的设计的初衷是为了支持物联网协议的报文解析/生成,是报文(Source) 和 值(Target)之间的解析层。最初基于水文协议的数据定义(eg. N(3.1)), VE表达式操作字符串数据源 并最终解析成定义的泛型的值和值字符视图。
(string)003821 **--->** N(6,2) **--->** (double)38.21
## 1. VE表达式 格式说明
基本格式 `Type(Format) ` 由一个Type字符起始, 紧跟着括号内的Format表示Type的补充说明根据Type字符的不同而不同
- Type 表示为该要素的数据类型 eg N表示BCD编码数字S表示字符 B表示字节T表示时间A表示数组
- Format 表示该数据类型的补充定义,如 当Type=N Format可以是一个大于0的数字如3.1 完整的 `N(3,1)` 表示总长3个字节的数字小数点1位。更多示例参考 附录 B
## 2. VE表达式 Type列表
| Type标识符 | 类型说明 | 编解码 | 占位符支持 | 基本格式 | 完整示例 | 示例说明 |
| ---------- | ------------------------ | -------- | ------------------ | --------------------------------- | -------------------------------------- | ------------------------------------------------------------ |
| N | 数值<br />Number | | 不支持 | N(字符数[,小数位]) | N(6,1) | 6字符的数字 例如 016751 将翻译为 1675.1<br />小数位如不声明或小于0则为0 |
| S | 字符<br />String | | 支持非数字占位符 | S(字符数) | S(16)<br />S(4-2-2 2:2:2) | 16个字符的字符<br />14个字符的字符, 例如 20220617104300 会被解释为 2022-06-17 10:43:00 |
| HN | HEX数值<br />Number | HEX | 不支持 | HN(字符数[,小数位]) | HN(6,2) | 6字符的HEX数字 例如 016751 将翻译为 919.85<br />小数位如不声明或小于0则为0 |
| HS | Hex字符<br />String | HEX | 支持非数字占位符 | HS(字符数) | HS(48)<br />HS(4-2-2 2:2:2) | 24个字节的Hex字符<br />14个字节的HEX字符, 例如 3230323230363137313031363030 会被解释为 2022-06-17 10:43:00 |
| HF | HEX-FLOAT数值<br />Float | IEEE 754 | 不支持 | HF(字符数) | HF(8) | 8个字符的HEX-FLOAT字符例如3F99999A 经IEEE754解码为 1.2 |
| E | 枚举值<br />String | | 不支持 | E(字符数,键-值...) | E(2,01-成功,02-失败) | 2个字符的枚举值,例如01翻译为成功, 02翻译为失败. 注意以 - 号区分, 如后续还有 - 号 仅仅取第一个 - 号 |
| T | 时间字符<br />Timestamp | | | T(字符数,解析格式[,展示格式]) | T(12,yyMMddHHmmss,yyyy-MM-dd HH:mm:ss) | 12个字符的字符例如220711121212 按照 yyMMddHHmmss解析后展示为yyyy-MM-dd HH:mm:ss格式,最终结果为2022-07-11 12:12:12 <br />展示格式如不声明则为解析格式 |
| B | 字节<br />byte[] | 不固定 | 不支持 | B(字符数[,编码格式]) | B(1024,HEX) | 1024个字符转换为HEX编码 = 512个字节编码格式如不声明默认为UTF-8 |
| A | 数组<br />Object[] | 不固定 | 视数组元素类型而定 | A(Type(Format)) | A(HN(2),12) | 共24个字节的Hex数字, 拆分为12组默认在数据解析完成后以逗号 ‘,’间隔 |
| C | 组合<br />List<Object> | 不固定 | 视数组元素类型而定 | C(Type1(Format),Type2(Format)...) | C(T(12,yyMMddHHmmss),N(6,2),S(10)) | 共12+6+10=28个字符的组合字符,解析为:<br />1. 时间数据 <br />2. 数值数据<br />3. 字符数据<br />例如 2207111659000048750123456789拆解为<br />1. 220711165900<br />2. 48.75<br />3. 0123456789<br />结果展示为:220711165900,48.75,0123456789 |
| E | 枚举<br />String | | 不支持 | E(字符数,值-表示...) | E(2,01-成功,02-失败) | 共2字符01时解析为成功02时解释为失败 |
## 3. 书写说明
### 3.1 格式书写要求
除非表达式支持占位符(eg S(4-2-2 2:2:2))或者格式(eg T), 否则不允许额外的空格, 表达式间隔符均使用 ,号
### 3.2 关于不定长数据的声明
字符(S),HEX字符(HS),字节数组(Bytes) 三个元素支持不定长数据的声明,声明方式为将 字符数 置为 0或者负数 , 例如 `B(0,HEX)` 表示所有字符都为字节数组的值
## 4. Java开发者指南
添加依赖
```xml
<dependency>
<groupId>com.fourfaith</groupId>
<artifactId>fourfaith-boot-utils</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
```
### 4.1 Java组件
| 组件 | 组件说明 |
| ------------ | ------------------------------------------------------------ |
| VExpFactory | 表达式工厂, 一个表达式工厂包含多个表达式生产者(VExpProducer), 生产者需要调用 `vExpFactory.attach(vExpProducer)` 手动添加到工厂。 |
| VExpProducer | 表达式生产者, 一个生产者用于生产对应 Type类型的表达式实例(VExp),同时将生产过的实例进行缓存。 |
| VExp | 表达式,一个表达式用于表达数据并生成表达值。 |
| ExpValue | 表达值,通过表达值可以获取指定类型的结果,或者调用 `toStringValue()` 获取字符值,同时表达值还存储了源数据的游标, 这个下标是当前表达式解析数据后源的游标,可以获取这个游标以帮助下一次的数据表达。 |
### 4.2 已支持的Type实现类
| Type | VExpProducer |
| ---- | ------------------------------------------------------------ |
| N | com.fourfaith.boot.utils.vexpression.producer.NumberExpProducer |
| S | com.fourfaith.boot.utils.vexpression.producer.StringPartsExpProducer |
| HN | com.fourfaith.boot.utils.vexpression.producer.HexNumberExpProducer |
| HS | com.fourfaith.boot.utils.vexpression.producer.HexStringPartsExpProducer |
| HF | com.fourfaith.boot.utils.vexpression.producer.HexFloatExpProducer |
| E | com.fourfaith.boot.utils.vexpression.producer.EnumsExpProducer |
| T | com.fourfaith.boot.utils.vexpression.producer.TimestampExpProducer |
| B | com.fourfaith.boot.utils.vexpression.producer.BytesExpProducer |
| A | com.fourfaith.boot.utils.vexpression.producer.ArraysExpProducer |
| C | com.fourfaith.boot.utils.vexpression.producer.CombinedExpProducer |
| E | com.fourfaith.boot.utils.vexpression.producer.EnumsExpProducer |
### 4.3 示例代码
本例演示了日期 数值 字符 组合表达式
```java
public static void main(String[] args) {
VExpFactory vExpFactory = VExpFactory.newWorkshop();
vExpFactory.attach(new TimestampExpProducer());
vExpFactory.attach(new NumberExpProducer());
vExpFactory.attach(new StringPartsExpProducer());
vExpFactory.attach(new CombinedExpProducer(vExpFactory));
String data = "2207111134010123546";
VExp<?> vExp = vExpFactory.getExpression("T(12,yyMMddHHmmss,yyyy-MM-dd HH:mm:ss)");
ExpValue<?> expValue = vExp.express(data);
System.out.println(expValue);
VExp<?> vExp1 = vExpFactory.getExpression("N(6,3)");
ExpValue<?> expValue1 = vExp1.express(data, expValue.getCursor());
System.out.println(expValue1);
VExp<?> vExp2 = vExpFactory.getExpression("S(1)");
ExpValue<?> expValue2 = vExp2.express(data, expValue1.getCursor());
System.out.println(expValue2);
VExp<?> expression = vExpFactory.getExpression("C(T(12,yyMMddHHmmss,yyyy-MM-dd HH:mm:ss),N(6,3),S(1))");
System.out.println(expression.express(data));
/*
执行结果
2022-07-11 11:34:01
12.354
6
2022-07-11 11:34:01,12.354,6
*/
}
```
### 4.4 自定义开发
如何声明一个属于自己的表达式
1. 定义Type标识符约定使用 **1-2 个长度的字母**声明一个Type标识符, 注意查看 `VExpType` 避免重复
2. 实现接口 VExp 实现表达式
3. 继承抽象类 VExpProducer 实现生产者
原则上, 建议遵守 Type(Format) 基本格式统一风格并在一定程度上保证可读性
**VExp 接口**
```java
package com.fourfaith.boot.utils.vexpression;
/**
* <p>
* 值表达式
* </p>
*
* @author Kern
*/
public interface VExp<T> {
/**
* 获取解析后的值长度
* @return 值长度
*/
int getExpectedStringLength();
/**
* 获取原始数据长度
* @return int
*/
int getExpectedValueLength();
/**
* 解析值
* @param src 原始值
* @return value
*/
ExpValue<T> doExpress(String src);
/**
* 获取值类型
* @return class
*/
Class<T> getVType();
/**
* 解析值
* @param src 原始值
* @return value
*/
default ExpValue<T> express(String src) {
return express(src, 0);
}
/**
* 解析值
* @param src 原始值
* @param startIndex 起始坐标
* @return value
*/
default ExpValue<T> express(String src, int startIndex) {
if (src == null) {
return null;
}
int endIndex = getEndIndex(src, startIndex);
if (src.length() < endIndex) {
return null;
}
String valSrc = src.substring(startIndex, endIndex);
ExpValue<T> expValue = doExpress(valSrc);
expValue.setCursor(endIndex);
return expValue;
}
default int getEndIndex(String src, int startIndex) {
int valueLength = getExpectedValueLength();
return valueLength <= 0 ? src.length() : startIndex + valueLength;
}
}
```
**VExpProducer 抽象类**
```java
package com.fourfaith.boot.utils.vexpression;
import com.fourfaith.boot.utils.vexpression.producer.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* <p>
* 值表达式生产者
* </p>
*
* @author Kern
*/
public abstract class VExpProducer<VE extends VExp<?>> {
private final Map<String, VE> expressionMap = new ConcurrentHashMap<String, VE>();
public static VExpProducer<?>[] allStandardProducers() {
return new VExpProducer[]{
new ArraysExpProducer(),
new BytesExpProducer(),
new CombinedExpProducer(),
new EnumsExpProducer(),
new HexFloatExpProducer(),
new HexNumberExpProducer(),
new HexStringPartsExpProducer(),
new NumberExpProducer(),
new StringPartsExpProducer(),
new TimestampExpProducer(),
};
}
/**
* 表达式的类型标识
* @return Type
*/
public abstract VExpType getType();
/**
* 生产表达式
* @param expression 表达式
* @return VExp
*/
public abstract VE produce(String expression);
/**
* 校验表达式
* @param expression 表达式
* @return true | false
*/
public abstract boolean valid(String expression);
/**
* 解析值
* @param expression 表达式
* @return value
*/
public VE getExpression(String expression) {
VE ve = expressionMap.get(expression);
if (ve == null) {
try {
ve = produce(expression);
} catch (Exception e) {
e.printStackTrace();
}
expressionMap.put(expression, ve);
}
return ve;
}
protected String skinning(String expression) {
return expression.substring(expression.indexOf(VExpConstants.LEFT_PARENTHESES) + 1,
expression.lastIndexOf(VExpConstants.RIGHT_PARENTHESES));
}
}
```