Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
H
HYH.APSJ
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
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
佟礼
HYH.APSJ
Commits
9a0c5df9
Commit
9a0c5df9
authored
Mar 06, 2026
by
Tong Li
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
策略最小化换线
parent
994fc45b
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
342 additions
and
38 deletions
+342
-38
OrderSortRule.java
src/main/java/com/aps/entity/Algorithm/OrderSortRule.java
+1
-0
Order.java
src/main/java/com/aps/entity/basic/Order.java
+7
-0
GeneticDecoder.java
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
+8
-8
MaterialRequirementService.java
...com/aps/service/Algorithm/MaterialRequirementService.java
+3
-0
OrderSortService.java
...main/java/com/aps/service/Algorithm/OrderSortService.java
+251
-19
PlanResultService.java
src/main/java/com/aps/service/plan/PlanResultService.java
+10
-0
OrderSortServiceTest.java
src/test/java/com/aps/demo/OrderSortServiceTest.java
+61
-10
PlanResultServiceTest.java
src/test/java/com/aps/demo/PlanResultServiceTest.java
+1
-1
No files found.
src/main/java/com/aps/entity/Algorithm/OrderSortRule.java
View file @
9a0c5df9
...
...
@@ -7,6 +7,7 @@ import java.util.List;
public
class
OrderSortRule
{
private
boolean
enabled
;
private
List
<
SortCondition
>
conditions
;
//private boolean minimizeChangeover=false;
@Data
public
static
class
SortCondition
{
...
...
src/main/java/com/aps/entity/basic/Order.java
View file @
9a0c5df9
...
...
@@ -176,4 +176,11 @@ public class Order {
* 物料需求
*/
private
List
<
OrderMaterialRequirement
>
materialRequirementList
;
/**
* 换线成本
*/
private
double
changeoverCost
;
private
int
changeoverPriority
;
}
\ No newline at end of file
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
View file @
9a0c5df9
...
...
@@ -489,19 +489,19 @@ if(finishedOrder==null||finishedOrder.size()==0)
}
int
setupTime
=
0
;
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
=
null
;
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
1
=
chromosome
.
getResult
().
stream
()
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
=
chromosome
.
getResult
().
stream
()
.
filter
(
t
->
t
.
getMachineId
()
==
machine
.
getId
())
.
sorted
(
Comparator
.
comparingInt
(
GAScheduleResult:
:
getStartTime
))
.
collect
(
Collectors
.
toCollection
(
CopyOnWriteArrayList:
:
new
));
GAScheduleResult
lastGeneOnMachine
=
null
;
if
(
machineTasks
1
!=
null
&&
machineTasks1
.
size
()>
0
)
if
(
machineTasks
!=
null
&&
machineTasks
.
size
()>
0
)
{
lastGeneOnMachine
=
machineTasks
1
.
get
(
machineTasks1
.
size
()-
1
);
lastGeneOnMachine
=
machineTasks
.
get
(
machineTasks
.
size
()-
1
);
}
if
(
lastGeneOnMachine
!=
null
)
if
(
lastGeneOnMachine
!=
null
&&
_globalParam
.
is_smoothChangeOver
()
)
{
earliestStartTime
=
Math
.
max
(
earliestStartTime
,
lastGeneOnMachine
.
getEndTime
());
}
...
...
@@ -543,16 +543,16 @@ if(finishedOrder==null||finishedOrder.size()==0)
}
machineTasks
1
=
chromosome
.
getResult
().
stream
()
machineTasks
=
chromosome
.
getResult
().
stream
()
.
filter
(
t
->
t
.
getMachineId
()
==
machine
.
getId
())
.
sorted
(
Comparator
.
comparingInt
(
GAScheduleResult:
:
getStartTime
))
.
collect
(
Collectors
.
toCollection
(
CopyOnWriteArrayList:
:
new
));
if
(
machineTasks
1
!=
null
&&
machineTasks1
.
size
()>
0
)
if
(
machineTasks
!=
null
&&
machineTasks
.
size
()>
0
&&
_globalParam
.
is_smoothChangeOver
()
)
{
lastGeneOnMachine
=
machineTasks
1
.
get
(
machineTasks1
.
size
()-
1
);
lastGeneOnMachine
=
machineTasks
.
get
(
machineTasks
.
size
()-
1
);
}
if
(
_globalParam
.
is_smoothChangeOver
())
{
...
...
src/main/java/com/aps/service/Algorithm/MaterialRequirementService.java
View file @
9a0c5df9
...
...
@@ -1617,6 +1617,9 @@ if(headers1==null)
redisUtils
.
del
(
sceneId
+
routingDiscreteParamCacheKey
);
GlobalCacheUtil
.
remove
(
sceneId
+
routingDiscreteParamCacheKey
);
redisUtils
.
del
(
sceneId
+
materialsCacheKey
);
GlobalCacheUtil
.
remove
(
sceneId
+
materialsCacheKey
);
}
...
...
src/main/java/com/aps/service/Algorithm/OrderSortService.java
View file @
9a0c5df9
...
...
@@ -90,6 +90,227 @@ public class OrderSortService {
orders
.
addAll
(
sortedOrders
);
}
/**
* 计算两个订单之间的换线成本
*/
private
double
calculateChangeoverCost
(
Order
order1
,
Order
order2
)
{
if
(
order1
==
null
||
order2
==
null
)
{
return
Double
.
MAX_VALUE
;
}
double
cost
=
0.0
;
// 1. 工艺路线不同,增加换线成本
if
(!
Objects
.
equals
(
order1
.
getRoutingId
(),
order2
.
getRoutingId
()))
{
cost
+=
100.0
;
}
// 2. 物料系列不同,增加换线成本
if
(!
Objects
.
equals
(
order1
.
getSerie
(),
order2
.
getSerie
()))
{
cost
+=
50.0
;
}
// 3. 物料代码不同,增加换线成本
if
(!
Objects
.
equals
(
order1
.
getMaterialCode
(),
order2
.
getMaterialCode
()))
{
cost
+=
25.0
;
}
return
cost
;
}
/**
* 基于最小化换线成本的排序
*/
/**
* 基于最小化换线成本的排序,并返回带有优先级信息的订单列表
*/
private
List
<
Order
>
sortByMinimizeChangeover
(
List
<
Order
>
orders
)
{
if
(
CollectionUtils
.
isEmpty
(
orders
))
{
return
new
ArrayList
<>();
}
List
<
Order
>
sortedOrders
=
new
ArrayList
<>();
List
<
Order
>
remainingOrders
=
new
ArrayList
<>(
orders
);
// 选择第一个订单作为起始点
Order
currentOrder
=
remainingOrders
.
remove
(
0
);
currentOrder
.
setChangeoverPriority
(
1
);
sortedOrders
.
add
(
currentOrder
);
int
priority
=
2
;
// 每次选择与当前最后一个订单换线成本最低的订单
while
(!
remainingOrders
.
isEmpty
())
{
double
minCost
=
Double
.
MAX_VALUE
;
List
<
Order
>
bestOrders
=
new
ArrayList
<>();
// 找到换线成本最低的所有订单
for
(
Order
order
:
remainingOrders
)
{
double
cost
=
calculateChangeoverCost
(
currentOrder
,
order
);
if
(
cost
<
minCost
)
{
minCost
=
cost
;
order
.
setChangeoverCost
(
cost
);
if
(
minCost
==
0
){
order
.
setChangeoverPriority
(
currentOrder
.
getPriority
());
}
else
{
order
.
setChangeoverPriority
(
priority
);
}
bestOrders
.
clear
();
bestOrders
.
add
(
order
);
}
else
if
(
cost
==
minCost
)
{
order
.
setChangeoverCost
(
cost
);
if
(
minCost
==
0
){
order
.
setChangeoverPriority
(
currentOrder
.
getPriority
());
}
else
{
order
.
setChangeoverPriority
(
priority
);
}
bestOrders
.
add
(
order
);
}
}
// 如果有多个成本相同的订单,将它们作为一组添加
if
(!
bestOrders
.
isEmpty
())
{
sortedOrders
.
addAll
(
bestOrders
);
remainingOrders
.
removeAll
(
bestOrders
);
// 更新当前订单为最后添加的订单
currentOrder
=
bestOrders
.
get
(
bestOrders
.
size
()
-
1
);
if
(
minCost
!=
0
)
{
priority
++;
}
}
else
{
// 以防万一,添加剩余的第一个订单
Order
nextOrder
=
remainingOrders
.
remove
(
0
);
sortedOrders
.
add
(
nextOrder
);
currentOrder
=
nextOrder
;
if
(
minCost
!=
0
)
{
priority
++;
}
}
}
return
sortedOrders
;
}
public
void
sortByMinimizeChangeover1
(
List
<
Order
>
orders
,
Map
<
Integer
,
List
<
Integer
>>
priorityPaths
)
{
List
<
Order
>
optimizedOrders
=
sortByMinimizeChangeover
(
orders
);
for
(
Order
order
:
optimizedOrders
)
{
priorityPaths
.
get
(
order
.
getId
()).
add
(
order
.
getChangeoverPriority
());
}
// convertPriorityPathsToNumeric(optimizedOrders, priorityPaths);
int
i
=
0
;
}
/**
* 处理排序条件,支持在任意位置应用最小化换线策略
*/
private
void
processConditionsWithMinimizeChangeover
(
List
<
Order
>
orders
,
List
<
OrderSortRule
.
SortCondition
>
conditions
,
int
conditionIndex
,
Map
<
Integer
,
List
<
Integer
>>
priorityPaths
)
{
// 递归终止:处理完所有条件后停止
if
(
conditionIndex
>=
conditions
.
size
()
||
CollectionUtils
.
isEmpty
(
orders
))
{
return
;
}
OrderSortRule
.
SortCondition
currentCondition
=
conditions
.
get
(
conditionIndex
);
// 检查是否是最小化换线策略
if
(
"minimize_changeover"
.
equals
(
currentCondition
.
getFieldName
()))
{
// 应用最小化换线策略
List
<
Order
>
optimizedOrders
=
sortByMinimizeChangeover
(
orders
);
// 为最小化换线后的订单分配层级序号
// 相同成本的订单分配相同的序号
for
(
Order
order
:
optimizedOrders
)
{
priorityPaths
.
get
(
order
.
getId
()).
add
(
order
.
getChangeoverPriority
());
}
Map
<
Integer
,
List
<
Order
>>
priorityGroups
=
new
HashMap
<>();
for
(
Order
order
:
optimizedOrders
)
{
int
priority
=
order
.
getChangeoverPriority
();
priorityGroups
.
computeIfAbsent
(
priority
,
k
->
new
ArrayList
<>()).
add
(
order
);
}
// 按优先级分组顺序处理
List
<
Integer
>
sortedPriorities
=
priorityGroups
.
keySet
().
stream
()
.
sorted
()
.
collect
(
Collectors
.
toList
());
for
(
Integer
priority
:
sortedPriorities
)
{
List
<
Order
>
groupOrders
=
priorityGroups
.
get
(
priority
);
// 递归处理下一个条件
processConditionsWithMinimizeChangeover
(
groupOrders
,
conditions
,
conditionIndex
+
1
,
priorityPaths
);
}
// 递归处理下一个条件
// processConditionsWithMinimizeChangeover(optimizedOrders, conditions, conditionIndex + 1, priorityPaths);
}
else
{
// 处理普通排序条件
String
fieldName
=
currentCondition
.
getFieldName
();
Function
<
Order
,
?>
keyExtractor
=
getFieldExtractor
(
fieldName
);
if
(
keyExtractor
==
null
)
{
log
.
warn
(
"跳过无效排序字段:{}"
,
currentCondition
.
getFieldName
());
processConditionsWithMinimizeChangeover
(
orders
,
conditions
,
conditionIndex
+
1
,
priorityPaths
);
return
;
}
// 1. 按当前条件分组 处理null键
Map
<
Object
,
List
<
Order
>>
groups
=
new
HashMap
<>();
orders
.
forEach
(
order
->
{
Object
key
=
keyExtractor
.
apply
(
order
);
groups
.
computeIfAbsent
(
key
,
k
->
new
ArrayList
<>()).
add
(
order
);
});
// 2. 对分组键排序(关键:按条件配置的方向排序)
List
<
Object
>
sortedKeys
=
getSortedKeys
(
groups
,
currentCondition
);
// 3. 为每个分组分配层级序号(从1开始)
for
(
int
groupIndex
=
0
;
groupIndex
<
sortedKeys
.
size
();
groupIndex
++)
{
Object
key
=
sortedKeys
.
get
(
groupIndex
);
List
<
Order
>
groupOrders
=
groups
.
get
(
key
);
// 分配当前层级序号(1、2、3...)
int
levelNumber
=
groupIndex
+
1
;
groupOrders
.
forEach
(
order
->
priorityPaths
.
get
(
order
.
getId
()).
add
(
levelNumber
));
// 递归处理下一级条件
processConditionsWithMinimizeChangeover
(
groupOrders
,
conditions
,
conditionIndex
+
1
,
priorityPaths
);
}
}
}
private
List
<
Order
>
sortWithConfiguredStrategy
(
List
<
Order
>
orders
,
OrderSortRule
rule
)
{
// if (rule.getConditions().size()==1&&rule.getConditions().get(0).getFieldName().equals("minimize_changeover")) {
// return sortByMinimizeChangeover(orders);
// }
List
<
Order
>
currentOrders
=
new
ArrayList
<>(
orders
);
List
<
OrderSortRule
.
SortCondition
>
conditions
=
rule
.
getConditions
().
stream
()
.
sorted
(
Comparator
.
comparingInt
(
OrderSortRule
.
SortCondition
::
getSequence
))
.
collect
(
Collectors
.
toList
());
// 为每个订单初始化优先级路径
Map
<
Integer
,
List
<
Integer
>>
priorityPaths
=
new
HashMap
<>();
currentOrders
.
forEach
(
order
->
priorityPaths
.
put
(
order
.
getId
(),
new
ArrayList
<>()));
// 处理排序条件
processConditionsWithMinimizeChangeover
(
currentOrders
,
conditions
,
0
,
priorityPaths
);
// 将路径转换为最终格式
convertPriorityPathsToNumeric
(
currentOrders
,
priorityPaths
);
// 按优先级升序排序(数值越小越优先)
return
currentOrders
.
stream
()
.
sorted
(
Comparator
.
comparingDouble
(
Order:
:
getActualPriority
))
.
collect
(
Collectors
.
toList
());
}
/**
* 根据数据库配置的规则对订单排序
...
...
@@ -136,14 +357,24 @@ public class OrderSortService {
return
new
ArrayList
<>(
orders
);
}
boolean
hasMinimizeChangeover
=
rule
.
getConditions
().
stream
()
.
anyMatch
(
condition
->
"minimize_changeover"
.
equals
(
condition
.
getFieldName
()));
if
(
hasMinimizeChangeover
)
{
// 仅使用最小化换线策略
return
sortWithConfiguredStrategy
(
orders
,
rule
);
}
else
{
// 按条件顺序排序(sequence越小越先执行)
List
<
OrderSortRule
.
SortCondition
>
sortedConditions
=
rule
.
getConditions
().
stream
()
.
sorted
(
Comparator
.
comparingInt
(
OrderSortRule
.
SortCondition
::
getSequence
))
.
collect
(
Collectors
.
toList
());
// 为每个订单初始化优先级路径
Map
<
Ord
er
,
List
<
Integer
>>
priorityPaths
=
new
HashMap
<>();
orders
.
forEach
(
order
->
priorityPaths
.
put
(
order
,
new
ArrayList
<>()));
Map
<
Integ
er
,
List
<
Integer
>>
priorityPaths
=
new
HashMap
<>();
orders
.
forEach
(
order
->
priorityPaths
.
put
(
order
.
getId
()
,
new
ArrayList
<>()));
// 递归分配层级化优先级(核心逻辑)
assignHierarchicalPriority
(
orders
,
sortedConditions
,
0
,
priorityPaths
);
...
...
@@ -156,6 +387,7 @@ public class OrderSortService {
.
sorted
(
Comparator
.
comparingDouble
(
Order:
:
getActualPriority
))
.
collect
(
Collectors
.
toList
());
}
}
/**
* 递归分配层级化优先级路径
...
...
@@ -163,7 +395,7 @@ public class OrderSortService {
private
void
assignHierarchicalPriority
(
List
<
Order
>
orders
,
List
<
OrderSortRule
.
SortCondition
>
conditions
,
int
conditionIndex
,
Map
<
Ord
er
,
List
<
Integer
>>
priorityPaths
)
{
Map
<
Integ
er
,
List
<
Integer
>>
priorityPaths
)
{
// 递归终止:处理完所有条件后停止,不再添加额外序号
if
(
conditionIndex
>=
conditions
.
size
()
||
CollectionUtils
.
isEmpty
(
orders
))
{
return
;
...
...
@@ -196,7 +428,7 @@ public class OrderSortService {
// 分配当前层级序号(1、2、3...)
int
levelNumber
=
groupIndex
+
1
;
groupOrders
.
forEach
(
order
->
priorityPaths
.
get
(
order
).
add
(
levelNumber
));
groupOrders
.
forEach
(
order
->
priorityPaths
.
get
(
order
.
getId
()
).
add
(
levelNumber
));
// 递归处理下一级条件
assignHierarchicalPriority
(
groupOrders
,
conditions
,
conditionIndex
+
1
,
priorityPaths
);
...
...
@@ -208,9 +440,9 @@ public class OrderSortService {
* 3个条件 → [1,1,1] → 1.11
* 4个条件 → [1,1,1,1] → 1.111
*/
private
void
convertPriorityPathsToNumeric
(
List
<
Order
>
orders
,
Map
<
Ord
er
,
List
<
Integer
>>
priorityPaths
)
{
private
void
convertPriorityPathsToNumeric
(
List
<
Order
>
orders
,
Map
<
Integ
er
,
List
<
Integer
>>
priorityPaths
)
{
for
(
Order
order
:
orders
)
{
List
<
Integer
>
path
=
priorityPaths
.
get
(
order
);
List
<
Integer
>
path
=
priorityPaths
.
get
(
order
.
getId
()
);
if
(
CollectionUtils
.
isEmpty
(
path
))
{
order
.
setActualPriority
(
0.0
);
continue
;
...
...
src/main/java/com/aps/service/plan/PlanResultService.java
View file @
9a0c5df9
...
...
@@ -1166,6 +1166,16 @@ private GlobalParam InitGlobalParam()
conditions
.
add
(
condition
);
files
.
add
(
"orderCode"
);
i
++;
}
else
if
(
strategy
.
getName
().
equals
(
"minimize_tool_changeovers"
))
{
// 最小化换线策略需要特殊处理,这里只做标记
// 实际排序逻辑将在orderSortService中处理
OrderSortRule
.
SortCondition
condition
=
new
OrderSortRule
.
SortCondition
();
condition
.
setSequence
(
i
);
condition
.
setFieldName
(
"minimize_changeover"
);
condition
.
setReverse
(
false
);
conditions
.
add
(
condition
);
files
.
add
(
"minimize_changeover"
);
i
++;
}
}
...
...
src/test/java/com/aps/demo/OrderSortServiceTest.java
View file @
9a0c5df9
...
...
@@ -13,9 +13,7 @@ import java.time.LocalDate;
import
java.time.LocalDateTime
;
import
java.time.OffsetDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.util.ArrayList
;
import
java.util.Comparator
;
import
java.util.List
;
import
java.util.*
;
import
java.util.stream.Collectors
;
/**
...
...
@@ -45,18 +43,70 @@ class OrderSortServiceTest {
// 创建多条件排序规则:先按dueDate,再按priority
OrderSortRule
rule
=
createMultiConditionRule
();
List
<
Order
>
orders
=
new
ArrayList
<>();
Order
order
=
new
Order
();
order
.
setId
(
1
);
order
.
setOrderId
(
"001"
);
order
.
setRoutingId
(
1
);
order
.
setMaterialCode
(
"M001"
);
order
.
setSerie
(
"S001"
);
orders
.
add
(
order
);
Order
order2
=
new
Order
();
order2
.
setId
(
2
);
order2
.
setOrderId
(
"002"
);
order2
.
setRoutingId
(
1
);
order2
.
setMaterialCode
(
"M002"
);
order2
.
setSerie
(
"S001"
);
orders
.
add
(
order2
);
Order
order3
=
new
Order
();
order3
.
setId
(
3
);
order3
.
setOrderId
(
"003"
);
order3
.
setRoutingId
(
1
);
order3
.
setMaterialCode
(
"M002"
);
order3
.
setSerie
(
"S001"
);
orders
.
add
(
order3
);
Order
order4
=
new
Order
();
order4
.
setId
(
4
);
order4
.
setOrderId
(
"004"
);
order4
.
setRoutingId
(
2
);
order4
.
setMaterialCode
(
"M001"
);
order4
.
setSerie
(
"S002"
);
orders
.
add
(
order4
);
Order
order5
=
new
Order
();
order5
.
setId
(
5
);
order5
.
setOrderId
(
"005"
);
order5
.
setRoutingId
(
2
);
order5
.
setMaterialCode
(
"M002"
);
order5
.
setSerie
(
"S003"
);
orders
.
add
(
order5
);
Order
order6
=
new
Order
();
order6
.
setId
(
6
);
order6
.
setOrderId
(
"005"
);
order6
.
setRoutingId
(
2
);
order6
.
setMaterialCode
(
"M002"
);
order6
.
setSerie
(
"S003"
);
orders
.
add
(
order6
);
// 执行测试
orderSortService
.
assignPriority
(
testOrders
,
rule
);
orderSortService
.
assignPriorityValues
(
orders
,
rule
);
// 按照优先级排序
testO
rders
.
sort
(
o
rders
.
sort
(
Comparator
.
comparing
(
Order:
:
getActualPriority
).
reversed
()
);
// 验证多级优先级分配
printOrderPriorities
(
testO
rders
);
// 调试输出
printOrderPriorities
(
o
rders
);
// 调试输出
}
...
...
@@ -102,17 +152,18 @@ class OrderSortServiceTest {
// 条件2:按到期日
OrderSortRule
.
SortCondition
condition2
=
new
OrderSortRule
.
SortCondition
();
condition2
.
setSequence
(
2
);
condition2
.
setFieldName
(
"
dueDate
"
);
condition2
.
setFieldName
(
"
minimize_changeover
"
);
condition2
.
setReverse
(
false
);
// 早到期在前
conditions
.
add
(
condition2
);
// 条件3:按优先级
OrderSortRule
.
SortCondition
condition3
=
new
OrderSortRule
.
SortCondition
();
condition3
.
setSequence
(
3
);
condition3
.
setFieldName
(
"
priority
"
);
condition3
.
setFieldName
(
"
materialCode
"
);
condition3
.
setReverse
(
true
);
// 高优先级在前
conditions
.
add
(
condition3
);
rule
.
setConditions
(
conditions
);
return
rule
;
}
...
...
src/test/java/com/aps/demo/PlanResultServiceTest.java
View file @
9a0c5df9
...
...
@@ -42,7 +42,7 @@ public class PlanResultServiceTest {
// planResultService.execute2("5475E00B844847ACB6DC20227967BA2F");
// planResultService.execute2("00E0C5D3E4AD4F36B56C39395906618D");
planResultService
.
execute2
(
"
31F0FF4DFAD844BD9C72EDEEF3430A1F
"
);
planResultService
.
execute2
(
"
D6836ADCEF5F4537A4BDD896ECC2EC6A
"
);
// planResultService.execute2("265F24B6DF3C40E4B17D193B0CC8AAF2");
// LocalDateTime t= LocalDateTime.of(2026, 02, 14, 1, 25, 52);
// List<Integer> opids=new ArrayList<>();//BCA6FA43FFA444D3952CF8F6E1EA291B
...
...
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