Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
I
integrated-scheduling-v3
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张晓彤
integrated-scheduling-v3
Commits
a9d69a45
Commit
a9d69a45
authored
Jan 25, 2024
by
Allvey
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
穿越装载区优化-独立分支
parent
1aba9343
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
235 additions
and
208 deletions
+235
-208
config.json
config.json
+3
-3
group.py
core/group.py
+191
-140
excavator.py
equipment/excavator.py
+1
-1
truck.py
equipment/truck.py
+1
-1
settings.py
settings.py
+2
-2
area_analysis.py
util/area_analysis.py
+37
-61
No files found.
config.json
View file @
a9d69a45
{
{
"para"
:
{
"para"
:
{
"log_path"
:
"/Users/guoao/Desktop/Log"
,
"log_path"
:
"/Users/guoao/Desktop/Log"
,
"empty_speed"
:
1
5
,
"empty_speed"
:
1
0
,
"heavy_speed"
:
1
5
,
"heavy_speed"
:
1
0
,
"dump_target_mass"
:
5000
,
"dump_target_mass"
:
5000
,
"excavator_target_mass"
:
5000
"excavator_target_mass"
:
5000
},
},
...
@@ -27,6 +27,6 @@
...
@@ -27,6 +27,6 @@
"gothrough"
:
{
"gothrough"
:
{
"closer_area_name"
:
"装-2"
,
"closer_area_name"
:
"装-2"
,
"further_area_name"
:
"装1"
,
"further_area_name"
:
"装1"
,
"factor"
:
"0.
4
"
"factor"
:
"0.
2
"
}
}
}
}
core/group.py
View file @
a9d69a45
...
@@ -564,74 +564,96 @@ class GroupDispatcher:
...
@@ -564,74 +564,96 @@ class GroupDispatcher:
self
.
logger
.
warning
(
f
'车辆 {truck_name} 位置信息丢失'
)
self
.
logger
.
warning
(
f
'车辆 {truck_name} 位置信息丢失'
)
truck_locate
=
None
truck_locate
=
None
# 获取车辆临时字段
#
#
获取车辆临时字段
try
:
#
try:
truck_is_temp
=
self
.
group
.
truck
.
truck_is_temp
[
truck_id
]
#
truck_is_temp = self.group.truck.truck_is_temp[truck_id]
except
Exception
as
es
:
#
except Exception as es:
truck_is_temp
=
False
#
truck_is_temp = False
self
.
logger
.
error
(
f
'车辆临时字段异常 {es}'
)
#
self.logger.error(f'车辆临时字段异常 {es}')
# 车辆
停止或者车辆位于卸载区内
, 调度车辆前往装载区
# 车辆
当前 1)位于备停区; 2)位于卸载区; 3)车辆重载行驶
, 调度车辆前往装载区
if
truck_task
in
[
-
2
,
3
,
4
,
5
]:
if
truck_task
in
[
-
2
,
3
,
4
,
5
]:
if
truck_task
==
3
:
if
truck_task
==
3
:
self
.
logger
.
info
(
"正常重载行驶, 执行重载周期调度"
)
self
.
logger
.
info
(
"正常重载行驶, 执行重载周期调度
, 调度车辆前往挖机
"
)
self
.
heavy_period_dispatch
(
s
,
truck_dispatch
,
truck_id
,
truck_info
,
truck_task
,
truck_trip
)
elif
truck_task
==
-
2
:
self
.
logger
.
info
(
"备停区内, 执行重载周期调度, 调度车辆前往挖机"
)
else
:
else
:
self
.
logger
.
info
(
"卸载区内, 执行重载周期调度"
)
self
.
logger
.
info
(
"卸载区内, 执行重载周期调度, 调度车辆前往挖机"
)
self
.
heavy_period_dispatch
(
s
,
truck_dispatch
,
truck_id
,
truck_info
,
truck_task
,
truck_trip
)
self
.
heavy_period_dispatch
(
s
,
truck_dispatch
,
truck_id
,
truck_info
,
truck_task
,
truck_trip
)
# 车辆
位于装载区内
, 调度车辆前往卸载区
# 车辆
当前 1)位于装载区; 2)车辆空载行驶
, 调度车辆前往卸载区
elif
truck_task
in
[
0
,
1
,
2
]:
elif
truck_task
in
[
0
,
1
,
2
]:
# 判断是否开启穿越装载区调度
planned
=
False
if
truck_task
==
0
and
self
.
gothrough_active
and
truck_locate
is
not
None
:
self
.
logger
.
info
(
"穿越装载区调度开启"
)
self
.
logger
.
info
(
f
'车辆 {truck_name}'
)
self
.
gothroghdispatcher
.
update_lanes_info
()
self
.
logger
.
info
(
f
'车辆位置 {truck_locate}'
)
self
.
logger
.
info
(
f
'车辆任务状态 {truck_info.get_sate()}'
)
try
:
excavator_prise_location
=
get_excavator_prise_location
(
self
.
gothroghdispatcher
.
closer_excavator_id
)
if
truck_task
==
0
:
truck_prise_location
=
self
.
group
.
truck
.
truck_current_prise_location
[
truck_id
]
# 判断是否开启穿越装载区调度
except
Exception
as
es
:
if
self
.
gothrough_active
and
truck_locate
is
not
None
:
self
.
logger
.
error
(
"挖机或车辆经纬信息未知"
)
self
.
logger
.
info
(
"穿越装载区调度开启"
)
self
.
logger
.
error
(
es
)
excavator_prise_location
=
[
-
1
,
-
1
]
# 更新判定路段区域
truck_prise_location
=
[
-
1
,
-
1
]
self
.
gothroghdispatcher
.
update_lanes_info
()
self
.
logger
.
info
(
f
'挖机经纬度信息 {excavator_prise_location}'
)
self
.
logger
.
info
(
f
'车辆经纬度信息 {truck_prise_location}'
)
from
haversine
import
haversine
self
.
logger
.
info
(
haversine
(
excavator_prise_location
,
truck_prise_location
))
if
excavator_prise_location
[
0
]
!=
-
1
and
\
truck_prise_location
[
0
]
!=
-
1
and
\
haversine
(
excavator_prise_location
,
truck_prise_location
)
>
0.000
and
\
truck_locate
in
self
.
gothroghdispatcher
.
closer_area_backtrack_lanes
:
self
.
gothroghdispatcher
.
redispatch_request
(
request_truck_id
=
truck_id
,
truck
=
self
.
group
.
truck
,
truck_dispatch
=
truck_dispatch
)
# 判断是否开启二次调度
elif
self
.
group
.
topo
is
not
None
and
truck_locate
is
not
None
:
self
.
logger
.
info
(
"潜在二次调度车辆状态"
)
# 车辆当前位于交叉路口前,且排队等待
self
.
logger
.
info
(
f
'车辆 {truck_name}'
)
self
.
logger
.
info
(
f
'车辆位置 {truck_locate}'
)
self
.
logger
.
info
(
f
'车辆状态 {self.group.truck.truck_current_state[truck_id]}'
)
self
.
logger
.
info
(
f
'车辆临时 {truck_is_temp}'
)
self
.
logger
.
info
(
self
.
group
.
topo
.
cross_bf_lanes
)
self
.
logger
.
info
(
self
.
group
.
truck
.
truck_current_state
)
if
(
truck_locate
in
self
.
group
.
topo
.
cross_bf_lanes
)
and
(
not
truck_is_temp
):
self
.
logger
.
info
(
"触发二次调度"
)
try
:
try
:
truck_info
.
redispatch
=
\
# 获取近端装载区入场点经纬度信息
self
.
redispatcher
.
redispatch_to_excavator
(
truck_id
,
truck_dispatch
,
truck_locate
,
excavator_prise_location
=
get_excavator_prise_location
(
self
.
gothroghdispatcher
.
closer_excavator_id
)
truck_trip
,
truck_info
)
# 获取车辆经纬度信息
truck_prise_location
=
self
.
group
.
truck
.
truck_current_prise_location
[
truck_id
]
except
Exception
as
es
:
except
Exception
as
es
:
self
.
logger
.
error
(
"
二次调度至装载点失败
"
)
self
.
logger
.
error
(
"
挖机或车辆经纬信息未知
"
)
self
.
logger
.
error
(
es
)
self
.
logger
.
error
(
es
)
excavator_prise_location
=
[
-
1
,
-
1
]
truck_prise_location
=
[
-
1
,
-
1
]
self
.
logger
.
info
(
f
'挖机经纬度信息 {excavator_prise_location}'
)
self
.
logger
.
info
(
f
'车辆经纬度信息 {truck_prise_location}'
)
from
haversine
import
haversine
truck_to_entrance_point_dis
=
haversine
(
excavator_prise_location
,
truck_prise_location
)
self
.
logger
.
info
(
f
'车辆到近端装载区入场点距离 {truck_to_entrance_point_dis}'
)
# 若车辆到入场点距离大于指定阈值
if
excavator_prise_location
[
0
]
!=
-
1
and
\
truck_prise_location
[
0
]
!=
-
1
and
\
haversine
(
excavator_prise_location
,
truck_prise_location
)
>
0.000
and
\
truck_locate
in
self
.
gothroghdispatcher
.
closer_area_backtrack_lanes
[
0
:
2
]:
try
:
self
.
logger
.
info
(
"触发穿越装载区调度"
)
planned
=
self
.
gothroghdispatcher
.
redispatch_request
(
request_truck_id
=
truck_id
,
truck
=
self
.
group
.
truck
,
truck_dispatch
=
truck_dispatch
,
truck_info
=
truck_info
)
self
.
logger
.
info
(
f
'穿越装载区调度成功? {planned}'
)
except
Exception
as
es
:
self
.
logger
.
error
(
"穿越调度失败"
)
self
.
logger
.
error
(
es
)
# 未触发穿越装载区调度, 判断是否开启二次调度
if
not
planned
and
self
.
group
.
topo
is
not
None
and
truck_locate
is
not
None
:
self
.
logger
.
info
(
"二次调度开启"
)
# 车辆当前位于交叉路口前,且排队等待
self
.
logger
.
info
(
"交叉口前路段id列表"
)
self
.
logger
.
info
(
self
.
group
.
topo
.
cross_bf_lanes
)
self
.
logger
.
info
(
truck_info
.
get_sate
())
if
truck_locate
in
self
.
group
.
topo
.
cross_bf_lanes
:
self
.
logger
.
info
(
"触发二次调度"
)
try
:
truck_info
.
redispatch
=
\
self
.
redispatcher
.
redispatch_to_excavator
(
truck_id
,
truck_dispatch
,
truck_locate
,
truck_trip
,
truck_info
)
planned
=
True
except
Exception
as
es
:
self
.
logger
.
error
(
"二次调度至装载点失败"
)
self
.
logger
.
error
(
es
)
planned
=
False
# 穿越调度和二次调度均不触发,正常空载调度
# 穿越调度和二次调度均不触发,正常空载调度
else
:
if
not
planned
or
truck_task
!=
0
:
self
.
logger
.
info
(
"正常空载行驶,执行空载周期调度"
)
self
.
logger
.
info
(
"正常空载行驶,执行空载周期调度"
)
self
.
empty_period_dispatch
(
s
,
truck_dispatch
,
truck_id
,
truck_info
,
truck_trip
)
self
.
empty_period_dispatch
(
s
,
truck_dispatch
,
truck_id
,
truck_info
,
truck_trip
)
...
@@ -1064,18 +1086,35 @@ class GoThroughDispatcher:
...
@@ -1064,18 +1086,35 @@ class GoThroughDispatcher:
def
__init__
(
self
,
group
:
Group
):
def
__init__
(
self
,
group
:
Group
):
self
.
logger
=
get_logger
(
"zxt.GoThroughDispatcher"
)
self
.
logger
=
get_logger
(
"zxt.GoThroughDispatcher"
)
self
.
group
=
group
self
.
group
=
group
self
.
closer_area_name
=
None
self
.
closer_area_id
=
None
self
.
further_area_name
=
None
self
.
further_area_id
=
None
self
.
closer_excavator_id
=
None
self
.
further_excavator_id
=
None
self
.
closer_excavator_state
=
None
self
.
further_excavator_state
=
None
self
.
closer_area_backtrack_lanes
=
None
self
.
further_area_backtrack_lanes
=
None
def
update_lanes_info
(
self
):
def
update_lanes_info
(
self
):
"""
Obtain gothrough trigger area, and lane set between closer and further loading areas.
"""
'''
1. 读取装载区、挖机、入场点信息
'''
try
:
try
:
with
open
(
json_file
,
encoding
=
'UTF-8'
)
as
f
:
with
open
(
json_file
,
encoding
=
'UTF-8'
)
as
f
:
load_value
=
json
.
load
(
f
)
load_value
=
json
.
load
(
f
)
gothrough_config
=
load_value
[
"gothrough"
]
gothrough_config
=
load_value
[
"gothrough"
]
# 读取近端装载区名称及id
self
.
closer_area_name
=
gothrough_config
[
"closer_area_name"
]
self
.
closer_area_name
=
gothrough_config
[
"closer_area_name"
]
self
.
closer_area_id
=
str
(
self
.
closer_area_id
=
str
(
session_postgre
.
query
(
DiggingWorkArea
)
.
filter_by
(
Name
=
self
.
closer_area_name
)
.
first
()
.
Id
)
session_postgre
.
query
(
DiggingWorkArea
)
.
filter_by
(
Name
=
self
.
closer_area_name
)
.
first
()
.
Id
)
# 读取远端装载区名称及id
self
.
further_area_name
=
gothrough_config
[
"further_area_name"
]
self
.
further_area_name
=
gothrough_config
[
"further_area_name"
]
self
.
further_area_id
=
str
(
self
.
further_area_id
=
str
(
session_postgre
.
query
(
DiggingWorkArea
)
.
filter_by
(
Name
=
self
.
further_area_name
)
.
first
()
.
Id
)
session_postgre
.
query
(
DiggingWorkArea
)
.
filter_by
(
Name
=
self
.
further_area_name
)
.
first
()
.
Id
)
...
@@ -1088,34 +1127,32 @@ class GoThroughDispatcher:
...
@@ -1088,34 +1127,32 @@ class GoThroughDispatcher:
self
.
logger
.
info
(
"DispatchInfo.load_excavator_dict"
)
self
.
logger
.
info
(
"DispatchInfo.load_excavator_dict"
)
self
.
logger
.
info
(
DispatchInfo
.
load_excavator_dict
)
self
.
logger
.
info
(
DispatchInfo
.
load_excavator_dict
)
# 读取
两个挖机id
# 读取
近端及远端挖机ids
self
.
logger
.
info
(
self
.
closer_area_id
in
DispatchInfo
.
load_excavator_dict
)
self
.
logger
.
info
(
self
.
closer_area_id
in
DispatchInfo
.
load_excavator_dict
)
self
.
logger
.
info
(
self
.
further_area_id
in
DispatchInfo
.
load_excavator_dict
)
self
.
logger
.
info
(
self
.
further_area_id
in
DispatchInfo
.
load_excavator_dict
)
if
(
self
.
closer_area_id
in
DispatchInfo
.
load_excavator_dict
)
and
\
if
(
self
.
closer_area_id
in
DispatchInfo
.
load_excavator_dict
)
and
\
(
self
.
further_area_id
in
DispatchInfo
.
load_excavator_dict
):
(
self
.
further_area_id
in
DispatchInfo
.
load_excavator_dict
):
self
.
closer_excavator_id
,
self
.
further_excavator_id
=
DispatchInfo
.
load_excavator_dict
[
self
.
closer_excavator_id
,
self
.
further_excavator_id
=
DispatchInfo
.
load_excavator_dict
[
self
.
closer_area_id
],
\
self
.
closer_area_id
],
\
DispatchInfo
.
load_excavator_dict
[
self
.
further_area_id
]
DispatchInfo
.
load_excavator_dict
[
self
.
further_area_id
]
else
:
else
:
self
.
logger
.
error
(
"装载点信息错误"
)
self
.
logger
.
error
(
"装载点信息错误"
)
return
return
False
# 读取挖机状态
# 读取挖机状态
self
.
closer_excavator_state
,
self
.
further_excavator_state
=
get_excavator_state
(
self
.
closer_excavator_id
),
\
self
.
closer_excavator_state
,
self
.
further_excavator_state
=
get_excavator_state
(
self
.
closer_excavator_id
),
\
get_excavator_state
(
self
.
further_excavator_id
)
get_excavator_state
(
self
.
further_excavator_id
)
# 读取两个装载区入场点id
# 读取两个装载区入场点id
s
self
.
closer_entrance_node_id
=
session_postgre
.
query
(
DiggingWorkArea
)
.
filter_by
(
closer_entrance_node_id
=
session_postgre
.
query
(
DiggingWorkArea
)
.
filter_by
(
Id
=
self
.
closer_area_id
)
.
first
()
.
EntranceNodeId
Id
=
self
.
closer_area_id
)
.
first
()
.
EntranceNodeId
self
.
further_entrance_node_id
=
session_postgre
.
query
(
DiggingWorkArea
)
.
filter_by
(
further_entrance_node_id
=
session_postgre
.
query
(
DiggingWorkArea
)
.
filter_by
(
Id
=
self
.
further_area_id
)
.
first
()
.
EntranceNodeId
Id
=
self
.
further_area_id
)
.
first
()
.
EntranceNodeId
self
.
logger
.
info
(
"近端装载区入场点"
)
self
.
logger
.
info
(
"近端装载区入场点"
)
self
.
logger
.
info
(
self
.
closer_entrance_node_id
)
self
.
logger
.
info
(
closer_entrance_node_id
)
self
.
logger
.
info
(
"远端装载区入场点"
)
self
.
logger
.
info
(
"远端装载区入场点"
)
self
.
logger
.
info
(
self
.
further_entrance_node_id
)
self
.
logger
.
info
(
further_entrance_node_id
)
except
Exception
as
es
:
except
Exception
as
es
:
self
.
logger
.
error
(
"读取装载区及车辆信息异常"
)
self
.
logger
.
error
(
"读取装载区及车辆信息异常"
)
...
@@ -1125,39 +1162,20 @@ class GoThroughDispatcher:
...
@@ -1125,39 +1162,20 @@ class GoThroughDispatcher:
'''
'''
2. 寻找车辆穿越装载区自动判定区域
2. 寻找车辆穿越装载区自动判定区域
'''
'''
try
:
# 交叉口路段
further_area_backtrack_node_id
=
self
.
further_entrance_node_id
cross_lane
=
None
self
.
further_area_backtrack_lanes
=
[]
for
i
in
range
(
30
):
# 查询回溯路段对象
item
=
session_postgre
.
query
(
Lane
)
.
filter_by
(
EndNodeId
=
further_area_backtrack_node_id
)
.
first
()
if
item
is
None
:
break
# 将路段Id加入回溯列表
self
.
further_area_backtrack_lanes
.
append
(
str
(
item
.
Id
))
# 更新当前回溯节点
further_area_backtrack_node_id
=
item
.
StartNodeId
except
Exception
as
es
:
self
.
logger
.
error
(
"回溯远端装载路段异常"
)
self
.
logger
.
error
(
es
)
try
:
try
:
lowest_common_ancestor
=
None
#
lowest_common_ancestor = None
closer_area_backtrack_node_id
=
self
.
closer_entrance_node_id
closer_area_backtrack_node_id
=
closer_entrance_node_id
self
.
closer_area_backtrack_lanes
=
[]
self
.
closer_area_backtrack_lanes
=
[]
for
i
in
range
(
4
):
for
i
in
range
(
30
):
# 查询回溯路段对象
# 查询回溯路段对象
item
=
session_postgre
.
query
(
Lane
)
.
filter_by
(
EndNodeId
=
closer_area_backtrack_node_id
)
.
first
()
item
=
session_postgre
.
query
(
Lane
)
.
filter_by
(
EndNodeId
=
closer_area_backtrack_node_id
)
.
first
()
logger
.
info
(
f
"查询到路段{str(item.Id)}"
)
logger
.
info
(
f
"查询到路段{str(item.Id)}"
)
if
item
is
None
:
if
item
is
None
:
break
break
# # 若查询到共同祖先
# if str(item.Id) in self.further_area_backtrack_lanes:
# lowest_common_ancestor = item
# break
# 将路段Id加入回溯列表
self
.
closer_area_backtrack_lanes
.
append
(
str
(
item
.
Id
))
self
.
closer_area_backtrack_lanes
.
append
(
str
(
item
.
Id
))
# 更新当前回溯节点
# 更新当前回溯节点
closer_area_backtrack_node_id
=
item
.
StartNodeId
closer_area_backtrack_node_id
=
item
.
StartNodeId
...
@@ -1166,20 +1184,39 @@ class GoThroughDispatcher:
...
@@ -1166,20 +1184,39 @@ class GoThroughDispatcher:
logger
.
info
(
"近端道路列表"
)
logger
.
info
(
"近端道路列表"
)
logger
.
info
(
self
.
closer_area_backtrack_lanes
)
logger
.
info
(
self
.
closer_area_backtrack_lanes
)
# 交叉口路段
cross_lane
=
self
.
closer_area_backtrack_lanes
[
0
]
except
Exception
as
es
:
except
Exception
as
es
:
self
.
logger
.
error
(
"回溯近端装载路段异常"
)
self
.
logger
.
error
(
"回溯近端装载路段异常"
)
self
.
logger
.
error
(
es
)
self
.
logger
.
error
(
es
)
return
try
:
further_area_backtrack_node_id
=
further_entrance_node_id
self
.
further_area_backtrack_lanes
=
[]
for
i
in
range
(
30
):
# 查询回溯路段对象
item
=
session_postgre
.
query
(
Lane
)
.
filter_by
(
EndNodeId
=
further_area_backtrack_node_id
)
.
first
()
if
item
is
None
:
break
# 若查询到共同祖先
if
str
(
item
.
Id
)
in
self
.
closer_area_backtrack_lanes
:
cross_lane
=
str
(
item
.
Id
)
break
# 将路段Id加入回溯列表
self
.
further_area_backtrack_lanes
.
append
(
str
(
item
.
Id
))
# 更新当前回溯节点
further_area_backtrack_node_id
=
item
.
StartNodeId
logger
.
info
(
"远端道路列表"
)
logger
.
info
(
self
.
further_area_backtrack_lanes
)
except
Exception
as
es
:
self
.
logger
.
error
(
"回溯远端装载路段异常"
)
self
.
logger
.
error
(
es
)
self
.
logger
.
info
(
"cross_lane"
)
self
.
logger
.
info
(
"cross_lane"
)
self
.
logger
.
info
(
cross_lane
)
self
.
logger
.
info
(
cross_lane
)
# 截取远端入场点到交叉口的路段
# 截取远端入场点到交叉口的路段
self
.
further_area_backtrack_lanes
=
self
.
further_area_backtrack_lanes
[
#
self.further_area_backtrack_lanes = self.further_area_backtrack_lanes[
0
:
self
.
further_area_backtrack_lanes
.
index
(
cross_lane
)]
#
0:self.further_area_backtrack_lanes.index(cross_lane)]
# # 穿越装载区检查路段
# # 穿越装载区检查路段
# self.gothrough_check_lanes = self.closer_area_backtrack_lanes
# self.gothrough_check_lanes = self.closer_area_backtrack_lanes
...
@@ -1197,7 +1234,7 @@ class GoThroughDispatcher:
...
@@ -1197,7 +1234,7 @@ class GoThroughDispatcher:
# self.logger.error("穿越装载区交叉口判断异常")
# self.logger.error("穿越装载区交叉口判断异常")
# self.logger.error(es)
# self.logger.error(es)
def
redispatch_request
(
self
,
request_truck_id
:
str
,
truck
:
TruckInfo
,
truck_dispatch
):
def
redispatch_request
(
self
,
request_truck_id
:
str
,
truck
:
TruckInfo
,
truck_dispatch
,
truck_info
:
CurrentTruck
):
# 调度开始时间
# 调度开始时间
rtd_start_time
=
datetime
.
now
()
rtd_start_time
=
datetime
.
now
()
...
@@ -1205,10 +1242,6 @@ class GoThroughDispatcher:
...
@@ -1205,10 +1242,6 @@ class GoThroughDispatcher:
# 更新周期参数
# 更新周期参数
self
.
logger
.
info
(
"#####################################请求调度更新开始#####################################"
)
self
.
logger
.
info
(
"#####################################请求调度更新开始#####################################"
)
'''
1. 读取二次调度所需信息(车辆位置、分组、挖机等)
'''
try
:
try
:
truck_uuid_to_name_dict
=
get_value
(
"truck_uuid_to_name_dict"
)
truck_uuid_to_name_dict
=
get_value
(
"truck_uuid_to_name_dict"
)
...
@@ -1221,7 +1254,7 @@ class GoThroughDispatcher:
...
@@ -1221,7 +1254,7 @@ class GoThroughDispatcher:
except
Exception
as
es
:
except
Exception
as
es
:
self
.
logger
.
error
(
"数据库异常, 车辆编号未知"
)
self
.
logger
.
error
(
"数据库异常, 车辆编号未知"
)
self
.
logger
.
error
(
es
)
self
.
logger
.
error
(
es
)
return
return
False
try
:
try
:
# 读取请调车辆所属分组
# 读取请调车辆所属分组
...
@@ -1230,7 +1263,7 @@ class GoThroughDispatcher:
...
@@ -1230,7 +1263,7 @@ class GoThroughDispatcher:
# 读取分组挖机集合
# 读取分组挖机集合
excavators_id
=
DispatchInfo
.
get_excavator
(
group_id
)
excavators_id
=
DispatchInfo
.
get_excavator
(
group_id
)
# 读取车辆位置
信息
# 读取车辆位置
列表
truck_locates_dict
=
get_trucks_locate
()
truck_locates_dict
=
get_trucks_locate
()
# self.logger.info("truck_locates_dict")
# self.logger.info("truck_locates_dict")
...
@@ -1239,6 +1272,7 @@ class GoThroughDispatcher:
...
@@ -1239,6 +1272,7 @@ class GoThroughDispatcher:
except
Exception
as
es
:
except
Exception
as
es
:
self
.
logger
.
error
(
"车辆位置信息读取异常"
)
self
.
logger
.
error
(
"车辆位置信息读取异常"
)
self
.
logger
.
error
(
es
)
self
.
logger
.
error
(
es
)
return
False
try
:
try
:
# 读取请调车辆所在路段信息
# 读取请调车辆所在路段信息
...
@@ -1251,66 +1285,79 @@ class GoThroughDispatcher:
...
@@ -1251,66 +1285,79 @@ class GoThroughDispatcher:
except
Exception
as
es
:
except
Exception
as
es
:
self
.
logger
.
error
(
f
'车辆 {request_truck_name} 位置信息不可用'
)
self
.
logger
.
error
(
f
'车辆 {request_truck_name} 位置信息不可用'
)
self
.
logger
.
error
(
es
)
self
.
logger
.
error
(
es
)
return
return
False
'''
3. 调度判断逻辑
'''
if
request_truck_id
not
in
self
.
group
.
truck_info_list
:
if
request_truck_id
not
in
self
.
group
.
truck_info_list
:
raise
CoreException
(
102
,
f
'truck_info_list 缺失车辆 {request_truck_id} 信息'
)
raise
CoreException
(
102
,
f
'truck_info_list 缺失车辆 {request_truck_id} 信息'
)
truck_trip
=
self
.
group
.
truck_info_list
[
request_truck_id
]
.
get_trip
()
# truck_trip = self.group.truck_info_list[request_truck_id].get_trip()
truck_trip
=
truck_info
.
get_trip
()
excavator_index
=
int
(
truck_trip
[
1
])
excavator_index
=
int
(
truck_trip
[
1
])
if
excavator_index
==
-
1
:
if
excavator_index
==
-
1
:
raise
CoreException
(
110
,
f
'驶往挖机车辆 {request_truck_id} 行程计划为-1'
)
raise
CoreException
(
110
,
f
'驶往挖机车辆 {request_truck_id} 行程计划为-1'
)
if
excavator_index
not
in
self
.
group
.
excavator_info
.
truck_index_to_uuid_dict
:
# if excavator_index not in self.group.excavator_info.truck_index_to_uuid_dict:
raise
CoreException
(
111
,
f
'truck_index_to_uuid_dict 缺失 {excavator_index} 号挖机信息'
)
# raise CoreException(111, f'truck_index_to_uuid_dict 缺失 {excavator_index} 号挖机信息')
current_truck_goto_excavator_id
=
self
.
group
.
excavator_info
.
truck_index_to_uuid_dict
[
excavator_index
]
# current_truck_goto_excavator_id = self.group.excavator_info.truck_index_to_uuid_dict[excavator_index]
current_truck_goto_excavator_id
=
truck_info
.
get_combined_excavator
()
# if request_truck_lane_id in self.gothrough_check_lanes:
logger
.
info
(
f
"近端lanes{self.closer_area_backtrack_lanes}"
)
logger
.
info
(
f
"近端lanes{self.closer_area_backtrack_lanes}"
)
# self.closer_area_backtrack_lanes.pop(0)
# logger.info(f"变更之后lanes{self.closer_area_backtrack_lanes}")
if
request_truck_lane_id
in
self
.
closer_area_backtrack_lanes
[
0
:
2
]:
if
request_truck_lane_id
in
self
.
closer_area_backtrack_lanes
:
# 选择合适装载区
# 选择合适装载区
target_excavator
=
None
target_excavator
=
None
closer_distance
=
self
.
group
.
to_excavator_distance
[
self
.
group
.
unload_area_uuid_index_dict
[
truck_info
.
get_combined_unload_area
()],
self
.
group
.
excavator_uuid_index_dict
[
self
.
closer_excavator_id
]]
further_distance
=
self
.
group
.
to_excavator_distance
[
self
.
group
.
unload_area_uuid_index_dict
[
truck_info
.
get_combined_unload_area
()],
self
.
group
.
excavator_uuid_index_dict
[
self
.
further_excavator_id
]]
self
.
logger
.
info
(
f
'distances {closer_distance}, {further_distance}, {closer_distance / further_distance}'
)
try
:
try
:
target_excavator
=
area_choose
(
excavators_id
,
self
.
closer_area_id
,
self
.
further_area_id
,
target_excavator
=
area_choose
(
request_truck_id
,
excavators_id
,
self
.
closer_area_id
,
self
.
further_area_id
,
self
.
further_area_backtrack_lanes
,
self
.
closer_area_backtrack_lanes
,
self
.
further_area_backtrack_lanes
,
self
.
closer_area_backtrack_lanes
,
self
.
logger
,
self
.
logger
,
truck
,
truck_locates_dict
,
self
.
closer_excavator_state
,
truck
,
truck_locates_dict
,
self
.
closer_excavator_state
,
self
.
further_excavator_state
)
self
.
further_excavator_state
,
self
.
group
.
truck
.
start_time
,
self
.
group
.
excavator_info
.
get_loading_task_time
()[
0
])
except
Exception
as
es
:
except
Exception
as
es
:
logger
.
error
(
"寻找最优装载区异常"
)
logger
.
error
(
"寻找最优装载区异常"
)
logger
.
error
(
es
)
logger
.
error
(
es
)
return
False
logger
.
info
(
f
"当前{current_truck_goto_excavator_id}"
)
logger
.
info
(
f
"当前{current_truck_goto_excavator_id}"
)
logger
.
info
(
f
"目标{target_excavator}"
)
logger
.
info
(
f
"目标{target_excavator}"
)
try
:
# try:
excavator_now
=
session_mysql
.
query
(
EquipmentPair
)
.
filter_by
(
truck_id
=
request_truck_id
,
# excavator_now = session_mysql.query(EquipmentPair).filter_by(truck_id=request_truck_id,
isdeleted
=
0
)
.
first
()
.
exactor_id
# isdeleted=0).first().exactor_id
self
.
logger
.
info
(
'查询到的挖机'
)
# self.logger.info('查询到的挖机')
self
.
logger
.
info
(
excavator_now
)
# self.logger.info(excavator_now)
self
.
logger
.
info
(
'当前目标挖机'
)
# self.logger.info('当前目标挖机')
self
.
logger
.
info
(
target_excavator
)
# self.logger.info(target_excavator)
#
except
Exception
as
es
:
# except Exception as es:
self
.
logger
.
error
(
"pair表查询异常"
)
# self.logger.error("pair表查询异常")
self
.
logger
.
error
(
es
)
# self.logger.error(es)
excavator_now
=
0
# excavator_now = 0
#
# try:
# current_unload_area_id = session_mysql.query(EquipmentPair).filter_by(truck_id=request_truck_id,
# isdeleted=0).first().unload_area_id
# except Exception as es:
# self.logger.error("pair表查询异常")
# self.logger.error(es)
try
:
current_unload_area_id
=
truck_info
.
get_combined_unload_area
()
current_unload_area_id
=
session_mysql
.
query
(
EquipmentPair
)
.
filter_by
(
truck_id
=
request_truck_id
,
isdeleted
=
0
)
.
first
()
.
unload_area_id
except
Exception
as
es
:
self
.
logger
.
error
(
"pair表查询异常"
)
self
.
logger
.
error
(
es
)
if
(
target_excavator
is
not
None
)
and
(
excavator_now
!=
target_excavator
):
if
(
target_excavator
is
not
None
)
and
(
current_truck_goto_excavator_id
!=
target_excavator
):
self
.
logger
.
info
(
f
'近端lanes {request_truck_name} 派车计划前往 {target_excavator}'
)
self
.
logger
.
info
(
f
'近端lanes {request_truck_name} 派车计划前往 {target_excavator}'
)
# 派车计划写入redis
# 派车计划写入redis
...
@@ -1333,3 +1380,7 @@ class GoThroughDispatcher:
...
@@ -1333,3 +1380,7 @@ class GoThroughDispatcher:
rtd_end_time
=
datetime
.
now
()
rtd_end_time
=
datetime
.
now
()
print
(
f
'调度时耗 {rtd_end_time - rtd_start_time}'
)
print
(
f
'调度时耗 {rtd_end_time - rtd_start_time}'
)
return
True
else
:
return
False
equipment/excavator.py
View file @
a9d69a45
...
@@ -83,7 +83,7 @@ class ExcavatorInfo(WalkManage):
...
@@ -83,7 +83,7 @@ class ExcavatorInfo(WalkManage):
self
.
logger
.
info
(
self
.
loading_time
)
self
.
logger
.
info
(
self
.
loading_time
)
self
.
logger
.
info
(
"excavator_uuid_to_index_dict"
)
self
.
logger
.
info
(
"excavator_uuid_to_index_dict"
)
self
.
logger
.
info
(
self
.
excavator_uuid_to_index_dict
)
self
.
logger
.
info
(
self
.
excavator_uuid_to_index_dict
)
self
.
loading_time
[
self
.
excavator_uuid_to_index_dict
[
excavator_id
]]
=
3
0
.00
self
.
loading_time
[
self
.
excavator_uuid_to_index_dict
[
excavator_id
]]
=
3.00
# 更新挖机设备出入时间
# 更新挖机设备出入时间
def
update_excavator_entrance_exit_time
(
self
):
def
update_excavator_entrance_exit_time
(
self
):
...
...
equipment/truck.py
View file @
a9d69a45
...
@@ -937,7 +937,7 @@ class TruckInfo(WalkManage):
...
@@ -937,7 +937,7 @@ class TruckInfo(WalkManage):
# 更新卡车当前任务
# 更新卡车当前任务
self
.
update_truck_current_task
()
self
.
update_truck_current_task
()
self
.
update_truck_is_temp
()
#
self.update_truck_is_temp()
# 更新卡车最后一次装载/卸载时间
# 更新卡车最后一次装载/卸载时间
self
.
update_truck_last_leave_time
()
self
.
update_truck_last_leave_time
()
...
...
settings.py
View file @
a9d69a45
...
@@ -69,8 +69,8 @@ def set_log():
...
@@ -69,8 +69,8 @@ def set_log():
# timefilehandler = logging.handlers.TimedRotatingFileHandler(log_path + "/dispatch.log", when='M', interval=1, backupCount=60)
# timefilehandler = logging.handlers.TimedRotatingFileHandler(log_path + "/dispatch.log", when='M', interval=1, backupCount=60)
filehandler
=
logging
.
handlers
.
RotatingFileHandler
(
log_path
+
"/dispatch.log"
,
maxBytes
=
30
*
1024
*
1024
,
backupCount
=
10
,
encoding
=
"utf-8"
)
#
filehandler = logging.handlers.RotatingFileHandler(log_path + "/dispatch.log", maxBytes=30*1024*1024, backupCount=10, encoding="utf-8")
#
filehandler = logging.handlers.RotatingFileHandler("./Logs/dispatch.log", maxBytes=3 * 1024 * 1024, backupCount=10, encoding="utf-8")
filehandler
=
logging
.
handlers
.
RotatingFileHandler
(
"./Logs/dispatch.log"
,
maxBytes
=
3
*
1024
*
1024
,
backupCount
=
10
,
encoding
=
"utf-8"
)
# 设置后缀名称,跟strftime的格式一样
# 设置后缀名称,跟strftime的格式一样
filehandler
.
suffix
=
"
%
Y-
%
m-
%
d_
%
H-
%
M.log"
filehandler
.
suffix
=
"
%
Y-
%
m-
%
d_
%
H-
%
M.log"
...
...
util/area_analysis.py
View file @
a9d69a45
...
@@ -13,10 +13,18 @@ import json
...
@@ -13,10 +13,18 @@ import json
import
random
import
random
def
area_choose
(
excavators_id
,
closer_area_id
,
further_area_id
,
further_lane_set
,
closer_lane_set
,
def
area_choose
(
request_truck_id
,
excavators_id
,
closer_area_id
,
further_area_id
,
further_lane_set
,
closer_lane_set
,
logger
,
truck
,
truck_locates_dict
,
closer_excavator_state
,
further_excavator_state
):
logger
,
truck
,
truck_locates_dict
,
closer_excavator_state
,
further_excavator_state
,
sys_start_time
,
loading_time
):
"""
"""
两装载区均不空闲,执行二次调度
装载区选择
:param sys_start_time:
:param closer_lane_set:
:param further_excavator_state:
:param further_lane_set:
:param request_truck_id:
:param loading_time:
:param closer_excavator_state:
:param excavators_id: 挖机集合
:param excavators_id: 挖机集合
:param closer_area_id: 近端装载区id
:param closer_area_id: 近端装载区id
:param further_area_id: 远端装载区id
:param further_area_id: 远端装载区id
...
@@ -53,100 +61,68 @@ def area_choose(excavators_id, closer_area_id, further_area_id, further_lane_set
...
@@ -53,100 +61,68 @@ def area_choose(excavators_id, closer_area_id, further_area_id, further_lane_set
logger
.
info
(
arrival_truck_list
)
logger
.
info
(
arrival_truck_list
)
logger
.
info
(
f
"输入的closer_lane_set{closer_lane_set}"
)
logger
.
info
(
f
"输入的closer_lane_set{closer_lane_set}"
)
closer_lane_set_nearest
=
[
closer_lane_set
[
0
]]
closer_lane_two
=
closer_lane_set
[:
2
]
# 前往装载的车的数量
# 前往装载的车的数量
arrival_truck_num
=
len
(
arrival_truck_set
)
arrival_truck_num
=
len
(
arrival_truck_set
)
# 统计不同状态车辆数量
# 统计不同状态车辆数量
goto_closer_area_num
=
0
goto_closer_area_num
=
0
goto_further_area_num
=
0
goto_further_area_num
=
0
before_cross_num
=
0
lane_two_num
=
0
# 统计车辆剩余行驶时间
closer_remaining_trip_time
=
0
further_remaining_trip_time
=
0
# 当前相对时间
relative_now_time
=
max
(
0.0
,
float
((
datetime
.
now
()
-
sys_start_time
)
/
timedelta
(
hours
=
0
,
minutes
=
1
,
seconds
=
0
)))
for
truck_id
,
reach_time
in
arrival_truck_list
:
for
truck_id
,
reach_time
in
arrival_truck_list
:
if
truck_id
==
request_truck_id
:
continue
if
truck_id
in
truck_locates_dict
:
if
truck_id
in
truck_locates_dict
:
truck_lane_id
=
truck_locates_dict
[
truck_id
]
truck_lane_id
=
truck_locates_dict
[
truck_id
]
# 车辆已经经过近端装载区
# 车辆已经经过近端装载区
if
truck_lane_id
in
further_lane_set
:
if
truck_lane_id
in
further_lane_set
:
# 前往远端装载区车辆数加1
# 前往远端装载区车辆数加1
goto_further_area_num
+=
1
goto_further_area_num
+=
1
# 车辆未经过近端装载区
further_remaining_trip_time
+=
(
max
(
0
,
max
(
0.0
,
float
(
reach_time
-
relative_now_time
)))
+
loading_time
)
if
truck_lane_id
in
closer_lane_two
:
# 在装载区外2个车道
elif
truck_lane_id
in
closer_lane_set
:
lane_two_num
+=
1
# 车辆未经过近端装载区
if
truck_lane_id
in
closer_lane_set_nearest
:
# 前往近端或近端装载区车辆数加1
# 前往近端或近端装载区车辆数加1
goto_closer_area_num
+=
1
goto_closer_area_num
+=
1
else
:
closer_remaining_trip_time
+=
(
max
(
0
,
max
(
0.0
,
float
(
reach_time
-
relative_now_time
)))
+
loading_time
)
before_cross_num
+=
1
else
:
else
:
continue
continue
import
datetime
# 获取当前时间
# 获取当前时间
current_time
=
datetime
.
datetime
.
now
()
current_time
=
datetime
.
now
()
# 打印当前时间
# 打印当前时间
logger
.
info
(
f
"近端挖机状态{closer_excavator_state} 当前时间{current_time}"
)
logger
.
info
(
f
"近端挖机状态{closer_excavator_state} 当前时间{current_time}"
)
logger
.
info
(
f
"
近端装载区外第一路段等待
车辆数{goto_closer_area_num}"
)
logger
.
info
(
f
"
已经前往近端的
车辆数{goto_closer_area_num}"
)
logger
.
info
(
f
"远端挖机状态{further_excavator_state}"
)
logger
.
info
(
f
"远端挖机状态{further_excavator_state}
当前时间{current_time}
"
)
logger
.
info
(
f
"已经前往远端的车辆数{goto_further_area_num}"
)
logger
.
info
(
f
"已经前往远端的车辆数{goto_further_area_num}"
)
logger
.
info
(
before_cross_num
)
# # 近端挖机空闲
# if closer_excavator_state == 0 and goto_closer_area_num == 0:
# logger.info("近端挖机空闲, 调度车辆前往近端装载区")
# target_excavator = DispatchInfo.load_excavator_dict[closer_area_id]
# # truck_dispatch_to_redis(request_truck_id, group_id, DispatchInfo.load_excavator_dict[closer_area_id])
# # 远端挖机空闲
# elif further_excavator_state == 0 and goto_further_area_num == 0:
# logger.info("远端挖机空闲, 调度车辆前往远端装载区")
# target_excavator = DispatchInfo.load_excavator_dict[further_area_id]
# # truck_dispatch_to_redis(request_truck_id, group_id, DispatchInfo.load_excavator_dict[further_area_id])
# # 两挖机均不空闲
# else:
# # goto_further_area_num -= 1
# logger.info("goto_further_area_num-goto_closer_area_num")
# logger.info(goto_further_area_num)
# logger.info(goto_closer_area_num)
# # 默认当前请调车辆与近端装载点前没有车辆,因此前往近端装载区的车辆仅其本身
# # goto_closer_area_num = 1
# # 在远处排队等待的车辆更少
# if goto_closer_area_num + before_cross_num > goto_further_area_num:
# logger.info("远端挖机排队时间短, 调度车辆前往")
# target_excavator = DispatchInfo.load_excavator_dict[further_area_id]
# # truck_dispatch_to_redis(request_truck_id, group_id, DispatchInfo.load_excavator_dict[further_area_id])
# else:
# logger.info("近端挖机排队时间短, 调度车辆前往")
# target_excavator = DispatchInfo.load_excavator_dict[closer_area_id]
# # truck_dispatch_to_redis(request_truck_id, group_id, DispatchInfo.load_excavator_dict[closer_area_id])
try
:
try
:
with
open
(
json_file
,
encoding
=
'UTF-8'
)
as
f
:
with
open
(
json_file
,
encoding
=
'UTF-8'
)
as
f
:
load_value
=
json
.
load
(
f
)
load_value
=
json
.
load
(
f
)
gothrough_config_area
=
load_value
[
"gothrough"
]
gothrough_config_area
=
load_value
[
"gothrough"
]
dispatch_factor
=
float
(
gothrough_config_area
[
"factor"
])
dispatch_factor
=
float
(
gothrough_config_area
[
"factor"
])
logger
.
info
(
f
'closer_remaining_time: {closer_remaining_trip_time}, further_remaining_time: {further_remaining_trip_time}'
)
# 近端挖机空闲
# 近端挖机空闲
if
closer_excavator_state
==
0
:
if
closer_excavator_state
==
0
:
logger
.
info
(
f
"穿越调度:近端挖机空闲, 调度车辆前往近端装载区,远端派车数量{goto_further_area_num},空载车总数{arrival_truck_num}"
)
logger
.
info
(
f
"穿越调度:近端挖机空闲, 调度车辆前往近端装载区,远端派车数量{goto_further_area_num},空载车总数{arrival_truck_num}"
)
target_excavator
=
DispatchInfo
.
load_excavator_dict
[
closer_area_id
]
target_excavator
=
DispatchInfo
.
load_excavator_dict
[
closer_area_id
]
# 远端挖机空闲
# 远端挖机空闲
# if further_excavator_state == 0 and goto_further_area_num == 0:
elif
goto_further_area_num
==
0
and
further_excavator_state
==
0
:
elif
goto_further_area_num
==
0
:
logger
.
info
(
f
"穿越调度:远端挖机空闲, 调度车辆前往远端装载区,远端派车数量{goto_further_area_num},空载车总数{arrival_truck_num}"
)
logger
.
info
(
f
"穿越调度:远端挖机空闲, 调度车辆前往远端装载区,远端派车数量{goto_further_area_num},空载车总数{arrival_truck_num}"
)
target_excavator
=
DispatchInfo
.
load_excavator_dict
[
further_area_id
]
target_excavator
=
DispatchInfo
.
load_excavator_dict
[
further_area_id
]
# elif closer_excavator_state == 0 and lane_two_num <= 1:
# logger.info(f"穿越调度:近端挖机空闲, 调度车辆前往近端装载区,远端派车数量{goto_further_area_num},空载车总数{arrival_truck_num}")
# target_excavator = DispatchInfo.load_excavator_dict[closer_area_id]
# 远端挖机已满载
# 远端挖机已满载
elif
goto_further_area_num
>=
int
(
dispatch_factor
*
arrival_truck_num
)
:
elif
further_remaining_trip_time
>=
closer_remaining_trip_time
:
logger
.
info
(
f
"穿越调度:远端挖机满载, 调度车辆前往近端装载区,远端派车数量{goto_further_area_num},空载车总数{arrival_truck_num}"
)
logger
.
info
(
f
"穿越调度:远端挖机满载, 调度车辆前往近端装载区,远端派车数量{goto_further_area_num},空载车总数{arrival_truck_num}"
)
target_excavator
=
DispatchInfo
.
load_excavator_dict
[
closer_area_id
]
target_excavator
=
DispatchInfo
.
load_excavator_dict
[
closer_area_id
]
# 近端挖机已满载
# 近端挖机已满载
...
@@ -404,9 +380,9 @@ def get_excavator_prise_location(excavator_id):
...
@@ -404,9 +380,9 @@ def get_excavator_prise_location(excavator_id):
logger
.
info
(
f
"正在获取的挖机id:{excavator_id}"
)
logger
.
info
(
f
"正在获取的挖机id:{excavator_id}"
)
device_name
=
session_mysql
.
query
(
Equipment
)
.
filter_by
(
id
=
excavator_id
,
device_type
=
2
)
.
first
()
.
device_name
device_name
=
session_mysql
.
query
(
Equipment
)
.
filter_by
(
id
=
excavator_id
,
device_type
=
2
)
.
first
()
.
device_name
are_entrance_key
=
str
(
"Area_"
+
device_name
)
are
a
_entrance_key
=
str
(
"Area_"
+
device_name
)
key_value_dict
=
json
.
loads
(
byte_to_str
(
redis0
.
get
(
are_entrance_key
)))
key_value_dict
=
json
.
loads
(
byte_to_str
(
redis0
.
get
(
are
a
_entrance_key
)))
loc_str
=
key_value_dict
[
'Entrance'
]
.
split
(
","
)
loc_str
=
key_value_dict
[
'Entrance'
]
.
split
(
","
)
longitude
=
float
(
loc_str
[
0
])
longitude
=
float
(
loc_str
[
0
])
latitude
=
float
(
loc_str
[
1
])
latitude
=
float
(
loc_str
[
1
])
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment