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
3d3915bf
Commit
3d3915bf
authored
May 20, 2026
by
DESKTOP-VKRD9QF\Administration
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/codex/jit-backward-changeover'
parents
23645062
19a80d12
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
1421 additions
and
53 deletions
+1421
-53
ScheduleResultDetail.java
...n/java/com/aps/entity/Algorithm/ScheduleResultDetail.java
+2
-0
BackwardChangeoverHelper.java
...a/com/aps/service/Algorithm/BackwardChangeoverHelper.java
+1015
-0
GeneticDecoder.java
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
+208
-53
MachineCalculator.java
...ain/java/com/aps/service/Algorithm/MachineCalculator.java
+196
-0
No files found.
src/main/java/com/aps/entity/Algorithm/ScheduleResultDetail.java
View file @
3d3915bf
...
...
@@ -18,6 +18,8 @@ public class ScheduleResultDetail {
private
double
Quantity
;
// 时间段
private
double
efficiency
=
1
;
//效率
private
List
<
TimeSegment
>
usedSegment
;
// true 表示换型段,false 表示加工段。
private
boolean
setup
;
// Key 的 getter/setter
...
...
src/main/java/com/aps/service/Algorithm/BackwardChangeoverHelper.java
0 → 100644
View file @
3d3915bf
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.common.util.FileHelper
;
import
com.aps.common.util.SpringContextUtil
;
import
com.aps.entity.Algorithm.GAScheduleResult
;
import
com.aps.entity.Algorithm.ScheduleResultDetail
;
import
com.aps.entity.basic.Entry
;
import
com.aps.entity.basic.GlobalParam
;
import
com.aps.entity.basic.Machine
;
import
com.aps.entity.basic.SegmentType
;
import
com.aps.entity.basic.TimeSegment
;
import
com.aps.service.DiscreteParameterDurationService
;
import
com.aps.service.DiscreteParameterMatrixService
;
import
java.time.LocalDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.time.temporal.ChronoUnit
;
import
java.util.Comparator
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.Set
;
import
java.util.UUID
;
import
java.util.concurrent.CopyOnWriteArrayList
;
import
java.util.stream.Collectors
;
/**
* 倒排换型辅助类:集中处理 JIT 倒排时绿色/蓝色/黄色之间的换型计算、窗口校验和黄色旧换型替换。
*/
class
BackwardChangeoverHelper
{
private
static
final
String
DEBUG_BACKWARD_SETUP_TIME_SECONDS_PROPERTY
=
"aps.debug.backwardSetupTimeSeconds"
;
private
static
final
String
DEBUG_BACKWARD_CHANGEOVER_VERBOSE_PROPERTY
=
"aps.debug.backwardChangeoverVerbose"
;
private
final
GlobalParam
globalParam
;
private
final
LocalDateTime
baseTime
;
private
final
MachineCalculator
machineCalculator
;
private
final
boolean
verboseLogging
;
BackwardChangeoverHelper
(
GlobalParam
globalParam
,
LocalDateTime
baseTime
,
MachineCalculator
machineCalculator
)
{
this
.
globalParam
=
globalParam
;
this
.
baseTime
=
baseTime
;
this
.
machineCalculator
=
machineCalculator
;
this
.
verboseLogging
=
Boolean
.
parseBoolean
(
System
.
getProperty
(
DEBUG_BACKWARD_CHANGEOVER_VERBOSE_PROPERTY
,
"false"
));
}
boolean
isVerboseLoggingEnabled
()
{
return
verboseLogging
;
}
void
writeVerboseLog
(
String
message
)
{
if
(
verboseLogging
)
{
FileHelper
.
writeLogFile
(
message
);
}
}
private
String
convertTime
(
int
second
)
{
return
baseTime
.
plusSeconds
(
second
).
format
(
DateTimeFormatter
.
ofPattern
(
"YYYY-MM-dd HH:mm"
));
}
private
boolean
isBackwardInsufficientTimeException
(
RuntimeException
ex
)
{
return
ex
instanceof
IllegalStateException
&&
ex
.
getMessage
()
!=
null
&&
ex
.
getMessage
().
contains
(
"\u8bbe\u5907\u53ef\u7528\u65f6\u95f4\u4e0d\u8db3"
);
}
// ==================== 倒排换型辅助方法开始 ====================
GAScheduleResult
getNextMachineTaskForBackward
(
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
,
int
processEndTime
)
{
if
(
machineTasks
==
null
||
machineTasks
.
isEmpty
())
{
return
null
;
}
return
machineTasks
.
stream
()
.
filter
(
t
->
getProcessStartTime
(
t
)
>=
processEndTime
)
.
min
(
Comparator
.
comparingInt
(
this
::
getProcessStartTime
))
.
orElse
(
null
);
}
GAScheduleResult
getPrevMachineTaskForBackward
(
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
,
int
processStartTime
)
{
if
(
machineTasks
==
null
||
machineTasks
.
isEmpty
())
{
return
null
;
}
return
machineTasks
.
stream
()
.
filter
(
t
->
getProcessEndTime
(
t
)
<=
processStartTime
)
.
max
(
Comparator
.
comparingInt
(
this
::
getProcessEndTime
))
.
orElse
(
null
);
}
int
getProcessStartTime
(
GAScheduleResult
result
)
{
if
(
result
==
null
)
{
return
0
;
}
return
getProcessStartTime
(
result
.
getGeneDetails
(),
result
.
getStartTime
());
}
int
getProcessEndTime
(
GAScheduleResult
result
)
{
if
(
result
==
null
)
{
return
0
;
}
return
getProcessEndTime
(
result
.
getGeneDetails
(),
result
.
getEndTime
());
}
int
getProcessStartTime
(
List
<
ScheduleResultDetail
>
details
,
int
fallback
)
{
if
(
details
==
null
||
details
.
isEmpty
())
{
return
fallback
;
}
return
details
.
stream
()
.
filter
(
detail
->
!
detail
.
isSetup
())
.
mapToInt
(
ScheduleResultDetail:
:
getStartTime
)
.
min
()
.
orElse
(
fallback
);
}
int
getProcessEndTime
(
List
<
ScheduleResultDetail
>
details
,
int
fallback
)
{
if
(
details
==
null
||
details
.
isEmpty
())
{
return
fallback
;
}
return
details
.
stream
()
.
filter
(
detail
->
!
detail
.
isSetup
())
.
mapToInt
(
ScheduleResultDetail:
:
getEndTime
)
.
max
()
.
orElse
(
fallback
);
}
JitBackwardScheduleResult
scheduleJitBackwardWithChangeover
(
Machine
machine
,
Entry
operation
,
int
latestEndTime
,
int
processingTimeTotal
,
double
processingTime
,
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
,
List
<
Entry
>
allOperations
)
{
// 倒排换型主流程:
// 1. 先不带换型试排当前工序,定位它后面的相邻任务;
// 2. 计算“当前 -> 后任务”的后置换型,把当前加工结束时间往前压;
// 3. 再计算“前任务 -> 当前”的前置换型,校验整块窗口能否插入;
// 4. 如果倒排已经压过基准时间或设备可用时间不足,交给外层整单正排兜底。
boolean
backwardScheduled
=
false
;
boolean
forwardFallbackRequired
=
false
;
int
backwardLatestEndTime
=
latestEndTime
;
int
backwardAttemptLimit
=
Math
.
max
(
1
,
(
machineTasks
==
null
?
0
:
machineTasks
.
size
())
+
1
);
int
setupTime
=
0
;
CopyOnWriteArrayList
<
ScheduleResultDetail
>
geneDetails
=
new
CopyOnWriteArrayList
<>();
int
baselineTime
=
0
;
for
(
int
backwardAttempt
=
0
;
backwardAttempt
<
backwardAttemptLimit
;
backwardAttempt
++)
{
CopyOnWriteArrayList
<
TimeSegment
>
emptySetupSegments
=
new
CopyOnWriteArrayList
<>();
Map
<
Integer
,
Object
>
previewResult
;
try
{
// 试排只用于拿当前加工段位置;试排会临时占用日历,拿到位置后马上释放。
previewResult
=
machineCalculator
.
CreateScheduleResultBackward
(
machine
,
operation
,
processingTimeTotal
,
backwardLatestEndTime
,
processingTime
,
operation
.
getQuantity
(),
0
,
backwardLatestEndTime
,
backwardLatestEndTime
,
emptySetupSegments
,
false
);
}
catch
(
RuntimeException
ex
)
{
if
(
isBackwardInsufficientTimeException
(
ex
))
{
writeVerboseLog
(
"[BackwardInsertWindow] switch to forward because backward preview has insufficient machine time"
+
", operationId="
+
operation
.
getId
()
+
", groupId="
+
operation
.
getGroupId
()
+
", machineId="
+
machine
.
getId
()
+
", attempt="
+
backwardAttempt
+
", backwardLatestEndTime="
+
backwardLatestEndTime
+
", backwardLatestEnd="
+
convertTime
(
backwardLatestEndTime
)
+
", processingSeconds="
+
processingTimeTotal
+
", reason="
+
ex
.
getMessage
());
forwardFallbackRequired
=
true
;
break
;
}
throw
ex
;
}
CopyOnWriteArrayList
<
ScheduleResultDetail
>
previewGeneDetails
=
(
CopyOnWriteArrayList
<
ScheduleResultDetail
>)
previewResult
.
get
(
2
);
int
previewProcessStartTime
=
previewGeneDetails
.
stream
()
.
mapToInt
(
ScheduleResultDetail:
:
getStartTime
)
.
min
()
.
orElse
(
backwardLatestEndTime
);
int
previewProcessEndTime
=
previewGeneDetails
.
stream
()
.
mapToInt
(
ScheduleResultDetail:
:
getEndTime
)
.
max
()
.
orElse
(
backwardLatestEndTime
);
machineCalculator
.
AddMachineAvailable
(
machine
,
previewGeneDetails
);
GAScheduleResult
nextGeneOnMachine
=
getNextMachineTaskForBackward
(
machineTasks
,
previewProcessEndTime
);
// 黄色任务旧换型可能占着“绿色 -> 黄色”的时间,先释放出来再判断蓝色能不能插进去。
CopyOnWriteArrayList
<
ScheduleResultDetail
>
releasedNextSetupDetails
=
releaseNextTaskSetupForBackward
(
nextGeneOnMachine
,
machine
);
int
nextSetupLatestEndTime
=
nextGeneOnMachine
==
null
?
previewProcessEndTime
:
Math
.
min
(
backwardLatestEndTime
,
getProcessStartTime
(
nextGeneOnMachine
));
Map
<
Integer
,
Object
>
nextSetupResult
=
calculateSetupTimeBackward
(
nextGeneOnMachine
,
operation
,
machine
,
nextSetupLatestEndTime
,
processingTimeTotal
,
globalParam
.
is_smoothChangeOverInWeek
(),
allOperations
,
machineTasks
);
// 后置换型属于后任务:它决定当前工序最晚只能加工到什么时候。
int
nextSetupTime
=
(
int
)
nextSetupResult
.
get
(
1
);
int
latestProcessEndTime
=
(
int
)
nextSetupResult
.
get
(
4
);
int
attemptProcessingTimeTotal
=
(
int
)
nextSetupResult
.
get
(
5
);
boolean
backwardChangeoverFeasible
=
true
;
if
(
Boolean
.
TRUE
.
equals
(
nextSetupResult
.
get
(
8
)))
{
backwardChangeoverFeasible
=
false
;
writeVerboseLog
(
"[BackwardInsertWindow] 后置换型可用时间不足,当前插入位置不可行"
+
", operationId="
+
operation
.
getId
()
+
", groupId="
+
operation
.
getGroupId
()
+
", machineId="
+
machine
.
getId
()
+
", setupTime="
+
nextSetupTime
+
", availableSeconds="
+
nextSetupResult
.
get
(
7
));
}
CopyOnWriteArrayList
<
TimeSegment
>
noSetupSegments
=
new
CopyOnWriteArrayList
<>();
Map
<
Integer
,
Object
>
candidateResult
;
try
{
// 按“扣掉后置换型后的最晚结束时间”重新倒排当前加工段。
candidateResult
=
machineCalculator
.
CreateScheduleResultBackward
(
machine
,
operation
,
attemptProcessingTimeTotal
,
latestProcessEndTime
,
processingTime
,
operation
.
getQuantity
(),
0
,
latestProcessEndTime
,
latestProcessEndTime
,
noSetupSegments
,
false
);
}
catch
(
RuntimeException
ex
)
{
if
(
isBackwardInsufficientTimeException
(
ex
))
{
restoreNextTaskSetupForBackward
(
nextGeneOnMachine
,
machine
,
releasedNextSetupDetails
);
writeVerboseLog
(
"[BackwardInsertWindow] switch to forward because backward candidate has insufficient machine time"
+
", operationId="
+
operation
.
getId
()
+
", groupId="
+
operation
.
getGroupId
()
+
", machineId="
+
machine
.
getId
()
+
", attempt="
+
backwardAttempt
+
", latestProcessEndTime="
+
latestProcessEndTime
+
", latestProcessEnd="
+
convertTime
(
latestProcessEndTime
)
+
", processingSeconds="
+
attemptProcessingTimeTotal
+
", reason="
+
ex
.
getMessage
());
forwardFallbackRequired
=
true
;
break
;
}
throw
ex
;
}
CopyOnWriteArrayList
<
ScheduleResultDetail
>
candidateGeneDetails
=
(
CopyOnWriteArrayList
<
ScheduleResultDetail
>)
candidateResult
.
get
(
2
);
int
finalProcessStartTime
=
getProcessStartTime
(
candidateGeneDetails
,
previewProcessStartTime
);
int
finalProcessEndTime
=
getProcessEndTime
(
candidateGeneDetails
,
previewProcessEndTime
);
GAScheduleResult
prevGeneOnMachine
=
getPrevMachineTaskForBackward
(
machineTasks
,
finalProcessStartTime
);
if
(
finalProcessStartTime
<
baselineTime
)
{
machineCalculator
.
AddMachineAvailable
(
machine
,
candidateGeneDetails
);
restoreNextTaskSetupForBackward
(
nextGeneOnMachine
,
machine
,
releasedNextSetupDetails
);
writeVerboseLog
(
"[BackwardInsertWindow] switch to forward because backward start is before baseline"
+
", operationId="
+
operation
.
getId
()
+
", groupId="
+
operation
.
getGroupId
()
+
", machineId="
+
machine
.
getId
()
+
", attempt="
+
backwardAttempt
+
", baselineTime="
+
baselineTime
+
", baseline="
+
convertTime
(
baselineTime
)
+
", finalProcessStart="
+
finalProcessStartTime
+
", finalProcessStartTime="
+
convertTime
(
finalProcessStartTime
));
forwardFallbackRequired
=
true
;
break
;
}
writeVerboseLog
(
"[BackwardChangeover] check previous adjacent task, operationId="
+
operation
.
getId
()
+
", groupId="
+
operation
.
getGroupId
()
+
", execId="
+
operation
.
getExecId
()
+
", machineId="
+
machine
.
getId
()
+
", attempt="
+
backwardAttempt
+
", finalProcessStart="
+
finalProcessStartTime
+
", finalProcessStartTime="
+
convertTime
(
finalProcessStartTime
)
+
", prevTask="
+
describeScheduleResult
(
prevGeneOnMachine
));
Map
<
Integer
,
Object
>
previousSetupResult
=
calculateSetupTimeBeforeBackward
(
prevGeneOnMachine
,
operation
,
machine
,
finalProcessStartTime
,
attemptProcessingTimeTotal
,
globalParam
.
is_smoothChangeOverInWeek
(),
allOperations
,
machineTasks
);
// 前置换型属于当前任务:要跟当前加工段一起作为一个 block 校验是否落在前后任务之间。
int
previousSetupTime
=
(
int
)
previousSetupResult
.
get
(
1
);
int
blockStartTime
=
finalProcessStartTime
;
if
(
previousSetupTime
>
0
)
{
blockStartTime
=
Math
.
min
(
blockStartTime
,
(
int
)
previousSetupResult
.
get
(
2
));
}
if
(
blockStartTime
<
baselineTime
)
{
machineCalculator
.
AddMachineAvailable
(
machine
,
candidateGeneDetails
);
restoreNextTaskSetupForBackward
(
nextGeneOnMachine
,
machine
,
releasedNextSetupDetails
);
writeVerboseLog
(
"[BackwardInsertWindow] switch to forward because backward block is before baseline"
+
", operationId="
+
operation
.
getId
()
+
", groupId="
+
operation
.
getGroupId
()
+
", machineId="
+
machine
.
getId
()
+
", attempt="
+
backwardAttempt
+
", baselineTime="
+
baselineTime
+
", baseline="
+
convertTime
(
baselineTime
)
+
", blockStart="
+
blockStartTime
+
", blockStartTime="
+
convertTime
(
blockStartTime
));
forwardFallbackRequired
=
true
;
break
;
}
if
(
Boolean
.
TRUE
.
equals
(
previousSetupResult
.
get
(
8
)))
{
backwardChangeoverFeasible
=
false
;
writeVerboseLog
(
"[BackwardInsertWindow] 前置换型可用时间不足,当前插入位置不可行"
+
", operationId="
+
operation
.
getId
()
+
", groupId="
+
operation
.
getGroupId
()
+
", machineId="
+
machine
.
getId
()
+
", setupTime="
+
previousSetupTime
+
", availableSeconds="
+
previousSetupResult
.
get
(
7
));
}
boolean
canInsertBackwardWindow
=
backwardChangeoverFeasible
&&
validateBackwardInsertWindow
(
prevGeneOnMachine
,
nextGeneOnMachine
,
operation
,
machine
,
finalProcessStartTime
,
finalProcessEndTime
,
previousSetupResult
,
nextSetupResult
);
if
(
canInsertBackwardWindow
)
{
// 插入成功后,当前任务保存前置换型;后任务保存新的后置换型。
setupTime
=
previousSetupTime
;
operation
.
setChangeLineTime
(
setupTime
);
geneDetails
=
candidateGeneDetails
;
if
(
previousSetupTime
>
0
)
{
CopyOnWriteArrayList
<
TimeSegment
>
previousSetupSegments
=
(
CopyOnWriteArrayList
<
TimeSegment
>)
previousSetupResult
.
get
(
6
);
CopyOnWriteArrayList
<
ScheduleResultDetail
>
previousSetupDetails
=
machineCalculator
.
CreateSetupDetailsBackward
(
machine
,
previousSetupTime
,
(
int
)
previousSetupResult
.
get
(
2
),
(
int
)
previousSetupResult
.
get
(
3
),
previousSetupSegments
,
globalParam
.
is_smoothChangeOverInWeek
());
if
(
previousSetupDetails
!=
null
&&
!
previousSetupDetails
.
isEmpty
())
{
geneDetails
.
addAll
(
previousSetupDetails
);
geneDetails
.
sort
(
Comparator
.
comparingInt
(
ScheduleResultDetail:
:
getStartTime
));
}
}
replaceNextTaskSetupForBackward
(
nextGeneOnMachine
,
machine
,
nextSetupTime
,
nextSetupResult
);
backwardScheduled
=
true
;
break
;
}
machineCalculator
.
AddMachineAvailable
(
machine
,
candidateGeneDetails
);
restoreNextTaskSetupForBackward
(
nextGeneOnMachine
,
machine
,
releasedNextSetupDetails
);
int
nextLatestEndTime
;
if
(
prevGeneOnMachine
!=
null
)
{
// 当前窗口放不下,就继续往更早的相邻任务之前找插入点。
nextLatestEndTime
=
getProcessStartTime
(
prevGeneOnMachine
);
}
else
{
nextLatestEndTime
=
Math
.
min
(
finalProcessStartTime
,
previewProcessStartTime
)
-
1
;
}
if
(
nextLatestEndTime
<=
0
||
nextLatestEndTime
>=
backwardLatestEndTime
)
{
break
;
}
backwardLatestEndTime
=
nextLatestEndTime
;
}
if
(!
backwardScheduled
&&
forwardFallbackRequired
)
{
writeVerboseLog
(
"[BackwardInsertWindow] backward search passed schedule baseline, request whole order forward fallback"
+
", operationId="
+
operation
.
getId
()
+
", groupId="
+
operation
.
getGroupId
()
+
", machineId="
+
machine
.
getId
()
+
", baselineTime="
+
baselineTime
+
", baseline="
+
convertTime
(
baselineTime
));
return
new
JitBackwardScheduleResult
(
new
CopyOnWriteArrayList
<>(),
0
,
true
);
}
else
if
(!
backwardScheduled
)
{
// 倒排换型插入不成功但没有触发基准线/设备时间兜底时,保留原倒排能力,不强行制造换型。
writeVerboseLog
(
"[BackwardInsertWindow] 所有倒排插入位置都放不下,回退为不带换型倒排"
+
", operationId="
+
operation
.
getId
()
+
", groupId="
+
operation
.
getGroupId
()
+
", machineId="
+
machine
.
getId
()
+
", latestEndTime="
+
latestEndTime
+
", latestEnd="
+
convertTime
(
latestEndTime
));
geneDetails
=
machineCalculator
.
getNextAvailableTime
(
machine
,
operation
,
latestEndTime
,
-
1
,
processingTimeTotal
,
machineTasks
,
operation
.
getIsInterrupt
()
!=
1
,
true
,
processingTime
,
operation
.
getQuantity
(),
true
,
true
);
setupTime
=
0
;
operation
.
setChangeLineTime
(
setupTime
);
}
return
new
JitBackwardScheduleResult
(
geneDetails
,
setupTime
,
false
);
}
/**
* helper 返回给 GeneticDecoder 的结果:
* geneDetails/setupTime 表示本工序排程成功;forwardFallbackRequired 表示需要外层整单正排。
*/
static
class
JitBackwardScheduleResult
{
private
final
CopyOnWriteArrayList
<
ScheduleResultDetail
>
geneDetails
;
private
final
int
setupTime
;
private
final
boolean
forwardFallbackRequired
;
JitBackwardScheduleResult
(
CopyOnWriteArrayList
<
ScheduleResultDetail
>
geneDetails
,
int
setupTime
,
boolean
forwardFallbackRequired
)
{
this
.
geneDetails
=
geneDetails
;
this
.
setupTime
=
setupTime
;
this
.
forwardFallbackRequired
=
forwardFallbackRequired
;
}
CopyOnWriteArrayList
<
ScheduleResultDetail
>
getGeneDetails
()
{
return
geneDetails
;
}
int
getSetupTime
()
{
return
setupTime
;
}
boolean
isForwardFallbackRequired
()
{
return
forwardFallbackRequired
;
}
}
boolean
validateBackwardInsertWindow
(
GAScheduleResult
prevGeneOnMachine
,
GAScheduleResult
nextGeneOnMachine
,
Entry
operation
,
Machine
machine
,
int
processStartTime
,
int
processEndTime
,
Map
<
Integer
,
Object
>
previousSetupResult
,
Map
<
Integer
,
Object
>
nextSetupResult
)
{
int
prevBoundaryTime
=
prevGeneOnMachine
==
null
?
Integer
.
MIN_VALUE
:
getProcessEndTime
(
prevGeneOnMachine
);
int
nextBoundaryTime
=
nextGeneOnMachine
==
null
?
Integer
.
MAX_VALUE
:
getProcessStartTime
(
nextGeneOnMachine
);
int
blockStartTime
=
processStartTime
;
int
previousSetupTime
=
getSetupTime
(
previousSetupResult
);
if
(
previousSetupTime
>
0
)
{
blockStartTime
=
Math
.
min
(
blockStartTime
,
(
int
)
previousSetupResult
.
get
(
2
));
}
int
blockEndTime
=
processEndTime
;
int
nextSetupTime
=
getSetupTime
(
nextSetupResult
);
if
(
nextSetupTime
>
0
)
{
blockEndTime
=
Math
.
max
(
blockEndTime
,
(
int
)
nextSetupResult
.
get
(
3
));
}
boolean
previousSetupInsufficient
=
Boolean
.
TRUE
.
equals
(
previousSetupResult
==
null
?
false
:
previousSetupResult
.
get
(
8
));
boolean
nextSetupInsufficient
=
Boolean
.
TRUE
.
equals
(
nextSetupResult
==
null
?
false
:
nextSetupResult
.
get
(
8
));
boolean
canInsert
=
!
previousSetupInsufficient
&&
!
nextSetupInsufficient
&&
blockStartTime
>=
prevBoundaryTime
&&
blockEndTime
<=
nextBoundaryTime
;
writeVerboseLog
(
"[BackwardInsertWindow] operationId="
+
operation
.
getId
()
+
", groupId="
+
operation
.
getGroupId
()
+
", machineId="
+
machine
.
getId
()
+
", prevTask="
+
describeScheduleResult
(
prevGeneOnMachine
)
+
", nextTask="
+
describeScheduleResult
(
nextGeneOnMachine
)
+
", prevBoundary="
+
(
prevGeneOnMachine
==
null
?
"null"
:
convertTime
(
prevBoundaryTime
))
+
", nextBoundary="
+
(
nextGeneOnMachine
==
null
?
"null"
:
convertTime
(
nextBoundaryTime
))
+
", blockStart="
+
convertTime
(
blockStartTime
)
+
", blockEnd="
+
convertTime
(
blockEndTime
)
+
", processStart="
+
convertTime
(
processStartTime
)
+
", processEnd="
+
convertTime
(
processEndTime
)
+
", previousSetupTime="
+
previousSetupTime
+
", nextSetupTime="
+
nextSetupTime
+
", canInsert="
+
canInsert
);
return
canInsert
;
}
/**
* 试插蓝色前,先把黄色任务身上的旧后置换型释放出来。
* 这里不永久删除业务含义,只是让设备日历先空出来,方便判断真实的绿色/黄色间隔。
*/
CopyOnWriteArrayList
<
ScheduleResultDetail
>
releaseNextTaskSetupForBackward
(
GAScheduleResult
nextGeneOnMachine
,
Machine
machine
)
{
CopyOnWriteArrayList
<
ScheduleResultDetail
>
releasedSetupDetails
=
new
CopyOnWriteArrayList
<>();
if
(
nextGeneOnMachine
==
null
||
nextGeneOnMachine
.
getGeneDetails
()
==
null
||
nextGeneOnMachine
.
getGeneDetails
().
isEmpty
())
{
return
releasedSetupDetails
;
}
CopyOnWriteArrayList
<
ScheduleResultDetail
>
nextDetails
=
new
CopyOnWriteArrayList
<>(
nextGeneOnMachine
.
getGeneDetails
());
nextDetails
.
stream
()
.
filter
(
ScheduleResultDetail:
:
isSetup
)
.
forEach
(
releasedSetupDetails:
:
add
);
if
(
releasedSetupDetails
.
isEmpty
())
{
return
releasedSetupDetails
;
}
machineCalculator
.
AddMachineAvailable
(
machine
,
releasedSetupDetails
);
nextDetails
.
removeIf
(
ScheduleResultDetail:
:
isSetup
);
nextGeneOnMachine
.
setGeneDetails
(
nextDetails
);
nextGeneOnMachine
.
setChangeOverTime
(
0
);
refreshScheduleResultWindow
(
nextGeneOnMachine
);
writeVerboseLog
(
"[BackwardInsertWindow] 临时释放黄色旧换型"
+
", nextTask="
+
describeScheduleResult
(
nextGeneOnMachine
)
+
", releasedSetupCount="
+
releasedSetupDetails
.
size
()
+
", releasedSetupSeconds="
+
sumSetupSeconds
(
releasedSetupDetails
));
return
releasedSetupDetails
;
}
/**
* 当前绿色/黄色间隔放不下蓝色时,把刚才释放的黄色旧换型恢复回去。
*/
void
restoreNextTaskSetupForBackward
(
GAScheduleResult
nextGeneOnMachine
,
Machine
machine
,
List
<
ScheduleResultDetail
>
releasedSetupDetails
)
{
if
(
releasedSetupDetails
==
null
||
releasedSetupDetails
.
isEmpty
())
{
return
;
}
setMachineAvailabilityUsed
(
machine
,
releasedSetupDetails
,
true
);
if
(
nextGeneOnMachine
!=
null
)
{
CopyOnWriteArrayList
<
ScheduleResultDetail
>
nextDetails
=
nextGeneOnMachine
.
getGeneDetails
()
==
null
?
new
CopyOnWriteArrayList
<>()
:
new
CopyOnWriteArrayList
<>(
nextGeneOnMachine
.
getGeneDetails
());
nextDetails
.
removeIf
(
ScheduleResultDetail:
:
isSetup
);
nextDetails
.
addAll
(
releasedSetupDetails
);
nextDetails
.
sort
(
Comparator
.
comparingInt
(
ScheduleResultDetail:
:
getStartTime
));
nextGeneOnMachine
.
setGeneDetails
(
nextDetails
);
nextGeneOnMachine
.
setChangeOverTime
(
sumSetupSeconds
(
releasedSetupDetails
));
refreshScheduleResultWindow
(
nextGeneOnMachine
);
}
writeVerboseLog
(
"[BackwardInsertWindow] 恢复黄色旧换型"
+
", nextTask="
+
describeScheduleResult
(
nextGeneOnMachine
)
+
", restoredSetupCount="
+
releasedSetupDetails
.
size
()
+
", restoredSetupSeconds="
+
sumSetupSeconds
(
releasedSetupDetails
));
}
/**
* 按排程明细里的 key/usedSegment,把设备日历设置为占用或可用。
* 恢复旧换型时用 true,释放时仍沿用 machineCalculator.AddMachineAvailable。
*/
private
void
setMachineAvailabilityUsed
(
Machine
machine
,
List
<
ScheduleResultDetail
>
geneDetails
,
boolean
used
)
{
if
(
machine
==
null
||
machine
.
getAvailability
()
==
null
||
geneDetails
==
null
||
geneDetails
.
isEmpty
())
{
return
;
}
Set
<
String
>
keys
=
new
HashSet
<>();
for
(
ScheduleResultDetail
detail
:
geneDetails
)
{
if
(
detail
==
null
)
{
continue
;
}
if
(
detail
.
getUsedSegment
()
!=
null
&&
!
detail
.
getUsedSegment
().
isEmpty
())
{
detail
.
getUsedSegment
().
stream
()
.
map
(
TimeSegment:
:
getKey
)
.
filter
(
Objects:
:
nonNull
)
.
forEach
(
keys:
:
add
);
}
else
if
(
detail
.
getKey
()
!=
null
)
{
keys
.
add
(
detail
.
getKey
());
}
}
if
(
keys
.
isEmpty
())
{
return
;
}
machine
.
getAvailability
().
stream
()
.
filter
(
t
->
keys
.
contains
(
t
.
getKey
()))
.
forEach
(
t
->
t
.
setUsed
(
used
));
}
private
int
sumSetupSeconds
(
List
<
ScheduleResultDetail
>
setupDetails
)
{
if
(
setupDetails
==
null
||
setupDetails
.
isEmpty
())
{
return
0
;
}
return
setupDetails
.
stream
()
.
mapToInt
(
ScheduleResultDetail:
:
getProcessingTime
)
.
sum
();
}
/**
* 后置换型归属黄色任务。
* 插入蓝色后,黄色原来的“绿色 -> 黄色”换型要释放掉,再替换成“蓝色 -> 黄色”的新换型。
*/
void
replaceNextTaskSetupForBackward
(
GAScheduleResult
nextGeneOnMachine
,
Machine
machine
,
int
setupTime
,
Map
<
Integer
,
Object
>
setupResult
)
{
if
(
nextGeneOnMachine
==
null
||
setupResult
==
null
)
{
return
;
}
CopyOnWriteArrayList
<
ScheduleResultDetail
>
nextDetails
=
nextGeneOnMachine
.
getGeneDetails
()
==
null
?
new
CopyOnWriteArrayList
<>()
:
new
CopyOnWriteArrayList
<>(
nextGeneOnMachine
.
getGeneDetails
());
List
<
ScheduleResultDetail
>
oldSetupDetails
=
nextDetails
.
stream
()
.
filter
(
ScheduleResultDetail:
:
isSetup
)
.
collect
(
Collectors
.
toList
());
if
(!
oldSetupDetails
.
isEmpty
())
{
machineCalculator
.
AddMachineAvailable
(
machine
,
oldSetupDetails
);
nextDetails
.
removeIf
(
ScheduleResultDetail:
:
isSetup
);
}
nextGeneOnMachine
.
setChangeOverTime
(
0
);
if
(
setupTime
>
0
)
{
CopyOnWriteArrayList
<
TimeSegment
>
setupSegments
=
(
CopyOnWriteArrayList
<
TimeSegment
>)
setupResult
.
get
(
6
);
CopyOnWriteArrayList
<
ScheduleResultDetail
>
newSetupDetails
=
machineCalculator
.
CreateSetupDetailsBackward
(
machine
,
setupTime
,
(
int
)
setupResult
.
get
(
2
),
(
int
)
setupResult
.
get
(
3
),
setupSegments
,
globalParam
.
is_smoothChangeOverInWeek
());
if
(
newSetupDetails
!=
null
&&
!
newSetupDetails
.
isEmpty
())
{
nextDetails
.
addAll
(
newSetupDetails
);
nextDetails
.
sort
(
Comparator
.
comparingInt
(
ScheduleResultDetail:
:
getStartTime
));
}
nextGeneOnMachine
.
setChangeOverTime
(
setupTime
);
}
nextGeneOnMachine
.
setGeneDetails
(
nextDetails
);
refreshScheduleResultWindow
(
nextGeneOnMachine
);
}
/**
* 根据明细刷新任务整体起止时间;加工开始/结束仍由 getProcessStartTime/getProcessEndTime 过滤 setup 得到。
*/
private
void
refreshScheduleResultWindow
(
GAScheduleResult
result
)
{
if
(
result
==
null
||
result
.
getGeneDetails
()
==
null
||
result
.
getGeneDetails
().
isEmpty
())
{
return
;
}
result
.
setStartTime
(
result
.
getGeneDetails
().
stream
()
.
mapToInt
(
ScheduleResultDetail:
:
getStartTime
)
.
min
()
.
orElse
(
result
.
getStartTime
()));
result
.
setEndTime
(
result
.
getGeneDetails
().
stream
()
.
mapToInt
(
ScheduleResultDetail:
:
getEndTime
)
.
max
()
.
orElse
(
result
.
getEndTime
()));
}
private
int
getSetupTime
(
Map
<
Integer
,
Object
>
setupResult
)
{
if
(
setupResult
==
null
||
setupResult
.
get
(
1
)
==
null
)
{
return
0
;
}
return
(
int
)
setupResult
.
get
(
1
);
}
String
describeScheduleResult
(
GAScheduleResult
result
)
{
if
(
result
==
null
)
{
return
"null"
;
}
return
"{operationId="
+
result
.
getOperationId
()
+
", groupId="
+
result
.
getGroupId
()
+
", execId="
+
result
.
getExecId
()
+
", start="
+
result
.
getStartTime
()
+
"("
+
convertTime
(
result
.
getStartTime
())
+
")"
+
", end="
+
result
.
getEndTime
()
+
"("
+
convertTime
(
result
.
getEndTime
())
+
")"
+
", processStart="
+
getProcessStartTime
(
result
)
+
"("
+
convertTime
(
getProcessStartTime
(
result
))
+
")"
+
", processEnd="
+
getProcessEndTime
(
result
)
+
"("
+
convertTime
(
getProcessEndTime
(
result
))
+
")"
+
", changeOverTime="
+
result
.
getChangeOverTime
()
+
"}"
;
}
String
describeDiscreteParams
(
Entry
operation
)
{
if
(
operation
==
null
||
operation
.
getDiscreteParameter
()
==
null
||
operation
.
getDiscreteParameter
().
isEmpty
())
{
return
"[]"
;
}
return
operation
.
getDiscreteParameter
().
stream
()
.
map
(
p
->
"{groupId="
+
p
.
getGroupId
()
+
", parameterId="
+
p
.
getParameterId
()
+
", parameterName="
+
p
.
getParameterName
()
+
"}"
)
.
collect
(
Collectors
.
joining
(
","
,
"["
,
"]"
));
}
/**
* 临时测试开关:启动参数传 -Daps.debug.backwardSetupTimeSeconds=秒数 时,
* 只覆盖倒排换型时间;不传或传负数时走原来的矩阵/时长计算。
*/
private
int
overrideSetupTimeForDebug
(
int
setupTime
,
String
direction
,
Entry
fromOperation
,
Entry
toOperation
,
Machine
machine
)
{
String
overrideValue
=
System
.
getProperty
(
DEBUG_BACKWARD_SETUP_TIME_SECONDS_PROPERTY
);
if
(
overrideValue
==
null
||
overrideValue
.
trim
().
isEmpty
())
{
return
setupTime
;
}
int
debugSetupTime
;
try
{
debugSetupTime
=
Integer
.
parseInt
(
overrideValue
.
trim
());
}
catch
(
NumberFormatException
ex
)
{
writeVerboseLog
(
"[BackwardChangeoverDebug] 手动换型时间参数无效,继续使用原计算值"
+
", property="
+
DEBUG_BACKWARD_SETUP_TIME_SECONDS_PROPERTY
+
", value="
+
overrideValue
+
", originalSetupTime="
+
setupTime
);
return
setupTime
;
}
if
(
debugSetupTime
<
0
)
{
return
setupTime
;
}
writeVerboseLog
(
"[BackwardChangeoverDebug] 使用手动换型时间"
+
", direction="
+
direction
+
", machineId="
+
(
machine
==
null
?
null
:
machine
.
getId
())
+
", fromOp="
+
(
fromOperation
==
null
?
null
:
fromOperation
.
getId
())
+
", toOp="
+
(
toOperation
==
null
?
null
:
toOperation
.
getId
())
+
", originalSetupTime="
+
setupTime
+
", debugSetupTime="
+
debugSetupTime
);
return
debugSetupTime
;
}
Map
<
Integer
,
Object
>
calculateSetupTimeBeforeBackward
(
GAScheduleResult
prevGeneOnMachine
,
Entry
operation
,
Machine
machine
,
int
processStartTime
,
int
processingTimeTotal
,
boolean
changeOverInWeek
,
List
<
Entry
>
allOperations
,
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
)
{
Map
<
Integer
,
Object
>
result
=
new
HashMap
<>();
CopyOnWriteArrayList
<
TimeSegment
>
setupSegments
=
new
CopyOnWriteArrayList
<>();
result
.
put
(
1
,
0
);
result
.
put
(
2
,
processStartTime
);
result
.
put
(
3
,
processStartTime
);
result
.
put
(
4
,
processStartTime
);
result
.
put
(
5
,
processingTimeTotal
);
result
.
put
(
6
,
setupSegments
);
result
.
put
(
7
,
0
);
result
.
put
(
8
,
false
);
if
(
prevGeneOnMachine
==
null
||
operation
==
null
||
machine
==
null
)
{
writeVerboseLog
(
"[BackwardChangeover] skip previous changeover, prev/operation/machine is null"
+
", operationId="
+
(
operation
==
null
?
null
:
operation
.
getId
())
+
", machineId="
+
(
machine
==
null
?
null
:
machine
.
getId
())
+
", processStartTime="
+
processStartTime
+
", processStart="
+
convertTime
(
processStartTime
)
+
", machineTaskCount="
+
(
machineTasks
==
null
?
0
:
machineTasks
.
size
()));
return
result
;
}
Entry
prevOperation
=
allOperations
==
null
?
null
:
allOperations
.
stream
()
.
filter
(
t
->
Objects
.
equals
(
t
.
getExecId
(),
prevGeneOnMachine
.
getExecId
()))
.
findFirst
()
.
orElse
(
null
);
if
(
prevOperation
==
null
)
{
writeVerboseLog
(
"[BackwardChangeover] skip previous changeover, previous Entry not found"
+
", operationId="
+
operation
.
getId
()
+
", prevTask="
+
describeScheduleResult
(
prevGeneOnMachine
)
+
", allOperationCount="
+
(
allOperations
==
null
?
0
:
allOperations
.
size
()));
return
result
;
}
DiscreteParameterMatrixService
discreteParameterMatrixService
=
SpringContextUtil
.
getBean
(
DiscreteParameterMatrixService
.
class
);
DiscreteParameterDurationService
discreteParameterDurationService
=
SpringContextUtil
.
getBean
(
DiscreteParameterDurationService
.
class
);
double
durationValue
=
discreteParameterDurationService
.
calculateChangeoverTime
(
prevOperation
,
operation
,
machine
,
allOperations
,
processStartTime
,
machineTasks
);
double
matrixValue
=
discreteParameterMatrixService
.
getDiscreteParameterMatrixValue
(
operation
,
prevOperation
);
int
setupTime
=
(
int
)
Math
.
max
(
durationValue
,
matrixValue
);
setupTime
=
overrideSetupTimeForDebug
(
setupTime
,
"prev->current"
,
prevOperation
,
operation
,
machine
);
writeVerboseLog
(
"[BackwardChangeover] previous changeover result prevOp="
+
prevOperation
.
getId
()
+
", currentOp="
+
operation
.
getId
()
+
", machineId="
+
machine
.
getId
()
+
", prevParams="
+
describeDiscreteParams
(
prevOperation
)
+
", currentParams="
+
describeDiscreteParams
(
operation
)
+
", durationValue="
+
durationValue
+
", matrixValue="
+
matrixValue
+
", setupTime="
+
setupTime
+
", processStart="
+
convertTime
(
processStartTime
)
+
", prevProcessEnd="
+
convertTime
(
getProcessEndTime
(
prevGeneOnMachine
)));
result
.
put
(
1
,
setupTime
);
if
(
setupTime
<=
0
)
{
return
result
;
}
int
prevBoundaryTime
=
getProcessEndTime
(
prevGeneOnMachine
);
LocalDateTime
setupEnd
=
baseTime
.
plusSeconds
(
processStartTime
);
if
(
changeOverInWeek
)
{
LocalDateTime
setupStart
=
setupEnd
.
minusSeconds
(
setupTime
);
int
setupStartTime
=
secondsFromBase
(
setupStart
);
setupSegments
=
collectAvailableSegmentsBetween
(
machine
,
setupStart
,
setupEnd
);
result
.
put
(
2
,
setupStartTime
);
result
.
put
(
3
,
processStartTime
);
result
.
put
(
4
,
processStartTime
);
result
.
put
(
6
,
setupSegments
);
result
.
put
(
7
,
Math
.
max
(
0
,
processStartTime
-
prevBoundaryTime
));
result
.
put
(
8
,
setupStartTime
<
prevBoundaryTime
);
writeVerboseLog
(
"[BackwardChangeover] previous changeover window currentOp="
+
operation
.
getId
()
+
", setupStart="
+
convertTime
(
setupStartTime
)
+
", setupEnd="
+
convertTime
(
processStartTime
)
+
", prevBoundary="
+
convertTime
(
prevBoundaryTime
)
+
", requiredSeconds="
+
setupTime
+
", availableCalendarSeconds="
+
result
.
get
(
7
)
+
", insufficient="
+
result
.
get
(
8
)
+
", segments="
+
describeTimeSegments
(
setupSegments
));
return
result
;
}
BackwardSetupWindow
setupWindow
=
collectBackwardSetupWindow
(
machine
,
setupEnd
,
setupTime
,
baseTime
.
plusSeconds
(
prevBoundaryTime
));
result
.
put
(
2
,
setupWindow
.
startTime
);
result
.
put
(
3
,
setupWindow
.
endTime
);
result
.
put
(
4
,
processStartTime
);
result
.
put
(
6
,
setupWindow
.
segments
);
result
.
put
(
7
,
setupWindow
.
availableSeconds
);
result
.
put
(
8
,
setupWindow
.
insufficient
);
writeVerboseLog
(
"[BackwardChangeover] previous changeover window currentOp="
+
operation
.
getId
()
+
", setupStart="
+
convertTime
(
setupWindow
.
startTime
)
+
", setupEnd="
+
convertTime
(
setupWindow
.
endTime
)
+
", prevBoundary="
+
convertTime
(
prevBoundaryTime
)
+
", requiredSeconds="
+
setupTime
+
", availableSeconds="
+
setupWindow
.
availableSeconds
+
", insufficient="
+
setupWindow
.
insufficient
+
", segments="
+
describeTimeSegments
(
setupWindow
.
segments
));
return
result
;
}
Map
<
Integer
,
Object
>
calculateSetupTimeBackward
(
GAScheduleResult
nextGeneOnMachine
,
Entry
operation
,
Machine
machine
,
int
latestEndTime
,
int
processingTimeTotal
,
boolean
changeOverInWeek
,
List
<
Entry
>
allOperations
,
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
)
{
Map
<
Integer
,
Object
>
result
=
new
HashMap
<>();
CopyOnWriteArrayList
<
TimeSegment
>
setupSegments
=
new
CopyOnWriteArrayList
<>();
result
.
put
(
1
,
0
);
result
.
put
(
2
,
latestEndTime
);
result
.
put
(
3
,
latestEndTime
);
result
.
put
(
4
,
latestEndTime
);
result
.
put
(
5
,
processingTimeTotal
);
result
.
put
(
6
,
setupSegments
);
result
.
put
(
7
,
0
);
result
.
put
(
8
,
false
);
if
(
nextGeneOnMachine
==
null
||
operation
==
null
||
machine
==
null
)
{
writeVerboseLog
(
"[倒排换型定位] 未计算换型:nextGeneOnMachine/operation/machine为空"
+
", operationId="
+
(
operation
==
null
?
null
:
operation
.
getId
())
+
", machineId="
+
(
machine
==
null
?
null
:
machine
.
getId
())
+
", latestEndTime="
+
latestEndTime
+
", latestEnd="
+
convertTime
(
latestEndTime
)
+
", machineTaskCount="
+
(
machineTasks
==
null
?
0
:
machineTasks
.
size
()));
return
result
;
}
Entry
nextOperation
=
allOperations
==
null
?
null
:
allOperations
.
stream
()
.
filter
(
t
->
Objects
.
equals
(
t
.
getExecId
(),
nextGeneOnMachine
.
getExecId
()))
.
findFirst
()
.
orElse
(
null
);
if
(
nextOperation
==
null
)
{
writeVerboseLog
(
"[倒排换型定位] 未计算换型:找不到后继工序Entry"
+
", operationId="
+
operation
.
getId
()
+
", nextTask="
+
describeScheduleResult
(
nextGeneOnMachine
)
+
", allOperationCount="
+
(
allOperations
==
null
?
0
:
allOperations
.
size
()));
return
result
;
}
DiscreteParameterMatrixService
discreteParameterMatrixService
=
SpringContextUtil
.
getBean
(
DiscreteParameterMatrixService
.
class
);
DiscreteParameterDurationService
discreteParameterDurationService
=
SpringContextUtil
.
getBean
(
DiscreteParameterDurationService
.
class
);
double
durationValue
=
discreteParameterDurationService
.
calculateChangeoverTime
(
operation
,
nextOperation
,
machine
,
allOperations
,
latestEndTime
,
machineTasks
);
double
matrixValue
=
discreteParameterMatrixService
.
getDiscreteParameterMatrixValue
(
nextOperation
,
operation
);
int
setupTime
=
(
int
)
Math
.
max
(
durationValue
,
matrixValue
);
setupTime
=
overrideSetupTimeForDebug
(
setupTime
,
"current->next"
,
operation
,
nextOperation
,
machine
);
nextOperation
.
setChangeLineTime
(
setupTime
);
int
nextProcessStartTime
=
getProcessStartTime
(
nextGeneOnMachine
);
writeVerboseLog
(
"[倒排换型定位] 换型查询结果 currentOp="
+
operation
.
getId
()
+
", nextOp="
+
nextOperation
.
getId
()
+
", machineId="
+
machine
.
getId
()
+
", currentParams="
+
describeDiscreteParams
(
operation
)
+
", nextParams="
+
describeDiscreteParams
(
nextOperation
)
+
", durationValue="
+
durationValue
+
", matrixValue="
+
matrixValue
+
", setupTime="
+
setupTime
+
", latestEnd="
+
convertTime
(
latestEndTime
)
+
", nextStart="
+
convertTime
(
nextProcessStartTime
));
result
.
put
(
1
,
setupTime
);
if
(
setupTime
<=
0
)
{
return
result
;
}
int
setupEndTime
=
Math
.
min
(
latestEndTime
,
nextProcessStartTime
);
LocalDateTime
setupEnd
=
baseTime
.
plusSeconds
(
setupEndTime
);
if
(
changeOverInWeek
)
{
LocalDateTime
setupStart
=
setupEnd
.
minusSeconds
(
setupTime
);
setupSegments
=
collectAvailableSegmentsBetween
(
machine
,
setupStart
,
setupEnd
);
result
.
put
(
2
,
secondsFromBase
(
setupStart
));
result
.
put
(
3
,
setupEndTime
);
result
.
put
(
4
,
secondsFromBase
(
setupStart
));
result
.
put
(
6
,
setupSegments
);
result
.
put
(
7
,
sumCalendarSeconds
(
setupSegments
));
writeVerboseLog
(
"[倒排换型定位] 换型窗口 currentOp="
+
operation
.
getId
()
+
", setupStart="
+
convertTime
(
secondsFromBase
(
setupStart
))
+
", setupEnd="
+
convertTime
(
setupEndTime
)
+
", latestProcessEnd="
+
convertTime
(
secondsFromBase
(
setupStart
))
+
", requiredSeconds="
+
setupTime
+
", availableSeconds="
+
result
.
get
(
7
)
+
", segments="
+
describeTimeSegments
(
setupSegments
));
return
result
;
}
BackwardSetupWindow
setupWindow
=
collectBackwardSetupWindow
(
machine
,
setupEnd
,
setupTime
);
result
.
put
(
2
,
setupWindow
.
startTime
);
result
.
put
(
3
,
setupWindow
.
endTime
);
result
.
put
(
4
,
setupWindow
.
startTime
);
result
.
put
(
6
,
setupWindow
.
segments
);
result
.
put
(
7
,
setupWindow
.
availableSeconds
);
result
.
put
(
8
,
setupWindow
.
insufficient
);
writeVerboseLog
(
"[倒排换型定位] 换型窗口 currentOp="
+
operation
.
getId
()
+
", setupStart="
+
convertTime
(
setupWindow
.
startTime
)
+
", setupEnd="
+
convertTime
(
setupWindow
.
endTime
)
+
", latestProcessEnd="
+
convertTime
(
setupWindow
.
startTime
)
+
", requiredSeconds="
+
setupTime
+
", availableSeconds="
+
setupWindow
.
availableSeconds
+
", insufficient="
+
setupWindow
.
insufficient
+
", segments="
+
describeTimeSegments
(
setupWindow
.
segments
));
return
result
;
}
private
BackwardSetupWindow
collectBackwardSetupWindow
(
Machine
machine
,
LocalDateTime
setupEnd
,
int
setupTime
)
{
return
collectBackwardSetupWindow
(
machine
,
setupEnd
,
setupTime
,
null
);
}
private
BackwardSetupWindow
collectBackwardSetupWindow
(
Machine
machine
,
LocalDateTime
setupEnd
,
int
setupTime
,
LocalDateTime
earliestStart
)
{
BackwardSetupWindow
window
=
new
BackwardSetupWindow
();
window
.
endTime
=
secondsFromBase
(
setupEnd
);
window
.
startTime
=
secondsFromBase
(
setupEnd
);
if
(
machine
==
null
||
machine
.
getAvailability
()
==
null
||
setupTime
<=
0
)
{
window
.
insufficient
=
setupTime
>
0
;
return
window
;
}
int
remaining
=
setupTime
;
LocalDateTime
cursor
=
setupEnd
;
List
<
TimeSegment
>
slots
=
machine
.
getAvailability
().
stream
()
.
filter
(
slot
->
!
slot
.
isUsed
())
.
filter
(
slot
->
slot
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
slot
->
slot
.
getStart
().
isBefore
(
setupEnd
))
.
filter
(
slot
->
earliestStart
==
null
||
slot
.
getEnd
().
isAfter
(
earliestStart
))
.
sorted
(
Comparator
.
comparing
(
TimeSegment:
:
getEnd
).
reversed
())
.
collect
(
Collectors
.
toList
());
for
(
TimeSegment
slot
:
slots
)
{
LocalDateTime
effectiveEnd
=
slot
.
getEnd
().
isAfter
(
cursor
)
?
cursor
:
slot
.
getEnd
();
LocalDateTime
effectiveSlotStart
=
earliestStart
!=
null
&&
slot
.
getStart
().
isBefore
(
earliestStart
)
?
earliestStart
:
slot
.
getStart
();
if
(!
effectiveSlotStart
.
isBefore
(
effectiveEnd
))
{
continue
;
}
int
availableSeconds
=
(
int
)
ChronoUnit
.
SECONDS
.
between
(
effectiveSlotStart
,
effectiveEnd
);
int
usedSeconds
=
Math
.
min
(
remaining
,
availableSeconds
);
LocalDateTime
effectiveStart
=
effectiveEnd
.
minusSeconds
(
usedSeconds
);
window
.
segments
.
add
(
copySegment
(
slot
,
effectiveStart
,
effectiveEnd
,
true
));
window
.
availableSeconds
+=
usedSeconds
;
remaining
-=
usedSeconds
;
cursor
=
effectiveStart
;
if
(
remaining
<=
0
)
{
break
;
}
}
window
.
segments
.
sort
(
Comparator
.
comparing
(
TimeSegment:
:
getStart
));
window
.
startTime
=
secondsFromBase
(
cursor
);
window
.
insufficient
=
remaining
>
0
;
return
window
;
}
private
CopyOnWriteArrayList
<
TimeSegment
>
collectAvailableSegmentsBetween
(
Machine
machine
,
LocalDateTime
startTime
,
LocalDateTime
endTime
)
{
if
(
machine
==
null
||
machine
.
getAvailability
()
==
null
||
!
startTime
.
isBefore
(
endTime
))
{
return
new
CopyOnWriteArrayList
<>();
}
return
machine
.
getAvailability
().
stream
()
.
filter
(
slot
->
!
slot
.
isUsed
())
.
filter
(
slot
->
slot
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
slot
->
slot
.
getStart
().
isBefore
(
endTime
)
&&
slot
.
getEnd
().
isAfter
(
startTime
))
.
map
(
slot
->
{
LocalDateTime
effectiveStart
=
slot
.
getStart
().
isBefore
(
startTime
)
?
startTime
:
slot
.
getStart
();
LocalDateTime
effectiveEnd
=
slot
.
getEnd
().
isAfter
(
endTime
)
?
endTime
:
slot
.
getEnd
();
return
copySegment
(
slot
,
effectiveStart
,
effectiveEnd
,
true
);
})
.
filter
(
slot
->
slot
.
getStart
().
isBefore
(
slot
.
getEnd
()))
.
sorted
(
Comparator
.
comparing
(
TimeSegment:
:
getStart
))
.
collect
(
Collectors
.
toCollection
(
CopyOnWriteArrayList:
:
new
));
}
private
TimeSegment
copySegment
(
TimeSegment
source
,
LocalDateTime
startTime
,
LocalDateTime
endTime
,
boolean
used
)
{
TimeSegment
segment
=
new
TimeSegment
();
segment
.
setStart
(
startTime
);
segment
.
setEnd
(
endTime
);
segment
.
setType
(
source
.
getType
());
segment
.
setHoliday
(
source
.
isHoliday
());
segment
.
setUsed
(
used
);
segment
.
setEfficiency
(
source
.
getEfficiency
());
return
segment
;
}
private
int
sumCalendarSeconds
(
List
<
TimeSegment
>
segments
)
{
if
(
segments
==
null
||
segments
.
isEmpty
())
{
return
0
;
}
return
segments
.
stream
()
.
mapToInt
(
segment
->
(
int
)
ChronoUnit
.
SECONDS
.
between
(
segment
.
getStart
(),
segment
.
getEnd
()))
.
sum
();
}
private
String
describeTimeSegments
(
List
<
TimeSegment
>
segments
)
{
if
(
segments
==
null
||
segments
.
isEmpty
())
{
return
"[]"
;
}
return
segments
.
stream
()
.
map
(
segment
->
"["
+
secondsFromBase
(
segment
.
getStart
())
+
"-"
+
secondsFromBase
(
segment
.
getEnd
())
+
"]("
+
segment
.
getStart
()
+
"-"
+
segment
.
getEnd
()
+
","
+
ChronoUnit
.
SECONDS
.
between
(
segment
.
getStart
(),
segment
.
getEnd
())
+
")"
)
.
collect
(
Collectors
.
joining
(
","
,
"["
,
"]"
));
}
private
int
secondsFromBase
(
LocalDateTime
time
)
{
return
(
int
)
ChronoUnit
.
SECONDS
.
between
(
baseTime
,
time
);
}
private
static
class
BackwardSetupWindow
{
private
int
startTime
;
private
int
endTime
;
private
int
availableSeconds
;
private
boolean
insufficient
;
private
CopyOnWriteArrayList
<
TimeSegment
>
segments
=
new
CopyOnWriteArrayList
<>();
}
// ==================== 倒排换型辅助方法结束 ====================
}
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
View file @
3d3915bf
...
...
@@ -622,6 +622,8 @@ public class GeneticDecoder {
// List<Entry> allScheduledOps = new ArrayList<>();
// 缓存机器任务
Map
<
Long
,
CopyOnWriteArrayList
<
GAScheduleResult
>>
machineTasksCache
=
new
HashMap
<>();
// 记录已经整单转正排的订单,避免倒排序列后续再次进入同一个 groupId。
Set
<
Integer
>
forwardFallbackOrderIds
=
new
HashSet
<>();
int
opCount
=
0
;
long
slowOpThresholdNs
=
500_000_000L
;
// 500ms
...
...
@@ -670,6 +672,9 @@ public class GeneticDecoder {
if
(
scheduledCount
>=
orderOps
.
size
())
{
if
(
forwardFallbackOrderIds
.
contains
(
groupId
))
{
continue
;
}
throw
new
IllegalStateException
(
String
.
format
(
"订单%d的工序已全部调度(共%d道),无需重复处理!"
,
groupId
,
orderOps
.
size
()));
...
...
@@ -702,7 +707,21 @@ public class GeneticDecoder {
int
dueDateForOp
=
opIsJit
?
orderAnchor
:
(
schedInfo
!=
null
?
schedInfo
.
getValue
()
:
0
);
int
actualEndTime
=
processOperation
(
currentOp
,
machineId
,
processTime
,
machineOption
,
chromosome
,
machineIdMap
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
dueDateForOp
,
true
,
opIsJit
);
OperationScheduleResult
operationResult
=
processOperationSchedule
(
currentOp
,
machineId
,
processTime
,
machineOption
,
chromosome
,
machineIdMap
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
dueDateForOp
,
true
,
opIsJit
);
if
(
operationResult
.
isForwardFallbackRequired
())
{
// 倒排中任一道序因换型/日历约束放不下时,先撤回本订单已倒排结果,再整单正排。
clearOrderSchedulingForForwardFallback
(
chromosome
,
groupId
,
machineTasksCache
,
scheduleIndexById
);
int
forwardEndTime
=
scheduleOrderForwardFallback
(
groupId
,
chromosome
,
machineIdMap
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
opMachineKeyMap
);
orderProcessCounter
.
put
(
groupId
,
entrysBygroupId
.
get
(
groupId
).
size
());
orderLastEndTime
.
put
(
groupId
,
forwardEndTime
);
forwardFallbackOrderIds
.
add
(
groupId
);
opCount
+=
entrysBygroupId
.
get
(
groupId
).
size
();
continue
;
}
int
actualEndTime
=
operationResult
.
getEndTime
();
long
opElapsed
=
System
.
nanoTime
()
-
opStart
;
opCount
++;
...
...
@@ -1167,52 +1186,130 @@ public class GeneticDecoder {
return
null
;
}
/**
* 倒排兜底:当前订单已排结果清掉后,按 sequence 从小到大重新正排整单。
*/
private
int
scheduleOrderForwardFallback
(
int
groupId
,
Chromosome
chromosome
,
Map
<
Long
,
Machine
>
machineIdMap
,
Map
<
Long
,
CopyOnWriteArrayList
<
GAScheduleResult
>>
machineTasksCache
,
Map
<
Integer
,
Entry
>
entryIndexById
,
Map
<
Integer
,
GAScheduleResult
>
scheduleIndexById
,
Map
<
String
,
OpMachine
>
opMachineKeyMap
)
{
List
<
Entry
>
orderOps
=
chromosome
.
getAllOperations
().
stream
()
.
filter
(
t
->
t
.
getGroupId
()
==
groupId
)
.
sorted
(
Comparator
.
comparing
(
Entry:
:
getSequence
))
.
collect
(
Collectors
.
toList
());
int
finalEndTime
=
0
;
for
(
Entry
currentOp
:
orderOps
)
{
String
opMachineKey
=
groupId
+
"_"
+
currentOp
.
getSequence
();
OpMachine
machineOption
=
opMachineKeyMap
.
get
(
opMachineKey
);
if
(
machineOption
==
null
)
{
throw
new
IllegalStateException
(
"未找到正排兜底设备选择,groupId="
+
groupId
+
", operationId="
+
currentOp
.
getId
()
+
", sequence="
+
currentOp
.
getSequence
());
}
private
int
processOperation
(
Entry
currentOp
,
Long
machineId
,
double
processTime
,
OpMachine
machineOption
,
Chromosome
chromosome
,
Map
<
Long
,
Machine
>
machineIdMap
,
Map
<
Long
,
CopyOnWriteArrayList
<
GAScheduleResult
>>
machineTasksCache
,
Map
<
Integer
,
Entry
>
entryIndexById
,
Map
<
Integer
,
GAScheduleResult
>
scheduleIndexById
,
int
orderDueDate
,
boolean
islockMachineTime
,
boolean
isJit
)
{
OperationScheduleResult
operationResult
=
processOperationSchedule
(
currentOp
,
machineOption
.
getMachineId
(),
machineOption
.
getProcessingTime
(),
machineOption
,
chromosome
,
machineIdMap
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
0
,
true
,
false
);
if
(
operationResult
.
isForwardFallbackRequired
())
{
throw
new
IllegalStateException
(
"正排兜底过程中不应再次触发正排兜底,groupId="
+
groupId
+
", operationId="
+
currentOp
.
getId
());
}
finalEndTime
=
operationResult
.
getEndTime
();
}
return
finalEndTime
;
}
/**
* 整单正排前先撤回本订单已经写入的倒排结果,同时归还机台可用时间。
*/
private
void
clearOrderSchedulingForForwardFallback
(
Chromosome
chromosome
,
int
groupId
,
Map
<
Long
,
CopyOnWriteArrayList
<
GAScheduleResult
>>
machineTasksCache
,
Map
<
Integer
,
GAScheduleResult
>
scheduleIndexById
)
{
List
<
GAScheduleResult
>
resultsToRemove
=
chromosome
.
getResult
().
stream
()
.
filter
(
t
->
t
.
getGroupId
()
==
groupId
)
.
collect
(
Collectors
.
toList
());
for
(
GAScheduleResult
result
:
resultsToRemove
)
{
Machine
machine
=
getMachineById
(
chromosome
,
result
.
getMachineId
());
if
(
machine
!=
null
)
{
AddMachineAvailable
(
machine
,
result
.
getGeneDetails
());
}
chromosome
.
getResult
().
remove
(
result
);
scheduleIndexById
.
remove
(
result
.
getOperationId
());
GAScheduleResult
result
=
processOperationResult
(
currentOp
,
machineId
,
processTime
,
machineOption
,
chromosome
,
machineIdMap
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
orderDueDate
,
islockMachineTime
,
isJit
);
if
(
result
==
null
)
return
0
;
return
result
.
getEndTime
();
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
=
machineTasksCache
.
get
(
result
.
getMachineId
());
if
(
machineTasks
!=
null
)
{
machineTasks
.
removeIf
(
t
->
t
.
getOperationId
()
==
result
.
getOperationId
());
}
}
machineTasksCache
.
clear
();
}
public
GAScheduleResult
processOperationResult
(
Entry
currentOp
,
Long
machineId
,
double
processTime
,
OpMachine
machineOption
,
Chromosome
chromosome
,
Map
<
Long
,
Machine
>
machineIdMap
,
Map
<
Long
,
CopyOnWriteArrayList
<
GAScheduleResult
>>
machineTasksCache
,
Map
<
Integer
,
Entry
>
entryIndexById
,
Map
<
Integer
,
GAScheduleResult
>
scheduleIndexById
,
int
orderDueDate
,
boolean
islockMachineTime
,
boolean
isJit
)
{
private
int
processOperation
(
Entry
currentOp
,
Long
machineId
,
double
processTime
,
OpMachine
machineOption
,
Chromosome
chromosome
,
Map
<
Long
,
Machine
>
machineIdMap
,
Map
<
Long
,
CopyOnWriteArrayList
<
GAScheduleResult
>>
machineTasksCache
,
Map
<
Integer
,
Entry
>
entryIndexById
,
Map
<
Integer
,
GAScheduleResult
>
scheduleIndexById
,
int
orderDueDate
,
boolean
islockMachineTime
,
boolean
isJit
)
{
OperationScheduleResult
result
=
processOperationSchedule
(
currentOp
,
machineId
,
processTime
,
machineOption
,
chromosome
,
machineIdMap
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
orderDueDate
,
islockMachineTime
,
isJit
);
return
result
.
getEndTime
();
}
private
OperationScheduleResult
processOperationSchedule
(
Entry
currentOp
,
Long
machineId
,
double
processTime
,
OpMachine
machineOption
,
Chromosome
chromosome
,
Map
<
Long
,
Machine
>
machineIdMap
,
Map
<
Long
,
CopyOnWriteArrayList
<
GAScheduleResult
>>
machineTasksCache
,
Map
<
Integer
,
Entry
>
entryIndexById
,
Map
<
Integer
,
GAScheduleResult
>
scheduleIndexById
,
int
orderDueDate
,
boolean
islockMachineTime
,
boolean
isJit
)
{
Machine
targetMachine
=
machineIdMap
.
get
(
machineId
);
int
prevtime
=
0
;
//后处理时间
// int teardownTime = currentOp.getTeardownTime();
if
(
isJit
)
{
prevtime
=
orderDueDate
;
if
(
isJit
)
{
// 倒排边界来自订单交期/锚点;如果存在后道序,则以后道序约束继续往前推。
prevtime
=
orderDueDate
;
if
(!
currentOp
.
getNextEntryIds
().
isEmpty
())
{
// 处理多个前工序
prevtime
=
CalNexttime
(
prevtime
,
currentOp
,
chromosome
,
processTime
,
targetMachine
,
entryIndexById
,
scheduleIndexById
);
prevtime
=
CalNexttime
(
prevtime
,
currentOp
,
chromosome
,
processTime
,
targetMachine
,
entryIndexById
,
scheduleIndexById
);
}
}
else
{
}
else
{
if
(!
currentOp
.
getPrevEntryIds
().
isEmpty
())
{
// 处理多个前工序
prevtime
=
CalPrevtime
(
prevtime
,
currentOp
,
chromosome
,
processTime
,
targetMachine
,
entryIndexById
,
scheduleIndexById
);
// 正排边界来自前道序完成时间。
prevtime
=
CalPrevtime
(
prevtime
,
currentOp
,
chromosome
,
processTime
,
targetMachine
,
entryIndexById
,
scheduleIndexById
);
}
}
int
prevendtime
=
prevtime
;
int
prevendtime
=
prevtime
;
Machine
machine
=
machineIdMap
.
get
(
machineId
);
return
processWithSingleMachine
(
currentOp
,
machine
,
processTime
,
prevtime
,
machineOption
,
chromosome
,
false
,
prevendtime
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
islockMachineTime
,
isJit
);
}
GAScheduleResult
result
=
processWithSingleMachine
(
currentOp
,
machine
,
processTime
,
prevtime
,
machineOption
,
chromosome
,
false
,
prevendtime
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
islockMachineTime
,
isJit
);
return
result
;
public
GAScheduleResult
processOperationResult
(
Entry
currentOp
,
Long
machineId
,
double
processTime
,
OpMachine
machineOption
,
Chromosome
chromosome
,
Map
<
Long
,
Machine
>
machineIdMap
,
Map
<
Long
,
CopyOnWriteArrayList
<
GAScheduleResult
>>
machineTasksCache
,
Map
<
Integer
,
Entry
>
entryIndexById
,
Map
<
Integer
,
GAScheduleResult
>
scheduleIndexById
,
int
orderDueDate
,
boolean
islockMachineTime
,
boolean
isJit
)
{
return
processOperationSchedule
(
currentOp
,
machineId
,
processTime
,
machineOption
,
chromosome
,
machineIdMap
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
orderDueDate
,
islockMachineTime
,
isJit
)
.
getScheduleResult
();
}
private
GA
ScheduleResult
processWithSingleMachine
(
Entry
operation
,
Machine
machine
,
double
processingTime
,
private
Operation
ScheduleResult
processWithSingleMachine
(
Entry
operation
,
Machine
machine
,
double
processingTime
,
int
prevOperationEndTime
,
OpMachine
machineOption
,
Chromosome
chromosome
,
boolean
calbom
,
int
prevendtime
,
Map
<
Long
,
CopyOnWriteArrayList
<
GAScheduleResult
>>
machineTasksCache
,
Map
<
Integer
,
Entry
>
entryIndexById
,
Map
<
Integer
,
GAScheduleResult
>
scheduleIndexById
,
boolean
islockMachineTime
,
boolean
isJit
)
{
long
pwsStart
=
System
.
nanoTime
();
...
...
@@ -1230,7 +1327,7 @@ public class GeneticDecoder {
throw
new
RuntimeException
(
"工序总加工时长"
+
days
+
"天超出限制"
);
}
if
(
machine
==
null
||
operation
.
getMachineOptions
()
==
null
)
{
return
null
;
return
OperationScheduleResult
.
success
(
null
)
;
}
// MachineOption machineOption= operation.getMachineOptions().stream()
...
...
@@ -1295,7 +1392,7 @@ public class GeneticDecoder {
// ", 前处理: " + preTime + ", 换型: " + 0 + ", 数量: " + operation.getQuantity() + ", 设备: " + machine.getId() + ", 是否可中断: " + operation.getIsInterrupt());
//
// }
return
result
;
return
OperationScheduleResult
.
success
(
result
)
;
}
...
...
@@ -1322,34 +1419,53 @@ public class GeneticDecoder {
CopyOnWriteArrayList
<
ScheduleResultDetail
>
geneDetails
=
new
CopyOnWriteArrayList
<>();
// 下面开始生成正式的 geneDetails。
// 与 buildMachinePreview 的区别是:这里得到的结果会继续向下写入 GAScheduleResult,成为真正排程结果。
if
(!
isJit
&&
_globalParam
.
is_smoothChangeOver
())
{
//是否考虑换型时间
Map
<
Integer
,
Object
>
reslte
=
calculateSetupTime
(
lastGeneOnMachine
,
operation
,
machine
,
earliestStartTime
,
processingTimeTotal
,
_globalParam
.
is_smoothChangeOverInWeek
(),
chromosome
.
getAllOperations
());
setupTime
=
(
int
)
reslte
.
get
(
1
);
//换型时间
int
setupStartTime
=
(
int
)
reslte
.
get
(
2
);
//换型开始时间
//earliestStartTime=(int)reslte.get(3);//上个任务的结束时间
earliestStartTime
=
(
int
)
reslte
.
get
(
4
);
//最早开工时间
processingTimeTotal
=
(
int
)
reslte
.
get
(
5
);
//processingTimeTotal
if
(
setupTime
==
0
)
{
geneDetails
=
machineCalculator
.
getNextAvailableTime
(
machine
,
operation
,
earliestStartTime
,
-
1
,
processingTimeTotal
,
machineTasks
,
operation
.
getIsInterrupt
()
!=
1
,
true
,
processingTime
,
operation
.
getQuantity
(),
islockMachineTime
,
isJit
);
BackwardChangeoverHelper
backwardChangeoverHelper
=
new
BackwardChangeoverHelper
(
_globalParam
,
baseTime
,
machineCalculator
);
// 下面开始生成正式的 geneDetails。
if
(
_globalParam
.
is_smoothChangeOver
())
{
if
(
isJit
&&
islockMachineTime
)
{
// JIT 倒排 + 换型:同时校验前置换型、当前加工、后置换型能否插入相邻任务窗口。
BackwardChangeoverHelper
.
JitBackwardScheduleResult
backwardResult
=
backwardChangeoverHelper
.
scheduleJitBackwardWithChangeover
(
machine
,
operation
,
earliestStartTime
,
processingTimeTotal
,
processingTime
,
machineTasks
,
chromosome
.
getAllOperations
());
if
(
backwardResult
.
isForwardFallbackRequired
())
{
return
OperationScheduleResult
.
forwardFallback
();
}
geneDetails
=
backwardResult
.
getGeneDetails
();
setupTime
=
backwardResult
.
getSetupTime
();
}
else
if
(!
isJit
)
{
// 非 JIT 正排 + 考虑换型:沿用原来的正排换型逻辑。
Map
<
Integer
,
Object
>
reslte
=
calculateSetupTime
(
lastGeneOnMachine
,
operation
,
machine
,
earliestStartTime
,
processingTimeTotal
,
_globalParam
.
is_smoothChangeOverInWeek
(),
chromosome
.
getAllOperations
());
setupTime
=
(
int
)
reslte
.
get
(
1
);
//换型时间
int
setupStartTime
=
(
int
)
reslte
.
get
(
2
);
//换型开始时间
earliestStartTime
=
(
int
)
reslte
.
get
(
4
);
//最早开工时间
processingTimeTotal
=
(
int
)
reslte
.
get
(
5
);
//processingTimeTotal
if
(
setupTime
==
0
)
{
geneDetails
=
machineCalculator
.
getNextAvailableTime
(
machine
,
operation
,
earliestStartTime
,
-
1
,
processingTimeTotal
,
machineTasks
,
operation
.
getIsInterrupt
()
!=
1
,
true
,
processingTime
,
operation
.
getQuantity
(),
islockMachineTime
,
isJit
);
}
else
{
CopyOnWriteArrayList
<
TimeSegment
>
AvailableTimeSegment
=
(
CopyOnWriteArrayList
<
TimeSegment
>)
reslte
.
get
(
6
);
Map
<
Integer
,
Object
>
result
=
machineCalculator
.
CreateScheduleResult
(
machine
,
operation
,
processingTimeTotal
,
earliestStartTime
,
AvailableTimeSegment
,
processingTime
,
operation
.
getQuantity
(),
operation
.
getIsInterrupt
()
!=
1
,
setupTime
,
_globalParam
.
is_smoothChangeOverInWeek
(),
setupStartTime
,
true
,
isJit
);
setupTime
=
(
int
)
result
.
get
(
1
);
operation
.
setChangeLineTime
(
setupTime
);
geneDetails
=
(
CopyOnWriteArrayList
<
ScheduleResultDetail
>)
result
.
get
(
2
);
}
}
else
{
CopyOnWriteArrayList
<
TimeSegment
>
AvailableTimeSegment
=
(
CopyOnWriteArrayList
<
TimeSegment
>)
reslte
.
get
(
6
);
Map
<
Integer
,
Object
>
result
=
machineCalculator
.
CreateScheduleResult
(
machine
,
operation
,
processingTimeTotal
,
earliestStartTime
,
AvailableTimeSegment
,
processingTime
,
operation
.
getQuantity
(),
operation
.
getIsInterrupt
()
!=
1
,
setupTime
,
_globalParam
.
is_smoothChangeOverInWeek
(),
setupStartTime
,
true
,
isJit
);
setupTime
=
(
int
)
result
.
get
(
1
);
operation
.
setChangeLineTime
(
setupTime
);
geneDetails
=
(
CopyOnWriteArrayList
<
ScheduleResultDetail
>)
result
.
get
(
2
);
geneDetails
=
machineCalculator
.
getNextAvailableTime
(
machine
,
operation
,
earliestStartTime
,
-
1
,
processingTimeTotal
,
machineTasks
,
operation
.
getIsInterrupt
()
!=
1
,
true
,
processingTime
,
operation
.
getQuantity
(),
islockMachineTime
,
isJit
);
}
}
else
{
geneDetails
=
machineCalculator
.
getNextAvailableTime
(
machine
,
operation
,
earliestStartTime
,
-
1
,
processingTimeTotal
,
machineTasks
,
operation
.
getIsInterrupt
()
!=
1
,
true
,
processingTime
,
operation
.
getQuantity
(),
islockMachineTime
,
isJit
);
processingTimeTotal
,
machineTasks
,
operation
.
getIsInterrupt
()
!=
1
,
true
,
processingTime
,
operation
.
getQuantity
(),
islockMachineTime
,
isJit
);
}
// FileHelper.writeLogFile(" 开始 "+operation.getGroupId()+" : "+operation.getId()+",处理时间: " + processingTime + ", 后处理: " + teardownTime +
...
...
@@ -1374,7 +1490,7 @@ public class GeneticDecoder {
}
return
null
;
return
OperationScheduleResult
.
success
(
null
)
;
}
...
...
@@ -1391,7 +1507,7 @@ public class GeneticDecoder {
// ", 前处理: " + preTime + ", 换型: " + setupTime + ", 数量: " + operation.getQuantity() + ", 设备: " + machine.getId() + ", 是否可中断: " + operation.getIsInterrupt());
}
return
null
;
return
OperationScheduleResult
.
success
(
null
)
;
}
if
(
startTime
==
0
&&
islockMachineTime
)
{
...
...
@@ -1446,7 +1562,7 @@ public class GeneticDecoder {
// + ", groupId=" + operation.getGroupId() + ", machineId=" + machineId
// + ", hasBOM=" + commitMaterialCheck + ", 耗时=" + fmtMs(pwsElapsed));
// }
return
result
;
return
OperationScheduleResult
.
success
(
result
)
;
}
private
GAScheduleResult
CreateResult
(
Entry
operation
,
long
machineId
...
...
@@ -1629,6 +1745,40 @@ if(geneDetails!=null&&geneDetails.size()>0)
.
orElse
(
null
);
}
/**
* 单道序排程结果。
* scheduleResult 用于正常推进订单进度;forwardFallbackRequired 用于把“整单转正排”显式带回外层。
*/
private
static
class
OperationScheduleResult
{
private
final
GAScheduleResult
scheduleResult
;
private
final
boolean
forwardFallbackRequired
;
private
OperationScheduleResult
(
GAScheduleResult
scheduleResult
,
boolean
forwardFallbackRequired
)
{
this
.
scheduleResult
=
scheduleResult
;
this
.
forwardFallbackRequired
=
forwardFallbackRequired
;
}
private
static
OperationScheduleResult
success
(
GAScheduleResult
scheduleResult
)
{
return
new
OperationScheduleResult
(
scheduleResult
,
false
);
}
private
static
OperationScheduleResult
forwardFallback
()
{
return
new
OperationScheduleResult
(
null
,
true
);
}
private
GAScheduleResult
getScheduleResult
()
{
return
scheduleResult
;
}
private
int
getEndTime
()
{
return
scheduleResult
==
null
?
0
:
scheduleResult
.
getEndTime
();
}
private
boolean
isForwardFallbackRequired
()
{
return
forwardFallbackRequired
;
}
}
private
static
class
MachineSchedulePreview
{
private
final
int
startTime
;
private
final
int
endTime
;
...
...
@@ -1959,7 +2109,12 @@ if(geneDetails!=null&&geneDetails.size()>0)
.
findFirst
()
.
orElse
(
null
);
// 缓存机器任务
processWithSingleMachine
(
currentOp
,
machine
,
processTime
,
prevtime
,
opMachine
,
chromosome
,
true
,
prevendtime
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
true
,
true
);
OperationScheduleResult
operationResult
=
processWithSingleMachine
(
currentOp
,
machine
,
processTime
,
prevtime
,
opMachine
,
chromosome
,
true
,
prevendtime
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
,
true
,
true
);
if
(
operationResult
.
isForwardFallbackRequired
())
{
return
;
}
}
...
...
src/main/java/com/aps/service/Algorithm/MachineCalculator.java
View file @
3d3915bf
...
...
@@ -72,6 +72,202 @@ public class MachineCalculator {
}
// 基于已选可用时段组装排程结果,并把换型段/加工段对应的 availability 临时占用。
// ==================== 倒排换型排程辅助方法开始 ====================
/**
* 倒排生成排程明细:从 latestEndTime 往前找加工可用段,可选地在加工段前拼上换型段。
* 返回值沿用老结构:1=换型时间,2=ScheduleResultDetail 列表。
*/
public
Map
<
Integer
,
Object
>
CreateScheduleResultBackward
(
Machine
machine
,
Entry
operation
,
int
processingTime
,
int
latestEndTime
,
double
oneTime
,
double
quantity
,
int
changeOverTime
,
int
setupStartTime
,
int
setupEndTime
,
CopyOnWriteArrayList
<
TimeSegment
>
setupSegments
,
boolean
setupInCalendarTime
)
{
Map
<
Integer
,
Object
>
result
=
new
HashMap
<>(
2
);
CopyOnWriteArrayList
<
ScheduleResultDetail
>
times
=
new
CopyOnWriteArrayList
<>();
CopyOnWriteArrayList
<
TimeSegment
>
usedSegments
=
new
CopyOnWriteArrayList
<>();
times
.
addAll
(
CreateSetupDetailsBackward
(
machine
,
changeOverTime
,
setupStartTime
,
setupEndTime
,
setupSegments
,
setupInCalendarTime
));
LocalDateTime
cursor
=
baseTime
.
plusSeconds
(
latestEndTime
);
int
remainingTime
=
processingTime
;
final
LocalDateTime
processSearchEnd
=
cursor
;
List
<
TimeSegment
>
processSegments
=
machine
.
getAvailability
().
stream
()
.
filter
(
slot
->
!
slot
.
isUsed
())
.
filter
(
slot
->
slot
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
slot
->
slot
.
getStart
().
isBefore
(
processSearchEnd
))
.
sorted
(
Comparator
.
comparing
(
TimeSegment:
:
getEnd
).
reversed
())
.
collect
(
Collectors
.
toList
());
double
totalAvailableSeconds
=
calculateTotalAvailableSecond
(
new
CopyOnWriteArrayList
<>(
processSegments
),
processSearchEnd
,
true
);
if
(
totalAvailableSeconds
+
0.0001
<
processingTime
)
{
throw
new
IllegalStateException
(
buildInsufficientScheduleMessage
(
machine
,
operation
,
processingTime
,
totalAvailableSeconds
,
processSegments
.
size
()));
}
for
(
TimeSegment
slot
:
processSegments
)
{
if
(
remainingTime
<=
0
)
{
break
;
}
LocalDateTime
effectiveEnd
=
slot
.
getEnd
().
isAfter
(
cursor
)
?
cursor
:
slot
.
getEnd
();
if
(!
slot
.
getStart
().
isBefore
(
effectiveEnd
))
{
continue
;
}
long
availableSeconds
=
ChronoUnit
.
SECONDS
.
between
(
slot
.
getStart
(),
effectiveEnd
);
long
effectiveSeconds
=
Math
.
round
(
availableSeconds
*
slot
.
getEfficiency
());
if
(
effectiveSeconds
<=
0
)
{
continue
;
}
int
processable
=
Math
.
min
(
remainingTime
,
(
int
)
effectiveSeconds
);
int
calendarSeconds
=
(
int
)
Math
.
ceil
(
processable
/
slot
.
getEfficiency
());
LocalDateTime
effectiveStart
=
effectiveEnd
.
minusSeconds
(
calendarSeconds
);
ScheduleResultDetail
time
=
createScheduleDetail
(
slot
,
effectiveStart
,
effectiveEnd
,
processable
,
oneTime
,
quantity
,
false
);
times
.
add
(
time
);
CopyOnWriteArrayList
<
TimeSegment
>
usedSegments1
=
RemoveMachineAvailable
(
machine
,
time
,
slot
);
if
(
usedSegments1
!=
null
&&
usedSegments1
.
size
()
>
0
)
{
usedSegments
.
addAll
(
usedSegments1
);
}
remainingTime
-=
processable
;
cursor
=
effectiveStart
;
}
if
(
remainingTime
>
0
)
{
throw
new
IllegalStateException
(
buildInsufficientScheduleMessage
(
machine
,
operation
,
processingTime
,
totalAvailableSeconds
,
processSegments
.
size
()));
}
if
(
usedSegments
!=
null
&&
usedSegments
.
size
()
>
0
)
{
machine
.
getAvailability
().
addAll
(
usedSegments
);
}
machine
.
getAvailability
().
sort
(
Comparator
.
comparing
(
TimeSegment:
:
getStart
));
times
.
sort
(
Comparator
.
comparingInt
(
ScheduleResultDetail:
:
getStartTime
));
result
.
put
(
1
,
changeOverTime
);
result
.
put
(
2
,
times
);
return
result
;
}
/**
* 生成倒排换型明细,并按配置决定换型是否占用设备日历工作段。
*/
public
CopyOnWriteArrayList
<
ScheduleResultDetail
>
CreateSetupDetailsBackward
(
Machine
machine
,
int
changeOverTime
,
int
setupStartTime
,
int
setupEndTime
,
CopyOnWriteArrayList
<
TimeSegment
>
setupSegments
,
boolean
setupInCalendarTime
)
{
CopyOnWriteArrayList
<
ScheduleResultDetail
>
times
=
new
CopyOnWriteArrayList
<>();
if
(
setupInCalendarTime
&&
changeOverTime
>
0
&&
setupEndTime
>
setupStartTime
)
{
ScheduleResultDetail
setupDetail
=
new
ScheduleResultDetail
();
setupDetail
.
setKey
(
UUID
.
randomUUID
().
toString
());
setupDetail
.
setStartTime
(
setupStartTime
);
setupDetail
.
setEndTime
(
setupEndTime
);
setupDetail
.
setSetup
(
true
);
if
(
setupSegments
!=
null
&&
setupSegments
.
size
()
>
0
)
{
List
<
TimeSegment
>
occupiedSetupSegments
=
new
ArrayList
<>();
for
(
TimeSegment
setupSegment
:
setupSegments
)
{
ScheduleResultDetail
occupiedSetupDetail
=
occupySetupSegment
(
machine
,
setupSegment
);
if
(
occupiedSetupDetail
!=
null
&&
occupiedSetupDetail
.
getKey
()
!=
null
)
{
TimeSegment
keyRef
=
new
TimeSegment
();
keyRef
.
setKey
(
occupiedSetupDetail
.
getKey
());
keyRef
.
setStart
(
baseTime
.
plusSeconds
(
occupiedSetupDetail
.
getStartTime
()));
keyRef
.
setEnd
(
baseTime
.
plusSeconds
(
occupiedSetupDetail
.
getEndTime
()));
occupiedSetupSegments
.
add
(
keyRef
);
}
}
setupDetail
.
setUsedSegment
(
occupiedSetupSegments
);
}
times
.
add
(
setupDetail
);
}
else
if
(
setupSegments
!=
null
&&
setupSegments
.
size
()
>
0
)
{
for
(
TimeSegment
setupSegment
:
setupSegments
)
{
ScheduleResultDetail
setupDetail
=
occupySetupSegment
(
machine
,
setupSegment
);
if
(
setupDetail
!=
null
)
{
times
.
add
(
setupDetail
);
}
}
}
else
if
(
changeOverTime
>
0
&&
setupEndTime
>
setupStartTime
)
{
ScheduleResultDetail
setupDetail
=
new
ScheduleResultDetail
();
setupDetail
.
setStartTime
(
setupStartTime
);
setupDetail
.
setEndTime
(
setupEndTime
);
setupDetail
.
setSetup
(
true
);
times
.
add
(
setupDetail
);
}
if
(
machine
!=
null
&&
machine
.
getAvailability
()
!=
null
)
{
machine
.
getAvailability
().
sort
(
Comparator
.
comparing
(
TimeSegment:
:
getStart
));
}
return
times
;
}
private
ScheduleResultDetail
occupySetupSegment
(
Machine
machine
,
TimeSegment
setupSegment
)
{
if
(
machine
==
null
||
setupSegment
==
null
||
!
setupSegment
.
getStart
().
isBefore
(
setupSegment
.
getEnd
()))
{
return
null
;
}
TimeSegment
targetSegment
=
machine
.
getAvailability
().
stream
()
.
filter
(
slot
->
!
slot
.
isUsed
())
.
filter
(
slot
->
slot
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
slot
->
slot
.
getStart
().
compareTo
(
setupSegment
.
getStart
())
<=
0
)
.
filter
(
slot
->
slot
.
getEnd
().
compareTo
(
setupSegment
.
getEnd
())
>=
0
)
.
findFirst
()
.
orElse
(
null
);
if
(
targetSegment
==
null
)
{
return
null
;
}
ScheduleResultDetail
setupDetail
=
new
ScheduleResultDetail
();
setupDetail
.
setStartTime
((
int
)
ChronoUnit
.
SECONDS
.
between
(
baseTime
,
setupSegment
.
getStart
()));
setupDetail
.
setEndTime
((
int
)
ChronoUnit
.
SECONDS
.
between
(
baseTime
,
setupSegment
.
getEnd
()));
setupDetail
.
setSetup
(
true
);
setupDetail
.
setKey
(
targetSegment
.
getKey
());
CopyOnWriteArrayList
<
TimeSegment
>
usedSegments
=
RemoveMachineAvailable
(
machine
,
setupDetail
,
targetSegment
);
if
(
usedSegments
!=
null
&&
usedSegments
.
size
()
>
0
)
{
machine
.
getAvailability
().
addAll
(
usedSegments
);
}
return
setupDetail
;
}
private
ScheduleResultDetail
createScheduleDetail
(
TimeSegment
slot
,
LocalDateTime
startTime
,
LocalDateTime
endTime
,
int
processable
,
double
oneTime
,
double
quantity
,
boolean
useWholeQuantity
)
{
ScheduleResultDetail
time
=
new
ScheduleResultDetail
();
time
.
setKey
(
slot
.
getKey
());
time
.
setStartTime
((
int
)
ChronoUnit
.
SECONDS
.
between
(
baseTime
,
startTime
));
time
.
setEndTime
((
int
)
ChronoUnit
.
SECONDS
.
between
(
baseTime
,
endTime
));
time
.
setEfficiency
(
slot
.
getEfficiency
());
time
.
setOneTime
(
oneTime
);
if
(
useWholeQuantity
)
{
time
.
setQuantity
(
quantity
);
}
else
if
(
oneTime
>
0
)
{
BigDecimal
bd
=
new
BigDecimal
(
String
.
valueOf
(
processable
/
oneTime
));
time
.
setQuantity
(
bd
.
setScale
(
4
,
RoundingMode
.
HALF_UP
).
doubleValue
());
}
return
time
;
}
private
String
buildInsufficientScheduleMessage
(
Machine
machine
,
Entry
operation
,
int
requiredSeconds
,
double
availableSeconds
,
int
segmentCount
)
{
String
machineId
=
machine
==
null
?
"null"
:
String
.
valueOf
(
machine
.
getId
());
String
machineName
=
machine
==
null
?
""
:
String
.
valueOf
(
machine
.
getName
());
String
operationId
=
operation
==
null
?
"null"
:
String
.
valueOf
(
operation
.
getId
());
String
groupId
=
operation
==
null
?
"null"
:
String
.
valueOf
(
operation
.
getGroupId
());
return
String
.
format
(
"设备可用时间不足,machineId=%s, machineName=%s, operationId=%s, groupId=%s, requiredSeconds=%d, availableSeconds=%.0f, segmentCount=%d"
,
machineId
,
machineName
,
operationId
,
groupId
,
requiredSeconds
,
availableSeconds
,
segmentCount
);
}
// ==================== 倒排换型排程辅助方法结束 ====================
public
Map
<
Integer
,
Object
>
CreateScheduleResult
(
Machine
machine
,
Entry
operation
,
int
processingTime
,
int
proposedStartTime
,
CopyOnWriteArrayList
<
TimeSegment
>
timeSegments
,
double
oneTime
,
double
quantity
...
...
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