Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions lib/heuristics/dichotomous_approach.rb
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,16 @@ def self.build_initial_routes(solutions)
next if solution.nil?

solution.routes.map{ |route|
mission_ids = route.stops.map(&:service_id).compact
next if mission_ids.empty?
missions = route.stops.map{ |stop|
next if stop.is_a?(Models::Solution::StopDepot) || stop.mission.is_a?(Models::Rest)

stop.mission
}.compact
next if missions.empty?

Models::Route.create(
vehicle: route.vehicle,
mission_ids: mission_ids
missions: missions
)
}
}.compact
Expand Down
7 changes: 4 additions & 3 deletions lib/heuristics/periodic_heuristic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,7 @@ def add_same_freq_located_points(best_index, route_data)
@services_assignment[service_id][:vehicles] |= [route_data[:vehicle_original_id]]
route_data[:stops].insert(best_index[:position] + i + 1,
id: service_id,
mission: @services_data[service_id][:raw],
point_id: best_index[:point],
start: start,
arrival: start,
Expand Down Expand Up @@ -1398,7 +1399,7 @@ def prepare_output_and_collect_routes(vrp)

vrp_routes << {
vehicle_id: vrp_vehicle.id,
mission_ids: computed_stops.collect{ |stop| stop[:service_id] }.compact
missions: computed_stops.map{ |stop| stop[:mission] }.compact
}

solution_routes << Models::Solution::Route.new(stops: computed_stops,
Expand Down Expand Up @@ -1552,10 +1553,10 @@ def construct_sub_vrp(vrp, vehicle, current_route)
route_vrp
end

def generate_route(vehicle, services)
def generate_route(vehicle, stops)
{
vehicle: vehicle,
mission_ids: services.collect{ |service| service[:id] }
missions: stops.map{ |stop| stop[:mission] }.compact
}
end

Expand Down
2 changes: 1 addition & 1 deletion lib/interpreters/multi_trip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def presolve(service_vrp, job = nil, &block)
solution.unassigned_stops = []

vehicles = under_used_routes.map(&:vehicle)
reload_depots = vehicles.flat_map(&:reload_depots)
reload_depots = vehicles.flat_map(&:reload_depots).uniq
points =
vehicles.map(&:start_point) +
vehicles.map(&:end_point) +
Expand Down
4 changes: 2 additions & 2 deletions models/activity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
# along with Mapotempo. If not, see:
# <http://www.gnu.org/licenses/agpl.html>
#
require './models/base'
require './models/mission'

module Models
class Activity < Base
class Activity < Mission
field :duration, default: 0
field :setup_duration, default: 0
field :additional_value, default: 0
Expand Down
23 changes: 23 additions & 0 deletions models/mission.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright © Cartoway, 2025
#
# This file is part of Cartoway Optimizer.
#
# Cartoway Planner is free software. You can redistribute it and/or
# modify since you respect the terms of the GNU Affero General
# Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
#
# Cartoway Optimizer is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the Licenses for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Cartoway Optimizer. If not, see:
# <http://www.gnu.org/licenses/agpl.html>
#

require './models/base'

module Models
class Mission < Base; end
end
20 changes: 20 additions & 0 deletions models/route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,25 @@ class Route < Base
field :mission_ids, default: []
field :day_index
belongs_to :vehicle, class_name: 'Models::Vehicle', as_json: :id

has_many :missions, class_name: 'Models::Mission', as_json: :ids

def initialize(hash)
hash[:missions] ||= []
if hash[:mission_ids].present?
hash[:missions] +=
hash[:mission_ids]&.map{ |mission_id|
Models::Service.find_by_id(mission_id) ||
Models::ReloadDepot.find_by_id(mission_id) ||
Models::Rest.find_by_id(mission_id)
}&.compact
hash.delete(:mission_ids)
end
super(hash)
end

def mission_ids
missions.map{ |mission| mission.original_id || mission.id }
end
end
end
2 changes: 1 addition & 1 deletion models/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
require './models/base'

module Models
class Service < Base
class Service < Mission
field :id
field :original_id, default: nil

Expand Down
3 changes: 3 additions & 0 deletions models/solution/parsers/stop_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def self.parse(service, options)
alternative: options[:index], # nil if unassigned but return by default the last activity
loads: build_loads(service, options),
activity: dup_activity,
mission: service,
info: options[:info] || Models::Solution::Stop::Info.new({}),
reason: options[:reason],
skills: options[:skills] || service.skills,
Expand Down Expand Up @@ -88,6 +89,7 @@ def self.parse(reload_depot, options)
type: :reload_depot,
loads: options[:loads],
activity: Models::ReloadDepot.new(reload_depot.as_json),
mission: reload_depot,
info: options[:info] || Models::Solution::Stop::Info.new({})
}
end
Expand All @@ -100,6 +102,7 @@ def self.parse(rest, options)
rest_id: rest.original_id || rest.id,
type: :rest,
activity: Models::Rest.new(rest.as_json),
mission: rest,
info: options[:info] || Models::Solution::Stop::Info.new({})
}
end
Expand Down
1 change: 1 addition & 0 deletions models/solution/stop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class Stop < Base
field :exclusion_cost

