Browse Source

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	package.json
#	pnpm-lock.yaml
master
修宁 6 months ago
parent
commit
4491d9d73a
  1. 966
      doc/RCS与AGV之间的指令数据格式.md
  2. 3
      package.json
  3. 457
      pnpm-lock.yaml
  4. 4
      src/components/ShowDialogWrap.vue
  5. 12
      src/editor/ModelMain.vue
  6. 128
      src/editor/OpenProject.vue
  7. 125
      src/editor/menus/FileMenu.ts
  8. 33
      src/modules/cl2/Cl23dObject.ts
  9. 7
      src/runtime/System.ts

966
doc/RCS与AGV之间的指令数据格式.md

@ -0,0 +1,966 @@
# RCS与AMR之间的指令数据格式
**RCS与AMR通信指令数据格式**
## 通信方式概览
- RCS(Robot Control System)与AMR(Autonomous Mobile Robot)间通过MQTT协议通信,MQTT服务器默认不设置密码。AMR内部设置MQTT服务器的信息(IP、端口、用户名密码等,需要在项目部署时设置),启动后会自动监听任务topic。
- RCS下发任务给AMR时使用的topic为"/wcs_server/<amr id>",其中"<amr id>"部分替换为AMR的编号,比如"/wcs_server/5",代表下发给5号车的topic。
- RCS可以通过"/wcs_broadcast"来发全局查询(内容与"/wcs_server/<amr id>"一致,但广播接口目前只支持查询状态,不支持其它报文),以便在RCS重启等情况下能同步所有车的状态。
- 所有AMR上报的信息统一发到"/agv_robot/status",这样RCS可以监听到所有配置了同一个MQTT服务的AMR的消息。
## RCS发往AMR的指令
### AMR作业指令 10010
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **ID#10010** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | |
| OperationType | 作业类型 | UInt8 | 0:运输 1:接货 2:卸货 3:充电 4:提升移栽取货或卸货 **5:滚筒取货或卸货(双向作业)** |
| ChargeDirection | 充电桩朝向UseBriefLocation | UInt8 | 0: X轴正向 1: Y轴正向 2: X轴负向 3: Y轴负向 |
| DistanceToCharging | 充电工位坐标和充电桩之间距离 | UInt16 | 单位:mm |
| StorageRacksNo | 目标货架编号 | String | 仅做校验使用(仅接货用) |
| StartX | 小车起始X坐标 | UInt16 | |
| StartY | 小车起始Y坐标 | UInt16 | |
| EndX | 小车目标X坐标 | UInt16 | |
| EndY | 小车目标Y坐标 | UInt16 | |
| GoNow | 任务是否立即执行 | bool | 默认为true |
| LinkCounts | 路径分段数 | UInt8 | |
| **Link** **(重复LinkCounts次)** | | | |
| X | 该段目标点X坐标 | UInt16 | 逻辑单位,乘以一定系数才是物理距离 |
| Y | 该段目标点Y坐标 | UInt16 | 同上 |
| Speed | 该段行驶速度 | Int16 | mm/s |
| **BuiltinSlotNo** | AMR内置货位ID(仅对多层移栽有意义) | UInt8 | 1~N |
| PickMode | 提升移栽货物拣货模式 | UInt8 | 0:不控制(无动作) 1:从货架上取货 2:将货物放到货架上 3:仅调整托盘高度(不进行取放货操作) 4:调整车身货物(仅供调试,RCS勿发送此命令) 5:仅调整载货台到取货高度,但是不动作 6:仅调整载货台到放货高度,但是不动作 |
| **GoodsSlotHeight** | **目标货位相对于地面的绝对高度** | **UInt16** | **单位:mm** |
| GoodsSlotDirection | 目标货位朝向 | UInt8 | 朝向定义与充电桩朝向相同。 0: X轴正向 1: Y轴正向 2: X轴负向 3: Y轴负向 |
| MPickMode | 多机构^\[1\]^的拣货模式 | UInt8\[3\] | 数组形式,意义同"PickMode" |
| MGoodsSlotHeight | 多机构^\[1\]^的目标货位高度 | UInt16\[3\] | 数组形式,意义同"GoodsSlotHeight" |
| MGoodsSlotDirection | 多机构^\[1\]^的目标货位朝向 | UInt8\[3\] | 数组形式,意义同"GoodsSlotDirection" |
| MStorageRacksNo | 多机构的目标货箱ID | String\[3\] | 数组形式,意义同"StorageRacksNo" |
| Roll1Motion | 滚筒1运动操作 对于左右滚动的双滚筒机型,此滚筒为靠近车头的滚筒。 对于前后滚动的双滚筒机型,此滚筒为车身左侧的滚筒。 | UInt8 | 同"PickMode" |
| Roll2Motion | 滚筒2运动操作 对于左右滚动的双滚筒机型,此滚筒为靠近车尾的滚筒。 对于前后滚动的双滚筒机型,此滚筒为车身右侧的滚筒。 对于单滚筒机型,此参数无意义。 | UInt8 | 同上 |
| Roll1StationDirection | 与滚筒1对接的站台朝向 | UInt8 | 朝向定义与充电桩朝向相同。 0: X轴正向 1: Y轴正向 2: X轴负向 3: Y轴负向 |
| Roll2StationDirection | 与滚筒2对接的站台朝向 对于单滚筒机型,此参数无意义。 | UInt8 | 同上 |
| Roll1GoodsLength | 滚筒1目标货物长度 | UInt16 | 单位:mm |
| Roll2GoodsLength | 滚筒2目标货物长度 对于单滚筒机型,此参数无意义。 | UInt16 | 同上 |
| Roll1GoodsQuantity | 滚筒1目标货物数量 | UInt16 | 1~N |
| Roll2GoodsQuantity | 滚筒2目标货物数量 对于单滚筒机型,此参数无意义。 | UInt16 | 同上 |
| MRollMotion | 多滚筒运动模式 | UInt8\[6\] | 数组形式,意义同"RollMotion" |
| MRollStationDirection | 多滚筒对接的站台朝向 | UInt8\[6\] | 数组形式,意义同"RollStationDirection" |
| MRollGoodsQuantity | 多滚筒目标货物数量 | UInt16\[6\] | 数组形式,意义同"RollGoodsQuantity" |
| Preparing | 是否仅执行任务的准备部分 | bool | 默认为false,仅执行该动作的准备部分,如仅进行导航,调整托盘高度等,但不进行取放货操作 |
| RackTypeId | 货架标识 | uint32 | 车根据货架类型查询尺寸进行避障 |
| EndSelfAdaption | 终点自适应 | bool | 默认为false,为true时,会根据任务和对应器件的位置,自动调整停止点 |
**注[1]:对于哪吒三层飞梭,下标0表示最低的取放货机构;下标1表示中间的取放货机构;下标2表示最高的取放货机构**。
例如:
```json
{
"id": 10010,
"content": {
"SeqNo": 4,
"OperationType": 0,
"StorageRacksNo": "R123",
"StartX": 700,
"StartY": 1400,
"EndX": 100,
"EndY": 100,
"LinkCounts": 4,
"Link": [
{
"X": 700,
"Y": 100,
"Speed": 150
},
{
"X": 100,
"Y": 100,
"Speed": 150
}
]
}
}
```
### 停止/解除 10040
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| --------------- | ------------- | -------- | --------------- |
| **ID#10040** | **指令ID** | | |
| SeqNo | 作业序号 | Uint32 | |
| OperationCode | 操作代码 | Uint8 | 0-停止 1-解除 |
| StopX | 停止点X坐标 | Uint16 | |
| StopY | 停止点Y坐标 | Uint16 | |
例如:
```json
{
"id": 10040,
"content": {
"SeqNo": 1023844,
"OperationCode": 0,
"StopX": 100,
"StopY": 100
}
}
```
### 电文应答 10050
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ------- | ---------- | -------- | ----------------------------- |
| **ID#10050** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | 所要应答消息的SeqNo(对方的) |
例如:
```json
{
"id": 10050,
"content": {
"SeqNo": 1
}
}
```
### 心跳 10100
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| -- | -- | -- | ----- |
| **ID#10100** | **指令ID** | | |
例如:
```json
{
"id": 10100,
"content": {}
}
```
### 配置信息 10060
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ------------- | ---------------------------------------------------- | -------- | ---------------------- |
| **ID#10060** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | 0(特殊) |
| XLength | X坐标最大长度 | UInt16 | 暂时未使用 |
| YLength | Y坐标最大长度 | UInt16 | 暂时未使用 |
| Gap | 相邻地标间距 | UInt16 | 单位:mm (目前等距) |
| HeartBeat | 心跳间隔 | UInt32 | 单位:秒 |
| MqRetryTime | 小车所有上报消息重试间隔(未收到应答消息时重发消息) | UInt32 | 单位:秒 |
例如:
```json
{
"id": 10060,
"content": {
"SeqNo": 0,
"XLength": 100,
"YLength": 100,
"Gap": 100,
"HeartBeat": 60,
"MqRetryTime": 3
}
}
```
### 旋转货架 10080
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ----------- | ------------- | -------- | --------------------------------------------- |
| **ID#10080** | **指令ID** | | |
| SeqNo | 作业序号 | Uint32 | |
| Direction | 货架A面朝向 | UInt8 | 0: X轴正向 1: Y轴正向 2: X轴负向 3: Y轴负向 |
例如:
```json
{
"id": 10080,
"content": {
"SeqNo": 110,
"Direction": 1
}
}
```
### 旋转车身 10081
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ----------- | ---------- | -------- | --------------------------------------------- |
| **ID#10081** | **指令ID** | | |
| SeqNo | 作业序号 | Uint32 | |
| Direction | 车头朝向 | UInt8 | 0: X轴正向 1: Y轴正向 2: X轴负向 3: Y轴负向 |
例如:
```json
{
"id": 10081,
"content": {
"SeqNo": 110,
"Direction": 1
}
}
```
### 状态查询 10110
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ------- | ---------- | -------- | ----- |
| **ID#10110** | **指令ID** | | |
| SeqNo | 作业序号 | Uint32 | |
查询车的当前状态。正常情况下只有当车正确初始化后才会应答,但有两种例外情况:
1. 通过广播接口发生的状态查询请求
2. 在AMR配置文件中设置了强制应答
在未完成初始化的情况下应答的状态报文可能是不准确的,比如位置未知,比如货位状态不准确等,上位系统需要针对性做处理。
### 控制卷帘门 10082
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ------------ | ---------- | -------- | ----------------------------------------------------------------------------- |
| **ID#10082** | **指令ID** | | |
| SeqNo | 作业序号 | Uint32 | |
| CurtainCmd | 卷帘状态 | UInt8 | 0: 不控制(无动作) 1: 关闭状态 2: 半开状态 3: 全开状态 4: 停止(暂不支持) |
例如:
```json
{
"id": 10082,
"content": {
"SeqNo": 110,
"CurtainCmd": 1
}
}
```
### 取消已下发小车任务 10120
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ------- | ---------- | -------- | ----- |
| **ID#10120** | **指令ID** | | |
| SeqNo | 作业序号 | Uint32 | |
例如:
```json
{
"id": 10120,
"content": {
"SeqNo": 110
}
}
```
### 等待就绪 19997
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| --------- | --------------- | -------- | ------------------------------------------------------------------------------------------ |
| **ID#19997** | **指令ID** | | |
| SeqNo | 作业序号 | Uint32 | |
| Seconds | 等待时间(s) | Double | -1:结束等待(仅SLAM车支持) 0:持续等待(仅SLAM车支持) 大于0:等待对应的时间后返回成功 |
例如:
```json
{
"id": 19997,
"content": {
"SeqNo": 110,
"Seconds": 0
}
}
```
### 设置小车坐标 10200
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ----------- | ---------- | -------- | ----------------------------------------------------------------------------- |
| **ID#10200** | **指令ID** | | |
| SeqNo | 作业序号 | Uint32 | |
| X | 点X坐标 | Uint16 | |
| Y | 点Y坐标 | Uint16 | |
| Direction | 车头朝向 | UInt8 | 0: X轴正向 1: Y轴正向 2: X轴负向 3: Y轴负向 15: 未知(小车保持原车头方向) |
例如:
```json
{
"id": 10200,
"content": {
"SeqNo": 110,
"X": 1,
"Y": 1,
"Direction": 15
}
}
```
---
---
## AMR发往RCS的指令
### 共同信息
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ---------------- | -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| SeqNo | 作业序号 | UInt32 | 从1开始,0作为超级序号使用,不参与序号规则判断 |
| VehicleId | AMR编号 | UInt16 | |
| CreateTime | 消息创建时间 | UInt64 | 单位:毫秒 示例:1685589750403 |
| CreateMonoTime | 消息创建时主机的开机时长 | UInt64 | 单位:毫秒 示例:822652887 备注:由于刚开机时设备内部时间可能不准,所以将开机时长也上报,这样可以在时间同步后,再反推时间同步前时间的时间 |
| SendTime | 发送消息的时间 | UInt64 | 单位:毫秒 示例:1685589750403 |
### 小车作业完成 20010
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ---------------------------- | ---------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **ID#20010** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | |
| VehicleId | AMR编号 | UInt16 | |
| CurX | 当前X坐标 | UInt16 | 逻辑单位,乘以一定系数才是物理距离 |
| CurY | 当前Y坐标 | UInt16 | 同上 |
| CurDirection | 当前方向 | UInt8 | 0: X轴正向 1: Y轴正向 2: X轴负向 3: Y轴负向 |
| OperationType | 作业类型 | UInt8 | 0:运输 1:接货 2:卸货 3:充电 4:提升移栽取货或卸货 **5:滚筒取货或卸货(双向作业)** 135:旋转货架 136:旋转车身 |
| OperationResult | 作业结果 | Int32 | 参考linux errno |
| StorageRacksNo | 货物ID | String | 在肥波类车型中是货架二维码值;在飞梭车中是箱码(目前未传,未来可能会有);在皮带飞梭中是货物上贴的二维码的码值(如果检测到多个码,则以" |
| Battery | 电量百分比 | Uint8 | |
| Summary | 任务描述 | Object | 目前仅在飞梭和侧叉车型的报文中有这个字段,字段详情见下文 |
| Summary.Actuators | 执行器(比如飞梭的载货台)任务概览列表 | Array | 车载多少个执行器,数组就有多少个成员 |
| Summary.Actuators.MechNo | 执行器编号 | UInt8 | 从1开始 |
| Summary.Actuators.Name | 执行器名称 | String | |
| Summary.Actuators.PickMode | 任务类型 | UInt8 | 同下发任务中PickMode的定义 |
| GoodsSlots | 车载货位信息 | [Object] | 参见[货位信息](#货位信息) |
示例:
```json
{
"content": {
"Battery": 84,
"CurDirection": 0,
"CurX": 10,
"CurY": 11,
"OperationResult": 0,
"OperationType": 4,
"SeqNo": 635,
"StorageDirection": 15,
"StorageRacksNo": "0331794865-S12",
"Summary": {
"Actuators": [
{
"MechNo": 1,
"Name": "Mech1",
"PickMode": 2
}
]
},
"VehicleId": 41
},
"id": 20010
}
```
### 小车子模块任务状态 20012
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ----------------- | ------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **ID#20012** **<sup>[2]</sup>** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | |
| VehicleId | AMR编号 | UInt16 | |
| MPickMode | 多机构^\[1\]^的拣货模式 | UInt8\[3\] | 数组形式,意义同"PickMode" |
| MechStatus | 子模块任务状态 | Uint8 \[3\] | 数组形式,数组下标^\[1\]^分别对应各子模块。 0:无任务 2:任务中 3:任务准备中(已有任务,但还没有执行任务的条件) 5:任务异常 7:任务完成 其它:保留 |
| MStorageRacksNo | 子模块货物ID | String\[3\] | 数组形式,分别对应各子模块上货箱的ID码的值,如果无货为"",如果解不出来为"unknown" |
**注[1]:对于哪吒三层飞梭,下标0表示最低的取放货机构;下标1表示中间的取放货机构;下标2表示最高的取放货机构**。
**注[2]:AMR主动上报此状态,用于上报20010前反馈给RCS当前子模块的任务状态,方便RCS对货位状态进行管理以及异常时候的任务取消和重发处理。**
### 地标报告 20020
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| -------------- | ----------- | -------- | --------------------------------------------- |
| **ID#20020** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | |
| VehicleId | AMR编号 | UInt16 | |
| CurX | 当前X坐标 | UInt16 | |
| CurY | 当前Y坐标 | UInt16 | |
| CurDirection | 当前方向 | UInt8 | 0: X轴正向 1: Y轴正向 2: X轴负向 3: Y轴负向 |
例如:
```json
{
"content": {
"CurDirection": 1,
"CurX": 700,
"CurY": 1400,
"SeqNo": 126,
"VehicleId": 0
},
"id": 20020
}
```
### 消息应答 20050
| **Field Name** | **Field Description** | **Format** | | **Values/** **Remarks** |
| ----------- | ---------- | -------- | -- | ----- |
| **ID#20050** | **指令ID** | | | |
| SeqNo | 作业序号 | UInt32 | | |
| VehicleId | AMR编号 | UInt16 | | |
例如:
```json
{
"content": {
"SeqNo": 2,
"VehicleId": 0
},
"id": 20050
}
```
### 状态上报 20060
| **Field Name** | **Field Description** | **Format** | | **Values/** **Remarks** |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **ID#20060** | **指令ID** | | | |
| CurBattery | 电池状态 | Struct | | |
| CurOrientation | 当前方向 | Double | | |
| CurLogicX | 当前所在站点的逻辑X坐标 | Int32 | | |
| CurLogicY | 当前所在站点的逻辑Y坐标 | Int32 | | |
| CurX | 当前X坐标 | Double | 当前实际位置在地图坐标系中的X坐标 | |
| CurY | 当前Y坐标 | Double | 当前实际位置在地图坐标系中的Y坐标 | |
| RackCurOrientation | 货架当前方向 | Double | | |
| RackCurPosition | 货架当前位置 | Double | | |
| SeqNo | 作业序号 | UInt32 | | |
| StorageRacksNo | 货架号 | String | | |
| MStorageRacksNo | 多载货机构上每个机构上面货物的ID | String\[\] | | 对于哪吒车型,前三项分别代表三层载货台上的货物ID,如果无货则为"",如果有货但解不出货物码或没有货物码,则为"unkonwn",如果有货且能解出货物码,则为货物码的码值 |
| TaskMode | 任务模式 | UInt8 | | |
| VehicleId | AMR编号 | UInt16 | | |
| X | 当前标准X坐标 | Int32 | 当前所在站点的X坐标 | |
| Y | 当前标准Y坐标 | Int32 | 当前所在站点的Y坐标 | |
| GoodsQuantity | 当前货物数量。数组形式。 对于单滚筒等单个托盘的机型,只需关注数据0; 对于双滚筒,滚筒1的数量放到数据0中,滚筒2的货物数量放到数据1中。 其它机型以此类推。 | UInt16 \[4\] | 数组形式 | |
| Exceptions | 异常数组,存放异常的所有异常ID | UInt16 \[4\] | 数组形式 | |
| InDock | 是否已进行精准停靠(是否能在原地直接执行对接任务) | bool | 默认为true | |
| GoodsSlots | 车载货位信息 | [Object] | 参见[货位信息](#货位信息) | |
例如:
```json
{
"content": {
"CurBattery": {
"ChargingCurrent": 0,
"DischargingCurrent": 3.8,
"SOC": 57,
"Voltage": 48.025
},
"CurOrientation": -179.776,
"CurX": 943.739,
"CurY": 99.3311,
"RackCurOrientation": -179.246,
"RackCurPosition": 0.0440616,
"SeqNo": 93585,
"StorageRacksNo": "R031",
"TaskMode": 2,
"VehicleId": 2,
"X": 900,
"Y": 100,
"GoodsQuantity": [
2,
0,
0,
0
],
"Exceptions": [],
"InDock": true
},
"id": 20060
}
```
### 心跳 20100
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| --------------------- | ------------------ | -------- | ------------------------------------------------------------------------------------- |
| **ID#20100** | **指令ID** | | |
| SeqNo | 作业序号 | Uint32 | |
| VehicleId | AMR编号 | Uint16 | |
| Battery | 电量 | UInt8 | |
| Uptime | 上电至今的毫秒数 | UInt64 | |
| Temperature.Battery | 电池温度 | Double | 单位:摄氏度 备注:一个电池可能有多个(不同型号的)温度传感器,目前只上报最高的温度 |
例如:
```json
{
"content": {
"SentTime": 1685589750403,
"Battery": 67,
"Temperature": {
"Battery": 30.5
},
"CreateMonoTime": 822652887,
"CreateTime": 1685589750403,
"SeqNo": 730,
"Uptime": 822652887,
"VehicleId": 1
},
"id": 20100
}
```
### 小车主程序启动 20149
代表车的主程序已经启动,接下来马上会进行初始化,如果初始化顺利完成便会上报上线消息。
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ------------ | ------------- | -------- | ---------------------------------------------------------------------------------------- |
| **ID#20149** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | |
| VehicleId | AMR编号 | UInt16 | |
| Battery | 电量 | UInt8 | |
| AGVModel | AMR底盘型号 | String | 对于底盘和功能一体的版本,由底盘型号便知道功能;对于通用底盘,底盘型号指定了底盘的版本 |
| AGVFnModel | AMR功能型号 | String | 底盘之上的工装的型号 |
### 小车上线 20150
代表车已经完成初始化,可以接收任务了。
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ----------- | ---------- | -------- | ----- |
| **ID#20150** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | |
| VehicleId | AMR编号 | UInt16 | |
例如:
```json
{
"content": {
"Battery": 88,
"SeqNo": 1,
"VehicleId": 0
},
"id": 20150
}
```
### 小车离线 20200
注意:目前未实现此报文
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ----------- | --------- | -------- | ----- |
| **ID#20200** | **指令ID** | | |
| VehicleId | AMR编号 | UInt16 | |
例如:
```json
{
"id": 20200,
"content": {
"VehicleId": 23412
}
}
```
### 异常上报 20250
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| -------------- | -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| **ID#20250** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | |
| VehicleId | AMR编号 | UInt16 | |
| ErrCode | 异常代码 | UInt8 | 1. 货架编号不匹配 2. 地标异常 3. 电量低 |
| MechNo | 异常机构代码 | UInt8 | 对于哪吒飞梭类的机型,增加MechNo用于指示异常具体是哪一层发生的。 MechNo: 1- 机构1^\[1\]^异常 2- 机构2异常 3- 机构3异常 |
| ErrMsg | 异常详情 | Struct | 根据不同的异常,字段不一样,比如针对ErrCode为2电量低的情况,ErrMsg中使用CurBattery标识当前电量; |
| ErrEvtType | 异常事件类型 | UInt8 | 0:无 1:开始 2:更新 3:结束 4:开始并结束 |
| ErrLevel | 异常等级 | UInt8 | 0:Undetermined 12:Info 13:Warning 14:Critical 15:Fatal |
| ErrLifecycle | 异常生命周期 | UInt8 | 0:易变的 1:一次性 2:持续性 3:任务期间 |
**注[1]:对于哪吒三层飞梭,机构1表示最低的取放货机构;机构2表示中间的取放货机构;机构3表示最高的取放货机构**。
例如:
```json
{
"id": 20250,
"content": {
"VehicleId": 23412,
"ErrCode": 2,
"ErrMsg": {
"CurBattery": 50
}
}
}
```
### 开机上报 20147
在开机后上报一次,此后AMR程序重启不会重新上报(与ID#20149的差异),除非人为清除内部记录已上报的标志。
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ------------ | ------------------ | -------- | ---------------------------------------------------------------------------------------- |
| **ID#20147** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | |
| VehicleId | AMR编号 | UInt16 | |
| Battery | 电量 | UInt8 | |
| AGVModel | AMR底盘型号 | String | 对于底盘和功能一体的版本,由底盘型号便知道功能;对于通用底盘,底盘型号指定了底盘的版本 |
| AGVFnModel | AMR功能型号 | String | 底盘之上的工装的型号 |
| Uptime | 上电至今的毫秒数 | UInt64 | |
### 关机上报 20148
在收到关机信号后上报此消息。
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ----------- | ------------------ | -------- | ----- |
| **ID#20148** | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | |
| VehicleId | AMR编号 | UInt16 | |
| Battery | 电量 | UInt8 | |
| Uptime | 上电至今的毫秒数 | UInt64 | |
### 任务状态上报 20011
任务状态变化时会上报此消息
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ----------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **ID#** 20011 | **指令ID** | | |
| SeqNo | 作业序号 | UInt32 | |
| VehicleId | AMR编号 | UInt16 | |
| EventId | 事件ID | UInt8 | 0:无变化 1:任务模式改变 2:任务接收成功 3:任务开始 4:任务完成 5:任务已取消 6:任务已停止 7:任务已恢复 8:任务已更变 |
| Info | 由事件ID决定 | Object | 默认消息(0,2,3,5,6,7):任务状态改变消息 1:任务模式消息 4:任务完成消息 8:任务类型改变消息 |
- 任务完成消息
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ------------------ | --------------- | ---------------- | ------------------ |
| OperationType | 任务类型 | | |
| OperationResult | 作业结果 | Int32 | 参考linux errno |
| CurLogicX | 当前X逻辑坐标 | Int32 | |
| CurLogicY | 当前Y逻辑坐标 | Int32 | |
| CurX | 当前X坐标 | Double | |
| CurY | 当前Y坐标 | Double | |
| CurDirection | 当前方向 | #### Direction | 参考通用类型定义 |
| StorageRacksNo | 货架号 | String | |
| StorageDirection | 货架朝向 | #### Direction | 参考通用类型定义 |
| Battery | 电量百分比 | Uint8 | |
| GoodsSlots | 车载货位信息 | [Object] | 参见[货位信息](#货位信息) |
- 任务模式改变消息
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| -------------- | ---------------- | --------------- | ------------------ |
| PrevTaskMode | 上一个任务模式 | TaskMode | 参考通用类型定义 |
| TaskMode | 当前任务模式 | #### TaskMode | 参考通用类型定义 |
- 任务类型改变消息
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ------------------- | ---------------- | --------------- | ------------------ |
| PrevOperationType | 上一个任务类型 | OperationType | 参考通用类型定义 |
| OperationType | 当前任务类型 | OperationType | 参考通用类型定义 |
- 任务状态改变消息
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| --------------- | -------------- | --------------- | ------------------ |
| OperationType | 当前任务类型 | OperationType | 参考通用类型定义 |
### 货位信息
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ----------------- | ------------------------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ID | 货位ID | UInt8 | 从0开始,每款车自己定义的车载货位的ID |
| Name | 货位名称 | String | 每款车自己定义的货位的名称 |
| ShouldHaveGoods | 逻辑上是否应该有货 | Bool | 有些车型有传感器来检测货物状态,这时实际的货物状态跟逻辑状态就可能不同 |
| DetectedStatus | 检测到的货位状态 | UInt8 | 0: 正常 1: 冲突,即传感器检测到的状态跟逻辑状态有冲突。逻辑上有货,实际无货,是一种冲突;逻辑上货的属性(比如高度)跟实际检测到的属性不同,也会产生冲突;对于一个货位有多个货物的情况,需要查看货物信息来查找具体是哪个货物出现了什么冲突 |
| Height | 货位放货平面离地高度 | Double | |
| Position | 货位相对原点(初始化后货位所在位置)的相对位置 | Object | 包含以下成员: OffsetX:Double类型 OffsetY:Double类型 OffsetZ:Double类型,代表在高度上的位移 Orientation:Double类型,用角度(Degree)表示 |
| Goods | 货物信息 | \[Object\] | 一个货位可能有多个货物,因此用数组表示;多数情况下一个货位仅存放一个货物 |
货物信息定义如下:
| **Field Name** | **Field Description** | **Format** | **Values/** **Remarks** |
| ------------------------- | ------------------------ | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Category | 货物类型 | UInt | 对于一类设备可能取放不同货物时有用;如果只有一类货物,则可以不(在地图或者RCS下发的任务报文中)指定,此种情况下报文中不包含此字段 |
| DetectedId | 传感器检测到的货物ID | String | 比如飞梭通过摄像头检测到的箱码、FM系列通过对上摄像头检测到的货架底部二维码等;如果车型没配备可以检测货物ID的传感器,则报文中不包含此字段 |
| UpperSystemDefinedId | 上位系统传过来的货物ID | String | 通常上位传过来的ID应该跟检测到的ID一致,否则会报错;对于没有检测ID功能的车型,不做校验而只是回传该值;有些项目可能会希望两个ID分开,对应此类需求可以特别设置不校验两类ID;也许有些项目会同时传入两种ID,当前暂不支持;如果上位没传入ID,则报文中不包含此字段 |
| HeightLevel | 高度等级 | UInt8 | 部分车型能检测货物高度,等级从0(最矮,默认值)开始,每一级递增1,各个等级的具体含义与车型有关,需要另行约定;~~如果没有对应的检测传感器,则报文中不包含此字段;~~ 如果有传感器但是由于传感器异常等原因无法获取到检测结果,则设置该值为0xff,代表状态未知。 |
| LengthLevel | 长度等级 | UInt8 | 类似上面,代表货物长度所属类别 |
| WidthLevel | 宽度等级 | UInt8 | 类似上面,代表货物宽度所属类别 |
| WeightLevel | 重量等级 | UInt8 | 类似上面,代表货物重量所属类别 |
| ApproximateWeight | 测算的重量,不一定准 | Double | 单位是千克 |
| Orientation | 货物在地图上的朝向 | Double | 角度(Degree)值 |
| RelativePosition | 货物相对机器人的位置 | Object | 包含以下成员: OffsetX:Int32类型 OffsetY:Int32类型 Orientation:Double类型,用角度(Degree)表示 |
| DetectedStatusConflicts | 检测到的货物状态冲突 | \[UInt8\] | 1:货物ID不匹配 2:货物高度不匹配 3:货物宽度不匹配 4:货物长度不匹配 5:应该有货但没检测到有货 6:应该没货但检测到有货 |
## 通用类型定义
#### OperationType
- 任务类型
- 类型 Uint8
| **Value** | **Description** | **Details** |
| ----- | ---------------------------- | -- |
| 0 | 运输 | |
| 1 | 接货 | |
| 2 | 卸货 | |
| 3 | 充电 | |
| 4 | 提升移栽取货或卸货 | |
| 5 | 滚筒取货或卸货(双向作业) | |
| 135 | 旋转货架 | |
| 136 | 旋转车身 | |
| 143 | 卷帘门控制 | |
| 224 | 等待就绪 | |
#### TaskMode
- 任务模式
- 类型 Uint8
| **Value** | **Description** | **Details** |
| --- | ---------------- | -------------------------- |
| 0 | 空闲模式 | |
| 1 | 初始化模式 | |
| 2 | 任务模式 | |
| 3 | 单动作模式 | |
| 4 | 手动模式 | |
| 5 | 遥控器模式 | |
| 6 | 充电模式 | |
| 7 | 任务被中断模式 | 有任务,但未收到终点坐标 |
| 8 | 自定义模式 | |
#### Direction
- 方向
- 类型 Uint8
| **Value** | **Description** | **Details** |
| ---- | ---------- | -- |
| 0 | X轴正向 | |
| 1 | Y轴正向 | |
| 2 | X轴负向 | |
| 3 | Y轴负向 | |
| 15 | 未知方向 | |
## 简要调用流程
```mermaid
sequenceDiagram
participant 操作员
participant RCS
participant AMR
activate RCS
critical 初始化
操作员->>+AMR: 上电开机
activate AMR
AMR-->>RCS: 主程序启动上报(20149)
AMR-->>RCS: 开机上报(20147)
AMR-->>RCS: 位姿上报(20020)
AMR-->>RCS: 上线上报(20150)
RCS->>AMR: 下发配置(10100)
end
critical 执行任务
RCS->>AMR: 作业指令(10010)
AMR-->>RCS: 任务开始(20011)
loop
AMR-->>RCS: (每当位置变化)位姿上报(20020)
RCS->>AMR: 更新任务路径(10010)
Note right of RCS: 只有当每次只下发小段路径时才需要动态更新路径
end
AMR-->>RCS: 任务完成(20010和20011)
end
critical 心跳上报
AMR-->>RCS: 心跳(20100)
Note left of AMR: 没有其它上报报文时才发心跳
end
critical 心跳下发
RCS->>AMR: 心跳(10100)
end
critical 消息应答
AMR-->>RCS: 除应答和心跳外的其它报文
RCS->>AMR: 应答(10050,SeqNo为所应答的报文的SeqNo值)
RCS->>AMR: 除应答和心跳外的其它报文
AMR-->>RCS: 应答(20050, SeqNo为所应答的报文的SeqNo值)
end
critical 异常上报
AMR->>AMR: 检测到异常
AMR-->>RCS: 异常上报(20250)
end
deactivate AMR
deactivate RCS
```
## 常见问题
### 任务创建时候路径分段数是什么?
指AMR作业指令(10010)中下发给AMR的路径段数,即Link字段(内容为数组)的元素的个数,其中每个元素为一段路径,一般每两个相邻站点生成一段路径,也可以只指定从当前起点到转弯位置作为一段路径。
比如:
```json
{
"id": 10010,
"content": {
"SeqNo": 4,
"OperationType": 0,
"StorageRacksNo": "R123",
"StartX": 7,
"StartY": 14,
"EndX": 1,
"EndY": 1,
"LinkCounts": 2,
"Link": [
{
"X": 7,
"Y": 1,
"Speed": 1000
},
{
"X": 1,
"Y": 1,
"Speed": 1000
}
]
}
}
```
代表从(7, 14)出发,走到终点(1, 1)的路径,其中起点由(StartX, StartY)来表示,先走到转弯点(7, 1),再从走到终点(1, 1)。
上述路径也可以分成两步发送,比如可以这样发:
```json
{
"id": 10010,
"content": {
"SeqNo": 4,
"OperationType": 0,
"StorageRacksNo": "R123",
"StartX": 7,
"StartY": 14,
"EndX": 1,
"EndY": 1,
"LinkCounts": 1,
"Link": [
{
"X": 7,
"Y": 1,
"Speed": 1000
}
]
}
}
```
```json
{
"id": 10010,
"content": {
"SeqNo": 5,
"OperationType": 0,
"StorageRacksNo": "R123",
"StartX": 7,
"StartY": 1,
"EndX": 1,
"EndY": 1,
"LinkCounts": 1,
"Link": [
{
"X": 1,
"Y": 1,
"Speed": 1000
}
]
}
}
```
上面两个命令报文将原有的从起点到终点的完整路径分成两个路径片段(注意SeqNo是不同的;一般是走到某个位置后再下发后续路段,以实现不需要提前规划完整路线,或者希望有更细粒度的交通管制控制的目的),AMR收到命令时会自动进行路径拼接。
### 电文应答的作用是什么?
因为网络存在不确定性(数据丢失和到达顺序错误。实际项目中都遇到过),所以通过应答消息来实现应用协议层的可靠性,保证上报的报文被对方收到后,才能下发/上报后续报文。
### 二维码内容的具体格式是什么?
外轮廓为5cmx5cm的QR码(码区域是3cmx3cm),目前有两种方式:
- 定长码:场地中所有码的间距都一样,码值即坐标值(比如"1,1"代表坐标(1, 1)),贴码的时候需要严格按照顺序粘贴,不需要安装额外的地图服务,但是限制较大,目前不推荐。
- 变长码:码值为数值(比如"000001"),粘贴时只需要保证方向和位置准确即可,不需要管码值。需要部署地图数据库,需要遍历学习过程。
### 如果AMR未接收RCS的心跳信息会怎么样?
目前不严格检查RCS端往AMR发的心跳信息,但是建议RCS检查AMR上报的心跳(及其它报文),以确定AMR是否在线。
### 异常情况中"易变的"是什么意思?
生命周期中的"易变的"(实际上应为"可变的"),指生命周期的属性可能发生改变,比如由"任务期间的"变成"持续的"。实际上这类异常极少。
### AMR初始化信息如何获取自己的位置信息?AMR是否需要初始化地图信息?
首次部署需要自动遍历地图来将码值与位置进行自动绑定。AMR初始化时检测所处位置的二维码,通过码值来确定自己的位置和朝向。
### "异常情况"包括哪些?
参考[请查看附件《AMR异常集》](assets/AMR异常集-20250417164721-ul9mqei.xlsx)

3
package.json

@ -13,8 +13,7 @@
"format": "prettier --write src/"
},
"dependencies": {
"@vueuse/core": "^13.2.0",
"fdir": "^6.4.6"
"@vueuse/core": "^13.2.0"
},
"devDependencies": {
"@ease-forge/runtime": "^1.0.12",

457
pnpm-lock.yaml

File diff suppressed because it is too large

4
src/components/ShowDialogWrap.vue

@ -53,9 +53,11 @@ export default {
dialogResolve: { type: Function, required: true },
dialogReject: { type: Function, required: true },
childCmp: { type: Object, default: undefined, required: false }
childCmp: { type: Object, default: undefined, required: false },
onMounted: { type: Function, default: undefined, required: false },
},
mounted() {
this.$props.onMounted?.(this);
},
data() {
return {

12
src/editor/ModelMain.vue

@ -52,11 +52,11 @@
</SplitArea>
<SplitArea class="section-center" :class="{'hidden-split':hideRight}" :size="calcCenterSize">
<el-tabs type="card" class="section-tabs" v-model="centerActiveName" @tab-click="handleCenterTabClick"
v-if="isShowEditor" :key="editorHash">
<el-tab-pane label="模型编辑" name="ModelEditor" lazy>
v-if="isModelOpen" :key="editorHash">
<el-tab-pane v-if="isShowEditor" label="模型编辑" name="ModelEditor" lazy>
<Model2DEditor @viewportChanged="setCurrentViewport" />
</el-tab-pane>
<el-tab-pane label="监控视图" name="ModelView" lazy>
<el-tab-pane v-if="isShowEditor" label="监控视图" name="ModelView" lazy>
<Model3DViewer />
</el-tab-pane>
<el-tab-pane label="楼层定义" name="ModelFile" lazy>
@ -103,13 +103,12 @@
</div>
</template>
<script>
import { markRaw } from 'vue'
import { renderIcon } from '@/utils/webutils.js'
import Split from '@/components/split/split.vue'
import SplitArea from '@/components/split/split-area.vue'
import { ModelMainInit, ModelMainMounted, ModelMainUnmounted } from './ModelMainInit.js'
import { getRootMenu } from '@/runtime/DefineMenu.js'
import { getWidgetByName, getWidgetBySide, getAllWidget } from '@/runtime/DefineWidget.js'
import { getAllWidget, getWidgetByName, getWidgetBySide } from '@/runtime/DefineWidget.js'
import Model2DEditor from './Model2DEditor.vue'
import Model3DViewer from './Model3DViewer.vue'
import { normalizeShortKey } from '@/utils/webutils.ts'
@ -188,6 +187,9 @@ export default {
}
},
computed: {
isModelOpen() {
return this.worldModel.state.isOpened;
},
worldModel() {
return window['worldModel']
},

128
src/editor/OpenProject.vue

@ -0,0 +1,128 @@
<script setup lang="ts">
import { onMounted, reactive, useTemplateRef } from "vue";
import { AgGridVue } from "ag-grid-vue3";
import { ElButton } from "element-plus";
import { type GridOptions } from "ag-grid-enterprise";
import { type GridApi, type GridReadyEvent } from "ag-grid-community";
import { Request } from "@ease-forge/shared";
import { localeText as localeTextCn } from "../components/yvTable/yv-aggrid-cn.locale";
import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-alpine.css";
defineOptions({
name: 'OpenProject',
});
//
const emit = defineEmits<{
"cancel": [];
"open": [row: any];
"add": [expose: OpenProjectExpose];
}>();
// Props
interface OpenProjectProps {
}
// props
const props = withDefaults(defineProps<OpenProjectProps>(), {});
// State
interface OpenProjectState {
grid1Data?: Array<any>;
}
// state
const state = reactive<OpenProjectState>({});
// Data
interface OpenProjectData {
gridSetting: Partial<GridOptions>;
api?: GridApi;
}
//
const data: OpenProjectData = {
gridSetting: {
localeText: localeTextCn,
// suppressNoRowsOverlay: true,
// suppressLoadingOverlay: true,
//
rowSelection: "single",
columnDefs: [
{ field: 'id', headerName: 'id', editable: false, hide: true },
{ field: 'projectUuid', headerName: '项目编号', editable: false },
{ field: 'projectLabel', headerName: '项目标题', editable: false },
{ field: 'projectVersion', headerName: '项目版本', editable: false },
{ field: 'server', headerName: '所在服务器', editable: false },
{ field: 'directoryData', headerName: '目录数据', editable: false },
{ field: 'otherData', headerName: '其他数据', editable: false },
{ field: 'createAt', headerName: '创建时间', editable: false },
{ field: 'createBy', headerName: '创建人', editable: false },
{ field: 'updateAt', headerName: '最后更新时间', editable: false },
{ field: 'updateBy', headerName: '更新人', editable: false },
],
onGridReady(event: GridReadyEvent) {
data.api = event.api
},
},
};
const grid = useTemplateRef<InstanceType<typeof AgGridVue>>("gridRef");
onMounted(loadData);
function loadData() {
Request.request.post("/api/workbench/LccModelManager@projectList").then(res => {
state.grid1Data = res.records;
});
}
function open() {
const row = data.api?.getSelectedRows()?.[0];
if (!row) return;
emit("open", row);
}
function add() {
emit("add", expose);
}
interface OpenProjectExpose {
state: OpenProjectState;
data: OpenProjectData;
loadData: () => void;
}
const expose: OpenProjectExpose = {
state,
data,
loadData,
};
//
defineExpose(expose);
export type {
OpenProjectProps,
OpenProjectState,
}
</script>
<template>
<div class="flex-column-container" style="height: 100%;">
<AgGridVue
ref="gridRef"
:class="['ag-theme-alpine', 'yv-table', 'hi-light-selected-row','allow-vertical-line', 'flex-item-fill']"
v-bind="{...data.gridSetting}"
:modelValue="state.grid1Data"
>
</AgGridVue>
<div class="flex-item-fixed" style="padding-top: 12px;padding-right: 12px;text-align: right;">
<ElButton type="primary" @click="open">打开</ElButton>
<ElButton type="primary" @click="add">新建项目</ElButton>
<ElButton @click="emit('cancel')">取消</ElButton>
</div>
</div>
</template>
<style scoped>
</style>

125
src/editor/menus/FileMenu.ts

@ -1,8 +1,55 @@
import { renderIcon, setQueryParam } from '@/utils/webutils.ts'
import { defineMenu } from '@/runtime/DefineMenu.ts'
import SvgCode from '@/components/icons/SvgCode'
import { nextTick } from 'vue'
import { createVNode, nextTick } from 'vue'
import type Viewport from '@/core/engine/Viewport.ts'
import DataForm from '@/components/data-form/DataForm.vue'
import OpenProject from '../OpenProject.vue'
import { Request } from '@ease-forge/shared'
function addProject(successful?: Function) {
const data = {
server: window.location.origin,
};
system.showDialog(createVNode(DataForm, {
style: {
paddingRight: "12px",
},
data: data,
formFields: [
{
dataPath: 'projectUuid', label: '项目编号', input: 'Input',
inputProps: {
placeholder: '请输入项目唯一编号',
},
},
{
dataPath: 'projectLabel', label: '项目标题', input: 'Input',
inputProps: {
placeholder: '请输入项目标题',
},
},
],
columnCount: 1,
labelWidth: "80px",
}), {
title: '创建项目',
width: 480,
height: 200,
showClose: true,
showMax: false,
showCancelButton: true,
showOkButton: true,
okButtonText: "创建",
cancelButtonText: "取消",
}).then(() => {
Request.request.post("/api/workbench/LccModelManager@addProject", data).then(()=> {
system.msg("创建成功");
successful?.();
})
}).finally();
}
export default defineMenu((menus) => {
menus.insertChildren('file',
@ -12,25 +59,65 @@ export default defineMenu((menus) => {
[
{
name: 'open', label: '打开', icon: SvgCode.open, order: 1, tip: 'Ctrl+O',
click: async () => {
worldModel.state.isOpened = false
worldModel.state.isDraft = false
worldModel.state.catalog = []
worldModel.state.catalogCode = ''
worldModel.state.stateManagerId = ''
setQueryParam('store', '')
click: async () => {
worldModel.state.isOpened = false
worldModel.state.isDraft = false
worldModel.state.catalog = []
worldModel.state.catalogCode = ''
worldModel.state.stateManagerId = ''
setQueryParam('store', '')
system.showLoading()
try {
await nextTick()
system.showLoading()
try {
await nextTick()
const res = await import('@/example/example1')
worldModel.state.isDraft = false
await worldModel.loadWorldFromRemoting(res.default)
const res = await import('@/example/example1')
worldModel.state.isDraft = false
await worldModel.loadWorldFromRemoting(res.default)
} finally {
system.clearLoading()
}
} finally {
system.clearLoading()
}
},
click2: async () => {
let dialog: any = null;
system.showDialog(createVNode(OpenProject, {
onCancel: () => dialog?.onClose(),
onOpen: async row => {
dialog?.onClose();
const veryBigData = JSON.parse(row.otherData);
veryBigData.catalog = JSON.parse(row.directoryData);
veryBigData.items = [];
worldModel.state.isOpened = false
worldModel.state.isDraft = false
worldModel.state.catalog = []
worldModel.state.catalogCode = ''
worldModel.state.stateManagerId = ''
setQueryParam('store', '')
system.showLoading()
try {
await nextTick()
worldModel.state.isDraft = false
await worldModel.loadWorldFromRemoting(veryBigData)
} finally {
system.clearLoading()
}
},
onAdd: that => {
addProject(() => that.loadData());
},
}), {
title: '打开项目',
width: 1500,
height: 500,
showClose: true,
showMax: true,
showCancelButton: false,
showOkButton: false,
okButtonText: "创建",
cancelButtonText: "取消",
onMounted: d => dialog = d,
}).finally();
}
},
{
@ -42,9 +129,9 @@ export default defineMenu((menus) => {
}
},
{
name: 'saveAs', label: '新建项目111', icon: renderIcon('ModelFile'), order: 3,
name: 'saveAs', label: '新建项目', icon: renderIcon('ModelFile'), order: 3,
click: () => {
system.msg('新建项目111')
addProject();
}
}
]

33
src/modules/cl2/Cl23dObject.ts

@ -3,6 +3,7 @@ import {CSG} from "three-csg-ts";
//@ts-ignore
import {mergeGeometries} from 'three/addons/utils/BufferGeometryUtils.js'
import gsap from "gsap";
// import mqtt from 'mqtt';
export default class Cl23dObject extends THREE.Object3D {
@ -326,6 +327,38 @@ export default class Cl23dObject extends THREE.Object3D {
groupPillar.add(ptrPillarMesh)
groupPillar.add(ptrForkMesh)
this.add(groupPillar)
// try {
// // 安全连接配置
// const client = mqtt.connect('mqtt://127.0.0.1:9001', {
// path: '/mqtt',
// clientId: 'virtual-' + item.id,
// clean: true,
// connectTimeout: 10000,
// username: 'user',
// password: 'user',
// unixSocket: true,
// keepalive: 60,
// });
//
// // 事件绑定
// client.on('connect', () => {
// console.log('Connected');
// client.subscribe(['sensor/#', 'alerts'], { qos: 0 });
// client.publish('status', 'online', { retain: true });
// });
//
// client.on('message', (topic, msg) => {
// console.log(`[${topic}] ${msg}`);
// });
//
// client.on('error', (error) => {
// console.error('Error:', error);
// });
// } catch (e) {
// console.error(e)
// }
}
animationShowFork(z: number): Promise<void> {

7
src/runtime/System.ts

@ -3,10 +3,10 @@ import _ from 'lodash'
import localforage from 'localforage'
import JSON5 from 'json5'
import hotkeys from 'hotkeys-js'
import { defineComponent, h, markRaw, nextTick, reactive, toRaw, unref, type App, createApp, type Component } from 'vue'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import { type App, type Component, createApp, defineComponent, h, markRaw, nextTick, reactive, toRaw, unref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { QuestionFilled } from '@element-plus/icons-vue'
import { renderIcon, createShortUUID, setQueryParam, getQueryParams } from '@/utils/webutils.ts'
import { createShortUUID, getQueryParams, renderIcon, setQueryParam } from '@/utils/webutils.ts'
import type { showDialogOption } from '@/SystemOption'
import ShowDialogWrap from '@/components/ShowDialogWrap.vue'
import LoadingDialog from '@/components/LoadingDialog.vue'
@ -290,4 +290,5 @@ export interface ShowDialogOption {
showOkButton?: boolean
cancelButtonText?: string
okButtonText?: string
onMounted?: (dialog:any)=> void;
}

Loading…
Cancel
Save