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

331 lines
12 KiB
Markdown
Raw Normal View History

2024-12-26 19:24:07 +08:00
# 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));
}
}
```