has_many :loads, class_name: 'Models::Solution::Load'
belongs_to :mission, class_name: 'Models::Mission', vrp_result: :hide
belongs_to :activity, class_name: 'Models::Activity'
belongs_to :info, class_name: 'Models::Solution::Stop::Info', vrp_result: :hide

Expand Down
35 changes: 19 additions & 16 deletions wrappers/ortools.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def solve(vrp, job, thread_proc = nil, &block)
}.each{ |relation|
order_route = {
vehicle: vrp.vehicles.size == 1 ? vrp.vehicles.first : nil,
mission_ids: relation.linked_service_ids
missions: relation.linked_services
}
vrp.routes += [order_route]
}
Expand Down Expand Up @@ -136,7 +136,7 @@ def solve(vrp, job, thread_proc = nil, &block)
@job = job
@previous_result = nil
relations = []
services = []
ortools_services = []
routes = []
services_activity_positions = { always_first: [], always_last: [], never_first: [], never_last: [] }
vrp.services.each_with_index{ |service, service_index|
Expand All @@ -161,7 +161,7 @@ def solve(vrp, job, thread_proc = nil, &block)
}

if service.activity
services << OrtoolsVrp::Service.new(
ortools_services << OrtoolsVrp::Service.new(
time_windows: service.activity.timewindows.collect{ |tw|
OrtoolsVrp::TimeWindow.new(start: tw.start, end: tw.end || 2147483647,
maximum_lateness: tw.maximum_lateness)
Expand Down Expand Up @@ -208,11 +208,12 @@ def solve(vrp, job, thread_proc = nil, &block)
alternative_index: 0
)

services = update_services_activity_positions(services, services_activity_positions, service.id,
service.activity.position, service_index, 0)
ortools_services =
update_services_activity_positions(ortools_services, services_activity_positions, service.id,
service.activity.position, service_index, 0)
elsif service.activities
service.activities.each_with_index{ |possible_activity, activity_index|
services << OrtoolsVrp::Service.new(
ortools_services << OrtoolsVrp::Service.new(
time_windows: possible_activity.timewindows.collect{ |tw|
OrtoolsVrp::TimeWindow.new(start: tw.start, end: tw.end || 2147483647,
maximum_lateness: tw.maximum_lateness)
Expand Down Expand Up @@ -257,8 +258,9 @@ def solve(vrp, job, thread_proc = nil, &block)
alternative_index: activity_index
)

services = update_services_activity_positions(services, services_activity_positions, service.id,
possible_activity.position, service_index, activity_index)
ortools_services =
update_services_activity_positions(ortools_services, services_activity_positions, service.id,
possible_activity.position, service_index, activity_index)
}
end
}
Expand All @@ -275,17 +277,17 @@ def solve(vrp, job, thread_proc = nil, &block)
}

vehicles = build_problem_vehicles(vrp, total_quantities)
build_problem_relations(vrp, services, relations)
build_problem_relations(vrp, ortools_services, relations)

vrp.routes.collect{ |route|
next if route.vehicle.nil? || route.mission_ids.empty?
next if route.vehicle.nil? || route.missions.empty?

service_ids = corresponding_mission_ids(services.collect(&:id), route.mission_ids)
next if service_ids.empty?
ortools_service_ids = corresponding_mission_ids(ortools_services, route.missions)
next if ortools_service_ids.empty?

routes << OrtoolsVrp::Route.new(
vehicle_id: route.vehicle.id.to_s,
service_ids: service_ids.map(&:to_s)
service_ids: ortools_service_ids.map(&:to_s)
)
}

Expand All @@ -308,7 +310,7 @@ def solve(vrp, job, thread_proc = nil, &block)

problem = OrtoolsVrp::Problem.new(
vehicles: vehicles,
services: services,
services: ortools_services,
matrices: matrices,
relations: relations,
routes: routes
Expand Down Expand Up @@ -710,8 +712,9 @@ def update_services_activity_positions(services, services_activity_positions,
}
end

def corresponding_mission_ids(available_ids, mission_ids)
mission_ids.collect{ |mission_id|
def corresponding_mission_ids(available_ortools_services, missions)
available_ids = available_ortools_services.map(&:id)
missions.map(&:id).collect{ |mission_id|
correct_id =
if available_ids.include?(mission_id)
mission_id
Expand Down
Loading
Loading