|
|
@@ -10,24 +10,34 @@ export interface WarehouseLayoutPosition {
|
|
|
gridCol: number
|
|
|
}
|
|
|
|
|
|
-interface LocalColumnAisleRule {
|
|
|
+type RowRange = readonly [number, number]
|
|
|
+
|
|
|
+interface RowScopedRule {
|
|
|
+ rows?: readonly number[]
|
|
|
+ rowRange?: RowRange
|
|
|
+}
|
|
|
+
|
|
|
+interface LocalColumnAisleRule extends RowScopedRule {
|
|
|
afterColumn: number
|
|
|
gapCols: number
|
|
|
- rows?: number[]
|
|
|
- rowRange?: [number, number]
|
|
|
}
|
|
|
|
|
|
-interface LocalColumnShiftRule {
|
|
|
+interface LocalColumnShiftRule extends RowScopedRule {
|
|
|
startColumn: number
|
|
|
shiftCols: number
|
|
|
- rows?: number[]
|
|
|
- rowRange?: [number, number]
|
|
|
}
|
|
|
|
|
|
interface GlobalColumnAisleRule {
|
|
|
breakpoint: number
|
|
|
- excludeRows?: number[]
|
|
|
- excludeRowRange?: [number, number]
|
|
|
+ excludeRows?: readonly number[]
|
|
|
+ excludeRowRange?: RowRange
|
|
|
+}
|
|
|
+
|
|
|
+interface WarehouseLayoutRuleSet {
|
|
|
+ rowAisleBreakpoints: readonly number[]
|
|
|
+ globalColumnAisles: readonly GlobalColumnAisleRule[]
|
|
|
+ localColumnAisles: readonly LocalColumnAisleRule[]
|
|
|
+ localColumnShifts: readonly LocalColumnShiftRule[]
|
|
|
}
|
|
|
|
|
|
interface WarehouseLayoutEnhancer {
|
|
|
@@ -38,160 +48,177 @@ interface WarehouseLayoutEnhancer {
|
|
|
) => WarehouseLayoutPosition
|
|
|
}
|
|
|
|
|
|
-const firstFloorEnhancer: WarehouseLayoutEnhancer = {
|
|
|
- supports: (level) => level === 1,
|
|
|
- apply: (currentPosition, parsedLocation) => {
|
|
|
- let nextPosition = currentPosition
|
|
|
- const firstFloorRowAisleBreakpoints = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
|
|
|
- const firstFloorGlobalColumnAisles: GlobalColumnAisleRule[] = [
|
|
|
- { breakpoint: 1 },
|
|
|
- {
|
|
|
- breakpoint: 19,
|
|
|
- excludeRows: [8, 9, 10, 11]
|
|
|
- },
|
|
|
- { breakpoint: 26 }
|
|
|
- ]
|
|
|
- const firstFloorLocalColumnAisles: LocalColumnAisleRule[] = [
|
|
|
- {
|
|
|
- rowRange: [0, 8],
|
|
|
- afterColumn: 7,
|
|
|
- gapCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rowRange: [11, 22],
|
|
|
- afterColumn: 7,
|
|
|
- gapCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [9, 10],
|
|
|
- afterColumn: 6,
|
|
|
- gapCols: 3
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [9, 10],
|
|
|
- afterColumn: 10,
|
|
|
- gapCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [8, 11],
|
|
|
- afterColumn: 12,
|
|
|
- gapCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [8, 11],
|
|
|
- afterColumn: 17,
|
|
|
- gapCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [9, 10],
|
|
|
- afterColumn: 20,
|
|
|
- gapCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [8, 11],
|
|
|
- afterColumn: 24,
|
|
|
- gapCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [9, 10],
|
|
|
- afterColumn: 14,
|
|
|
- gapCols: 3
|
|
|
- }
|
|
|
- ]
|
|
|
- const firstFloorLocalColumnShifts: LocalColumnShiftRule[] = [
|
|
|
- {
|
|
|
- rows: [6, 13],
|
|
|
- startColumn: 0,
|
|
|
- shiftCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [6, 13],
|
|
|
- startColumn: 5,
|
|
|
- shiftCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [6, 13],
|
|
|
- startColumn: 11,
|
|
|
- shiftCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [6, 13],
|
|
|
- startColumn: 17,
|
|
|
- shiftCols: 1
|
|
|
- },
|
|
|
- {
|
|
|
- rows: [7],
|
|
|
- startColumn: 20,
|
|
|
- shiftCols: 1
|
|
|
- }
|
|
|
- ]
|
|
|
-
|
|
|
- const columnShiftCount = firstFloorLocalColumnShifts.reduce((total, rule) => {
|
|
|
- const matchedRows = rule.rows?.includes(parsedLocation.y) ?? false
|
|
|
- const matchedRange = rule.rowRange
|
|
|
- ? parsedLocation.y >= rule.rowRange[0] && parsedLocation.y <= rule.rowRange[1]
|
|
|
- : false
|
|
|
-
|
|
|
- if ((matchedRows || matchedRange) && parsedLocation.x >= rule.startColumn) {
|
|
|
- return total + rule.shiftCols
|
|
|
- }
|
|
|
-
|
|
|
- return total
|
|
|
- }, 0)
|
|
|
- const effectiveColumn = parsedLocation.x + columnShiftCount
|
|
|
+const FIRST_FLOOR_RULE_SET: WarehouseLayoutRuleSet = {
|
|
|
+ rowAisleBreakpoints: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22],
|
|
|
+ globalColumnAisles: [
|
|
|
+ { breakpoint: 1 },
|
|
|
+ {
|
|
|
+ breakpoint: 19,
|
|
|
+ excludeRows: [8, 9, 10, 11]
|
|
|
+ },
|
|
|
+ { breakpoint: 26 }
|
|
|
+ ],
|
|
|
+ localColumnAisles: [
|
|
|
+ {
|
|
|
+ rowRange: [0, 8],
|
|
|
+ afterColumn: 7,
|
|
|
+ gapCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rowRange: [11, 22],
|
|
|
+ afterColumn: 7,
|
|
|
+ gapCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [9, 10],
|
|
|
+ afterColumn: 6,
|
|
|
+ gapCols: 3
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [9, 10],
|
|
|
+ afterColumn: 10,
|
|
|
+ gapCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [8, 11],
|
|
|
+ afterColumn: 12,
|
|
|
+ gapCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [8, 11],
|
|
|
+ afterColumn: 17,
|
|
|
+ gapCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [9, 10],
|
|
|
+ afterColumn: 20,
|
|
|
+ gapCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [8, 11],
|
|
|
+ afterColumn: 24,
|
|
|
+ gapCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [9, 10],
|
|
|
+ afterColumn: 14,
|
|
|
+ gapCols: 3
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ localColumnShifts: [
|
|
|
+ {
|
|
|
+ rows: [6, 13],
|
|
|
+ startColumn: 0,
|
|
|
+ shiftCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [6, 13],
|
|
|
+ startColumn: 5,
|
|
|
+ shiftCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [6, 13],
|
|
|
+ startColumn: 11,
|
|
|
+ shiftCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [6, 13],
|
|
|
+ startColumn: 17,
|
|
|
+ shiftCols: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ rows: [7],
|
|
|
+ startColumn: 20,
|
|
|
+ shiftCols: 1
|
|
|
+ }
|
|
|
+ ]
|
|
|
+}
|
|
|
+
|
|
|
+const matchesRowScope = (row: number, rule: RowScopedRule) => {
|
|
|
+ const matchedRows = rule.rows?.includes(row) ?? false
|
|
|
+ const matchedRange = rule.rowRange ? row >= rule.rowRange[0] && row <= rule.rowRange[1] : false
|
|
|
+
|
|
|
+ return matchedRows || matchedRange
|
|
|
+}
|
|
|
+
|
|
|
+const isRowExcluded = (row: number, rule: GlobalColumnAisleRule) => {
|
|
|
+ const excludedByRows = rule.excludeRows?.includes(row) ?? false
|
|
|
+ const excludedByRange = rule.excludeRowRange
|
|
|
+ ? row >= rule.excludeRowRange[0] && row <= rule.excludeRowRange[1]
|
|
|
+ : false
|
|
|
|
|
|
- if (columnShiftCount > 0) {
|
|
|
- nextPosition = {
|
|
|
- ...nextPosition,
|
|
|
- gridCol: nextPosition.gridCol + columnShiftCount
|
|
|
- }
|
|
|
+ return excludedByRows || excludedByRange
|
|
|
+}
|
|
|
+
|
|
|
+const sumRowAisleOffsets = (row: number, breakpoints: readonly number[]) =>
|
|
|
+ breakpoints.reduce((total, breakpoint) => (row >= breakpoint ? total + 1 : total), 0)
|
|
|
+
|
|
|
+const sumGlobalColumnAisleOffsets = (
|
|
|
+ row: number,
|
|
|
+ effectiveColumn: number,
|
|
|
+ rules: readonly GlobalColumnAisleRule[]
|
|
|
+) =>
|
|
|
+ rules.reduce((total, rule) => {
|
|
|
+ if (!isRowExcluded(row, rule) && effectiveColumn >= rule.breakpoint) {
|
|
|
+ return total + 1
|
|
|
+ }
|
|
|
+
|
|
|
+ return total
|
|
|
+ }, 0)
|
|
|
+
|
|
|
+const sumLocalColumnAisleOffsets = (
|
|
|
+ row: number,
|
|
|
+ effectiveColumn: number,
|
|
|
+ rules: readonly LocalColumnAisleRule[]
|
|
|
+) =>
|
|
|
+ rules.reduce((total, rule) => {
|
|
|
+ if (matchesRowScope(row, rule) && effectiveColumn > rule.afterColumn) {
|
|
|
+ return total + rule.gapCols
|
|
|
+ }
|
|
|
+
|
|
|
+ return total
|
|
|
+ }, 0)
|
|
|
+
|
|
|
+const sumLocalColumnShiftOffsets = (
|
|
|
+ parsedLocation: WarehouseLayoutParsedLocation,
|
|
|
+ rules: readonly LocalColumnShiftRule[]
|
|
|
+) =>
|
|
|
+ rules.reduce((total, rule) => {
|
|
|
+ if (matchesRowScope(parsedLocation.y, rule) && parsedLocation.x >= rule.startColumn) {
|
|
|
+ return total + rule.shiftCols
|
|
|
}
|
|
|
|
|
|
- // 1 层增强规则:在指定列断点之间插入整列过道。
|
|
|
- firstFloorGlobalColumnAisles.forEach((rule) => {
|
|
|
- const excludedByRows = rule.excludeRows?.includes(parsedLocation.y) ?? false
|
|
|
- const excludedByRange = rule.excludeRowRange
|
|
|
- ? parsedLocation.y >= rule.excludeRowRange[0] && parsedLocation.y <= rule.excludeRowRange[1]
|
|
|
- : false
|
|
|
-
|
|
|
- if (!excludedByRows && !excludedByRange && effectiveColumn >= rule.breakpoint) {
|
|
|
- nextPosition = {
|
|
|
- ...nextPosition,
|
|
|
- gridCol: nextPosition.gridCol + 1
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- // 1 层增强规则:在指定行断点之间插入整行过道。
|
|
|
- firstFloorRowAisleBreakpoints.forEach((breakpoint) => {
|
|
|
- if (parsedLocation.y >= breakpoint) {
|
|
|
- nextPosition = {
|
|
|
- ...nextPosition,
|
|
|
- gridRow: nextPosition.gridRow + 1
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- // 1 层增强规则:指定行内在某列右侧插入局部整列过道。
|
|
|
- firstFloorLocalColumnAisles.forEach((rule) => {
|
|
|
- const matchedRows = rule.rows?.includes(parsedLocation.y) ?? false
|
|
|
- const matchedRange = rule.rowRange
|
|
|
- ? parsedLocation.y >= rule.rowRange[0] && parsedLocation.y <= rule.rowRange[1]
|
|
|
- : false
|
|
|
-
|
|
|
- if ((matchedRows || matchedRange) && effectiveColumn > rule.afterColumn) {
|
|
|
- nextPosition = {
|
|
|
- ...nextPosition,
|
|
|
- gridCol: nextPosition.gridCol + rule.gapCols
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- return nextPosition
|
|
|
+ return total
|
|
|
+ }, 0)
|
|
|
+
|
|
|
+const createConfiguredEnhancer = (
|
|
|
+ level: number,
|
|
|
+ ruleSet: WarehouseLayoutRuleSet
|
|
|
+): WarehouseLayoutEnhancer => ({
|
|
|
+ supports: (currentLevel) => currentLevel === level,
|
|
|
+ apply: (currentPosition, parsedLocation) => {
|
|
|
+ const columnShiftCount = sumLocalColumnShiftOffsets(parsedLocation, ruleSet.localColumnShifts)
|
|
|
+ const effectiveColumn = parsedLocation.x + columnShiftCount
|
|
|
+ const gridColOffset =
|
|
|
+ columnShiftCount +
|
|
|
+ sumGlobalColumnAisleOffsets(
|
|
|
+ parsedLocation.y,
|
|
|
+ effectiveColumn,
|
|
|
+ ruleSet.globalColumnAisles
|
|
|
+ ) +
|
|
|
+ sumLocalColumnAisleOffsets(parsedLocation.y, effectiveColumn, ruleSet.localColumnAisles)
|
|
|
+ const gridRowOffset = sumRowAisleOffsets(parsedLocation.y, ruleSet.rowAisleBreakpoints)
|
|
|
+
|
|
|
+ return {
|
|
|
+ gridRow: currentPosition.gridRow + gridRowOffset,
|
|
|
+ gridCol: currentPosition.gridCol + gridColOffset
|
|
|
+ }
|
|
|
}
|
|
|
-}
|
|
|
+})
|
|
|
|
|
|
-const LAYOUT_ENHANCERS: WarehouseLayoutEnhancer[] = [firstFloorEnhancer]
|
|
|
+const LAYOUT_ENHANCERS: WarehouseLayoutEnhancer[] = [
|
|
|
+ createConfiguredEnhancer(1, FIRST_FLOOR_RULE_SET)
|
|
|
+]
|
|
|
|
|
|
export const applyWarehouseLayoutEnhancement = (
|
|
|
level: number,
|